    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    }