001 // Copyright 2006, 2007, 2008, 2009 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
015 package org.apache.tapestry5.ioc.services;
016
017 import org.apache.tapestry5.IOCSymbols;
018 import org.apache.tapestry5.ioc.*;
019 import org.apache.tapestry5.ioc.annotations.*;
020 import org.apache.tapestry5.ioc.internal.services.*;
021 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
022 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
023 import org.apache.tapestry5.ioc.util.TimeInterval;
024
025 import java.io.File;
026 import java.lang.reflect.Array;
027 import java.math.BigDecimal;
028 import java.math.BigInteger;
029 import java.util.*;
030 import java.util.concurrent.LinkedBlockingQueue;
031 import java.util.concurrent.ThreadPoolExecutor;
032 import java.util.concurrent.TimeUnit;
033
034 /**
035 * Defines the base set of services for the Tapestry IOC container.
036 */
037 @Marker(Builtin.class)
038 @PreventServiceDecoration
039 public final class TapestryIOCModule
040 {
041 public static void bind(ServiceBinder binder)
042 {
043 binder.bind(LoggingDecorator.class, LoggingDecoratorImpl.class);
044 binder.bind(ChainBuilder.class, ChainBuilderImpl.class);
045 binder.bind(PropertyAccess.class, PropertyAccessImpl.class);
046 binder.bind(StrategyBuilder.class, StrategyBuilderImpl.class);
047 binder.bind(PropertyShadowBuilder.class, PropertyShadowBuilderImpl.class);
048 binder.bind(PipelineBuilder.class, PipelineBuilderImpl.class);
049 binder.bind(DefaultImplementationBuilder.class, DefaultImplementationBuilderImpl.class);
050 binder.bind(ExceptionTracker.class, ExceptionTrackerImpl.class);
051 binder.bind(ExceptionAnalyzer.class, ExceptionAnalyzerImpl.class);
052 binder.bind(TypeCoercer.class, TypeCoercerImpl.class);
053 binder.bind(ThreadLocale.class, ThreadLocaleImpl.class);
054 binder.bind(SymbolSource.class, SymbolSourceImpl.class);
055 binder.bind(SymbolProvider.class, MapSymbolProvider.class).withId("ApplicationDefaults")
056 .withMarker(ApplicationDefaults.class);
057 binder.bind(SymbolProvider.class, MapSymbolProvider.class).withId("FactoryDefaults")
058 .withMarker(FactoryDefaults.class);
059 binder.bind(Runnable.class, RegistryStartup.class).withId("RegistryStartup");
060 binder.bind(MasterObjectProvider.class, MasterObjectProviderImpl.class);
061 binder.bind(ClassNameLocator.class, ClassNameLocatorImpl.class);
062 binder.bind(AspectDecorator.class, AspectDecoratorImpl.class);
063 binder.bind(ClasspathURLConverter.class, ClasspathURLConverterImpl.class);
064 binder.bind(ServiceOverride.class, ServiceOverrideImpl.class);
065 binder.bind(LoggingAdvisor.class, LoggingAdvisorImpl.class);
066 binder.bind(LazyAdvisor.class, LazyAdvisorImpl.class);
067 binder.bind(ThunkCreator.class, ThunkCreatorImpl.class);
068 }
069
070 /**
071 * Provides access to additional service lifecycles. One lifecycle is built in ("singleton") but additional ones are
072 * accessed via this service (and its mapped configuration). Only proxiable services (those with explicit service
073 * interfaces) can be managed in terms of a lifecycle.
074 */
075 public static ServiceLifecycleSource build(Map<String, ServiceLifecycle> configuration)
076 {
077 final Map<String, ServiceLifecycle2> lifecycles = CollectionFactory.newCaseInsensitiveMap();
078
079 for (String name : configuration.keySet())
080 {
081 lifecycles.put(name, InternalUtils.toServiceLifecycle2(configuration.get(name)));
082 }
083
084 return new ServiceLifecycleSource()
085 {
086 public ServiceLifecycle get(String scope)
087 {
088 return lifecycles.get(scope);
089 }
090 };
091 }
092
093 /**
094 * Contributes the "perthread" scope.
095 */
096 public static void contributeServiceLifecycleSource(MappedConfiguration<String, ServiceLifecycle> configuration)
097 {
098 configuration.addInstance(ScopeConstants.PERTHREAD, PerThreadServiceLifecycle.class);
099 }
100
101 /**
102 * <dl> <dt>AnnotationBasedContributions</dt> <dd>Empty placeholder used to seperate annotation-based ObjectProvider
103 * contributions (which come before) from non-annotation based (ServiceOverride here, Alias in tapestry-core) which
104 * come after. </dd> <dt>Value</dt> <dd>Supports the {@link org.apache.tapestry5.ioc.annotations.Value}
105 * annotation</dd> <dt>Symbol</dt> <dd>Supports the {@link org.apache.tapestry5.ioc.annotations.Symbol}
106 * annotations</dd> <dt>Autobuild</dt> <dd>Supports the {@link org.apache.tapestry5.ioc.annotations.Autobuild}
107 * annotation</dd> <dt>ServiceOverride</dt> <dd>Allows simple service overrides via the {@link
108 * org.apache.tapestry5.ioc.services.ServiceOverride} service (and its configuration)</dl>
109 */
110 public static void contributeMasterObjectProvider(OrderedConfiguration<ObjectProvider> configuration,
111 @Local final ServiceOverride serviceOverride)
112 {
113 configuration.add("AnnotationBasedContributions", null);
114
115 configuration.addInstance("Value", ValueObjectProvider.class, "before:AnnotationBasedContributions");
116 configuration.addInstance("Symbol", SymbolObjectProvider.class, "before:AnnotationBasedContributions");
117 configuration.add("Autobuild", new AutobuildObjectProvider(), "before:AnnotationBasedContributions");
118
119
120 ObjectProvider wrapper = new ObjectProvider()
121 {
122 public <T> T provide(Class<T> objectType, AnnotationProvider annotationProvider, ObjectLocator locator)
123 {
124 return serviceOverride.getServiceOverrideProvider().provide(objectType, annotationProvider, locator);
125 }
126 };
127
128 configuration.add("ServiceOverride", wrapper, "after:AnnotationBasedContributions");
129 }
130
131 /**
132 * Contributes a set of standard type coercions to the {@link TypeCoercer} service: <ul> <li>Object to String</li>
133 * <li>String to Double</li> <li>String to BigDecimal</li> <li>BigDecimal to Double</li> <li>Double to
134 * BigDecimal</li> <li>String to BigInteger</li> <li>BigInteger to Long</li> <li>String to Long</li> <li>Long to
135 * Byte</li> <li>Long to Short</li> <li>Long to Integer</li> <li>Double to Long</li> <li>Double to Float</li>
136 * <li>Float to Double</li> <li>Long to Double</li> <li>String to Boolean ("false" is always false, other non-blank
137 * strings are true)</li> <li>Long to Boolean (true if long value is non zero)</li> <li>Null to Boolean (always
138 * false)</li> <li>Collection to Boolean (false if empty)</li> <li>Object[] to List</li> <li>primitive[] to
139 * List</li> <li>Object to List (by wrapping as a singleton list)</li> <li>String to File</li> <li>String to {@link
140 * org.apache.tapestry5.ioc.util.TimeInterval}</li> <li>{@link org.apache.tapestry5.ioc.util.TimeInterval} to
141 * Long</li> <li>Object to Object[] (wrapping the object as an array)</li> <li>Collection to Object[] (via the
142 * toArray() method)</ul>
143 */
144 @SuppressWarnings("unchecked")
145 public static void contributeTypeCoercer(Configuration<CoercionTuple> configuration)
146 {
147 add(configuration, Object.class, String.class, new Coercion<Object, String>()
148 {
149 public String coerce(Object input)
150 {
151 return input.toString();
152 }
153 });
154
155 add(configuration, String.class, Double.class, new Coercion<String, Double>()
156 {
157 public Double coerce(String input)
158 {
159 return new Double(input);
160 }
161 });
162
163 // String to BigDecimal is important, as String->Double->BigDecimal would lose
164 // precision.
165
166 add(configuration, String.class, BigDecimal.class, new Coercion<String, BigDecimal>()
167 {
168 public BigDecimal coerce(String input)
169 {
170 return new BigDecimal(input);
171 }
172 });
173
174 add(configuration, BigDecimal.class, Double.class, new Coercion<BigDecimal, Double>()
175 {
176 public Double coerce(BigDecimal input)
177 {
178 return input.doubleValue();
179 }
180 });
181
182 add(configuration, String.class, BigInteger.class, new Coercion<String, BigInteger>()
183 {
184 public BigInteger coerce(String input)
185 {
186 return new BigInteger(input);
187 }
188 });
189
190 add(configuration, String.class, Long.class, new Coercion<String, Long>()
191 {
192 public Long coerce(String input)
193 {
194 return new Long(input);
195 }
196 });
197
198 add(configuration, Long.class, Byte.class, new Coercion<Long, Byte>()
199 {
200 public Byte coerce(Long input)
201 {
202 return input.byteValue();
203 }
204 });
205
206 add(configuration, Long.class, Short.class, new Coercion<Long, Short>()
207 {
208 public Short coerce(Long input)
209 {
210 return input.shortValue();
211 }
212 });
213
214 add(configuration, Long.class, Integer.class, new Coercion<Long, Integer>()
215 {
216 public Integer coerce(Long input)
217 {
218 return input.intValue();
219 }
220 });
221
222 add(configuration, Number.class, Long.class, new Coercion<Number, Long>()
223 {
224 public Long coerce(Number input)
225 {
226 return input.longValue();
227 }
228 });
229
230 add(configuration, Double.class, Float.class, new Coercion<Double, Float>()
231 {
232 public Float coerce(Double input)
233 {
234 return input.floatValue();
235 }
236 });
237
238 add(configuration, Long.class, Double.class, new Coercion<Long, Double>()
239 {
240 public Double coerce(Long input)
241 {
242 return input.doubleValue();
243 }
244 });
245
246 add(configuration, String.class, Boolean.class, new Coercion<String, Boolean>()
247 {
248 public Boolean coerce(String input)
249 {
250 String trimmed = input.trim();
251
252 if (trimmed.equalsIgnoreCase("false") || trimmed.length() == 0) return false;
253
254 // Any non-blank string but "false"
255
256 return true;
257 }
258 });
259
260 add(configuration, Long.class, Boolean.class, new Coercion<Long, Boolean>()
261 {
262 public Boolean coerce(Long input)
263 {
264 return input.longValue() != 0;
265 }
266 });
267
268 add(configuration, void.class, Boolean.class, new Coercion<Void, Boolean>()
269 {
270 public Boolean coerce(Void input)
271 {
272 return false;
273 }
274 });
275
276
277 add(configuration, Collection.class, Boolean.class, new Coercion<Collection, Boolean>()
278 {
279 public Boolean coerce(Collection input)
280 {
281 return !input.isEmpty();
282 }
283 });
284
285 add(configuration, Object.class, List.class, new Coercion<Object, List>()
286 {
287 public List coerce(Object input)
288 {
289 return Collections.singletonList(input);
290 }
291 });
292
293 add(configuration, Object[].class, List.class, new Coercion<Object[], List>()
294 {
295 public List coerce(Object[] input)
296 {
297 return Arrays.asList(input);
298 }
299 });
300
301 add(configuration, Float.class, Double.class, new Coercion<Float, Double>()
302 {
303 public Double coerce(Float input)
304 {
305 return input.doubleValue();
306 }
307 });
308
309 Coercion primitiveArrayCoercion = new Coercion<Object, List>()
310 {
311 public List<Object> coerce(Object input)
312 {
313 int length = Array.getLength(input);
314 Object[] array = new Object[length];
315 for (int i = 0; i < length; i++)
316 {
317 array[i] = Array.get(input, i);
318 }
319 return Arrays.asList(array);
320 }
321 };
322
323 add(configuration, byte[].class, List.class, primitiveArrayCoercion);
324 add(configuration, short[].class, List.class, primitiveArrayCoercion);
325 add(configuration, int[].class, List.class, primitiveArrayCoercion);
326 add(configuration, long[].class, List.class, primitiveArrayCoercion);
327 add(configuration, float[].class, List.class, primitiveArrayCoercion);
328 add(configuration, double[].class, List.class, primitiveArrayCoercion);
329 add(configuration, char[].class, List.class, primitiveArrayCoercion);
330 add(configuration, boolean[].class, List.class, primitiveArrayCoercion);
331
332 add(configuration, String.class, File.class, new Coercion<String, File>()
333 {
334 public File coerce(String input)
335 {
336 return new File(input);
337 }
338 });
339
340 add(configuration, String.class, TimeInterval.class, new Coercion<String, TimeInterval>()
341 {
342 public TimeInterval coerce(String input)
343 {
344 return new TimeInterval(input);
345 }
346 });
347
348 add(configuration, TimeInterval.class, Long.class, new Coercion<TimeInterval, Long>()
349 {
350 public Long coerce(TimeInterval input)
351 {
352 return input.milliseconds();
353 }
354 });
355
356 add(configuration, Object.class, Object[].class, new Coercion<Object, Object[]>()
357 {
358 public Object[] coerce(Object input)
359 {
360 return new Object[] { input };
361 }
362 });
363
364 add(configuration, Collection.class, Object[].class, new Coercion<Collection, Object[]>()
365 {
366 public Object[] coerce(Collection input)
367 {
368 return input.toArray();
369 }
370 });
371 }
372
373 private static <S, T> void add(Configuration<CoercionTuple> configuration, Class<S> sourceType, Class<T> targetType,
374 Coercion<S, T> coercion)
375 {
376 CoercionTuple<S, T> tuple = new CoercionTuple<S, T>(sourceType, targetType, coercion);
377
378 configuration.add(tuple);
379 }
380
381 public static void contributeSymbolSource(OrderedConfiguration<SymbolProvider> configuration,
382 @ApplicationDefaults SymbolProvider applicationDefaults,
383
384 @FactoryDefaults SymbolProvider factoryDefaults)
385 {
386 configuration.add("SystemProperties", new SystemPropertiesSymbolProvider(), "before:*");
387 configuration.add("ApplicationDefaults", applicationDefaults, "after:SystemProperties");
388 configuration.add("FactoryDefaults", factoryDefaults, "after:ApplicationDefaults");
389 }
390
391 public static ParallelExecutor buildDeferredExecution(
392 @Symbol(IOCSymbols.THREAD_POOL_CORE_SIZE)
393 int coreSize,
394
395 @Symbol(IOCSymbols.THREAD_POOL_MAX_SIZE)
396 int maxSize,
397
398 @Symbol(IOCSymbols.THREAD_POOL_KEEP_ALIVE)
399 @IntermediateType(TimeInterval.class)
400 int keepAliveMillis,
401
402 @Symbol(IOCSymbols.THREAD_POOL_ENABLED)
403 boolean threadPoolEnabled,
404
405 PerthreadManager perthreadManager,
406
407 RegistryShutdownHub shutdownHub,
408
409 ThunkCreator thunkCreator)
410 {
411
412 if (!threadPoolEnabled)
413 return new NonParallelExecutor();
414
415 final ThreadPoolExecutor executorService = new ThreadPoolExecutor(coreSize, maxSize,
416 keepAliveMillis, TimeUnit.MILLISECONDS,
417 new LinkedBlockingQueue(maxSize));
418
419 shutdownHub.addRegistryShutdownListener(new RegistryShutdownListener()
420 {
421 public void registryDidShutdown()
422 {
423 executorService.shutdown();
424 }
425 });
426
427 return new ParallelExecutorImpl(executorService, thunkCreator, perthreadManager);
428 }
429
430 public static void contributeFactoryDefaults(MappedConfiguration<String, String> configuration)
431 {
432 configuration.add(IOCSymbols.THREAD_POOL_CORE_SIZE, "3");
433 configuration.add(IOCSymbols.THREAD_POOL_MAX_SIZE, "20");
434 configuration.add(IOCSymbols.THREAD_POOL_KEEP_ALIVE, "1 m");
435 configuration.add(IOCSymbols.THREAD_POOL_ENABLED, "true");
436 }
437 }