All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.tapestry.ioc.services.TapestryIOCModule Maven / Gradle / Ivy

// Copyright 2006, 2007 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.tapestry.ioc.services;

import org.apache.tapestry.ioc.*;
import static org.apache.tapestry.ioc.IOCConstants.PERTHREAD_SCOPE;
import org.apache.tapestry.ioc.annotations.Marker;
import org.apache.tapestry.ioc.annotations.Value;
import org.apache.tapestry.ioc.internal.services.*;
import org.apache.tapestry.ioc.util.TimePeriod;
import org.apache.tapestry.services.MasterObjectProvider;

import java.io.File;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

/**
 * Defines the base set of services for the Tapestry IOC container.
 */
@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);
        binder.bind(DefaultImplementationBuilder.class, DefaultImplementationBuilderImpl.class);
        binder.bind(ExceptionTracker.class, ExceptionTrackerImpl.class);
        binder.bind(ExceptionAnalyzer.class, ExceptionAnalyzerImpl.class);
        binder.bind(TypeCoercer.class, TypeCoercerImpl.class);
        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).withId("RegistryStartup");
        binder.bind(MasterObjectProvider.class, MasterObjectProviderImpl.class);
    }

    /**
     * Provides access to additional service lifecycles. One lifecycles 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.
     */
    public static ServiceLifecycleSource build(final Map configuration)
    {
        return new ServiceLifecycleSource()
        {
            public ServiceLifecycle get(String lifecycleName)
            {
                return configuration.get(lifecycleName);
            }
        };
    }

    /**
     * Contributes the "perthread" scope.
     */
    public void contributeServiceLifecycleSource(MappedConfiguration configuration,
                                                 ObjectLocator locator)
    {
        configuration.add(PERTHREAD_SCOPE, locator.autobuild(PerThreadServiceLifecycle.class));
    }

    /**
     * Contributes "DefaultProvider", ordered last, that delegates to
     * {@link ObjectLocator#getService(Class)}.
     * 

* Contributes "Value", which injects values (not services) triggered by the {@link Value} * annotation. */ public static void contributeMasterObjectProvider(OrderedConfiguration configuration, ObjectLocator locator) { configuration.add("Value", locator.autobuild(ValueObjectProvider.class)); configuration.add("Symbol", locator.autobuild(SymbolObjectProvider.class)); } /** * Contributes a set of standard type coercions to the {@link TypeCoercer} service: *

    *
  • Object to String
  • *
  • String to Double
  • *
  • String to BigDecimal
  • *
  • BigDecimal to Double
  • *
  • Double to BigDecimal
  • *
  • String to BigInteger
  • *
  • BigInteger to Long
  • *
  • String to Long
  • *
  • Long to Byte
  • *
  • Long to Short
  • *
  • Long to Integer
  • *
  • Double to Long
  • *
  • Double to Float
  • *
  • Float to Double
  • *
  • Long to Double
  • *
  • String to Boolean ("false" is always false, other non-blank strings are true)
  • *
  • Long to Boolean (true if long value is non zero)
  • *
  • Null to Boolean (always false)
  • *
  • Null to String (still null)
  • *
  • Collection to Boolean (false if empty)
  • *
  • Object[] to List
  • *
  • primitive[] to List
  • *
  • Object to List (by wrapping as a singleton list)
  • *
  • Null to List (still null)
  • *
  • Null to Long (zero)
  • *
  • Null to BigDecimal (zero)
  • *
  • Null to BigInteger (zero)
  • *
  • String to File
  • *
  • String to {@link org.apache.tapestry.ioc.util.TimePeriod}
  • *
  • {@link org.apache.tapestry.ioc.util.TimePeriod} to Long
  • *
*

* The coercion of String to Long, BigInteger, Double and BigDecimal causes some minor headaches * when attempting to add coercions from null to various numeric types: we end up having to have * many more coercions for the null case to prevent null --> String --> BigInteger. This may * indicate a weakness in the algorithm, in that coercions through String should be considered * "weaker" than other coercions. Alternately, coercions from null may need to be handled * specially. We'll see if we tweak the algorithm in the future. */ @SuppressWarnings("unchecked") public static void contributeTypeCoercer(Configuration configuration) { add(configuration, Object.class, String.class, new Coercion() { public String coerce(Object input) { return input.toString(); } }); // This is necessary, otherwise we get a failure because void --> Object : Object --> String // throws an NPE add(configuration, void.class, String.class, new Coercion() { public String coerce(Void input) { return null; } }); // This keeps a null -> List from being null -> Object : Object -> List (i.e., an empty List // of a single null). add(configuration, void.class, List.class, new Coercion() { public List coerce(Void input) { return null; } }); add(configuration, String.class, Double.class, new Coercion() { 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() { public BigDecimal coerce(String input) { return new BigDecimal(input); } }); add(configuration, BigDecimal.class, Double.class, new Coercion() { public Double coerce(BigDecimal input) { return input.doubleValue(); } }); add(configuration, String.class, BigInteger.class, new Coercion() { public BigInteger coerce(String input) { return new BigInteger(input); } }); add(configuration, String.class, Long.class, new Coercion() { public Long coerce(String input) { return new Long(input); } }); add(configuration, Long.class, Byte.class, new Coercion() { public Byte coerce(Long input) { return input.byteValue(); } }); add(configuration, Long.class, Short.class, new Coercion() { public Short coerce(Long input) { return input.shortValue(); } }); add(configuration, Long.class, Integer.class, new Coercion() { public Integer coerce(Long input) { return input.intValue(); } }); add(configuration, Number.class, Long.class, new Coercion() { public Long coerce(Number input) { return input.longValue(); } }); add(configuration, Double.class, Float.class, new Coercion() { public Float coerce(Double input) { return input.floatValue(); } }); add(configuration, Long.class, Double.class, new Coercion() { public Double coerce(Long input) { return input.doubleValue(); } }); add(configuration, String.class, Boolean.class, new Coercion() { public Boolean coerce(String input) { String trimmed = input.trim(); if (trimmed.equalsIgnoreCase("false") || trimmed.length() == 0) return false; // Any non-blank string but "false" return true; } }); add(configuration, Long.class, Boolean.class, new Coercion() { public Boolean coerce(Long input) { return input.longValue() != 0; } }); add(configuration, void.class, Boolean.class, new Coercion() { public Boolean coerce(Void input) { return false; } }); add(configuration, void.class, Long.class, new Coercion() { public Long coerce(Void input) { return 0l; } }); add(configuration, void.class, BigDecimal.class, new Coercion() { public BigDecimal coerce(Void input) { return BigDecimal.ZERO; } }); add(configuration, void.class, BigInteger.class, new Coercion() { public BigInteger coerce(Void input) { return BigInteger.ZERO; } }); add(configuration, void.class, Double.class, new Coercion() { public Double coerce(Void input) { return 0d; } }); add(configuration, Collection.class, Boolean.class, new Coercion() { public Boolean coerce(Collection input) { return !input.isEmpty(); } }); add(configuration, Object.class, List.class, new Coercion() { public List coerce(Object input) { return Collections.singletonList(input); } }); add(configuration, Object[].class, List.class, new Coercion() { public List coerce(Object[] input) { return Arrays.asList(input); } }); add(configuration, Float.class, Double.class, new Coercion() { public Double coerce(Float input) { return input.doubleValue(); } }); Coercion primitiveArrayCoercion = new Coercion() { public List 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() { public File coerce(String input) { return new File(input); } }); add(configuration, String.class, TimePeriod.class, new Coercion() { public TimePeriod coerce(String input) { return new TimePeriod(input); } }); add(configuration, TimePeriod.class, Long.class, new Coercion() { public Long coerce(TimePeriod input) { return input.milliseconds(); } }); } private static void add(Configuration configuration, Class sourceType, Class targetType, Coercion coercion) { CoercionTuple tuple = new CoercionTuple(sourceType, targetType, coercion); configuration.add(tuple); } public static void contributeSymbolSource(OrderedConfiguration configuration, @ApplicationDefaults SymbolProvider applicationDefaults, @FactoryDefaults SymbolProvider factoryDefaults) { configuration.add("SystemProperties", new SystemPropertiesSymbolProvider()); configuration.add("ApplicationDefaults", applicationDefaults, "after:SystemProperties"); configuration.add("FactoryDefaults", factoryDefaults, "after:ApplicationDefaults"); } }