1818
1919package org .apache .flink .statefun .flink .core .jsonmodule ;
2020
21- import static org .apache .flink .statefun .flink .core .spi .ExtensionResolverAccessor .getExtensionResolver ;
22-
23- import java .util .List ;
24- import java .util .Map ;
25- import java .util .Objects ;
26- import java .util .stream .Collectors ;
27- import java .util .stream .StreamSupport ;
21+ import org .apache .commons .lang3 .NotImplementedException ;
22+ import org .apache .flink .api .java .utils .ParameterTool ;
2823import org .apache .flink .shaded .jackson2 .com .fasterxml .jackson .databind .JsonNode ;
24+ import org .apache .flink .shaded .jackson2 .com .fasterxml .jackson .databind .node .*;
2925import org .apache .flink .statefun .extensions .ComponentBinder ;
3026import org .apache .flink .statefun .extensions .ComponentJsonObject ;
3127import org .apache .flink .statefun .flink .core .spi .ExtensionResolver ;
3228import org .apache .flink .statefun .sdk .spi .StatefulFunctionModule ;
29+ import org .slf4j .Logger ;
30+ import org .slf4j .LoggerFactory ;
3331
34- public final class RemoteModule implements StatefulFunctionModule {
32+ import java .util .*;
33+ import java .util .function .Function ;
34+ import java .util .regex .Matcher ;
35+ import java .util .regex .Pattern ;
36+ import java .util .stream .Collectors ;
37+ import java .util .stream .Stream ;
38+ import java .util .stream .StreamSupport ;
3539
40+ import static org .apache .flink .statefun .flink .core .spi .ExtensionResolverAccessor .getExtensionResolver ;
41+
42+ public final class RemoteModule implements StatefulFunctionModule {
43+ private static final Logger LOG = LoggerFactory .getLogger (RemoteModule .class );
44+ private static final Pattern PLACEHOLDER_REGEX = Pattern .compile ("\\ $\\ {(.*?)\\ }" );
3645 private final List <JsonNode > componentNodes ;
3746
3847 RemoteModule (List <JsonNode > componentNodes ) {
@@ -41,8 +50,16 @@ public final class RemoteModule implements StatefulFunctionModule {
4150
4251 @ Override
4352 public void configure (Map <String , String > globalConfiguration , Binder moduleBinder ) {
53+ Map <String , String > systemPropsThenEnvVarsThenGlobalConfig =
54+ ParameterTool .fromSystemProperties ()
55+ .mergeWith (
56+ ParameterTool .fromMap (System .getenv ())
57+ .mergeWith (ParameterTool .fromMap (globalConfiguration )))
58+ .toMap ();
4459 parseComponentNodes (componentNodes )
45- .forEach (component -> bindComponent (component , moduleBinder ));
60+ .forEach (
61+ component ->
62+ bindComponent (component , moduleBinder , systemPropsThenEnvVarsThenGlobalConfig ));
4663 }
4764
4865 private static List <ComponentJsonObject > parseComponentNodes (
@@ -53,10 +70,97 @@ private static List<ComponentJsonObject> parseComponentNodes(
5370 .collect (Collectors .toList ());
5471 }
5572
56- private static void bindComponent (ComponentJsonObject component , Binder moduleBinder ) {
73+ private static void bindComponent (
74+ ComponentJsonObject component , Binder moduleBinder , Map <String , String > configuration ) {
75+
76+ JsonNode resolvedSpec = valueResolutionFunction (configuration ).apply (component .specJsonNode ());
77+ ComponentJsonObject resolvedComponent =
78+ new ComponentJsonObject (component .get (), resolvedSpec );
79+
5780 final ExtensionResolver extensionResolver = getExtensionResolver (moduleBinder );
5881 final ComponentBinder componentBinder =
59- extensionResolver .resolveExtension (component .binderTypename (), ComponentBinder .class );
60- componentBinder .bind (component , moduleBinder );
82+ extensionResolver .resolveExtension (resolvedComponent .binderTypename (), ComponentBinder .class );
83+ componentBinder .bind (resolvedComponent , moduleBinder );
84+ }
85+
86+ private static Function <JsonNode , JsonNode > valueResolutionFunction (Map <String , String > config ) {
87+ return value -> {
88+ if (value .isObject ()) {
89+ return resolveObject ((ObjectNode ) value , config );
90+ } else if (value .isArray ()) {
91+ return resolveArray ((ArrayNode ) value , config );
92+ } else if (value .isValueNode ()) {
93+ return resolveValueNode ((ValueNode ) value , config );
94+ }
95+
96+ LOG .warn (
97+ "Unrecognised type (not in: object, array, value). Skipping ${placeholder} resolution for that node." );
98+ return value ;
99+ };
100+ }
101+
102+ private static Function <Map .Entry <String , JsonNode >, AbstractMap .SimpleEntry <String , JsonNode >>
103+ keyValueResolutionFunction (Map <String , String > config ) {
104+ return fieldNameValuePair ->
105+ new AbstractMap .SimpleEntry <>(
106+ fieldNameValuePair .getKey (),
107+ valueResolutionFunction (config ).apply (fieldNameValuePair .getValue ()));
108+ }
109+
110+ private static ValueNode resolveValueNode (ValueNode node , Map <String , String > config ) {
111+ StringBuffer stringBuffer = new StringBuffer ();
112+ Matcher placeholderMatcher = PLACEHOLDER_REGEX .matcher (node .asText ());
113+ boolean placeholderReplaced = false ;
114+
115+ while (placeholderMatcher .find ()) {
116+ if (config .containsKey (placeholderMatcher .group (1 ))) {
117+ placeholderMatcher .appendReplacement (stringBuffer , config .get (placeholderMatcher .group (1 )));
118+ placeholderReplaced = true ;
119+ }
120+ }
121+
122+ if (placeholderReplaced ) {
123+ placeholderMatcher .appendTail (stringBuffer );
124+ return new TextNode (stringBuffer .toString ());
125+ }
126+
127+ return node ;
128+ }
129+
130+ private static ObjectNode resolveObject (ObjectNode node , Map <String , String > config ) {
131+ return getFieldStream (node )
132+ .map (keyValueResolutionFunction (config ))
133+ .reduce (
134+ new ObjectNode (JsonNodeFactory .instance ),
135+ (accumulatedObjectNode , resolvedFieldNameValueTuple ) -> {
136+ accumulatedObjectNode .put (
137+ resolvedFieldNameValueTuple .getKey (), resolvedFieldNameValueTuple .getValue ());
138+ return accumulatedObjectNode ;
139+ },
140+ (objectNode1 , objectNode2 ) -> {
141+ throw new NotImplementedException ("This reduce is not used with parallel streams" );
142+ });
143+ }
144+
145+ private static ArrayNode resolveArray (ArrayNode node , Map <String , String > config ) {
146+ return getElementStream (node )
147+ .map (valueResolutionFunction (config ))
148+ .reduce (
149+ new ArrayNode (JsonNodeFactory .instance ),
150+ (accumulatedArrayNode , resolvedValue ) -> {
151+ accumulatedArrayNode .add (resolvedValue );
152+ return accumulatedArrayNode ;
153+ },
154+ (arrayNode1 , arrayNode2 ) -> {
155+ throw new NotImplementedException ("This reduce is not used with parallel streams" );
156+ });
157+ }
158+
159+ private static Stream <Map .Entry <String , JsonNode >> getFieldStream (ObjectNode node ) {
160+ return StreamSupport .stream (Spliterators .spliteratorUnknownSize (node .fields (), 0 ), false );
161+ }
162+
163+ private static Stream <JsonNode > getElementStream (ArrayNode node ) {
164+ return StreamSupport .stream (Spliterators .spliteratorUnknownSize (node .elements (), 0 ), false );
61165 }
62166}
0 commit comments