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
015package org.apache.tapestry5.ioc.services;
016
017import org.apache.tapestry5.func.Flow;
018import org.apache.tapestry5.ioc.*;
019import org.apache.tapestry5.ioc.annotations.*;
020import org.apache.tapestry5.ioc.internal.services.*;
021import org.apache.tapestry5.ioc.internal.services.cron.PeriodicExecutorImpl;
022import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
023import org.apache.tapestry5.ioc.internal.util.InternalUtils;
024import org.apache.tapestry5.ioc.services.cron.PeriodicExecutor;
025import org.apache.tapestry5.ioc.util.TimeInterval;
026import org.apache.tapestry5.services.UpdateListenerHub;
027
028import java.io.File;
029import java.lang.reflect.Array;
030import java.math.BigDecimal;
031import java.math.BigInteger;
032import java.util.*;
033import java.util.concurrent.LinkedBlockingQueue;
034import java.util.concurrent.ThreadPoolExecutor;
035import java.util.concurrent.TimeUnit;
036
037import static org.apache.tapestry5.ioc.OrderConstraintBuilder.after;
038import 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)
045public 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}