Skip to content

Commit 0a830a7

Browse files
authored
Unhelpful error message when a required parameter is not configured (#161)
* Parameter name in error messages when parameter is missing * Trying new approach * New approach * Fixing test errors * Only add not null values * undo changes * Refactoring * Comments * Removed default value setter * Use default value on ressource parameter with value and default value * Reduced number of loops for parsing parameter set * Changes requested in code review.
1 parent bdbaf20 commit 0a830a7

File tree

6 files changed

+79
-113
lines changed

6 files changed

+79
-113
lines changed

ojdbc-provider-common/src/main/java/oracle/jdbc/provider/parameter/ParameterSetBuilder.java

+8-4
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,17 @@
4444
public interface ParameterSetBuilder {
4545

4646
/**
47-
* Adds a parameter with a given name and value.
47+
* Adds a parameter with a given name and value. If the value is null, the
48+
* ParameterSet will behave as if no value has been set for the parameter.
49+
* Calling this method with a null value allows the ParameterSet to create
50+
* error messages that identify the {@code name} of missing parameters.
4851
*
4952
* @param <T> The type of value that is assigned to the parameter
50-
* @param name The parameter name
53+
* @param name The parameter name. Not null.
5154
* @param parameter The parameter that is assigned with the {@code name} and
52-
* {@code value} in this set
53-
* @param value The value
55+
* {@code value} in this set. Not null.
56+
* @param value The value, or null if the parameter is not configured with a
57+
* value.
5458
* @return A reference to this object
5559
*/
5660
<T> ParameterSetBuilder add(String name, Parameter<T> parameter, T value);

ojdbc-provider-common/src/main/java/oracle/jdbc/provider/parameter/ParameterSetBuilderImpl.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ final class ParameterSetBuilderImpl implements ParameterSetBuilder {
5252

5353
@Override
5454
public <T> ParameterSetBuilder add(String name, Parameter<T> parameter, T value) {
55-
parameterValues.put(parameter, value);
55+
if (value != null) {
56+
parameterValues.put(parameter, value);
57+
}
5658
parameterNames.put(parameter, name);
5759
return this;
5860
}

ojdbc-provider-common/src/main/java/oracle/jdbc/provider/parameter/ParameterSetParser.java

+5-47
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,11 @@ <T> Builder addParameter(
189189
* the parsed input includes an assignment such as "x=0", then "0" is input
190190
* to the {@code valueSetter}.
191191
* </p><p>
192+
* The {@code valueSetter} must be able to handle a null input. A null input will occur
193+
* when no value is set for the parameter. The {@code valueSetter} may simply pass
194+
* the null value onto the {@code ParameterSetBuilder}, or it may pass in a default
195+
* value.
196+
* </p><p>
192197
* This method is designed for cases where a single parameter in text format
193198
* may map to multiple {@link Parameter} objects. The {@code valueSetter}
194199
* function can perform multiple calls to set each parameter, as in this
@@ -212,52 +217,5 @@ <T> Builder addParameter(
212217
Builder addParameter(
213218
String name, BiConsumer<String, ParameterSetBuilder> valueSetter);
214219

215-
/**
216-
* <p>
217-
* Adds a parameter that a parser identifies by a given
218-
* {@code name}, and invokes a {@code defaultValueSetter} to assign a value
219-
* if the name is not present, or invokes a {@code valueSetter} function for
220-
* assigning the value if the name is present. The input to the
221-
* {@code defaultValueSetter} function is a {@link ParameterSetBuilder} that
222-
* builds the parsed {@link ParameterSet}. The input to the
223-
* {@code valueSetter} is the value as it appears in text form, and a
224-
* {@link ParameterSetBuilder} that builds the parsed {@link ParameterSet}. If
225-
* the name passed to this method is "x", and the parsed input includes an
226-
* assignment such as "x=0", then "0" is input to the {@code valueSetter}.
227-
* </p><p>
228-
* This method is designed for cases where a single parameter in text format
229-
* may map to multiple {@link Parameter} objects. The
230-
* {@code defaultValueSetter} and {@code valueSetter} functions can perform
231-
* multiple calls to set each parameter, as in this example:
232-
* </p>
233-
* <pre>{@code
234-
* builder.addParameter(
235-
* "coordinate",
236-
* (parameterSetBuilder) -> {
237-
* // Assign the default value of 0 to X, Y, and Z
238-
* parameterSetBuilder.add("coordinate", X, 0);
239-
* parameterSetBuilder.add("coordinate", Y, 0);
240-
* parameterSetBuilder.add("coordinate", Z, 0);
241-
* },
242-
* (value, parameterSetBuilder) -> {
243-
* // Split "x,y,z" formatted value
244-
* String[] xyz = value.split(",");
245-
* parameterSetBuilder.add("coordinate", X, xyz[0]);
246-
* parameterSetBuilder.add("coordinate", Y, xyz[1]);
247-
* parameterSetBuilder.add("coordinate", Z, xyz[2]);
248-
* });
249-
* }</pre>
250-
*
251-
* @param name Name of the parsed parameter. Not null.
252-
* @param defaultValueSetter Parses and sets the default value of
253-
* parameter(s). Not null.
254-
* @param valueSetter Parses and sets the value of parameter(s) from text
255-
* input. Not null.
256-
* @return This builder
257-
*/
258-
Builder addParameter(
259-
String name,
260-
Consumer<ParameterSetBuilder> defaultValueSetter,
261-
BiConsumer<String, ParameterSetBuilder> valueSetter);
262220
}
263221
}

ojdbc-provider-common/src/main/java/oracle/jdbc/provider/parameter/ParameterSetParserImpl.java

+30-39
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,21 @@
3838

3939
package oracle.jdbc.provider.parameter;
4040

41-
import java.util.Arrays;
4241
import java.util.HashMap;
4342
import java.util.Map;
4443
import java.util.TreeMap;
44+
import java.util.TreeSet;
45+
import java.util.Map.Entry;
46+
import java.util.Set;
4547
import java.util.function.BiConsumer;
46-
import java.util.function.Consumer;
4748
import java.util.function.Function;
49+
import java.util.stream.Collectors;
4850

4951
import static java.lang.String.format;
5052
import static java.util.Objects.requireNonNull;
5153

54+
import java.util.Arrays;
55+
5256
final class ParameterSetParserImpl implements ParameterSetParser {
5357

5458
/** Parameter value parsers mapped to a case insensitive parameter name */
@@ -65,14 +69,19 @@ public ParameterSet parseNamedValues(Map<String, String> namedValues) {
6569

6670
ParameterSetBuilder builder = ParameterSet.builder();
6771

68-
for (ParameterParser parameterParser : parameterParsers.values())
69-
parameterParser.setDefaultValue(builder);
72+
// Make a copy of the keyset containing lower case keys, from which we will
73+
// remove those present in the namedValues map
74+
Set<String> missingKeys = parameterParsers
75+
.keySet().stream().map(String::toLowerCase).collect(Collectors.toSet());
7076

7177
for (Map.Entry<String, String> namedValue : namedValues.entrySet()) {
7278

7379
String name = namedValue.getKey();
7480
ParameterParser parameterParser = parameterParsers.get(name);
7581

82+
// remove key from missing keys
83+
missingKeys.remove(name.toLowerCase());
84+
7685
if (parameterParser == null) {
7786
throw new IllegalArgumentException(format(
7887
"Unrecognized parameter name: \"%s\". Valid parameter names are: %s",
@@ -82,6 +91,11 @@ public ParameterSet parseNamedValues(Map<String, String> namedValues) {
8291
parameterParser.setValue(builder, namedValue.getValue());
8392
}
8493

94+
// Set value null to parameters that were not present in namedValues
95+
for (String missingKey : missingKeys) {
96+
parameterParsers.get(missingKey).setValue(builder, null);
97+
}
98+
8599
return builder.build();
86100
}
87101

@@ -106,7 +120,6 @@ public ParameterSetParser build() {
106120
public Builder addParameter(String name, Parameter<String> parameter) {
107121
addParameterParser(
108122
name,
109-
ParameterParser.SET_NO_DEFAULT,
110123
(value, builder) -> builder.add(name, parameter, value));
111124
return this;
112125
}
@@ -115,9 +128,9 @@ public Builder addParameter(String name, Parameter<String> parameter) {
115128
public Builder addParameter(
116129
String name, Parameter<String> parameter, String defaultValue) {
117130
addParameterParser(
118-
name,
119-
(builder) -> builder.add(name, parameter, defaultValue),
120-
(value, builder) -> builder.add(name, parameter, value));
131+
name,
132+
(value, builder) -> builder.add(name, parameter,
133+
value == null ? defaultValue : value));
121134
return this;
122135
}
123136

@@ -126,7 +139,6 @@ public <T> Builder addParameter(
126139
String name, Parameter<T> parameter, Function<String, T> valueParser) {
127140
addParameterParser(
128141
name,
129-
ParameterParser.SET_NO_DEFAULT,
130142
(value, builder) ->
131143
builder.add(name, parameter, valueParser.apply(value)));
132144
return this;
@@ -138,37 +150,29 @@ public <T> Builder addParameter(
138150
Function<String, T> valueParser) {
139151
addParameterParser(
140152
name,
141-
builder -> builder.add(name, parameter, defaultValue),
142-
(value, builder) ->
143-
builder.add(name, parameter, valueParser.apply(value)));
153+
(value, builder) -> {
154+
// If the value is null set the default value.
155+
if (value != null)
156+
builder.add(name, parameter, valueParser.apply(value));
157+
else
158+
builder.add(name, parameter, defaultValue);
159+
});
144160
return this;
145161
}
146162

147163
@Override
148164
public Builder addParameter(
149165
String name, BiConsumer<String, ParameterSetBuilder> valueSetter) {
150-
addParameterParser(
151-
name, ParameterParser.SET_NO_DEFAULT, valueSetter);
152-
return this;
153-
}
154-
155-
@Override
156-
public Builder addParameter(
157-
String name,
158-
Consumer<ParameterSetBuilder> defaultValueSetter,
159-
BiConsumer<String, ParameterSetBuilder> valueSetter) {
160-
161-
addParameterParser(name, defaultValueSetter, valueSetter);
166+
addParameterParser(name, valueSetter);
162167
return this;
163168
}
164169

165170
private void addParameterParser(
166171
String name,
167-
Consumer<ParameterSetBuilder> defaultValueSetter,
168172
BiConsumer<String, ParameterSetBuilder> valueSetter) {
169173

170174
ParameterParser parameterParser =
171-
new ParameterParser(defaultValueSetter, valueSetter);
175+
new ParameterParser(valueSetter);
172176
parameterParsers.put(name, parameterParser);
173177
}
174178

@@ -180,27 +184,14 @@ private void addParameterParser(
180184
*/
181185
private static final class ParameterParser {
182186

183-
/** A consumer that sets no default value for a parameter */
184-
private static final Consumer<ParameterSetBuilder> SET_NO_DEFAULT =
185-
builder -> { };
186-
187-
/** A consumer that sets a default value for a parameter */
188-
private final Consumer<ParameterSetBuilder> defaultValueSetter;
189-
190187
/** A consumer that parses and sets a value for a parameter */
191188
private final BiConsumer<String, ParameterSetBuilder> valueSetter;
192189

193190
private ParameterParser(
194-
Consumer<ParameterSetBuilder> defaultValueSetter,
195191
BiConsumer<String, ParameterSetBuilder> valueSetter) {
196-
this.defaultValueSetter = defaultValueSetter;
197192
this.valueSetter = valueSetter;
198193
}
199194

200-
void setDefaultValue(ParameterSetBuilder builder) {
201-
defaultValueSetter.accept(builder);
202-
}
203-
204195
void setValue(ParameterSetBuilder builder, String value) {
205196
valueSetter.accept(value, builder);
206197
}

ojdbc-provider-common/src/main/java/oracle/jdbc/provider/resource/ResourceParameter.java

+17-21
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
import oracle.jdbc.spi.OracleResourceProvider;
4444

4545
import java.util.function.BiConsumer;
46-
import java.util.function.Consumer;
4746
import java.util.function.Function;
4847

4948
/**
@@ -71,11 +70,6 @@ public final class ResourceParameter
7170
*/
7271
private final boolean isRequired;
7372

74-
/**
75-
* Configures a {@code ParameterSetBuilder} with a default value, if any.
76-
*/
77-
private final Consumer<ParameterSetBuilder> defaultValueSetter;
78-
7973
/**
8074
* Configures a {@code ParameterSetBuilder} with a value parsed from a
8175
* {@code String}.
@@ -111,10 +105,12 @@ public ResourceParameter(
111105
this(name, defaultValue,
112106
providerParameter.isRequired(),
113107
providerParameter.isSensitive(),
114-
defaultValue == null
115-
? builder -> { }
116-
: builder -> builder.add(name, providerParameter, defaultValue),
117-
(value, builder) -> builder.add(name, providerParameter, value));
108+
(value, builder) -> {
109+
if (value != null)
110+
builder.add(name, providerParameter, value);
111+
else
112+
builder.add(name, providerParameter, defaultValue);
113+
});
118114
}
119115

120116
/**
@@ -136,12 +132,17 @@ public <T> ResourceParameter(
136132
this(name, defaultValue,
137133
providerParameter.isRequired(),
138134
providerParameter.isSensitive(),
139-
defaultValue == null
140-
? builder -> { }
141-
: builder ->
142-
builder.add(name, providerParameter, parser.apply(defaultValue)),
143-
(value, builder) ->
144-
builder.add(name, providerParameter, parser.apply(value)));
135+
(value, builder) -> {
136+
if (value != null)
137+
builder.add(name, providerParameter, parser.apply(value));
138+
else
139+
// If no value is set, set default value or null. Default value needs
140+
// to be parsed, null should not be parsed.
141+
if (defaultValue != null)
142+
builder.add(name, providerParameter, parser.apply(defaultValue));
143+
else
144+
builder.add(name, providerParameter, null);
145+
});
145146
}
146147

147148
/**
@@ -157,20 +158,16 @@ public <T> ResourceParameter(
157158
* parameter, or {@code false} if not.
158159
* @param isSensitive {@code true} if value of the parameter is security
159160
* sensitive, or {@code false} if not.
160-
* @param defaultValueSetter Configures the default value of the parameter,
161-
* if any.
162161
* @param parser Parses the value of the parameter. Not null.
163162
*/
164163
public ResourceParameter(
165164
String name, String defaultValue, boolean isRequired,
166165
boolean isSensitive,
167-
Consumer<ParameterSetBuilder> defaultValueSetter,
168166
BiConsumer<String, ParameterSetBuilder> parser) {
169167
this.name = name;
170168
this.defaultValue = defaultValue;
171169
this.isRequired = isRequired;
172170
this.isSensitive = isSensitive;
173-
this.defaultValueSetter = defaultValueSetter;
174171
this.parser = parser;
175172
}
176173

@@ -201,7 +198,6 @@ public CharSequence defaultValue() {
201198
void configureParser(ParameterSetParser.Builder builder) {
202199
builder.addParameter(
203200
name,
204-
defaultValueSetter,
205201
parser);
206202
}
207203

ojdbc-provider-oci/src/test/java/oracle/jdbc/provider/oci/resource/DatabaseConnectionStringProviderTest.java

+16-1
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,11 @@
4747
import java.util.HashMap;
4848
import java.util.Map;
4949

50-
import static oracle.jdbc.provider.TestProperties.getOrAbort;
5150
import static oracle.jdbc.provider.resource.ResourceProviderTestUtil.createParameterValues;
5251
import static oracle.jdbc.provider.resource.ResourceProviderTestUtil.findProvider;
52+
import static org.junit.jupiter.api.Assertions.assertEquals;
5353
import static org.junit.jupiter.api.Assertions.assertNotNull;
54+
import static org.junit.jupiter.api.Assertions.assertThrows;
5455

5556
/** Verifies {@link DatabaseConnectionStringProvider} */
5657
public class DatabaseConnectionStringProviderTest {
@@ -77,6 +78,20 @@ public void test() {
7778
assertNotNull(descriptor);
7879
}
7980

81+
@Test
82+
public void missingOCIDTest() {
83+
Map<String, CharSequence> testParameters = new HashMap<>();
84+
testParameters.put("authenticationMethod", "config-file");
85+
86+
Map<Parameter, CharSequence> parameterValues =
87+
createParameterValues(PROVIDER, testParameters);
88+
IllegalStateException illegalStateException = assertThrows(IllegalStateException.class, () -> {
89+
PROVIDER.getConnectionString(parameterValues);
90+
});
91+
assertEquals("No value defined for parameter \"ocid\"",
92+
illegalStateException.getCause().getMessage(), "Wrong error message");
93+
}
94+
8095
@Test
8196
public void testWithConsumerGroup() {
8297
Map<String, CharSequence> testParameters = new HashMap<>();

0 commit comments

Comments
 (0)