View Javadoc
1   package guru.mikelue.jdut;
2   
3   import java.io.Reader;
4   import java.sql.Connection;
5   import java.sql.SQLException;
6   import java.util.Collections;
7   import java.util.HashMap;
8   import java.util.Map;
9   import java.util.Optional;
10  import java.util.function.Consumer;
11  import java.util.function.Function;
12  import java.util.function.Supplier;
13  
14  import org.apache.commons.lang3.StringUtils;
15  import org.apache.commons.lang3.Validate;
16  
17  import guru.mikelue.jdut.decorate.DataGrainDecorator;
18  import guru.mikelue.jdut.jdbc.JdbcFunction;
19  import guru.mikelue.jdut.jdbc.SQLExceptionConvert;
20  import guru.mikelue.jdut.operation.DataGrainOperator;
21  import guru.mikelue.jdut.operation.OperatorFactory;
22  
23  /**
24   * Defines the configuration for building block of conduction.<br>
25   *
26   * This object supports hierarchy, if the getXXX method cannot found one, try fetch from parent.
27   *
28   * <h3>Building blocks</h3>
29   * <ol>
30   * 	<li>The loader of resource</li>
31   * 	<li>The {@link SQLExceptionConvert SQLExceptionConvert}</li>
32   * 	<li>The {@link OperatorFactory operation factory}</li>
33   * 	<li>The named {@link DataGrainOperator operators}</li>
34   * 	<li>The named {@link DataGrainDecorator decorations}</li>
35   * 	<li>The named {@link JdbcFunction JdbcFunction}</li>
36   * </ol>
37   */
38  public class ConductorConfig {
39  	/**
40  	 * This object is fed by {@link ConductorConfig#build ConductorConfig.build} through {@link Consumer}.
41  	 */
42  	public class Builder {
43  		private Builder() {};
44  
45  		/**
46  		 * Sets the parent configuration
47  		 *
48  		 * @param newParent The parent configuration, could be null
49  		 *
50  		 * @return cascading self
51  		 */
52  		public Builder parent(ConductorConfig newParent)
53  		{
54  			parent = Optional.ofNullable(newParent);
55  			return this;
56  		}
57  
58  		/**
59  		 * Sets the resource reader
60  		 *
61  		 * @param newResourceLoader The loader of resource
62  		 *
63  		 * @return cascading self
64  		 */
65  		public Builder resourceLoader(Function<String, Reader> newResourceLoader)
66  		{
67  			resourceLoader = Optional.ofNullable(newResourceLoader);
68  			return this;
69  		}
70  
71  		/**
72  		 * Sets the conversion for {@link SQLException}.
73  		 *
74  		 * @param <E> The type of {@link RuntimeException} to be the result exception
75  		 * @param newSqlExceptionConvert The conversion for {@link SQLException}
76  		 *
77  		 * @return cascading self
78  		 */
79  		public <E extends RuntimeException> Builder sqlExceptionConvert(SQLExceptionConvert<E> newSqlExceptionConvert)
80  		{
81  			sqlExceptionConvert = Optional.ofNullable(newSqlExceptionConvert);
82  			return this;
83  		}
84  
85  		/**
86  		 * Sets operator factory.
87  		 *
88  		 * @param newOperatorFactory The operator factory, could be null
89  		 *
90  		 * @return cascading self
91  		 */
92  		public Builder operatorFactory(OperatorFactory newOperatorFactory)
93  		{
94  			operatorFactory = Optional.ofNullable(newOperatorFactory);
95  			return this;
96  		}
97  
98  		/**
99  		 * Puts named operator
100 		 *
101 		 * @param name The name of operator used to be fetched
102 		 * @param operator The target operator
103 		 *
104 		 * @return cascading self
105 		 */
106 		public Builder namedOperator(String name, DataGrainOperator operator)
107 		{
108 			name = StringUtils.trimToNull(name);
109 			Validate.notNull(name, "Need viable name of operator");
110 			Validate.notNull(operator, "Need viable operator");
111 
112 			namedOperators.put(name, operator);
113 			return this;
114 		}
115 
116 		/**
117 		 * Puts named supplier
118 		 *
119 		 * @param name The name of operator used to be fetched
120 		 * @param supplier The supplier matching the name
121 		 *
122 		 * @return cascading self
123 		 */
124 		public Builder namedSupplier(String name, Supplier<?> supplier)
125 		{
126 			name = StringUtils.trimToNull(name);
127 			Validate.notNull(name, "Need viable name of supplier");
128 			Validate.notNull(supplier, "Need viable supplier");
129 
130 			namedSuppliers.put(name, supplier);
131 			return this;
132 		}
133 
134 		/**
135 		 * Puts named JDBC function.
136 		 *
137 		 * @param name The name of function used to be fetched
138 		 * @param jdbcFunction The target function of JDBC
139 		 *
140 		 * @return cascading self
141 		 */
142 		@SuppressWarnings("unchecked")
143 		public Builder namedJdbcFunction(String name, JdbcFunction<? extends Connection, ?> jdbcFunction)
144 		{
145 			name = StringUtils.trimToNull(name);
146 			Validate.notNull(name, "Need viable name of JDBC function");
147 			Validate.notNull(jdbcFunction, "Need viable operator");
148 
149 			namedJdbcFunctions.put(name, (JdbcFunction<Connection, ?>)jdbcFunction);
150 			return this;
151 		}
152 
153 		/**
154 		 * Puts named decorator.
155 		 *
156 		 * @param name The name of decorator used to be fetched
157 		 * @param decorator target The decorator
158 		 *
159 		 * @return cascading self
160 		 */
161 		public Builder namedDecorator(String name, DataGrainDecorator decorator)
162 		{
163 			name = StringUtils.trimToNull(name);
164 			Validate.notNull(name, "Need viable name of JDBC function");
165 			Validate.notNull(decorator, "Need viable decorator");
166 
167 			namedDecorators.put(name, decorator);
168 			return this;
169 		}
170 	}
171 
172 	/**
173 	 * Builds configuration with {@link Consumer} of builder.
174 	 *
175 	 * @param builderConsumer the consumer of builder
176 	 *
177 	 * @return The initialized configuration
178 	 *
179 	 * @see Builder
180 	 */
181 	public static ConductorConfig build(Consumer<Builder> builderConsumer)
182 	{
183 		ConductorConfig config = new ConductorConfig();
184 		ConductorConfig.Builder newBuilder = config.new Builder();
185 
186 		builderConsumer.accept(newBuilder);
187 
188 		return config.clone();
189 	}
190 
191 	/**
192 	 * Builds configuration with {@link Consumer} of builder and another configuration.
193 	 *
194 	 * @param builderConsumer the consumer of builder
195 	 * @param clonedConfig The cloned configuration
196 	 *
197 	 * @return The initialized configuration
198 	 *
199 	 * @see Builder
200 	 */
201 	public static ConductorConfigl#ConductorConfig">ConductorConfig build(Consumer<Builder> builderConsumer, ConductorConfig clonedConfig)
202 	{
203 		ConductorConfig newConfig = clonedConfig.modifiableClone();
204 		ConductorConfig.Builder newBuilder = newConfig.new Builder();
205 
206 		builderConsumer.accept(newBuilder);
207 
208 		return newConfig.clone();
209 	}
210 
211 	private Optional<ConductorConfig> parent = Optional.empty();
212 
213 	private Map<String, DataGrainOperator> namedOperators = new HashMap<>(4);
214 	private Map<String, JdbcFunction<Connection, ?>> namedJdbcFunctions = new HashMap<>(4);
215 	private Map<String, DataGrainDecorator> namedDecorators = new HashMap<>(4);
216 	private Map<String, Supplier<?>> namedSuppliers = new HashMap<>(4);
217 	private Optional<Function<String, Reader>> resourceLoader = Optional.empty();
218 	private Optional<OperatorFactory> operatorFactory = Optional.empty();
219 	private Optional<SQLExceptionConvert<?>> sqlExceptionConvert = Optional.empty();
220 
221 	private ConductorConfig() {}
222 
223 	/**
224 	 * Gets loader of resource(optional).
225 	 *
226 	 * @return The loader of resource or parent's one
227 	 */
228 	public Optional<Function<String, Reader>> getResourceLoader()
229 	{
230 		if (!resourceLoader.isPresent() && parent.isPresent()) {
231 			return parent.get().getResourceLoader();
232 		}
233 
234 		return resourceLoader;
235 	}
236 
237 	/**
238 	 * Gets factory of operator(optional).
239 	 *
240 	 * @return The factory of operators
241 	 */
242 	public Optional<OperatorFactory> getOperatorFactory()
243 	{
244 		return operatorFactory;
245 	}
246 
247 	/**
248 	 * Gets operator by various sources.
249 	 *
250 	 * Priority of fetching:
251 	 * <ol>
252 	 * 	<li>Fetches from named operator</li>
253 	 * 	<li>Fetches by operator factory(if provided)</li>
254 	 * 	<li>Fetches from named operator of parent</li>
255 	 * 	<li>Fetches by operator factory of parent(if provided)</li>
256 	 * </ol>
257 	 *
258 	 * @param name The name of operator
259 	 *
260 	 * @return matched operator
261 	 */
262 	public Optional<DataGrainOperator> getOperator(String name)
263 	{
264 		if (namedOperators.containsKey(name)) {
265 			return Optional.of(namedOperators.get(name));
266 		}
267 
268 		if (operatorFactory.isPresent()) {
269 			DataGrainOperator operator = operatorFactory.get().get(name);
270 			if (operator != null) {
271 				return Optional.of(operator);
272 			}
273 		}
274 
275 		if (parent.isPresent()) {
276 			return parent.get().getOperator(name);
277 		}
278 
279 		return Optional.empty();
280 	}
281 
282 	/**
283 	 * Gets decorator by various sources.
284 	 *
285 	 * Priority of fetching:
286 	 * <ol>
287 	 * 	<li>Fetches from named decorator</li>
288 	 * 	<li>Fetches from named decorator of parent</li>
289 	 * </ol>
290 	 *
291 	 * @param name The name of decorator
292 	 *
293 	 * @return matched decorator
294 	 */
295 	public Optional<DataGrainDecorator> getDecorator(String name)
296 	{
297 		if (namedDecorators.containsKey(name)) {
298 			return Optional.of(namedDecorators.get(name));
299 		}
300 
301 		if (parent.isPresent()) {
302 			return parent.get().getDecorator(name);
303 		}
304 
305 		return Optional.empty();
306 	}
307 
308 	/**
309 	 * Gets supplier by various sources.
310 	 *
311 	 * Priority of fetching:
312 	 * <ol>
313 	 * 	<li>Fetches from named supplier</li>
314 	 * 	<li>Fetches from named supplier of parent</li>
315 	 * </ol>
316 	 *
317 	 * @param name The name of supplier
318 	 *
319 	 * @return matched supplier
320 	 */
321 	public Optional<Supplier<?>> getSupplier(String name)
322 	{
323 		if (namedSuppliers.containsKey(name)) {
324 			return Optional.of(namedSuppliers.get(name));
325 		}
326 
327 		if (parent.isPresent()) {
328 			return parent.get().getSupplier(name);
329 		}
330 
331 		return Optional.empty();
332 	}
333 
334 	/**
335 	 * Gets JDBC function by various sources.
336 	 *
337 	 * Priority of fetching:
338 	 * <ol>
339 	 * 	<li>Fetches from named JDBC function</li>
340 	 * 	<li>Fetches from named JDBC function of parent</li>
341 	 * </ol>
342 	 *
343 	 * @param name The name of JDBC function
344 	 *
345 	 * @return matched JDBC function
346 	 */
347 	public Optional<JdbcFunction<Connection, ?>> getJdbcFunction(String name)
348 	{
349 		if (namedJdbcFunctions.containsKey(name)) {
350 			return Optional.of(namedJdbcFunctions.get(name));
351 		}
352 
353 		if (parent.isPresent()) {
354 			return parent.get().getJdbcFunction(name);
355 		}
356 
357 		return Optional.empty();
358 	}
359 
360 	/**
361 	 * Gets the function for conversion of {@link SQLException}.
362 	 *
363 	 * @return The optional object of lambda
364 	 */
365 	public Optional<SQLExceptionConvert<?>> getSqlExceptionConvert()
366 	{
367 		if (sqlExceptionConvert.isPresent()) {
368 			return sqlExceptionConvert;
369 		}
370 
371 		if (parent.isPresent()) {
372 			return parent.get().getSqlExceptionConvert();
373 		}
374 
375 		return Optional.empty();
376 	}
377 
378 	@Override
379 	protected ConductorConfig clone()
380 	{
381 		ConductorConfig newConfig = commonClone();
382 		newConfig.namedOperators = Collections.unmodifiableMap(this.namedOperators);
383 		newConfig.namedJdbcFunctions = Collections.unmodifiableMap(this.namedJdbcFunctions);
384 		newConfig.namedDecorators = Collections.unmodifiableMap(this.namedDecorators);
385 		newConfig.namedSuppliers = Collections.unmodifiableMap(this.namedSuppliers);
386 
387 		return newConfig;
388 	}
389 
390 	private ConductorConfig modifiableClone()
391 	{
392 		ConductorConfig newConfig = commonClone();
393 		newConfig.namedOperators = new HashMap<>(this.namedOperators);
394 		newConfig.namedJdbcFunctions = new HashMap<>(this.namedJdbcFunctions);
395 		newConfig.namedDecorators = new HashMap<>(this.namedDecorators);
396 		newConfig.namedSuppliers = new HashMap<>(this.namedSuppliers);
397 
398 		return newConfig;
399 	}
400 
401 	private ConductorConfig commonClone()
402 	{
403 		ConductorConfig newConfig = new ConductorConfig();
404 		newConfig.parent = this.parent;
405 		newConfig.resourceLoader = this.resourceLoader;
406 		newConfig.operatorFactory = this.operatorFactory;
407 		newConfig.sqlExceptionConvert = this.sqlExceptionConvert;
408 
409 		return newConfig;
410 	}
411 }