    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.beanmodel.internal.services.PropertyAccessImpl;
    018import org.apache.tapestry5.commons.*;
    019import org.apache.tapestry5.commons.internal.BasicTypeCoercions;
    020import org.apache.tapestry5.commons.internal.services.*;
    021import org.apache.tapestry5.commons.services.*;
    022import org.apache.tapestry5.commons.util.CollectionFactory;
    023import org.apache.tapestry5.commons.util.TimeInterval;
    024import org.apache.tapestry5.func.Flow;
    025import org.apache.tapestry5.ioc.IOCSymbols;
    026import org.apache.tapestry5.ioc.ScopeConstants;
    027import org.apache.tapestry5.ioc.ServiceBinder;
    028import org.apache.tapestry5.ioc.ServiceLifecycle;
    029import org.apache.tapestry5.ioc.ServiceLifecycle2;
    030import org.apache.tapestry5.ioc.annotations.*;
    031import org.apache.tapestry5.ioc.internal.services.AspectDecoratorImpl;
    032import org.apache.tapestry5.ioc.internal.services.AutobuildObjectProvider;
    033import org.apache.tapestry5.ioc.internal.services.ChainBuilderImpl;
    034import org.apache.tapestry5.ioc.internal.services.ClassNameLocatorImpl;
    035import org.apache.tapestry5.ioc.internal.services.ClasspathScannerImpl;
    036import org.apache.tapestry5.ioc.internal.services.ClasspathURLConverterImpl;
    037import org.apache.tapestry5.ioc.internal.services.DefaultImplementationBuilderImpl;
    038import org.apache.tapestry5.ioc.internal.services.ExceptionAnalyzerImpl;
    039import org.apache.tapestry5.ioc.internal.services.ExceptionTrackerImpl;
    040import org.apache.tapestry5.ioc.internal.services.LazyAdvisorImpl;
    041import org.apache.tapestry5.ioc.internal.services.LoggingAdvisorImpl;
    042import org.apache.tapestry5.ioc.internal.services.LoggingDecoratorImpl;
    043import org.apache.tapestry5.ioc.internal.services.MapSymbolProvider;
    044import org.apache.tapestry5.ioc.internal.services.MasterObjectProviderImpl;
    045import org.apache.tapestry5.ioc.internal.services.NonParallelExecutor;
    046import org.apache.tapestry5.ioc.internal.services.OperationAdvisorImpl;
    047import org.apache.tapestry5.ioc.internal.services.ParallelExecutorImpl;
    048import org.apache.tapestry5.ioc.internal.services.PerThreadServiceLifecycle;
    049import org.apache.tapestry5.ioc.internal.services.PipelineBuilderImpl;
    050import org.apache.tapestry5.ioc.internal.services.PropertyShadowBuilderImpl;
    051import org.apache.tapestry5.ioc.internal.services.RegistryStartup;
    052import org.apache.tapestry5.ioc.internal.services.ServiceOverrideImpl;
    053import org.apache.tapestry5.ioc.internal.services.StrategyBuilderImpl;
    054import org.apache.tapestry5.ioc.internal.services.SymbolObjectProvider;
    055import org.apache.tapestry5.ioc.internal.services.SymbolSourceImpl;
    056import org.apache.tapestry5.ioc.internal.services.SystemEnvSymbolProvider;
    057import org.apache.tapestry5.ioc.internal.services.SystemPropertiesSymbolProvider;
    058import org.apache.tapestry5.ioc.internal.services.ThreadLocaleImpl;
    059import org.apache.tapestry5.ioc.internal.services.ThunkCreatorImpl;
    060import org.apache.tapestry5.ioc.internal.services.UpdateListenerHubImpl;
    061import org.apache.tapestry5.ioc.internal.services.ValueObjectProvider;
    062import org.apache.tapestry5.ioc.internal.services.cron.PeriodicExecutorImpl;
    063import org.apache.tapestry5.ioc.internal.util.InternalUtils;
    064import org.apache.tapestry5.ioc.services.ApplicationDefaults;
    065import org.apache.tapestry5.ioc.services.AspectDecorator;
    066import org.apache.tapestry5.ioc.services.Builtin;
    067import org.apache.tapestry5.ioc.services.ChainBuilder;
    068import org.apache.tapestry5.ioc.services.ClassNameLocator;
    069import org.apache.tapestry5.ioc.services.ClasspathScanner;
    070import org.apache.tapestry5.ioc.services.ClasspathURLConverter;
    071import org.apache.tapestry5.ioc.services.DefaultImplementationBuilder;
    072import org.apache.tapestry5.ioc.services.ExceptionAnalyzer;
    073import org.apache.tapestry5.ioc.services.ExceptionTracker;
    074import org.apache.tapestry5.ioc.services.FactoryDefaults;
    075import org.apache.tapestry5.ioc.services.LazyAdvisor;
    076import org.apache.tapestry5.ioc.services.LoggingAdvisor;
    077import org.apache.tapestry5.ioc.services.LoggingDecorator;
    078import org.apache.tapestry5.ioc.services.MasterObjectProvider;
    079import org.apache.tapestry5.ioc.services.OperationAdvisor;
    080import org.apache.tapestry5.ioc.services.ParallelExecutor;
    081import org.apache.tapestry5.ioc.services.PerthreadManager;
    082import org.apache.tapestry5.ioc.services.PipelineBuilder;
    083import org.apache.tapestry5.ioc.services.PropertyShadowBuilder;
    084import org.apache.tapestry5.ioc.services.RegistryShutdownHub;
    085import org.apache.tapestry5.ioc.services.ServiceConfigurationListenerHub;
    086import org.apache.tapestry5.ioc.services.ServiceLifecycleSource;
    087import org.apache.tapestry5.ioc.services.ServiceOverride;
    088import org.apache.tapestry5.ioc.services.StrategyBuilder;
    089import org.apache.tapestry5.ioc.services.SymbolProvider;
    090import org.apache.tapestry5.ioc.services.SymbolSource;
    091import org.apache.tapestry5.ioc.services.ThreadLocale;
    092import org.apache.tapestry5.ioc.services.ThunkCreator;
    093import org.apache.tapestry5.ioc.services.UpdateListenerHub;
    094import org.apache.tapestry5.ioc.services.cron.PeriodicExecutor;
    095
    096import java.io.File;
    097import java.lang.reflect.Array;
    098import java.math.BigDecimal;
    099import java.math.BigInteger;
    100import java.util.*;
    101import java.util.Map.Entry;
    102import java.util.concurrent.LinkedBlockingQueue;
    103import java.util.concurrent.ThreadPoolExecutor;
    104import java.util.concurrent.TimeUnit;
    105
    106import static org.apache.tapestry5.ioc.OrderConstraintBuilder.after;
    107import static org.apache.tapestry5.ioc.OrderConstraintBuilder.before;
    108
    109/**
    110 * Defines the base set of services for the Tapestry IOC container.
    111 */
    112@SuppressWarnings("all")
    113@Marker(Builtin.class)
    114public final class TapestryIOCModule
    115{
    116    public static void bind(ServiceBinder binder)
    117    {
    118        binder.bind(LoggingDecorator.class, LoggingDecoratorImpl.class);
    119        binder.bind(ChainBuilder.class, ChainBuilderImpl.class);
    120        binder.bind(PropertyAccess.class, PropertyAccessImpl.class);
    121        binder.bind(StrategyBuilder.class, StrategyBuilderImpl.class);
    122        binder.bind(PropertyShadowBuilder.class, PropertyShadowBuilderImpl.class);
    123        binder.bind(PipelineBuilder.class, PipelineBuilderImpl.class).preventReloading();
    124        binder.bind(DefaultImplementationBuilder.class, DefaultImplementationBuilderImpl.class);
    125        binder.bind(ExceptionTracker.class, ExceptionTrackerImpl.class);
    126        binder.bind(ExceptionAnalyzer.class, ExceptionAnalyzerImpl.class);
    127        binder.bind(TypeCoercer.class, TypeCoercerImpl.class).preventReloading();
    128        binder.bind(ThreadLocale.class, ThreadLocaleImpl.class);
    129        binder.bind(SymbolSource.class, SymbolSourceImpl.class);
    130        binder.bind(SymbolProvider.class, MapSymbolProvider.class).withId("ApplicationDefaults")
    131                .withMarker(ApplicationDefaults.class);
    132        binder.bind(SymbolProvider.class, MapSymbolProvider.class).withId("FactoryDefaults")
    133                .withMarker(FactoryDefaults.class);
    134        binder.bind(Runnable.class, RegistryStartup.class).withSimpleId();
    135        binder.bind(MasterObjectProvider.class, MasterObjectProviderImpl.class).preventReloading();
    136        binder.bind(ClassNameLocator.class, ClassNameLocatorImpl.class);
    137        binder.bind(ClasspathScanner.class, ClasspathScannerImpl.class);
    138        binder.bind(AspectDecorator.class, AspectDecoratorImpl.class);
    139        binder.bind(ClasspathURLConverter.class, ClasspathURLConverterImpl.class);
    140        binder.bind(ServiceOverride.class, ServiceOverrideImpl.class);
    141        binder.bind(LoggingAdvisor.class, LoggingAdvisorImpl.class);
    142        binder.bind(LazyAdvisor.class, LazyAdvisorImpl.class);
    143        binder.bind(ThunkCreator.class, ThunkCreatorImpl.class);
    144        binder.bind(UpdateListenerHub.class, UpdateListenerHubImpl.class).preventReloading();
    145        binder.bind(PeriodicExecutor.class, PeriodicExecutorImpl.class);
    146        binder.bind(OperationAdvisor.class, OperationAdvisorImpl.class);
    147        binder.bind(ServiceConfigurationListenerHub.class);
    148    }
    149
    150    /**
    151     * Provides access to additional service lifecycles. One lifecycle is built in ("singleton") but additional ones are
    152     * accessed via this service (and its mapped configuration). Only proxiable services (those with explicit service
    153     * interfaces) can be managed in terms of a lifecycle.
    154     */
    155    @PreventServiceDecoration
    156    public static ServiceLifecycleSource build(Map<String, ServiceLifecycle> configuration)
    157    {
    158        final Map<String, ServiceLifecycle2> lifecycles = CollectionFactory.newCaseInsensitiveMap();
    159
    160        for (Entry<String, ServiceLifecycle> entry : configuration.entrySet())
    161        {
    162            lifecycles.put(entry.getKey(), InternalUtils.toServiceLifecycle2(entry.getValue()));
    163        }
    164
    165        return new ServiceLifecycleSource()
    166        {
    167            @Override
    168            public ServiceLifecycle get(String scope)
    169            {
    170                return lifecycles.get(scope);
    171            }
    172        };
    173    }
    174
    175    /**
    176     * Contributes the "perthread" scope.
    177     */
    178    @Contribute(ServiceLifecycleSource.class)
    179    public static void providePerthreadScope(MappedConfiguration<String, ServiceLifecycle> configuration)
    180    {
    181        configuration.addInstance(ScopeConstants.PERTHREAD, PerThreadServiceLifecycle.class);
    182    }
    183
    184    /**
    185     * <dl>
    186     * <dt>AnnotationBasedContributions</dt>
    187     * <dd>Empty placeholder used to separate annotation-based ObjectProvider contributions (which come before) from
    188     * non-annotation based (such as ServiceOverride) which come after.</dd>
    189     * <dt>Value</dt>
    190     * <dd>Supports the {@link org.apache.tapestry5.ioc.annotations.Value} annotation</dd>
    191     * <dt>Symbol</dt>
    192     * <dd>Supports the {@link org.apache.tapestry5.ioc.annotations.Symbol} annotations</dd>
    193     * <dt>Autobuild</dt>
    194     * <dd>Supports the {@link org.apache.tapestry5.ioc.annotations.Autobuild} annotation</dd>
    195     * <dt>ServiceOverride</dt>
    196     * <dd>Allows simple service overrides via the {@link org.apache.tapestry5.ioc.services.ServiceOverride} service
    197     * (and its configuration)
    198     * </dl>
    199     */
    200    @Contribute(MasterObjectProvider.class)
    201    public static void setupObjectProviders(OrderedConfiguration<ObjectProvider> configuration, @Local
    202    final ServiceOverride serviceOverride)
    203    {
    204        configuration.add("AnnotationBasedContributions", null);
    205
    206        configuration.addInstance("Value", ValueObjectProvider.class, before("AnnotationBasedContributions").build());
    207        configuration.addInstance("Symbol", SymbolObjectProvider.class, before("AnnotationBasedContributions").build());
    208        configuration.add("Autobuild", new AutobuildObjectProvider(), before("AnnotationBasedContributions").build());
    209
    210        ObjectProvider wrapper = new ObjectProvider()
    211        {
    212            @Override
    213            public <T> T provide(Class<T> objectType, AnnotationProvider annotationProvider, ObjectLocator locator)
    214            {
    215                return serviceOverride.getServiceOverrideProvider().provide(objectType, annotationProvider, locator);
    216            }
    217        };
    218
    219        configuration.add("ServiceOverride", wrapper, after("AnnotationBasedContributions").build());
    220    }
    221
    222    /**
    223     * Contributes a set of standard type coercions to the {@link TypeCoercer} service:
    224     * <ul>
    225     * <li>Object to String</li>
    226     * <li>Object to Boolean</li>
    227     * <li>String to Double</li>
    228     * <li>String to BigDecimal</li>
    229     * <li>BigDecimal to Double</li>
    230     * <li>Double to BigDecimal</li>
    231     * <li>String to BigInteger</li>
    232     * <li>BigInteger to Long</li>
    233     * <li>String to Long</li>
    234     * <li>Long to Byte</li>
    235     * <li>Long to Short</li>
    236     * <li>Long to Integer</li>
    237     * <li>Double to Long</li>
    238     * <li>Double to Float</li>
    239     * <li>Float to Double</li>
    240     * <li>Long to Double</li>
    241     * <li>String to Boolean ("false" is always false, other non-blank strings are true)</li>
    242     * <li>Number to Boolean (true if number value is non zero)</li>
    243     * <li>Null to Boolean (always false)</li>
    244     * <li>Collection to Boolean (false if empty)</li>
    245     * <li>Object[] to List</li>
    246     * <li>primitive[] to List</li>
    247     * <li>Object to List (by wrapping as a singleton list)</li>
    248     * <li>String to File</li>
    249     * <li>String to {@link org.apache.tapestry5.commons.util.TimeInterval}</li>
    250     * <li>{@link org.apache.tapestry5.commons.util.TimeInterval} to Long</li>
    251     * <li>Object to Object[] (wrapping the object as an array)</li>
    252     * <li>Collection to Object[] (via the toArray() method)
    253     * <li>{@link Flow} to List</li>
    254     * <li>{@link Flow} to Boolean (false if empty)</li>
    255     * </ul>
    256     */
    257    @Contribute(TypeCoercer.class)
    258    public static void provideBasicTypeCoercions(MappedConfiguration<CoercionTuple.Key, CoercionTuple> configuration)
    259    {
    260        BasicTypeCoercions.provideBasicTypeCoercions(configuration);
    261    }
    262
    263    /**
    264     * Contributes coercions to and from Java Time API (JSR 310) classes.
    265     * <ul>
    266     * <li>java.time.Year to Integer</li>
    267     * <li>Integer to java.time.Year</li>
    268     * <li>java.time.Month to Integer</li>
    269     * <li>Integer to Java.time.Month</li>
    270     * <li>java.time.Month to String</li>
    271     * <li>String to java.time.Month</li>
    272     * <li>String to java.time.YearMonth</li>
    273     * <li>java.time.YearMonth to java.time.Year</li>
    274     * <li>java.time.YearMonth to java.time.Month</li>
    275     * <li>String to java.time.MonthDay</li>
    276     * <li>java.time.MonthDay to java.time.Month</li>
    277     * <li>java.time.DayOfWeek to Integer</li>
    278     * <li>Integer to java.time.DayOfWeek</li>
    279     * <li>java.time.DayOfWeek to String</li>
    280     * <li>String to java.time.DayOfWeek</li>
    281     * <li>java.time.LocalDate to java.time.Instant</li>
    282     * <li>java.time.Instant to java.time.LocalDate</li>
    283     * <li>String to java.time.LocalDate</li>
    284     * <li>java.time.LocalDate to java.time.YearMonth</li>
    285     * <li>java.time.LocalDate to java.time.MonthDay</li>
    286     * <li>java.time.LocalTime to Long</li>
    287     * <li>Long to java.time.LocalTime</li>
    288     * <li>String to java.time.LocalDateTime</li>
    289     * <li>java.time.LocalDateTime to java.time.Instant</li>
    290     * <li>java.time.Instant to LocalDateTime</li>
    291     * <li>java.time.LocalDateTime to java.time.LocalDate</li>
    292     * <li>String to java.time.OffsetDateTime</li>
    293     * <li>java.time.OffsetDateTime to java.time.Instant</li>
    294     * <li>java.time.Instant to java.time.OffsetDateTime</li>
    295     * <li>String to java.time.ZoneId</li>
    296     * <li>String to java.time.ZoneOffset</li>
    297     * <li>String to java.time.ZonedDateTime</li>
    298     * <li>java.time.ZonedDateTime to java.time.Instant</li>
    299     * <li>java.time.ZonedDateTime to java.time.ZoneId</li>
    300     * <li>java.time.Instant to Long</li>
    301     * <li>Long to java.time.Instant</li>
    302     * <li>java.time.Instant to java.util.Date</li>
    303     * <li>java.util.Date to java.time.Instant</li>
    304     * <li>java.time.Duration to Long</li>
    305     * <li>Long to java.time.Duration</li>
    306     * <li>String to java.time.Period</li>
    307     * </ul>
    308     */
    309    @Contribute(TypeCoercer.class)
    310    public static void provideJSR10TypeCoercions(MappedConfiguration<CoercionTuple.Key, CoercionTuple> configuration)
    311    {
    312        BasicTypeCoercions.provideJSR310TypeCoercions(configuration);
    313    }
    314
    315    /**
    316     * <dl>
    317     * <dt>SystemProperties</dt>
    318     * <dd>Exposes JVM System properties as symbols (currently case-sensitive)</dd>
    319     * <dt>EnvironmentVariables</dt>
    320     * <dd>Exposes environment variables as symbols (adding a "env." prefix)</dd>
    321     * <dt>ApplicationDefaults</dt>
    322     * <dd>Values contributed to @{@link SymbolProvider} @{@link ApplicationDefaults}</dd>
    323     * <dt>FactoryDefaults</dt>
    324     * <dd>Values contributed to @{@link SymbolProvider} @{@link FactoryDefaults}</dd>
    325     * </dl>
    326     */
    327    @Contribute(SymbolSource.class)
    328    public static void setupStandardSymbolProviders(OrderedConfiguration<SymbolProvider> configuration,
    329                                                    @ApplicationDefaults
    330                                                    SymbolProvider applicationDefaults,
    331
    332                                                    @FactoryDefaults
    333                                                    SymbolProvider factoryDefaults)
    334    {
    335        configuration.add("SystemProperties", new SystemPropertiesSymbolProvider(), "before:*");
    336        configuration.add("EnvironmentVariables", new SystemEnvSymbolProvider());
    337        configuration.add("ApplicationDefaults", applicationDefaults);
    338        configuration.add("FactoryDefaults", factoryDefaults);
    339    }
    340
    341    public static ParallelExecutor buildDeferredExecution(@Symbol(IOCSymbols.THREAD_POOL_CORE_SIZE)
    342                                                          int coreSize,
    343
    344                                                          @Symbol(IOCSymbols.THREAD_POOL_MAX_SIZE)
    345                                                          int maxSize,
    346
    347                                                          @Symbol(IOCSymbols.THREAD_POOL_KEEP_ALIVE)
    348                                                          @IntermediateType(TimeInterval.class)
    349                                                          int keepAliveMillis,
    350
    351                                                          @Symbol(IOCSymbols.THREAD_POOL_ENABLED)
    352                                                          boolean threadPoolEnabled,
    353
    354                                                          @Symbol(IOCSymbols.THREAD_POOL_QUEUE_SIZE)
    355                                                          int queueSize,
    356
    357                                                          PerthreadManager perthreadManager,
    358
    359                                                          RegistryShutdownHub shutdownHub,
    360
    361                                                          ThunkCreator thunkCreator)
    362    {
    363
    364        if (!threadPoolEnabled)
    365            return new NonParallelExecutor();
    366
    367        LinkedBlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(queueSize);
    368
    369        final ThreadPoolExecutor executorService = new ThreadPoolExecutor(coreSize, maxSize, keepAliveMillis,
    370                TimeUnit.MILLISECONDS, workQueue);
    371
    372        shutdownHub.addRegistryShutdownListener(new Runnable()
    373        {
    374            @Override
    375            public void run()
    376            {
    377                executorService.shutdown();
    378            }
    379        });
    380
    381        return new ParallelExecutorImpl(executorService, thunkCreator, perthreadManager);
    382    }
    383
    384    @Contribute(SymbolProvider.class)
    385    @FactoryDefaults
    386    public static void setupDefaultSymbols(MappedConfiguration<String, Object> configuration)
    387    {
    388        configuration.add(IOCSymbols.THREAD_POOL_CORE_SIZE, 3);
    389        configuration.add(IOCSymbols.THREAD_POOL_MAX_SIZE, 20);
    390        configuration.add(IOCSymbols.THREAD_POOL_KEEP_ALIVE, "1 m");
    391        configuration.add(IOCSymbols.THREAD_POOL_ENABLED, true);
    392        configuration.add(IOCSymbols.THREAD_POOL_QUEUE_SIZE, 100);
    393    }
    394
    395    public static void contributeRegistryStartup(OrderedConfiguration<Runnable> configuration,
    396            PeriodicExecutor periodicExecutor)
    397    {
    398        configuration.add(PeriodicExecutor.class.getSimpleName(),
    399                () ->  periodicExecutor.init());
    400    }
    401
    402    /**
    403     * Builds the {@link RecursiveValueProvider} service.
    404     * @since 5.9.0
    405     */
    406    public static RecursiveValueProvider buildRecursiveValueProvider(List<RecursiveValueProvider> providers, ChainBuilder chainBuilder)
    407    {
    408        return chainBuilder.build(RecursiveValueProvider.class, providers);
    409    }
    410
    411}