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