22
33import com .google .inject .AbstractModule ;
44import com .google .inject .name .Names ;
5+ import java .util .Map ;
56import java .util .Properties ;
67import java .util .logging .Logger ;
8+ import java .util .regex .Matcher ;
9+ import java .util .regex .Pattern ;
710import org .jsmart .zerocode .core .di .module .CsvParserModule ;
811import org .jsmart .zerocode .core .di .module .GsonModule ;
912import org .jsmart .zerocode .core .di .module .HttpClientModule ;
3033import org .jsmart .zerocode .core .runner .ZeroCodeMultiStepsScenarioRunner ;
3134import org .jsmart .zerocode .core .runner .ZeroCodeMultiStepsScenarioRunnerImpl ;
3235
36+ import static org .jsmart .zerocode .core .utils .EnvUtils .getEnvValueString ;
3337import static org .jsmart .zerocode .core .utils .PropertiesProviderUtils .checkAndLoadOldProperties ;
3438import static org .jsmart .zerocode .core .utils .PropertiesProviderUtils .loadAbsoluteProperties ;
3539import static org .jsmart .zerocode .core .utils .SmartUtils .isValidAbsolutePath ;
3640
3741public class ApplicationMainModule extends AbstractModule {
3842 private static final Logger LOGGER = Logger .getLogger (ApplicationMainModule .class .getName ());
3943
44+ private static final Pattern ENV_PLACEHOLDER_PATTERN = Pattern .compile ("\\ $\\ {([^}]+)\\ }" );
45+
4046 private final String serverEnv ;
4147
4248 public ApplicationMainModule (String serverEnv ) {
@@ -79,7 +85,7 @@ public Properties getProperties(String host) {
7985 final Properties properties = new Properties ();
8086
8187 if (isValidAbsolutePath (host )){
82- return loadAbsoluteProperties (host , properties );
88+ return resolveEnvPlaceholders ( loadAbsoluteProperties (host , properties ) );
8389 }
8490
8591 try {
@@ -96,7 +102,54 @@ public Properties getProperties(String host) {
96102 throw new RuntimeException ("could not read the target-env properties file --" + host + "-- from the classpath." );
97103 }
98104
105+ return resolveEnvPlaceholders (properties );
106+ }
107+
108+ /**
109+ * Resolves any {@code ${name}} placeholders found in the loaded property values against
110+ * system properties and OS environment variables (in that order, via
111+ * {@link org.jsmart.zerocode.core.utils.EnvUtils#getEnvValueString(String)}).
112+ * <p>
113+ * This lets a target-env properties file reference values supplied on the command line, e.g.
114+ * <pre>
115+ * db.username=${db.username}
116+ * </pre>
117+ * run with {@code -Ddb.username=alice} so the injected value becomes {@code alice}.
118+ * <p>
119+ * A placeholder whose name resolves to no system property or env var is left untouched, so
120+ * files that legitimately contain {@code ${...}} for downstream tooling are not corrupted.
121+ * Values without any placeholder are returned unchanged.
122+ */
123+ public Properties resolveEnvPlaceholders (Properties properties ) {
124+ if (properties == null ) {
125+ return null ;
126+ }
127+
128+ for (Map .Entry <Object , Object > entry : properties .entrySet ()) {
129+ Object value = entry .getValue ();
130+ if (value instanceof String ) {
131+ properties .setProperty ((String ) entry .getKey (), resolvePlaceholders ((String ) value ));
132+ }
133+ }
134+
99135 return properties ;
100136 }
101137
138+ private static String resolvePlaceholders (String value ) {
139+ Matcher matcher = ENV_PLACEHOLDER_PATTERN .matcher (value );
140+ StringBuffer resolved = new StringBuffer ();
141+
142+ while (matcher .find ()) {
143+ String placeholderName = matcher .group (1 );
144+ String replacement = getEnvValueString (placeholderName );
145+
146+ // Leave the placeholder literally in place when it cannot be resolved (backward compatible).
147+ String token = replacement != null ? replacement : matcher .group (0 );
148+ matcher .appendReplacement (resolved , Matcher .quoteReplacement (token ));
149+ }
150+ matcher .appendTail (resolved );
151+
152+ return resolved .toString ();
153+ }
154+
102155}
0 commit comments