Skip to content

Commit da3c1c5

Browse files
committed
first thoughts on type-based configuration
Signed-off-by: Sebastian Daschner <mail@sebastian-daschner.com>
1 parent f15f4ee commit da3c1c5

File tree

3 files changed

+628
-0
lines changed

3 files changed

+628
-0
lines changed
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
/*
2+
* Copyright (c) 2016-2018 Contributors to the Eclipse Foundation
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* You may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
package javax.config;
21+
22+
import javax.config.inject.ConfigProperty;
23+
import javax.enterprise.util.Nonbinding;
24+
import java.lang.annotation.Retention;
25+
import java.lang.annotation.Target;
26+
import java.util.concurrent.TimeUnit;
27+
28+
import static java.lang.annotation.ElementType.TYPE;
29+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
30+
31+
/**
32+
* Specifies a configuration type as composed configuration.
33+
* Composed configuration types comprise multiple, potentially hierarchical, configuration values.
34+
*
35+
* <h2>Examples</h2>
36+
*
37+
* <h3>Composed, coherent configuration</h3>
38+
* <p>
39+
* The following example defines a coherent server socket configuration.
40+
*
41+
* <pre>
42+
* &#064;ComposedConfig
43+
* public class SocketConfig {
44+
*
45+
* &#064;ConfigProperty(name = "name")
46+
* private String name;
47+
*
48+
* &#064;ConfigProperty(name = "protocol", defaultValue = "http")
49+
* private String protocolName;
50+
*
51+
* &#064;ConfigProperty(name = "port")
52+
* private int port;
53+
*
54+
* // getters & setters
55+
* }
56+
* </pre>
57+
* <p>
58+
* The {@code SocketConfig} configuration can be retrieved like any other configured value, by programmatic lookup, or dependency injection:
59+
*
60+
* <pre>
61+
* public class SomeBean {
62+
*
63+
* &#064;Inject
64+
* &#064;ConfigProperty(name = "server.socket")
65+
* private SocketConfig socketConfig;
66+
*
67+
* }
68+
* </pre>
69+
* <p>
70+
* The example will resolve the configuration values as follows, provided by the corresponding property keys:
71+
*
72+
* <pre>
73+
* server.socket.name
74+
* server.socket.protocol
75+
* server.socket.port
76+
* </pre>
77+
*
78+
* <h3>Implicit property resolution</h3>
79+
* <p>
80+
* It's possible to omit the individual {@link ConfigProperty} annotations on the fields of the composed type.
81+
* In this case the composed property keys are derived from the field names:
82+
* <p>
83+
*
84+
* <pre>
85+
* public class SomeBean {
86+
*
87+
* &#064;Inject
88+
* &#064;ConfigProperty(name = "server.socket")
89+
* private SocketConfig socketConfig;
90+
*
91+
* &#064;ComposedConfig
92+
* public static class SocketConfig {
93+
*
94+
* private String name;
95+
* private String protocol;
96+
* private int port;
97+
*
98+
* // getters & setters
99+
* }
100+
* }
101+
* </pre>
102+
*
103+
* <p>
104+
* This example will result in the same configuration resolution as in the previous example, apart from the default value for {@code server.socket.protocol}.
105+
* <p>
106+
* If the property keys differ from the field names, they can be overridden individually via the {@link ConfigProperty} annotation.
107+
* The same is true for default values, as seen before.
108+
* <p>
109+
* The {@link ConfigProperty} annotation can be annotated on fields as well as on methods.
110+
* The latter is useful if interfaces instead of classes are defined as composed types.
111+
* Per default, methods are not implicitly taken to resolve composed configuration properties.
112+
* If both fields and methods are annotated within a single type, methods take precedence.
113+
* <p>
114+
* See the following example for a composed interface configuration type.
115+
* <p>
116+
*
117+
* <pre>
118+
* &#064;ComposedConfig
119+
* public interface SocketConfig {
120+
*
121+
* &#064;ConfigProperty(name = "name")
122+
* String name();
123+
*
124+
* &#064;ConfigProperty(name = "protocol", defaultValue = "http")
125+
* String protocolName();
126+
*
127+
* &#064;ConfigProperty(name = "port")
128+
* int getPort();
129+
* }
130+
* </pre>
131+
* <p>
132+
* This example will result in the same configuration as before.
133+
*
134+
* <h3>Hierarchical type resolution</h3>
135+
* <p>
136+
* The configuration properties of composed types are resolved hierarchically.
137+
* That is, properties in composed types are implicitly considered as possible composed types themselves, as well.
138+
* This allows developers to define complex configuration structures without repeating annotations.
139+
* <p>
140+
* The types of composed properties are therefore resolved as possible composed configuration types, if no built-in, custom, or implicit converters are defined.
141+
* <p>
142+
* The hierarchical resolution works both for implicitly resolved fields and explicitly annotated members.
143+
*
144+
* <pre>
145+
* &#064;ComposedConfig
146+
* public class ServerConfig {
147+
*
148+
* private String host;
149+
* private SocketConfig socket;
150+
*
151+
* // getters & setters
152+
*
153+
* public static class SocketConfig {
154+
* private String name;
155+
* private String protocol;
156+
* private int port;
157+
*
158+
* // getters & setters
159+
* }
160+
*
161+
* }
162+
* </pre>
163+
* <p>
164+
* If a {@code ServerConfig} configuration type is retrieved, the property keys are resolved as follows:
165+
*
166+
* <pre>
167+
* public class SomeBean {
168+
*
169+
* &#064;Inject
170+
* &#064;ConfigProperty(name = "server")
171+
* private ServerConfig serverConfig;
172+
*
173+
* }
174+
* </pre>
175+
* <p>
176+
* This leads to:
177+
*
178+
* <pre>
179+
* server.host
180+
* server.socket.name
181+
* server.socket.protocol
182+
* server.socket.port
183+
* </pre>
184+
* <p>
185+
* The property keys are resolved by the field names, or the names defined in {@link ConfigProperty}, respectively, and combined via dot ({@code .}).
186+
* <p>
187+
* The example above is congruent with annotating {@code SocketConfig} with {@link ComposedConfig}, as well.
188+
*
189+
* <h3>Collection resolution</h3>
190+
* <p>
191+
* Composed configuration types also resolve collections and array types.
192+
*
193+
* <pre>
194+
* &#064;ComposedConfig
195+
* public class MultiSocketServerConfig {
196+
*
197+
* private String[] hosts;
198+
* private List<SocketConfig> sockets;
199+
*
200+
* // getters & setters
201+
*
202+
* public static class SocketConfig {
203+
* private String name;
204+
* private String protocol;
205+
* private int port;
206+
*
207+
* // getters & setters
208+
* }
209+
*
210+
* }
211+
* </pre>
212+
* <p>
213+
* If the {@code MultiSocketServerConfig} type is resolved by key {@code alternative-server}, it results in the following:
214+
*
215+
* <pre>
216+
* server.hosts.0
217+
* server.hosts.1
218+
*
219+
* server.sockets.0.name
220+
* server.sockets.0.protocol
221+
* server.sockets.1.name
222+
* </pre>
223+
* <p>
224+
* Element types of collections and arrays are resolved by an implicit zero-based index, which is part of the resulting, combined property key.
225+
* <p>
226+
* This collection resolution works for array types, and types that are assignable to {@link java.util.Collection}.
227+
* For unordered collection types, e.g. {@link java.util.Set}, the order in which the configured elements will be retrieved is non-deterministic, despite the (zero-based) indexed key names.
228+
* <p>
229+
* Similar to singular sub-types, the element types within the collection or array are resolved by potentially existent converters, and resolved recursively if no built-in, custom, or implicit converters are defined.
230+
*
231+
* @author <a href="mailto:mail@sebastian-daschner.com">Sebastian Daschner</a>
232+
*/
233+
@Retention(RUNTIME)
234+
@Target(TYPE)
235+
public @interface ComposedConfig {
236+
237+
/**
238+
* Only valid for injection of dynamically readable values, e.g. {@code Provider<String>}!
239+
*
240+
* @return {@code TimeUnit} for {@link #cacheFor()}
241+
*/
242+
@Nonbinding
243+
TimeUnit cacheTimeUnit() default TimeUnit.SECONDS;
244+
245+
/**
246+
* Only valid for injection of dynamically readable values, e.g. {@code Provider<String>}!
247+
*
248+
* @return how long should dynamic values be locally cached. Measured in {@link #cacheTimeUnit()}.
249+
*/
250+
@Nonbinding
251+
long cacheFor() default 0L;
252+
}

0 commit comments

Comments
 (0)