blob: a9af358eab68a1ddbbb30e0c878b097c204e4d98 [file] [log] [blame]
// Copyright 2006-2014 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.apache.tapestry5.ioc.modules;
import org.apache.tapestry5.func.Flow;
import org.apache.tapestry5.ioc.*;
import org.apache.tapestry5.ioc.annotations.*;
import org.apache.tapestry5.ioc.internal.services.*;
import org.apache.tapestry5.ioc.internal.services.cron.PeriodicExecutorImpl;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.ioc.services.*;
import org.apache.tapestry5.ioc.services.cron.PeriodicExecutor;
import org.apache.tapestry5.ioc.util.TimeInterval;
import org.apache.tapestry5.services.UpdateListenerHub;
import java.io.File;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import static org.apache.tapestry5.ioc.OrderConstraintBuilder.after;
import static org.apache.tapestry5.ioc.OrderConstraintBuilder.before;
/**
* Defines the base set of services for the Tapestry IOC container.
*/
@SuppressWarnings("all")
@Marker(Builtin.class)
public final class TapestryIOCModule
{
public static void bind(ServiceBinder binder)
{
binder.bind(LoggingDecorator.class, LoggingDecoratorImpl.class);
binder.bind(ChainBuilder.class, ChainBuilderImpl.class);
binder.bind(PropertyAccess.class, PropertyAccessImpl.class);
binder.bind(StrategyBuilder.class, StrategyBuilderImpl.class);
binder.bind(PropertyShadowBuilder.class, PropertyShadowBuilderImpl.class);
binder.bind(PipelineBuilder.class, PipelineBuilderImpl.class).preventReloading();
binder.bind(DefaultImplementationBuilder.class, DefaultImplementationBuilderImpl.class);
binder.bind(ExceptionTracker.class, ExceptionTrackerImpl.class);
binder.bind(ExceptionAnalyzer.class, ExceptionAnalyzerImpl.class);
binder.bind(TypeCoercer.class, TypeCoercerImpl.class).preventReloading();
binder.bind(ThreadLocale.class, ThreadLocaleImpl.class);
binder.bind(SymbolSource.class, SymbolSourceImpl.class);
binder.bind(SymbolProvider.class, MapSymbolProvider.class).withId("ApplicationDefaults")
.withMarker(ApplicationDefaults.class);
binder.bind(SymbolProvider.class, MapSymbolProvider.class).withId("FactoryDefaults")
.withMarker(FactoryDefaults.class);
binder.bind(Runnable.class, RegistryStartup.class).withSimpleId();
binder.bind(MasterObjectProvider.class, MasterObjectProviderImpl.class).preventReloading();
binder.bind(ClassNameLocator.class, ClassNameLocatorImpl.class);
binder.bind(ClasspathScanner.class, ClasspathScannerImpl.class);
binder.bind(AspectDecorator.class, AspectDecoratorImpl.class);
binder.bind(ClasspathURLConverter.class, ClasspathURLConverterImpl.class);
binder.bind(ServiceOverride.class, ServiceOverrideImpl.class);
binder.bind(LoggingAdvisor.class, LoggingAdvisorImpl.class);
binder.bind(LazyAdvisor.class, LazyAdvisorImpl.class);
binder.bind(ThunkCreator.class, ThunkCreatorImpl.class);
binder.bind(UpdateListenerHub.class, UpdateListenerHubImpl.class).preventReloading();
binder.bind(PeriodicExecutor.class, PeriodicExecutorImpl.class);
binder.bind(OperationAdvisor.class, OperationAdvisorImpl.class);
binder.bind(ServiceConfigurationListenerHub.class);
}
/**
* Provides access to additional service lifecycles. One lifecycle is built in ("singleton") but additional ones are
* accessed via this service (and its mapped configuration). Only proxiable services (those with explicit service
* interfaces) can be managed in terms of a lifecycle.
*/
@PreventServiceDecoration
public static ServiceLifecycleSource build(Map<String, ServiceLifecycle> configuration)
{
final Map<String, ServiceLifecycle2> lifecycles = CollectionFactory.newCaseInsensitiveMap();
for (String name : configuration.keySet())
{
lifecycles.put(name, InternalUtils.toServiceLifecycle2(configuration.get(name)));
}
return new ServiceLifecycleSource()
{
@Override
public ServiceLifecycle get(String scope)
{
return lifecycles.get(scope);
}
};
}
/**
* Contributes the "perthread" scope.
*/
@Contribute(ServiceLifecycleSource.class)
public static void providePerthreadScope(MappedConfiguration<String, ServiceLifecycle> configuration)
{
configuration.addInstance(ScopeConstants.PERTHREAD, PerThreadServiceLifecycle.class);
}
/**
* <dl>
* <dt>AnnotationBasedContributions</dt>
* <dd>Empty placeholder used to separate annotation-based ObjectProvider contributions (which come before) from
* non-annotation based (such as ServiceOverride) which come after.</dd>
* <dt>Value</dt>
* <dd>Supports the {@link org.apache.tapestry5.ioc.annotations.Value} annotation</dd>
* <dt>Symbol</dt>
* <dd>Supports the {@link org.apache.tapestry5.ioc.annotations.Symbol} annotations</dd>
* <dt>Autobuild</dt>
* <dd>Supports the {@link org.apache.tapestry5.ioc.annotations.Autobuild} annotation</dd>
* <dt>ServiceOverride</dt>
* <dd>Allows simple service overrides via the {@link org.apache.tapestry5.ioc.services.ServiceOverride} service
* (and its configuration)
* </dl>
*/
@Contribute(MasterObjectProvider.class)
public static void setupObjectProviders(OrderedConfiguration<ObjectProvider> configuration, @Local
final ServiceOverride serviceOverride)
{
configuration.add("AnnotationBasedContributions", null);
configuration.addInstance("Value", ValueObjectProvider.class, before("AnnotationBasedContributions").build());
configuration.addInstance("Symbol", SymbolObjectProvider.class, before("AnnotationBasedContributions").build());
configuration.add("Autobuild", new AutobuildObjectProvider(), before("AnnotationBasedContributions").build());
ObjectProvider wrapper = new ObjectProvider()
{
@Override
public <T> T provide(Class<T> objectType, AnnotationProvider annotationProvider, ObjectLocator locator)
{
return serviceOverride.getServiceOverrideProvider().provide(objectType, annotationProvider, locator);
}
};
configuration.add("ServiceOverride", wrapper, after("AnnotationBasedContributions").build());
}
/**
* Contributes a set of standard type coercions to the {@link TypeCoercer} service:
* <ul>
* <li>Object to String</li>
* <li>Object to Boolean</li>
* <li>String to Double</li>
* <li>String to BigDecimal</li>
* <li>BigDecimal to Double</li>
* <li>Double to BigDecimal</li>
* <li>String to BigInteger</li>
* <li>BigInteger to Long</li>
* <li>String to Long</li>
* <li>Long to Byte</li>
* <li>Long to Short</li>
* <li>Long to Integer</li>
* <li>Double to Long</li>
* <li>Double to Float</li>
* <li>Float to Double</li>
* <li>Long to Double</li>
* <li>String to Boolean ("false" is always false, other non-blank strings are true)</li>
* <li>Number to Boolean (true if number value is non zero)</li>
* <li>Null to Boolean (always false)</li>
* <li>Collection to Boolean (false if empty)</li>
* <li>Object[] to List</li>
* <li>primitive[] to List</li>
* <li>Object to List (by wrapping as a singleton list)</li>
* <li>String to File</li>
* <li>String to {@link org.apache.tapestry5.ioc.util.TimeInterval}</li>
* <li>{@link org.apache.tapestry5.ioc.util.TimeInterval} to Long</li>
* <li>Object to Object[] (wrapping the object as an array)</li>
* <li>Collection to Object[] (via the toArray() method)
* <li>{@link Flow} to List</li>
* <li>{@link Flow} to Boolean (false if empty)</li>
* </ul>
*/
@Contribute(TypeCoercer.class)
public static void provideBasicTypeCoercions(Configuration<CoercionTuple> configuration)
{
add(configuration, Object.class, String.class, new Coercion<Object, String>()
{
@Override
public String coerce(Object input)
{
return input.toString();
}
});
add(configuration, Object.class, Boolean.class, new Coercion<Object, Boolean>()
{
@Override
public Boolean coerce(Object input)
{
return input != null;
}
});
add(configuration, String.class, Double.class, new Coercion<String, Double>()
{
@Override
public Double coerce(String input)
{
return new Double(input);
}
});
// String to BigDecimal is important, as String->Double->BigDecimal would lose
// precision.
add(configuration, String.class, BigDecimal.class, new Coercion<String, BigDecimal>()
{
@Override
public BigDecimal coerce(String input)
{
return new BigDecimal(input);
}
});
add(configuration, BigDecimal.class, Double.class, new Coercion<BigDecimal, Double>()
{
@Override
public Double coerce(BigDecimal input)
{
return input.doubleValue();
}
});
add(configuration, String.class, BigInteger.class, new Coercion<String, BigInteger>()
{
@Override
public BigInteger coerce(String input)
{
return new BigInteger(input);
}
});
add(configuration, String.class, Long.class, new Coercion<String, Long>()
{
@Override
public Long coerce(String input)
{
return new Long(input);
}
});
add(configuration, Long.class, Byte.class, new Coercion<Long, Byte>()
{
@Override
public Byte coerce(Long input)
{
return input.byteValue();
}
});
add(configuration, Long.class, Short.class, new Coercion<Long, Short>()
{
@Override
public Short coerce(Long input)
{
return input.shortValue();
}
});
add(configuration, Long.class, Integer.class, new Coercion<Long, Integer>()
{
@Override
public Integer coerce(Long input)
{
return input.intValue();
}
});
add(configuration, Number.class, Long.class, new Coercion<Number, Long>()
{
@Override
public Long coerce(Number input)
{
return input.longValue();
}
});
add(configuration, Double.class, Float.class, new Coercion<Double, Float>()
{
@Override
public Float coerce(Double input)
{
return input.floatValue();
}
});
add(configuration, Long.class, Double.class, new Coercion<Long, Double>()
{
@Override
public Double coerce(Long input)
{
return input.doubleValue();
}
});
add(configuration, String.class, Boolean.class, new Coercion<String, Boolean>()
{
@Override
public Boolean coerce(String input)
{
String trimmed = input == null ? "" : input.trim();
if (trimmed.equalsIgnoreCase("false") || trimmed.length() == 0)
return false;
// Any non-blank string but "false"
return true;
}
});
add(configuration, Number.class, Boolean.class, new Coercion<Number, Boolean>()
{
@Override
public Boolean coerce(Number input)
{
return input.longValue() != 0;
}
});
add(configuration, Void.class, Boolean.class, new Coercion<Void, Boolean>()
{
@Override
public Boolean coerce(Void input)
{
return false;
}
});
add(configuration, Collection.class, Boolean.class, new Coercion<Collection, Boolean>()
{
@Override
public Boolean coerce(Collection input)
{
return !input.isEmpty();
}
});
add(configuration, Object.class, List.class, new Coercion<Object, List>()
{
@Override
public List coerce(Object input)
{
return Collections.singletonList(input);
}
});
add(configuration, Object[].class, List.class, new Coercion<Object[], List>()
{
@Override
public List coerce(Object[] input)
{
return Arrays.asList(input);
}
});
add(configuration, Object[].class, Boolean.class, new Coercion<Object[], Boolean>()
{
@Override
public Boolean coerce(Object[] input)
{
return input != null && input.length > 0;
}
});
add(configuration, Float.class, Double.class, new Coercion<Float, Double>()
{
@Override
public Double coerce(Float input)
{
return input.doubleValue();
}
});
Coercion primitiveArrayCoercion = new Coercion<Object, List>()
{
@Override
public List<Object> coerce(Object input)
{
int length = Array.getLength(input);
Object[] array = new Object[length];
for (int i = 0; i < length; i++)
{
array[i] = Array.get(input, i);
}
return Arrays.asList(array);
}
};
add(configuration, byte[].class, List.class, primitiveArrayCoercion);
add(configuration, short[].class, List.class, primitiveArrayCoercion);
add(configuration, int[].class, List.class, primitiveArrayCoercion);
add(configuration, long[].class, List.class, primitiveArrayCoercion);
add(configuration, float[].class, List.class, primitiveArrayCoercion);
add(configuration, double[].class, List.class, primitiveArrayCoercion);
add(configuration, char[].class, List.class, primitiveArrayCoercion);
add(configuration, boolean[].class, List.class, primitiveArrayCoercion);
add(configuration, String.class, File.class, new Coercion<String, File>()
{
@Override
public File coerce(String input)
{
return new File(input);
}
});
add(configuration, String.class, TimeInterval.class, new Coercion<String, TimeInterval>()
{
@Override
public TimeInterval coerce(String input)
{
return new TimeInterval(input);
}
});
add(configuration, TimeInterval.class, Long.class, new Coercion<TimeInterval, Long>()
{
@Override
public Long coerce(TimeInterval input)
{
return input.milliseconds();
}
});
add(configuration, Object.class, Object[].class, new Coercion<Object, Object[]>()
{
@Override
public Object[] coerce(Object input)
{
return new Object[]
{input};
}
});
add(configuration, Collection.class, Object[].class, new Coercion<Collection, Object[]>()
{
@Override
public Object[] coerce(Collection input)
{
return input.toArray();
}
});
add(configuration, Flow.class, List.class, new Coercion<Flow, List>()
{
@Override
public List coerce(Flow input)
{
return input.toList();
}
});
add(configuration, Flow.class, Boolean.class, new Coercion<Flow, Boolean>()
{
@Override
public Boolean coerce(Flow input)
{
return !input.isEmpty();
}
});
}
private static <S, T> void add(Configuration<CoercionTuple> configuration, Class<S> sourceType,
Class<T> targetType, Coercion<S, T> coercion)
{
configuration.add(CoercionTuple.create(sourceType, targetType, coercion));
}
/**
* <dl>
* <dt>SystemProperties</dt>
* <dd>Exposes JVM System properties as symbols (currently case-sensitive)</dd>
* <dt>EnvironmentVariables</dt>
* <dd>Exposes environment variables as symbols (adding a "env." prefix)</dd>
* <dt>ApplicationDefaults</dt>
* <dd>Values contributed to @{@link SymbolProvider} @{@link ApplicationDefaults}</dd>
* <dt>FactoryDefaults</dt>
* <dd>Values contributed to @{@link SymbolProvider} @{@link FactoryDefaults}</dd>
* </dl>
*/
@Contribute(SymbolSource.class)
public static void setupStandardSymbolProviders(OrderedConfiguration<SymbolProvider> configuration,
@ApplicationDefaults
SymbolProvider applicationDefaults,
@FactoryDefaults
SymbolProvider factoryDefaults)
{
configuration.add("SystemProperties", new SystemPropertiesSymbolProvider(), "before:*");
configuration.add("EnvironmentVariables", new SystemEnvSymbolProvider());
configuration.add("ApplicationDefaults", applicationDefaults);
configuration.add("FactoryDefaults", factoryDefaults);
}
public static ParallelExecutor buildDeferredExecution(@Symbol(IOCSymbols.THREAD_POOL_CORE_SIZE)
int coreSize,
@Symbol(IOCSymbols.THREAD_POOL_MAX_SIZE)
int maxSize,
@Symbol(IOCSymbols.THREAD_POOL_KEEP_ALIVE)
@IntermediateType(TimeInterval.class)
int keepAliveMillis,
@Symbol(IOCSymbols.THREAD_POOL_ENABLED)
boolean threadPoolEnabled,
@Symbol(IOCSymbols.THREAD_POOL_QUEUE_SIZE)
int queueSize,
PerthreadManager perthreadManager,
RegistryShutdownHub shutdownHub,
ThunkCreator thunkCreator)
{
if (!threadPoolEnabled)
return new NonParallelExecutor();
LinkedBlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(queueSize);
final ThreadPoolExecutor executorService = new ThreadPoolExecutor(coreSize, maxSize, keepAliveMillis,
TimeUnit.MILLISECONDS, workQueue);
shutdownHub.addRegistryShutdownListener(new Runnable()
{
@Override
public void run()
{
executorService.shutdown();
}
});
return new ParallelExecutorImpl(executorService, thunkCreator, perthreadManager);
}
@Contribute(SymbolProvider.class)
@FactoryDefaults
public static void setupDefaultSymbols(MappedConfiguration<String, Object> configuration)
{
configuration.add(IOCSymbols.THREAD_POOL_CORE_SIZE, 3);
configuration.add(IOCSymbols.THREAD_POOL_MAX_SIZE, 20);
configuration.add(IOCSymbols.THREAD_POOL_KEEP_ALIVE, "1 m");
configuration.add(IOCSymbols.THREAD_POOL_ENABLED, true);
configuration.add(IOCSymbols.THREAD_POOL_QUEUE_SIZE, 100);
}
}