001 // Copyright 2006, 2007, 2008, 2009, 2010, 2011 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.tapestry5.ioc.services;
016
017 import org.apache.tapestry5.func.Flow;
018 import org.apache.tapestry5.ioc.*;
019 import org.apache.tapestry5.ioc.annotations.*;
020 import org.apache.tapestry5.ioc.internal.services.*;
021 import org.apache.tapestry5.ioc.internal.services.cron.PeriodicExecutorImpl;
022 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
023 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
024 import org.apache.tapestry5.ioc.services.cron.PeriodicExecutor;
025 import org.apache.tapestry5.ioc.util.TimeInterval;
026 import org.apache.tapestry5.services.UpdateListenerHub;
027
028 import java.io.File;
029 import java.lang.reflect.Array;
030 import java.math.BigDecimal;
031 import java.math.BigInteger;
032 import java.util.*;
033 import java.util.concurrent.LinkedBlockingQueue;
034 import java.util.concurrent.ThreadPoolExecutor;
035 import java.util.concurrent.TimeUnit;
036
037 import static org.apache.tapestry5.ioc.OrderConstraintBuilder.after;
038 import static org.apache.tapestry5.ioc.OrderConstraintBuilder.before;
039
040 /**
041 * Defines the base set of services for the Tapestry IOC container.
042 */
043 @SuppressWarnings("all")
044 @Marker(Builtin.class)
045 public final class TapestryIOCModule
046 {
047 public static void bind(ServiceBinder binder)
048 {
049 binder.bind(LoggingDecorator.class, LoggingDecoratorImpl.class);
050 binder.bind(ChainBuilder.class, ChainBuilderImpl.class);
051 binder.bind(PropertyAccess.class, PropertyAccessImpl.class);
052 binder.bind(StrategyBuilder.class, StrategyBuilderImpl.class);
053 binder.bind(PropertyShadowBuilder.class, PropertyShadowBuilderImpl.class);
054 binder.bind(PipelineBuilder.class, PipelineBuilderImpl.class).preventReloading();
055 binder.bind(DefaultImplementationBuilder.class, DefaultImplementationBuilderImpl.class);
056 binder.bind(ExceptionTracker.class, ExceptionTrackerImpl.class);
057 binder.bind(ExceptionAnalyzer.class, ExceptionAnalyzerImpl.class);
058 binder.bind(TypeCoercer.class, TypeCoercerImpl.class).preventReloading();
059 binder.bind(ThreadLocale.class, ThreadLocaleImpl.class);
060 binder.bind(SymbolSource.class, SymbolSourceImpl.class);
061 binder.bind(SymbolProvider.class, MapSymbolProvider.class).withId("ApplicationDefaults")
062 .withMarker(ApplicationDefaults.class);
063 binder.bind(SymbolProvider.class, MapSymbolProvider.class).withId("FactoryDefaults")
064 .withMarker(FactoryDefaults.class);
065 binder.bind(Runnable.class, RegistryStartup.class).withSimpleId();
066 binder.bind(MasterObjectProvider.class, MasterObjectProviderImpl.class).preventReloading();
067 binder.bind(ClassNameLocator.class, ClassNameLocatorImpl.class);
068 binder.bind(AspectDecorator.class, AspectDecoratorImpl.class);
069 binder.bind(ClasspathURLConverter.class, ClasspathURLConverterImpl.class);
070 binder.bind(ServiceOverride.class, ServiceOverrideImpl.class);
071 binder.bind(LoggingAdvisor.class, LoggingAdvisorImpl.class);
072 binder.bind(LazyAdvisor.class, LazyAdvisorImpl.class);
073 binder.bind(ThunkCreator.class, ThunkCreatorImpl.class);
074 binder.bind(UpdateListenerHub.class, UpdateListenerHubImpl.class).preventReloading();
075 binder.bind(PeriodicExecutor.class, PeriodicExecutorImpl.class);
076 }
077
078 /**
079 * Provides access to additional service lifecycles. One lifecycle is built in ("singleton") but additional ones are
080 * accessed via this service (and its mapped configuration). Only proxiable services (those with explicit service
081 * interfaces) can be managed in terms of a lifecycle.
082 */
083 @PreventServiceDecoration
084 public static ServiceLifecycleSource build(Map<String, ServiceLifecycle> configuration)
085 {
086 final Map<String, ServiceLifecycle2> lifecycles = CollectionFactory.newCaseInsensitiveMap();
087
088 for (String name : configuration.keySet())
089 {
090 lifecycles.put(name, InternalUtils.toServiceLifecycle2(configuration.get(name)));
091 }
092
093 return new ServiceLifecycleSource()
094 {
095 public ServiceLifecycle get(String scope)
096 {
097 return lifecycles.get(scope);
098 }
099 };
100 }
101
102 /**
103 * Contributes the "perthread" scope.
104 */
105 @Contribute(ServiceLifecycleSource.class)
106 public static void providePerthreadScope(MappedConfiguration<String, ServiceLifecycle> configuration)
107 {
108 configuration.addInstance(ScopeConstants.PERTHREAD, PerThreadServiceLifecycle.class);
109 }
110
111 /**
112 * <dl>
113 * <dt>AnnotationBasedContributions</dt>
114 * <dd>Empty placeholder used to separate annotation-based ObjectProvider contributions (which come before) from
115 * non-annotation based (such as ServiceOverride) which come after.</dd>
116 * <dt>Value</dt>
117 * <dd>Supports the {@link org.apache.tapestry5.ioc.annotations.Value} annotation</dd>
118 * <dt>Symbol</dt>
119 * <dd>Supports the {@link org.apache.tapestry5.ioc.annotations.Symbol} annotations</dd>
120 * <dt>Autobuild</dt>
121 * <dd>Supports the {@link org.apache.tapestry5.ioc.annotations.Autobuild} annotation</dd>
122 * <dt>ServiceOverride</dt>
123 * <dd>Allows simple service overrides via the {@link org.apache.tapestry5.ioc.services.ServiceOverride} service
124 * (and its configuration)
125 * </dl>
126 */
127 @Contribute(MasterObjectProvider.class)
128 public static void setupObjectProviders(OrderedConfiguration<ObjectProvider> configuration, @Local
129 final ServiceOverride serviceOverride)
130 {
131 configuration.add("AnnotationBasedContributions", null);
132
133 configuration.addInstance("Value", ValueObjectProvider.class, before("AnnotationBasedContributions").build());
134 configuration.addInstance("Symbol", SymbolObjectProvider.class, before("AnnotationBasedContributions").build());
135 configuration.add("Autobuild", new AutobuildObjectProvider(), before("AnnotationBasedContributions").build());
136
137 ObjectProvider wrapper = new ObjectProvider()
138 {
139 public <T> T provide(Class<T> objectType, AnnotationProvider annotationProvider, ObjectLocator locator)
140 {
141 return serviceOverride.getServiceOverrideProvider().provide(objectType, annotationProvider, locator);
142 }
143 };
144
145 configuration.add("ServiceOverride", wrapper, after("AnnotationBasedContributions").build());
146 }
147
148 /**
149 * Contributes a set of standard type coercions to the {@link TypeCoercer} service:
150 * <ul>
151 * <li>Object to String</li>
152 * <li>Object to Boolean</li>
153 * <li>String to Double</li>
154 * <li>String to BigDecimal</li>
155 * <li>BigDecimal to Double</li>
156 * <li>Double to BigDecimal</li>
157 * <li>String to BigInteger</li>
158 * <li>BigInteger to Long</li>
159 * <li>String to Long</li>
160 * <li>Long to Byte</li>
161 * <li>Long to Short</li>
162 * <li>Long to Integer</li>
163 * <li>Double to Long</li>
164 * <li>Double to Float</li>
165 * <li>Float to Double</li>
166 * <li>Long to Double</li>
167 * <li>String to Boolean ("false" is always false, other non-blank strings are true)</li>
168 * <li>Number to Boolean (true if number value is non zero)</li>
169 * <li>Null to Boolean (always false)</li>
170 * <li>Collection to Boolean (false if empty)</li>
171 * <li>Object[] to List</li>
172 * <li>primitive[] to List</li>
173 * <li>Object to List (by wrapping as a singleton list)</li>
174 * <li>String to File</li>
175 * <li>String to {@link org.apache.tapestry5.ioc.util.TimeInterval}</li>
176 * <li>{@link org.apache.tapestry5.ioc.util.TimeInterval} to Long</li>
177 * <li>Object to Object[] (wrapping the object as an array)</li>
178 * <li>Collection to Object[] (via the toArray() method)
179 * <li>{@link Flow} to List</li>
180 * <li>{@link Flow} to Boolean (false if empty)</li>
181 * </ul>
182 */
183 @Contribute(TypeCoercer.class)
184 public static void provideBasicTypeCoercions(Configuration<CoercionTuple> configuration)
185 {
186 add(configuration, Object.class, String.class, new Coercion<Object, String>()
187 {
188 public String coerce(Object input)
189 {
190 return input.toString();
191 }
192 });
193
194 add(configuration, Object.class, Boolean.class, new Coercion<Object, Boolean>()
195 {
196 public Boolean coerce(Object input)
197 {
198 return input != null;
199 }
200 });
201
202 add(configuration, String.class, Double.class, new Coercion<String, Double>()
203 {
204 public Double coerce(String input)
205 {
206 return new Double(input);
207 }
208 });
209
210 // String to BigDecimal is important, as String->Double->BigDecimal would lose
211 // precision.
212
213 add(configuration, String.class, BigDecimal.class, new Coercion<String, BigDecimal>()
214 {
215 public BigDecimal coerce(String input)
216 {
217 return new BigDecimal(input);
218 }
219 });
220
221 add(configuration, BigDecimal.class, Double.class, new Coercion<BigDecimal, Double>()
222 {
223 public Double coerce(BigDecimal input)
224 {
225 return input.doubleValue();
226 }
227 });
228
229 add(configuration, String.class, BigInteger.class, new Coercion<String, BigInteger>()
230 {
231 public BigInteger coerce(String input)
232 {
233 return new BigInteger(input);
234 }
235 });
236
237 add(configuration, String.class, Long.class, new Coercion<String, Long>()
238 {
239 public Long coerce(String input)
240 {
241 return new Long(input);
242 }
243 });
244
245 add(configuration, Long.class, Byte.class, new Coercion<Long, Byte>()
246 {
247 public Byte coerce(Long input)
248 {
249 return input.byteValue();
250 }
251 });
252
253 add(configuration, Long.class, Short.class, new Coercion<Long, Short>()
254 {
255 public Short coerce(Long input)
256 {
257 return input.shortValue();
258 }
259 });
260
261 add(configuration, Long.class, Integer.class, new Coercion<Long, Integer>()
262 {
263 public Integer coerce(Long input)
264 {
265 return input.intValue();
266 }
267 });
268
269 add(configuration, Number.class, Long.class, new Coercion<Number, Long>()
270 {
271 public Long coerce(Number input)
272 {
273 return input.longValue();
274 }
275 });
276
277 add(configuration, Double.class, Float.class, new Coercion<Double, Float>()
278 {
279 public Float coerce(Double input)
280 {
281 return input.floatValue();
282 }
283 });
284
285 add(configuration, Long.class, Double.class, new Coercion<Long, Double>()
286 {
287 public Double coerce(Long input)
288 {
289 return input.doubleValue();
290 }
291 });
292
293 add(configuration, String.class, Boolean.class, new Coercion<String, Boolean>()
294 {
295 public Boolean coerce(String input)
296 {
297 String trimmed = input == null ? "" : input.trim();
298
299 if (trimmed.equalsIgnoreCase("false") || trimmed.length() == 0)
300 return false;
301
302 // Any non-blank string but "false"
303
304 return true;
305 }
306 });
307
308 add(configuration, Number.class, Boolean.class, new Coercion<Number, Boolean>()
309 {
310 public Boolean coerce(Number input)
311 {
312 return input.longValue() != 0;
313 }
314 });
315
316 add(configuration, void.class, Boolean.class, new Coercion<Void, Boolean>()
317 {
318 public Boolean coerce(Void input)
319 {
320 return false;
321 }
322 });
323
324 add(configuration, Collection.class, Boolean.class, new Coercion<Collection, Boolean>()
325 {
326 public Boolean coerce(Collection input)
327 {
328 return !input.isEmpty();
329 }
330 });
331
332 add(configuration, Object.class, List.class, new Coercion<Object, List>()
333 {
334 public List coerce(Object input)
335 {
336 return Collections.singletonList(input);
337 }
338 });
339
340 add(configuration, Object[].class, List.class, new Coercion<Object[], List>()
341 {
342 public List coerce(Object[] input)
343 {
344 return Arrays.asList(input);
345 }
346 });
347
348 add(configuration, Object[].class, Boolean.class, new Coercion<Object[], Boolean>()
349 {
350 public Boolean coerce(Object[] input)
351 {
352 return input != null && input.length > 0;
353 }
354 });
355
356 add(configuration, Float.class, Double.class, new Coercion<Float, Double>()
357 {
358 public Double coerce(Float input)
359 {
360 return input.doubleValue();
361 }
362 });
363
364 Coercion primitiveArrayCoercion = new Coercion<Object, List>()
365 {
366 public List<Object> coerce(Object input)
367 {
368 int length = Array.getLength(input);
369 Object[] array = new Object[length];
370 for (int i = 0; i < length; i++)
371 {
372 array[i] = Array.get(input, i);
373 }
374 return Arrays.asList(array);
375 }
376 };
377
378 add(configuration, byte[].class, List.class, primitiveArrayCoercion);
379 add(configuration, short[].class, List.class, primitiveArrayCoercion);
380 add(configuration, int[].class, List.class, primitiveArrayCoercion);
381 add(configuration, long[].class, List.class, primitiveArrayCoercion);
382 add(configuration, float[].class, List.class, primitiveArrayCoercion);
383 add(configuration, double[].class, List.class, primitiveArrayCoercion);
384 add(configuration, char[].class, List.class, primitiveArrayCoercion);
385 add(configuration, boolean[].class, List.class, primitiveArrayCoercion);
386
387 add(configuration, String.class, File.class, new Coercion<String, File>()
388 {
389 public File coerce(String input)
390 {
391 return new File(input);
392 }
393 });
394
395 add(configuration, String.class, TimeInterval.class, new Coercion<String, TimeInterval>()
396 {
397 public TimeInterval coerce(String input)
398 {
399 return new TimeInterval(input);
400 }
401 });
402
403 add(configuration, TimeInterval.class, Long.class, new Coercion<TimeInterval, Long>()
404 {
405 public Long coerce(TimeInterval input)
406 {
407 return input.milliseconds();
408 }
409 });
410
411 add(configuration, Object.class, Object[].class, new Coercion<Object, Object[]>()
412 {
413 public Object[] coerce(Object input)
414 {
415 return new Object[]
416 {input};
417 }
418 });
419
420 add(configuration, Collection.class, Object[].class, new Coercion<Collection, Object[]>()
421 {
422 public Object[] coerce(Collection input)
423 {
424 return input.toArray();
425 }
426 });
427
428 add(configuration, Flow.class, List.class, new Coercion<Flow, List>()
429 {
430 public List coerce(Flow input)
431 {
432 return input.toList();
433 }
434 });
435
436 add(configuration, Flow.class, Boolean.class, new Coercion<Flow, Boolean>()
437 {
438 public Boolean coerce(Flow input)
439 {
440 return !input.isEmpty();
441 }
442 });
443
444 }
445
446 private static <S, T> void add(Configuration<CoercionTuple> configuration, Class<S> sourceType,
447 Class<T> targetType, Coercion<S, T> coercion)
448 {
449 CoercionTuple<S, T> tuple = new CoercionTuple<S, T>(sourceType, targetType, coercion);
450
451 configuration.add(tuple);
452 }
453
454 /**
455 * <dl>
456 * <dt>SystemProperties</dt>
457 * <dd>Exposes JVM System properties as symbols (currently case-sensitive)</dd>
458 * <dt>EnvironmentVariables</dt>
459 * <dd>Exposes environment variables as symbols (adding a "env." prefix)</dd>
460 * <dt>ApplicationDefaults</dt>
461 * <dd>Values contributed to @{@link SymbolProvider} @{@link ApplicationDefaults}</dd>
462 * <dt>FactoryDefaults</dt>
463 * <dd>Values contributed to @{@link SymbolProvider} @{@link FactoryDefaults}</dd>
464 * </dl>
465 */
466 @Contribute(SymbolSource.class)
467 public static void setupStandardSymbolProviders(OrderedConfiguration<SymbolProvider> configuration,
468 @ApplicationDefaults
469 SymbolProvider applicationDefaults,
470
471 @FactoryDefaults
472 SymbolProvider factoryDefaults)
473 {
474 configuration.add("SystemProperties", new SystemPropertiesSymbolProvider(), "before:*");
475 configuration.add("EnvironmentVariables", new SystemEnvSymbolProvider());
476 configuration.add("ApplicationDefaults", applicationDefaults);
477 configuration.add("FactoryDefaults", factoryDefaults);
478 }
479
480 public static ParallelExecutor buildDeferredExecution(@Symbol(IOCSymbols.THREAD_POOL_CORE_SIZE)
481 int coreSize,
482
483 @Symbol(IOCSymbols.THREAD_POOL_MAX_SIZE)
484 int maxSize,
485
486 @Symbol(IOCSymbols.THREAD_POOL_KEEP_ALIVE)
487 @IntermediateType(TimeInterval.class)
488 int keepAliveMillis,
489
490 @Symbol(IOCSymbols.THREAD_POOL_ENABLED)
491 boolean threadPoolEnabled,
492
493 @Symbol(IOCSymbols.THREAD_POOL_QUEUE_SIZE)
494 int queueSize,
495
496 PerthreadManager perthreadManager,
497
498 RegistryShutdownHub shutdownHub,
499
500 ThunkCreator thunkCreator)
501 {
502
503 if (!threadPoolEnabled)
504 return new NonParallelExecutor();
505
506 LinkedBlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(queueSize);
507
508 final ThreadPoolExecutor executorService = new ThreadPoolExecutor(coreSize, maxSize, keepAliveMillis,
509 TimeUnit.MILLISECONDS, workQueue);
510
511 shutdownHub.addRegistryShutdownListener(new Runnable()
512 {
513 public void run()
514 {
515 executorService.shutdown();
516 }
517 });
518
519 return new ParallelExecutorImpl(executorService, thunkCreator, perthreadManager);
520 }
521
522 @Contribute(SymbolProvider.class)
523 @FactoryDefaults
524 public static void setupDefaultSymbols(MappedConfiguration<String, Object> configuration)
525 {
526 configuration.add(IOCSymbols.THREAD_POOL_CORE_SIZE, 3);
527 configuration.add(IOCSymbols.THREAD_POOL_MAX_SIZE, 20);
528 configuration.add(IOCSymbols.THREAD_POOL_KEEP_ALIVE, "1 m");
529 configuration.add(IOCSymbols.THREAD_POOL_ENABLED, true);
530 configuration.add(IOCSymbols.THREAD_POOL_QUEUE_SIZE, 100);
531 }
532 }