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