001 // Copyright 2006-2012 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 }