24
24
import java .util .regex .Pattern ;
25
25
import org .snakeyaml .engine .v2 .api .Load ;
26
26
import org .snakeyaml .engine .v2 .api .LoadSettings ;
27
+ import org .snakeyaml .engine .v2 .common .ScalarStyle ;
27
28
import org .snakeyaml .engine .v2 .constructor .StandardConstructor ;
29
+ import org .snakeyaml .engine .v2 .exceptions .ConstructorException ;
30
+ import org .snakeyaml .engine .v2 .exceptions .YamlEngineException ;
28
31
import org .snakeyaml .engine .v2 .nodes .MappingNode ;
29
- import org .yaml .snakeyaml .Yaml ;
32
+ import org .snakeyaml .engine .v2 .nodes .Node ;
33
+ import org .snakeyaml .engine .v2 .nodes .NodeTuple ;
34
+ import org .snakeyaml .engine .v2 .nodes .ScalarNode ;
35
+ import org .snakeyaml .engine .v2 .schema .CoreSchema ;
30
36
31
37
/**
32
38
* Configure {@link OpenTelemetrySdk} from YAML configuration files conforming to the schema in <a
@@ -127,7 +133,7 @@ static OpenTelemetryConfiguration parse(
127
133
128
134
// Visible for testing
129
135
static Object loadYaml (InputStream inputStream , Map <String , String > environmentVariables ) {
130
- LoadSettings settings = LoadSettings .builder ().build ();
136
+ LoadSettings settings = LoadSettings .builder ().setSchema ( new CoreSchema ()). build ();
131
137
Load yaml = new Load (settings , new EnvSubstitutionConstructor (settings , environmentVariables ));
132
138
return yaml .loadFromInputStream (inputStream );
133
139
}
@@ -145,52 +151,94 @@ static Object loadYaml(InputStream inputStream, Map<String, String> environmentV
145
151
*/
146
152
private static final class EnvSubstitutionConstructor extends StandardConstructor {
147
153
148
- // Yaml is not thread safe but this instance is always used on the same thread
149
- private final Yaml yaml = new Yaml () ;
154
+ // Load is not thread safe but this instance is always used on the same thread
155
+ private final Load load ;
150
156
private final Map <String , String > environmentVariables ;
151
157
152
158
private EnvSubstitutionConstructor (
153
159
LoadSettings loadSettings , Map <String , String > environmentVariables ) {
154
160
super (loadSettings );
161
+ load = new Load (loadSettings );
155
162
this .environmentVariables = environmentVariables ;
156
163
}
157
164
165
+ /**
166
+ * Implementation is same as {@link
167
+ * org.snakeyaml.engine.v2.constructor.BaseConstructor#constructMapping(MappingNode)} except we
168
+ * override the resolution of values with our custom {@link #constructValueObject(Node)}, which
169
+ * performs environment variable substitution.
170
+ */
158
171
@ Override
172
+ @ SuppressWarnings ({"ReturnValueIgnored" , "CatchingUnchecked" })
159
173
protected Map <Object , Object > constructMapping (MappingNode node ) {
160
- // First call the super to construct mapping from MappingNode as usual
161
- Map <Object , Object > result = super .constructMapping (node );
162
-
163
- // Iterate through the map entries, and:
164
- // 1. Identify entries which are scalar strings eligible for environment variable substitution
165
- // 2. Apply environment variable substitution
166
- // 3. Re-parse substituted value so it has correct type (i.e. yaml.load(newVal))
167
- for (Map .Entry <Object , Object > entry : result .entrySet ()) {
168
- Object value = entry .getValue ();
169
- if (!(value instanceof String )) {
170
- continue ;
174
+ Map <Object , Object > mapping = settings .getDefaultMap ().apply (node .getValue ().size ());
175
+ List <NodeTuple > nodeValue = node .getValue ();
176
+ for (NodeTuple tuple : nodeValue ) {
177
+ Node keyNode = tuple .getKeyNode ();
178
+ Object key = constructObject (keyNode );
179
+ if (key != null ) {
180
+ try {
181
+ key .hashCode (); // check circular dependencies
182
+ } catch (Exception e ) {
183
+ throw new ConstructorException (
184
+ "while constructing a mapping" ,
185
+ node .getStartMark (),
186
+ "found unacceptable key " + key ,
187
+ tuple .getKeyNode ().getStartMark (),
188
+ e );
189
+ }
171
190
}
172
-
173
- String val = (String ) value ;
174
- Matcher matcher = ENV_VARIABLE_REFERENCE .matcher (val );
175
- if (!matcher .find ()) {
176
- continue ;
191
+ Node valueNode = tuple .getValueNode ();
192
+ Object value = constructValueObject (valueNode );
193
+ if (keyNode .isRecursive ()) {
194
+ if (settings .getAllowRecursiveKeys ()) {
195
+ postponeMapFilling (mapping , key , value );
196
+ } else {
197
+ throw new YamlEngineException (
198
+ "Recursive key for mapping is detected but it is not configured to be allowed." );
199
+ }
200
+ } else {
201
+ mapping .put (key , value );
177
202
}
203
+ }
178
204
179
- int offset = 0 ;
180
- StringBuilder newVal = new StringBuilder ();
181
- do {
182
- MatchResult matchResult = matcher .toMatchResult ();
183
- String replacement = environmentVariables .getOrDefault (matcher .group (1 ), "" );
184
- newVal .append (val , offset , matchResult .start ()).append (replacement );
185
- offset = matchResult .end ();
186
- } while (matcher .find ());
187
- if (offset != val .length ()) {
188
- newVal .append (val , offset , val .length ());
189
- }
190
- entry .setValue (yaml .load (newVal .toString ()));
205
+ return mapping ;
206
+ }
207
+
208
+ private Object constructValueObject (Node node ) {
209
+ Object value = constructObject (node );
210
+ if (!(node instanceof ScalarNode )) {
211
+ return value ;
212
+ }
213
+ if (!(value instanceof String )) {
214
+ return value ;
215
+ }
216
+
217
+ String val = (String ) value ;
218
+ Matcher matcher = ENV_VARIABLE_REFERENCE .matcher (val );
219
+ if (!matcher .find ()) {
220
+ return value ;
191
221
}
192
222
193
- return result ;
223
+ int offset = 0 ;
224
+ StringBuilder newVal = new StringBuilder ();
225
+ ScalarStyle scalarStyle = ((ScalarNode ) node ).getScalarStyle ();
226
+ do {
227
+ MatchResult matchResult = matcher .toMatchResult ();
228
+ String replacement = environmentVariables .getOrDefault (matcher .group (1 ), "" );
229
+ newVal .append (val , offset , matchResult .start ()).append (replacement );
230
+ offset = matchResult .end ();
231
+ } while (matcher .find ());
232
+ if (offset != val .length ()) {
233
+ newVal .append (val , offset , val .length ());
234
+ }
235
+ // If the value was double quoted, retain the double quotes so we don't change a value
236
+ // intended to be a string to a different type after environment variable substitution
237
+ if (scalarStyle == ScalarStyle .DOUBLE_QUOTED ) {
238
+ newVal .insert (0 , "\" " );
239
+ newVal .append ("\" " );
240
+ }
241
+ return load .loadFromString (newVal .toString ());
194
242
}
195
243
}
196
244
}
0 commit comments