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}