77import org .slf4j .Logger ;
88import org .slf4j .LoggerFactory ;
99
10+ import java .io .File ;
1011import java .io .IOException ;
1112import java .net .URL ;
13+ import java .nio .file .Path ;
14+ import java .nio .file .Paths ;
1215import java .util .ArrayList ;
1316import java .util .List ;
1417import restx .common .ConfigElement ;
1518import restx .common .RestxConfig ;
1619import restx .common .StdRestxConfig ;
1720
21+ import static com .google .common .io .Files .asCharSource ;
22+
1823/**
1924 * User: xavierhanin
2025 * Date: 9/24/13
@@ -34,7 +39,11 @@ public ConfigLoader(Optional<String> env) {
3439 *
3540 * The name of the resource provided must not contain the extension, which must be .properties
3641 *
37- * The loader will first try to load an env specific resource named [resource].[env].properties.
42+ * The loader will first try to load config from a file which location is provided by a system
43+ * property, the name of the property being the name returned by #locationKeyForResource.
44+ *
45+ * Then it will try to load an env specific resource named [resource].[env].properties.
46+ *
3847 * Then it will load a resource named [resource].properties.
3948 *
4049 * @param resource the path of the resource to load, without extension.
@@ -46,14 +55,107 @@ public ConfigSupplier fromResource(final String resource) {
4655 @ Override
4756 public RestxConfig get () {
4857 List <ConfigElement > elements = new ArrayList <>();
49- loadResourceInto (resource + "." + env + ".properties" , elements );
50- loadResourceInto (resource + ".properties" , elements );
58+
59+ loadAllFromResource (elements , resource );
60+
61+ return StdRestxConfig .of (elements );
62+ }
63+ };
64+ }
65+
66+ /**
67+ * Provides a ConfigSupplier loading config from a file.
68+ *
69+ * The loader will first try to load an env specific file named [path].[env].properties.
70+ *
71+ * Then it will try to load a file named [path].properties.
72+ *
73+ * Then it will try to load a file named [path] (without extension).
74+ *
75+ * @param path the path of the file to load config from.
76+ *
77+ * @return a ConfigSupplier ready to load corresponding file.
78+ */
79+ public ConfigSupplier fromFile (final String path ) {
80+ return new ConfigSupplier () {
81+ @ Override
82+ public RestxConfig get () {
83+ List <ConfigElement > elements = new ArrayList <>();
84+
85+ loadAllFromFile (elements , path );
86+
5187 return StdRestxConfig .of (elements );
5288 }
5389 };
5490 }
5591
56- private void loadResourceInto (String name , List <ConfigElement > elements ) {
92+ /**
93+ * Gives the name of the system property that can be used to provide a file location
94+ * to load to override settings for a particular resource.
95+ *
96+ * By defaut the name is equal to the resource name where slashes `/` are replaced by dots `.`,
97+ * with `.location` suffix.
98+ *
99+ * Eg.
100+ * `myapp/settings` becomes `myapp.settings.location`
101+ *
102+ * @param resource the resource for which the system property name should be provided.
103+ *
104+ * @return the system property name.
105+ */
106+ public String locationKeyForResource (String resource ) {
107+ return resource .replace ('/' , '.' ) + ".location" ;
108+ }
109+
110+ protected void loadAllFromResource (List <ConfigElement > elements , String resource ) {
111+ String locationKey = locationKeyForResource (resource );
112+ String location = System .getProperty (locationKey );
113+ if (location != null ) {
114+ Path path = Paths .get (location ).toAbsolutePath ();
115+ logger .info ("loading {} settings from {}" , resource , path );
116+ loadFileInto (path , elements );
117+ } else {
118+ logger .debug ("system property `{}` is not set, no file to load to override settings from {}" ,
119+ locationKey , resource );
120+ }
121+
122+ loadResourceInto (resource + "." + env + ".properties" , elements );
123+ loadResourceInto (resource + ".properties" , elements );
124+ }
125+
126+ protected void loadAllFromFile (List <ConfigElement > elements , String path ) {
127+ if (!path .endsWith (".properties" )) {
128+ loadFileInto (Paths .get (path + "." + env + ".properties" ), elements );
129+ loadFileInto (Paths .get (path + ".properties" ), elements );
130+ }
131+
132+ loadFileInto (Paths .get (path ), elements );
133+ }
134+
135+ protected void loadFileInto (Path path , List <ConfigElement > elements ) {
136+ File file = path .toFile ().getAbsoluteFile ();
137+ if (!file .exists ()) {
138+ logger .debug ("no settings loaded from {}: file not available" , file );
139+ return ;
140+ }
141+
142+ if (!file .isFile ()) {
143+ logger .warn ("no settings loaded from {}: this is not a file" , file );
144+ return ;
145+ }
146+
147+ try {
148+ Iterable <ConfigElement > loadedElements = StdRestxConfig .parse ("file://" + file ,
149+ asCharSource (file , Charsets .UTF_8 )).elements ();
150+ Iterables .addAll (elements , loadedElements );
151+ logger .debug ("loaded {} elements from {}" , Iterables .size (loadedElements ), file );
152+ } catch (IOException e ) {
153+ logger .warn ("can't load " + file + ": " + e .getMessage (), e );
154+ }
155+
156+ }
157+
158+ protected void loadResourceInto (String name , List <ConfigElement > elements ) {
57159 URL r ;
58160 r = Thread .currentThread ().getContextClassLoader ().getResource (name );
59161 if (r != null ) {
0 commit comments