77import java .nio .file .Files ;
88import java .nio .file .Path ;
99import java .nio .file .Paths ;
10+ import java .util .ArrayList ;
11+ import java .util .Arrays ;
1012import java .util .Collections ;
1113import java .util .HashMap ;
1214import java .util .LinkedHashMap ;
1315import java .util .LinkedList ;
1416import java .util .List ;
1517import java .util .Map ;
1618import java .util .Properties ;
19+ import java .util .concurrent .atomic .AtomicReference ;
20+ import java .util .function .BiFunction ;
21+ import java .util .function .Consumer ;
1722import java .util .stream .Stream ;
1823
1924public class Env extends Value .Object {
2025
26+ public static class PropertySource {
27+ private String name ;
28+
29+ private Map <String , String > properties ;
30+
31+ public PropertySource (String name , Map properties ) {
32+ this .name = name ;
33+ this .properties = properties ;
34+ }
35+
36+ public String name () {
37+ return name ;
38+ }
39+
40+ public Map <String , String > properties () {
41+ return properties ;
42+ }
43+
44+ @ Override public String toString () {
45+ return name ;
46+ }
47+ }
48+
2149 private final String name ;
2250
23- private final Map < String , String > properties = new LinkedHashMap <>();
51+ private final List < PropertySource > sources = new ArrayList <>();
2452
2553 public Env (final String name ) {
2654 this .name = name ;
@@ -30,9 +58,9 @@ public Env(final String name) {
3058 Value result = super .get (name );
3159 if (result .isMissing ()) {
3260 // fallback to full path access
33- String value = properties . get ( name );
61+ String value = fromSource ( sources , name , null );
3462 if (value != null ) {
35- result = Value .value (name , value );
63+ return Value .value (name , value );
3664 }
3765 }
3866 return result ;
@@ -42,9 +70,21 @@ public Env(final String name) {
4270 return name ;
4371 }
4472
45- public static Map <String , String > parse (String ... args ) {
73+ @ Override public String toString () {
74+ StringBuilder buff = new StringBuilder ();
75+ buff .append (name ).append ("\n " );
76+ String indent = " " ;
77+ for (int i = sources .size () - 1 ; i >= 0 ; i --) {
78+ String sname = sources .get (i ).name ;
79+ buff .append (indent ).append ("::" ).append (sname ).append ("\n " );
80+ indent += " " ;
81+ }
82+ return buff .toString ().trim ();
83+ }
84+
85+ public static PropertySource parse (String ... args ) {
4686 if (args == null || args .length == 0 ) {
47- return Collections .emptyMap ();
87+ return new PropertySource ( "args" , Collections .emptyMap () );
4888 }
4989 Map <String , String > conf = new HashMap <>();
5090 for (String arg : args ) {
@@ -56,25 +96,28 @@ public static Map<String, String> parse(String... args) {
5696 conf .putIfAbsent ("application.env" , arg );
5797 }
5898 }
59- return conf ;
99+ return new PropertySource ( "args" , conf ) ;
60100 }
61101
62- public static Map <String , String > load (ClassLoader loader , String filename ) {
63- return load (loader , filename , stream -> {
64- Map <String , String > result = new LinkedHashMap <>();
65- Properties properties = new Properties ();
66- properties .load (stream );
67- properties .forEach ((k , v ) -> result .put (k .toString (), v .toString ()));
68- return result ;
102+ public static PropertySource load (ClassLoader loader , String filename ) {
103+ return load (loader , filename , (name , stream ) -> {
104+ try {
105+ Properties properties = new Properties ();
106+ properties .load (stream );
107+ return new PropertySource (name , properties );
108+ } catch (IOException x ) {
109+ throw Throwing .sneakyThrow (x );
110+ }
69111 });
70112 }
71113
72- public static Map <String , String > load (ClassLoader loader , String filename ,
73- Throwing .Function <InputStream , Map <String , String >> propertyLoader ) {
74- InputStream stream = findProperties (loader , filename );
114+ public static PropertySource load (ClassLoader loader , String filename ,
115+ BiFunction <String , InputStream , PropertySource > propertyLoader ) {
116+ AtomicReference <String > fullpath = new AtomicReference <>();
117+ InputStream stream = findProperties (loader , filename , fullpath ::set );
75118 if (stream != null ) {
76119 try {
77- return propertyLoader .apply (stream );
120+ return propertyLoader .apply (fullpath . get (), stream );
78121 } finally {
79122 try {
80123 stream .close ();
@@ -83,68 +126,90 @@ public static Map<String, String> load(ClassLoader loader, String filename,
83126 }
84127 }
85128 }
86- return Collections .emptyMap ();
129+ return new PropertySource (filename , Collections .emptyMap ());
130+ }
131+
132+ public static PropertySource systemProperties () {
133+ return new PropertySource ("systemProperties" , System .getProperties ());
134+ }
135+
136+ public static PropertySource systemEnv () {
137+ return new PropertySource ("systemEnv" , System .getenv ());
87138 }
88139
89140 public static Env defaultEnvironment (String ... args ) {
90141 ClassLoader classLoader = Env .class .getClassLoader ();
91- Map < String , String > argMap = parse (args );
92- String env = argMap .getOrDefault ("application.env" , "dev" );
93- List <Map > sources = new LinkedList <>();
142+ PropertySource argMap = parse (args );
143+ String env = argMap .properties . getOrDefault ("application.env" , "dev" );
144+ List <PropertySource > sources = new LinkedList <>();
94145 Stream
95146 .of ("application.properties" , "application." + env + ".properties" )
96147 .map (filename -> load (classLoader , filename ))
97- .filter (props -> props .size () > 0 )
148+ .filter (props -> props .properties (). size () > 0 )
98149 .forEach (sources ::add );
99- sources .add (System .getProperties ());
100- sources .add (System .getenv ());
101- if (argMap .size () > 0 ) {
102- sources .add (argMap );
103- }
104- return build (sources .toArray (new Map [sources .size ()]));
150+ sources .add (systemProperties ());
151+ sources .add (systemEnv ());
152+ sources .add (argMap );
153+ return build (sources .toArray (new PropertySource [sources .size ()]));
105154 }
106155
107- public static Env build (Map <String , String >... sources ) {
108- Env env = new Env (environmentName (sources ));
109- for (Map <String , String > source : sources ) {
110- env .properties .putAll (source );
156+ public static Env build (PropertySource ... sources ) {
157+ Env env = new Env (fromSource (Arrays .asList (sources ), "application.env" , "dev" ).toLowerCase ());
158+ /** Merge to build values: */
159+ Map <String , String > merged = new LinkedHashMap <>();
160+ for (PropertySource source : sources ) {
161+ merged .putAll (source .properties );
111162 }
112- env .properties .forEach ((k , v ) -> {
113- try {
114- String [] values = v .split ("," );
115- for (String value : values ) {
116- env .put (k , value .trim ());
163+ List <String > skip = Arrays .asList ("java." , "sun." );
164+ merged .forEach ((k , v ) -> {
165+ if (skip .stream ().anyMatch (it -> !it .startsWith (k ))) {
166+ // ignore java. and sun. properties (too many, not worth it)
167+ try {
168+ String [] values = v .split ("," );
169+ for (String value : values ) {
170+ env .put (k , value .trim ());
171+ }
172+ } catch (ClassCastException x ) {
173+ // Caused by system properties like:
174+ // java.vendor.url vs java.vendor.url.bug
117175 }
118- } catch (ClassCastException x ) {
119- // Caused by system properties like:
120- // java.vendor.url vs java.vendor.url.bug
121176 }
122177 });
178+ // Only worth it property from java.*
179+ env .put ("java.io.tmpdir" , System .getProperty ("java.io.tmpdir" ));
180+
181+ merged .clear ();
182+
183+ /** Add sources */
184+ Stream .of (sources ).forEach (env .sources ::add );
123185 return env ;
124186 }
125187
126- private static InputStream findProperties (ClassLoader loader , String filename ) {
188+ private static InputStream findProperties (ClassLoader loader , String filename ,
189+ Consumer <String > fullpath ) {
127190 try {
128191 String envdir = System .getProperty ("env.dir" , "conf" );
129192 Path file = Paths .get (System .getProperty ("user.dir" ), envdir , filename ).toAbsolutePath ();
130193 if (Files .exists (file )) {
194+ fullpath .accept (file .toString ());
131195 return new FileInputStream (file .toFile ());
132196 }
133- return loader .getResourceAsStream (envdir + "/" + filename );
197+ String resource = envdir + "/" + filename ;
198+ fullpath .accept (resource );
199+ return loader .getResourceAsStream (resource );
134200 } catch (IOException x ) {
135201 throw Throwing .sneakyThrow (x );
136202 }
137203 }
138204
139- private static String environmentName ( Map < String , String >[] sources ) {
140- for (int i = sources .length - 1 ; i >= 0 ; i --) {
141- Map < String , String > properties = sources [ i ] ;
142- String name = properties .get ("application.env" );
143- if (name != null ) {
144- return name . toLowerCase () ;
205+ private static String fromSource ( List < PropertySource > sources , String name , String defaults ) {
206+ for (int i = sources .size () - 1 ; i >= 0 ; i --) {
207+ PropertySource source = sources . get ( i ) ;
208+ String value = source . properties () .get (name );
209+ if (value != null ) {
210+ return value ;
145211 }
146212 }
147- return "dev" ;
213+ return defaults ;
148214 }
149-
150215}
0 commit comments