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}