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.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.*;
025import org.apache.tapestry5.ioc.services.cron.PeriodicExecutor;
026import org.apache.tapestry5.ioc.util.TimeInterval;
027import org.apache.tapestry5.services.UpdateListenerHub;
028
029import java.io.File;
030import java.lang.reflect.Array;
031import java.math.BigDecimal;
032import java.math.BigInteger;
033import java.util.*;
034import java.util.concurrent.LinkedBlockingQueue;
035import java.util.concurrent.ThreadPoolExecutor;
036import java.util.concurrent.TimeUnit;
037
038import static org.apache.tapestry5.ioc.OrderConstraintBuilder.after;
039import static org.apache.tapestry5.ioc.OrderConstraintBuilder.before;
040
041/**
042 * Defines the base set of services for the Tapestry IOC container.
043 */
044@SuppressWarnings("all")
045@Marker(Builtin.class)
046public final class TapestryIOCModule
047{
048    public static void bind(ServiceBinder binder)
049    {
050        binder.bind(LoggingDecorator.class, LoggingDecoratorImpl.class);
051        binder.bind(ChainBuilder.class, ChainBuilderImpl.class);
052        binder.bind(PropertyAccess.class, PropertyAccessImpl.class);
053        binder.bind(StrategyBuilder.class, StrategyBuilderImpl.class);
054        binder.bind(PropertyShadowBuilder.class, PropertyShadowBuilderImpl.class);
055        binder.bind(PipelineBuilder.class, PipelineBuilderImpl.class).preventReloading();
056        binder.bind(DefaultImplementationBuilder.class, DefaultImplementationBuilderImpl.class);
057        binder.bind(ExceptionTracker.class, ExceptionTrackerImpl.class);
058        binder.bind(ExceptionAnalyzer.class, ExceptionAnalyzerImpl.class);
059        binder.bind(TypeCoercer.class, TypeCoercerImpl.class).preventReloading();
060        binder.bind(ThreadLocale.class, ThreadLocaleImpl.class);
061        binder.bind(SymbolSource.class, SymbolSourceImpl.class);
062        binder.bind(SymbolProvider.class, MapSymbolProvider.class).withId("ApplicationDefaults")
063                .withMarker(ApplicationDefaults.class);
064        binder.bind(SymbolProvider.class, MapSymbolProvider.class).withId("FactoryDefaults")
065                .withMarker(FactoryDefaults.class);
066        binder.bind(Runnable.class, RegistryStartup.class).withSimpleId();
067        binder.bind(MasterObjectProvider.class, MasterObjectProviderImpl.class).preventReloading();
068        binder.bind(ClassNameLocator.class, ClassNameLocatorImpl.class);
069        binder.bind(ClasspathScanner.class, ClasspathScannerImpl.class);
070        binder.bind(AspectDecorator.class, AspectDecoratorImpl.class);
071        binder.bind(ClasspathURLConverter.class, ClasspathURLConverterImpl.class);
072        binder.bind(ServiceOverride.class, ServiceOverrideImpl.class);
073        binder.bind(LoggingAdvisor.class, LoggingAdvisorImpl.class);
074        binder.bind(LazyAdvisor.class, LazyAdvisorImpl.class);
075        binder.bind(ThunkCreator.class, ThunkCreatorImpl.class);
076        binder.bind(UpdateListenerHub.class, UpdateListenerHubImpl.class).preventReloading();
077        binder.bind(PeriodicExecutor.class, PeriodicExecutorImpl.class);
078        binder.bind(OperationAdvisor.class, OperationAdvisorImpl.class);
079        binder.bind(ServiceConfigurationListenerHub.class);
080    }
081
082    /**
083     * Provides access to additional service lifecycles. One lifecycle is built in ("singleton") but additional ones are
084     * accessed via this service (and its mapped configuration). Only proxiable services (those with explicit service
085     * interfaces) can be managed in terms of a lifecycle.
086     */
087    @PreventServiceDecoration
088    public static ServiceLifecycleSource build(Map<String, ServiceLifecycle> configuration)
089    {
090        final Map<String, ServiceLifecycle2> lifecycles = CollectionFactory.newCaseInsensitiveMap();
091
092        for (String name : configuration.keySet())
093        {
094            lifecycles.put(name, InternalUtils.toServiceLifecycle2(configuration.get(name)));
095        }
096
097        return new ServiceLifecycleSource()
098        {
099            @Override
100            public ServiceLifecycle get(String scope)
101            {
102                return lifecycles.get(scope);
103            }
104        };
105    }
106
107    /**
108     * Contributes the "perthread" scope.
109     */
110    @Contribute(ServiceLifecycleSource.class)
111    public static void providePerthreadScope(MappedConfiguration<String, ServiceLifecycle> configuration)
112    {
113        configuration.addInstance(ScopeConstants.PERTHREAD, PerThreadServiceLifecycle.class);
114    }
115
116    /**
117     * <dl>
118     * <dt>AnnotationBasedContributions</dt>
119     * <dd>Empty placeholder used to separate annotation-based ObjectProvider contributions (which come before) from
120     * non-annotation based (such as ServiceOverride) which come after.</dd>
121     * <dt>Value</dt>
122     * <dd>Supports the {@link org.apache.tapestry5.ioc.annotations.Value} annotation</dd>
123     * <dt>Symbol</dt>
124     * <dd>Supports the {@link org.apache.tapestry5.ioc.annotations.Symbol} annotations</dd>
125     * <dt>Autobuild</dt>
126     * <dd>Supports the {@link org.apache.tapestry5.ioc.annotations.Autobuild} annotation</dd>
127     * <dt>ServiceOverride</dt>
128     * <dd>Allows simple service overrides via the {@link org.apache.tapestry5.ioc.services.ServiceOverride} service
129     * (and its configuration)
130     * </dl>
131     */
132    @Contribute(MasterObjectProvider.class)
133    public static void setupObjectProviders(OrderedConfiguration<ObjectProvider> configuration, @Local
134    final ServiceOverride serviceOverride)
135    {
136        configuration.add("AnnotationBasedContributions", null);
137
138        configuration.addInstance("Value", ValueObjectProvider.class, before("AnnotationBasedContributions").build());
139        configuration.addInstance("Symbol", SymbolObjectProvider.class, before("AnnotationBasedContributions").build());
140        configuration.add("Autobuild", new AutobuildObjectProvider(), before("AnnotationBasedContributions").build());
141
142        ObjectProvider wrapper = new ObjectProvider()
143        {
144            @Override
145            public <T> T provide(Class<T> objectType, AnnotationProvider annotationProvider, ObjectLocator locator)
146            {
147                return serviceOverride.getServiceOverrideProvider().provide(objectType, annotationProvider, locator);
148            }
149        };
150
151        configuration.add("ServiceOverride", wrapper, after("AnnotationBasedContributions").build());
152    }
153
154    /**
155     * Contributes a set of standard type coercions to the {@link TypeCoercer} service:
156     * <ul>
157     * <li>Object to String</li>
158     * <li>Object to Boolean</li>
159     * <li>String to Double</li>
160     * <li>String to BigDecimal</li>
161     * <li>BigDecimal to Double</li>
162     * <li>Double to BigDecimal</li>
163     * <li>String to BigInteger</li>
164     * <li>BigInteger to Long</li>
165     * <li>String to Long</li>
166     * <li>Long to Byte</li>
167     * <li>Long to Short</li>
168     * <li>Long to Integer</li>
169     * <li>Double to Long</li>
170     * <li>Double to Float</li>
171     * <li>Float to Double</li>
172     * <li>Long to Double</li>
173     * <li>String to Boolean ("false" is always false, other non-blank strings are true)</li>
174     * <li>Number to Boolean (true if number value is non zero)</li>
175     * <li>Null to Boolean (always false)</li>
176     * <li>Collection to Boolean (false if empty)</li>
177     * <li>Object[] to List</li>
178     * <li>primitive[] to List</li>
179     * <li>Object to List (by wrapping as a singleton list)</li>
180     * <li>String to File</li>
181     * <li>String to {@link org.apache.tapestry5.ioc.util.TimeInterval}</li>
182     * <li>{@link org.apache.tapestry5.ioc.util.TimeInterval} to Long</li>
183     * <li>Object to Object[] (wrapping the object as an array)</li>
184     * <li>Collection to Object[] (via the toArray() method)
185     * <li>{@link Flow} to List</li>
186     * <li>{@link Flow} to Boolean (false if empty)</li>
187     * </ul>
188     */
189    @Contribute(TypeCoercer.class)
190    public static void provideBasicTypeCoercions(Configuration<CoercionTuple> configuration)
191    {
192        add(configuration, Object.class, String.class, new Coercion<Object, String>()
193        {
194            @Override
195            public String coerce(Object input)
196            {
197                return input.toString();
198            }
199        });
200
201        add(configuration, Object.class, Boolean.class, new Coercion<Object, Boolean>()
202        {
203            @Override
204            public Boolean coerce(Object input)
205            {
206                return input != null;
207            }
208        });
209
210        add(configuration, String.class, Double.class, new Coercion<String, Double>()
211        {
212            @Override
213            public Double coerce(String input)
214            {
215                return new Double(input);
216            }
217        });
218
219        // String to BigDecimal is important, as String->Double->BigDecimal would lose
220        // precision.
221
222        add(configuration, String.class, BigDecimal.class, new Coercion<String, BigDecimal>()
223        {
224            @Override
225            public BigDecimal coerce(String input)
226            {
227                return new BigDecimal(input);
228            }
229        });
230
231        add(configuration, BigDecimal.class, Double.class, new Coercion<BigDecimal, Double>()
232        {
233            @Override
234            public Double coerce(BigDecimal input)
235            {
236                return input.doubleValue();
237            }
238        });
239
240        add(configuration, String.class, BigInteger.class, new Coercion<String, BigInteger>()
241        {
242            @Override
243            public BigInteger coerce(String input)
244            {
245                return new BigInteger(input);
246            }
247        });
248
249        add(configuration, String.class, Long.class, new Coercion<String, Long>()
250        {
251            @Override
252            public Long coerce(String input)
253            {
254                return new Long(input);
255            }
256        });
257
258        add(configuration, Long.class, Byte.class, new Coercion<Long, Byte>()
259        {
260            @Override
261            public Byte coerce(Long input)
262            {
263                return input.byteValue();
264            }
265        });
266
267        add(configuration, Long.class, Short.class, new Coercion<Long, Short>()
268        {
269            @Override
270            public Short coerce(Long input)
271            {
272                return input.shortValue();
273            }
274        });
275
276        add(configuration, Long.class, Integer.class, new Coercion<Long, Integer>()
277        {
278            @Override
279            public Integer coerce(Long input)
280            {
281                return input.intValue();
282            }
283        });
284
285        add(configuration, Number.class, Long.class, new Coercion<Number, Long>()
286        {
287            @Override
288            public Long coerce(Number input)
289            {
290                return input.longValue();
291            }
292        });
293
294        add(configuration, Double.class, Float.class, new Coercion<Double, Float>()
295        {
296            @Override
297            public Float coerce(Double input)
298            {
299                return input.floatValue();
300            }
301        });
302
303        add(configuration, Long.class, Double.class, new Coercion<Long, Double>()
304        {
305            @Override
306            public Double coerce(Long input)
307            {
308                return input.doubleValue();
309            }
310        });
311
312        add(configuration, String.class, Boolean.class, new Coercion<String, Boolean>()
313        {
314            @Override
315            public Boolean coerce(String input)
316            {
317                String trimmed = input == null ? "" : input.trim();
318
319                if (trimmed.equalsIgnoreCase("false") || trimmed.length() == 0)
320                    return false;
321
322                // Any non-blank string but "false"
323
324                return true;
325            }
326        });
327
328        add(configuration, Number.class, Boolean.class, new Coercion<Number, Boolean>()
329        {
330            @Override
331            public Boolean coerce(Number input)
332            {
333                return input.longValue() != 0;
334            }
335        });
336
337        add(configuration, Void.class, Boolean.class, new Coercion<Void, Boolean>()
338        {
339            @Override
340            public Boolean coerce(Void input)
341            {
342                return false;
343            }
344        });
345
346        add(configuration, Collection.class, Boolean.class, new Coercion<Collection, Boolean>()
347        {
348            @Override
349            public Boolean coerce(Collection input)
350            {
351                return !input.isEmpty();
352            }
353        });
354
355        add(configuration, Object.class, List.class, new Coercion<Object, List>()
356        {
357            @Override
358            public List coerce(Object input)
359            {
360                return Collections.singletonList(input);
361            }
362        });
363
364        add(configuration, Object[].class, List.class, new Coercion<Object[], List>()
365        {
366            @Override
367            public List coerce(Object[] input)
368            {
369                return Arrays.asList(input);
370            }
371        });
372
373        add(configuration, Object[].class, Boolean.class, new Coercion<Object[], Boolean>()
374        {
375            @Override
376            public Boolean coerce(Object[] input)
377            {
378                return input != null && input.length > 0;
379            }
380        });
381
382        add(configuration, Float.class, Double.class, new Coercion<Float, Double>()
383        {
384            @Override
385            public Double coerce(Float input)
386            {
387                return input.doubleValue();
388            }
389        });
390
391        Coercion primitiveArrayCoercion = new Coercion<Object, List>()
392        {
393            @Override
394            public List<Object> coerce(Object input)
395            {
396                int length = Array.getLength(input);
397                Object[] array = new Object[length];
398                for (int i = 0; i < length; i++)
399                {
400                    array[i] = Array.get(input, i);
401                }
402                return Arrays.asList(array);
403            }
404        };
405
406        add(configuration, byte[].class, List.class, primitiveArrayCoercion);
407        add(configuration, short[].class, List.class, primitiveArrayCoercion);
408        add(configuration, int[].class, List.class, primitiveArrayCoercion);
409        add(configuration, long[].class, List.class, primitiveArrayCoercion);
410        add(configuration, float[].class, List.class, primitiveArrayCoercion);
411        add(configuration, double[].class, List.class, primitiveArrayCoercion);
412        add(configuration, char[].class, List.class, primitiveArrayCoercion);
413        add(configuration, boolean[].class, List.class, primitiveArrayCoercion);
414
415        add(configuration, String.class, File.class, new Coercion<String, File>()
416        {
417            @Override
418            public File coerce(String input)
419            {
420                return new File(input);
421            }
422        });
423
424        add(configuration, String.class, TimeInterval.class, new Coercion<String, TimeInterval>()
425        {
426            @Override
427            public TimeInterval coerce(String input)
428            {
429                return new TimeInterval(input);
430            }
431        });
432
433        add(configuration, TimeInterval.class, Long.class, new Coercion<TimeInterval, Long>()
434        {
435            @Override
436            public Long coerce(TimeInterval input)
437            {
438                return input.milliseconds();
439            }
440        });
441
442        add(configuration, Object.class, Object[].class, new Coercion<Object, Object[]>()
443        {
444            @Override
445            public Object[] coerce(Object input)
446            {
447                return new Object[]
448                        {input};
449            }
450        });
451
452        add(configuration, Collection.class, Object[].class, new Coercion<Collection, Object[]>()
453        {
454            @Override
455            public Object[] coerce(Collection input)
456            {
457                return input.toArray();
458            }
459        });
460
461        add(configuration, Flow.class, List.class, new Coercion<Flow, List>()
462        {
463            @Override
464            public List coerce(Flow input)
465            {
466                return input.toList();
467            }
468        });
469
470        add(configuration, Flow.class, Boolean.class, new Coercion<Flow, Boolean>()
471        {
472            @Override
473            public Boolean coerce(Flow input)
474            {
475                return !input.isEmpty();
476            }
477        });
478
479    }
480
481    private static <S, T> void add(Configuration<CoercionTuple> configuration, Class<S> sourceType,
482                                   Class<T> targetType, Coercion<S, T> coercion)
483    {
484        configuration.add(CoercionTuple.create(sourceType, targetType, coercion));
485    }
486
487    /**
488     * <dl>
489     * <dt>SystemProperties</dt>
490     * <dd>Exposes JVM System properties as symbols (currently case-sensitive)</dd>
491     * <dt>EnvironmentVariables</dt>
492     * <dd>Exposes environment variables as symbols (adding a "env." prefix)</dd>
493     * <dt>ApplicationDefaults</dt>
494     * <dd>Values contributed to @{@link SymbolProvider} @{@link ApplicationDefaults}</dd>
495     * <dt>FactoryDefaults</dt>
496     * <dd>Values contributed to @{@link SymbolProvider} @{@link FactoryDefaults}</dd>
497     * </dl>
498     */
499    @Contribute(SymbolSource.class)
500    public static void setupStandardSymbolProviders(OrderedConfiguration<SymbolProvider> configuration,
501                                                    @ApplicationDefaults
502                                                    SymbolProvider applicationDefaults,
503
504                                                    @FactoryDefaults
505                                                    SymbolProvider factoryDefaults)
506    {
507        configuration.add("SystemProperties", new SystemPropertiesSymbolProvider(), "before:*");
508        configuration.add("EnvironmentVariables", new SystemEnvSymbolProvider());
509        configuration.add("ApplicationDefaults", applicationDefaults);
510        configuration.add("FactoryDefaults", factoryDefaults);
511    }
512
513    public static ParallelExecutor buildDeferredExecution(@Symbol(IOCSymbols.THREAD_POOL_CORE_SIZE)
514                                                          int coreSize,
515
516                                                          @Symbol(IOCSymbols.THREAD_POOL_MAX_SIZE)
517                                                          int maxSize,
518
519                                                          @Symbol(IOCSymbols.THREAD_POOL_KEEP_ALIVE)
520                                                          @IntermediateType(TimeInterval.class)
521                                                          int keepAliveMillis,
522
523                                                          @Symbol(IOCSymbols.THREAD_POOL_ENABLED)
524                                                          boolean threadPoolEnabled,
525
526                                                          @Symbol(IOCSymbols.THREAD_POOL_QUEUE_SIZE)
527                                                          int queueSize,
528
529                                                          PerthreadManager perthreadManager,
530
531                                                          RegistryShutdownHub shutdownHub,
532
533                                                          ThunkCreator thunkCreator)
534    {
535
536        if (!threadPoolEnabled)
537            return new NonParallelExecutor();
538
539        LinkedBlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(queueSize);
540
541        final ThreadPoolExecutor executorService = new ThreadPoolExecutor(coreSize, maxSize, keepAliveMillis,
542                TimeUnit.MILLISECONDS, workQueue);
543
544        shutdownHub.addRegistryShutdownListener(new Runnable()
545        {
546            @Override
547            public void run()
548            {
549                executorService.shutdown();
550            }
551        });
552
553        return new ParallelExecutorImpl(executorService, thunkCreator, perthreadManager);
554    }
555
556    @Contribute(SymbolProvider.class)
557    @FactoryDefaults
558    public static void setupDefaultSymbols(MappedConfiguration<String, Object> configuration)
559    {
560        configuration.add(IOCSymbols.THREAD_POOL_CORE_SIZE, 3);
561        configuration.add(IOCSymbols.THREAD_POOL_MAX_SIZE, 20);
562        configuration.add(IOCSymbols.THREAD_POOL_KEEP_ALIVE, "1 m");
563        configuration.add(IOCSymbols.THREAD_POOL_ENABLED, true);
564        configuration.add(IOCSymbols.THREAD_POOL_QUEUE_SIZE, 100);
565    }
566}