001: // Copyright 2006, 2007 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.tapestry.ioc.services;
016:
017: import static org.apache.tapestry.ioc.IOCConstants.PERTHREAD_SCOPE;
018:
019: import java.math.BigDecimal;
020: import java.math.BigInteger;
021: import java.util.Arrays;
022: import java.util.Collection;
023: import java.util.Collections;
024: import java.util.List;
025: import java.util.Map;
026:
027: import org.apache.tapestry.ioc.AnnotationProvider;
028: import org.apache.tapestry.ioc.Configuration;
029: import org.apache.tapestry.ioc.MappedConfiguration;
030: import org.apache.tapestry.ioc.ObjectProvider;
031: import org.apache.tapestry.ioc.OrderedConfiguration;
032: import org.apache.tapestry.ioc.ServiceBinder;
033: import org.apache.tapestry.ioc.ServiceLifecycle;
034: import org.apache.tapestry.ioc.ObjectLocator;
035: import org.apache.tapestry.ioc.annotations.InjectService;
036: import org.apache.tapestry.ioc.annotations.Value;
037: import org.apache.tapestry.ioc.internal.services.ChainBuilderImpl;
038: import org.apache.tapestry.ioc.internal.services.DefaultImplementationBuilderImpl;
039: import org.apache.tapestry.ioc.internal.services.ExceptionAnalyzerImpl;
040: import org.apache.tapestry.ioc.internal.services.ExceptionTrackerImpl;
041: import org.apache.tapestry.ioc.internal.services.LoggingDecoratorImpl;
042: import org.apache.tapestry.ioc.internal.services.MapSymbolProvider;
043: import org.apache.tapestry.ioc.internal.services.PerThreadServiceLifecycle;
044: import org.apache.tapestry.ioc.internal.services.PipelineBuilderImpl;
045: import org.apache.tapestry.ioc.internal.services.PropertyAccessImpl;
046: import org.apache.tapestry.ioc.internal.services.PropertyShadowBuilderImpl;
047: import org.apache.tapestry.ioc.internal.services.StrategyBuilderImpl;
048: import org.apache.tapestry.ioc.internal.services.SymbolObjectProvider;
049: import org.apache.tapestry.ioc.internal.services.SymbolSourceImpl;
050: import org.apache.tapestry.ioc.internal.services.SystemPropertiesSymbolProvider;
051: import org.apache.tapestry.ioc.internal.services.ThreadLocaleImpl;
052: import org.apache.tapestry.ioc.internal.services.TypeCoercerImpl;
053: import org.apache.tapestry.ioc.internal.services.ValueObjectProvider;
054:
055: /**
056: * Defines the base set of services for the Tapestry IOC container.
057: */
058: public final class TapestryIOCModule {
059: public static void bind(ServiceBinder binder) {
060: binder.bind(LoggingDecorator.class, LoggingDecoratorImpl.class);
061: binder.bind(ChainBuilder.class, ChainBuilderImpl.class);
062: binder.bind(PropertyAccess.class, PropertyAccessImpl.class);
063: binder.bind(StrategyBuilder.class, StrategyBuilderImpl.class);
064: binder.bind(PropertyShadowBuilder.class,
065: PropertyShadowBuilderImpl.class);
066: binder.bind(PipelineBuilder.class, PipelineBuilderImpl.class);
067: binder.bind(DefaultImplementationBuilder.class,
068: DefaultImplementationBuilderImpl.class);
069: binder.bind(ExceptionTracker.class, ExceptionTrackerImpl.class);
070: binder.bind(ExceptionAnalyzer.class,
071: ExceptionAnalyzerImpl.class);
072: binder.bind(TypeCoercer.class, TypeCoercerImpl.class);
073: binder.bind(ThreadLocale.class, ThreadLocaleImpl.class);
074: binder.bind(SymbolSource.class, SymbolSourceImpl.class);
075: binder.bind(SymbolProvider.class, MapSymbolProvider.class)
076: .withId("ApplicationDefaults");
077: binder.bind(SymbolProvider.class, MapSymbolProvider.class)
078: .withId("FactoryDefaults");
079: }
080:
081: /**
082: * Provides access to additional service lifecycles. One lifecycles is built in ("singleton")
083: * but additional ones are accessed via this service (and its mapped configuration). Only
084: * proxiable services (those with explicit service interfaces) can be managed in terms of a
085: * lifecycle.
086: */
087: public static ServiceLifecycleSource build(
088: final Map<String, ServiceLifecycle> configuration) {
089: return new ServiceLifecycleSource() {
090: public ServiceLifecycle get(String lifecycleName) {
091: return configuration.get(lifecycleName);
092: }
093: };
094: }
095:
096: /** Contributes the "perthread" scope. */
097: public void contributeServiceLifecycleSource(
098: MappedConfiguration<String, ServiceLifecycle> configuration,
099: ObjectLocator locator) {
100: configuration.add(PERTHREAD_SCOPE, locator
101: .autobuild(PerThreadServiceLifecycle.class));
102: }
103:
104: /**
105: * The master {@link ObjectProvider} is responsible for identifying a particular ObjectProvider
106: * by its prefix, and delegating to that instance.
107: */
108: public static ObjectProvider buildMasterObjectProvider(
109: List<ObjectProvider> configuration,
110:
111: @InjectService("ChainBuilder")
112: ChainBuilder chainBuilder) {
113: return chainBuilder.build(ObjectProvider.class, configuration);
114: }
115:
116: /**
117: * Contributes "DefaultProvider", ordered last, that delegates to
118: * {@link ObjectLocator#getService(Class)}.
119: * <p>
120: * Contributes "Value", which injects values (not services) triggered by the {@link Value}
121: * annotation.
122: */
123: public static void contributeMasterObjectProvider(
124: OrderedConfiguration<ObjectProvider> configuration,
125:
126: ObjectLocator locator) {
127: ObjectProvider defaultProvider = new ObjectProvider() {
128:
129: public <T> T provide(Class<T> objectType,
130: AnnotationProvider annotationProvider,
131: ObjectLocator locator) {
132: return locator.getService(objectType);
133: }
134: };
135:
136: configuration
137: .add("DefaultProvider", defaultProvider, "after:*");
138: configuration.add("Value", locator
139: .autobuild(ValueObjectProvider.class));
140: configuration.add("Symbol", locator
141: .autobuild(SymbolObjectProvider.class));
142: }
143:
144: /**
145: * Contributes a set of standard type coercions:
146: * <ul>
147: * <li>Object to String</li>
148: * <li>String to Double</li>
149: * <li>String to BigDecimal</li>
150: * <li>BigDecimal to Double</li>
151: * <li>Double to BigDecimal</li>
152: * <li>String to BigInteger</li>
153: * <li>BigInteger to Long</li>
154: * <li>String to Long</li>
155: * <li>Long to Byte</li>
156: * <li>Long to Short</li>
157: * <li>Long to Integer</li>
158: * <li>Double to Long</li>
159: * <li>Double to Float</li>
160: * <li>Float to Double</li>
161: * <li>Long to Double</li>
162: * <li>String to Boolean ("false" is always false, other non-blank strings are true)</li>
163: * <li>Long to Boolean (true if long value is non zero)</li>
164: * <li>Null to Boolean (always false)</li>
165: * <li>Null to String (still null)</li>
166: * <li>Collection to Boolean (false if empty)</li>
167: * <li>Object[] to List</li>
168: * <li>Object to List (by wrapping as a singleton list)</li>
169: * <li>Null to List (still null)</li>
170: * </ul>
171: *
172: * @see #buildTypeCoercer(Collection, ComponentInstantiatorSource)
173: */
174:
175: public static void contributeTypeCoercer(
176: Configuration<CoercionTuple> configuration) {
177: add(configuration, Object.class, String.class,
178: new Coercion<Object, String>() {
179: public String coerce(Object input) {
180: return input.toString();
181: }
182: });
183:
184: // This is necessary, otherwise we get a failure because void --> Object : Object --> String
185: // throws an NPE
186:
187: add(configuration, void.class, String.class,
188: new Coercion<Void, String>() {
189: public String coerce(Void input) {
190: return null;
191: }
192: });
193:
194: // This keeps a null -> List from being null -> Object : Object -> List (i.e., an empty List
195: // of a single null).
196:
197: add(configuration, void.class, List.class,
198: new Coercion<Void, List>() {
199: public List coerce(Void input) {
200: return null;
201: }
202: });
203:
204: add(configuration, String.class, Double.class,
205: new Coercion<String, Double>() {
206: public Double coerce(String input) {
207: return new Double(input);
208: }
209: });
210:
211: // String to BigDecimal is important, as String->Double->BigDecimal would lose
212: // precision.
213:
214: add(configuration, String.class, BigDecimal.class,
215: new Coercion<String, BigDecimal>() {
216: public BigDecimal coerce(String input) {
217: return new BigDecimal(input);
218: }
219: });
220:
221: add(configuration, BigDecimal.class, Double.class,
222: new Coercion<BigDecimal, Double>() {
223: public Double coerce(BigDecimal input) {
224: return input.doubleValue();
225: }
226: });
227:
228: add(configuration, String.class, BigInteger.class,
229: new Coercion<String, BigInteger>() {
230: public BigInteger coerce(String input) {
231: return new BigInteger(input);
232: }
233: });
234:
235: add(configuration, String.class, Long.class,
236: new Coercion<String, Long>() {
237: public Long coerce(String input) {
238: return new Long(input);
239: }
240: });
241:
242: add(configuration, Long.class, Byte.class,
243: new Coercion<Long, Byte>() {
244: public Byte coerce(Long input) {
245: return input.byteValue();
246: }
247: });
248:
249: add(configuration, Long.class, Short.class,
250: new Coercion<Long, Short>() {
251: public Short coerce(Long input) {
252: return input.shortValue();
253: }
254: });
255:
256: add(configuration, Long.class, Integer.class,
257: new Coercion<Long, Integer>() {
258: public Integer coerce(Long input) {
259: return input.intValue();
260: }
261: });
262:
263: add(configuration, Number.class, Long.class,
264: new Coercion<Number, Long>() {
265: public Long coerce(Number input) {
266: return input.longValue();
267: }
268: });
269:
270: add(configuration, Double.class, Float.class,
271: new Coercion<Double, Float>() {
272: public Float coerce(Double input) {
273: return input.floatValue();
274: }
275: });
276:
277: add(configuration, Long.class, Double.class,
278: new Coercion<Long, Double>() {
279: public Double coerce(Long input) {
280: return input.doubleValue();
281: }
282: });
283:
284: add(configuration, String.class, Boolean.class,
285: new Coercion<String, Boolean>() {
286: public Boolean coerce(String input) {
287: String trimmed = input.trim();
288:
289: if (trimmed.equalsIgnoreCase("false")
290: || trimmed.length() == 0)
291: return false;
292:
293: // Any non-blank string but "false"
294:
295: return true;
296: }
297: });
298:
299: add(configuration, Long.class, Boolean.class,
300: new Coercion<Long, Boolean>() {
301: public Boolean coerce(Long input) {
302: return input.longValue() != 0;
303: }
304: });
305:
306: add(configuration, void.class, Boolean.class,
307: new Coercion<Void, Boolean>() {
308: public Boolean coerce(Void input) {
309: return false;
310: }
311: });
312:
313: add(configuration, Collection.class, Boolean.class,
314: new Coercion<Collection, Boolean>() {
315: public Boolean coerce(Collection input) {
316: return !input.isEmpty();
317: }
318: });
319:
320: add(configuration, Object.class, List.class,
321: new Coercion<Object, List>() {
322: public List coerce(Object input) {
323: return Collections.singletonList(input);
324: }
325: });
326:
327: add(configuration, Object[].class, List.class,
328: new Coercion<Object[], List>() {
329: public List coerce(Object[] input) {
330: return Arrays.asList(input);
331: }
332: });
333:
334: add(configuration, Float.class, Double.class,
335: new Coercion<Float, Double>() {
336: public Double coerce(Float input) {
337: return input.doubleValue();
338: }
339: });
340: }
341:
342: private static <S, T> void add(
343: Configuration<CoercionTuple> configuration,
344: Class<S> sourceType, Class<T> targetType,
345: Coercion<S, T> coercion) {
346: CoercionTuple<S, T> tuple = new CoercionTuple<S, T>(sourceType,
347: targetType, coercion);
348:
349: configuration.add(tuple);
350: }
351:
352: public static void contributeSymbolSource(
353: OrderedConfiguration<SymbolProvider> configuration,
354: @InjectService("ApplicationDefaults")
355: SymbolProvider applicationDefaults,
356: @InjectService("FactoryDefaults")
357: SymbolProvider factoryDefaults) {
358: configuration.add("SystemProperties",
359: new SystemPropertiesSymbolProvider());
360: configuration.add("ApplicationDefaults", applicationDefaults,
361: "after:SystemProperties");
362: configuration.add("FactoryDefaults", factoryDefaults,
363: "after:ApplicationDefaults");
364: }
365: }
|