001// Copyright 2006, 2007, 2008, 2009, 2010, 2011 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.internal;
016
017import org.apache.tapestry5.commons.*;
018import org.apache.tapestry5.commons.internal.util.*;
019import org.apache.tapestry5.commons.services.PlasticProxyFactory;
020import org.apache.tapestry5.commons.util.CollectionFactory;
021import org.apache.tapestry5.ioc.ModuleBuilderSource;
022import org.apache.tapestry5.ioc.ServiceResources;
023import org.apache.tapestry5.ioc.def.ContributionDef3;
024import org.apache.tapestry5.ioc.internal.util.DelegatingInjectionResources;
025import org.apache.tapestry5.ioc.internal.util.InjectionResources;
026import org.apache.tapestry5.ioc.internal.util.InternalUtils;
027import org.apache.tapestry5.ioc.internal.util.MapInjectionResources;
028import org.apache.tapestry5.ioc.internal.util.WrongConfigurationTypeGuard;
029import org.slf4j.Logger;
030
031import java.lang.reflect.InvocationTargetException;
032import java.lang.reflect.Method;
033import java.util.Map;
034import java.util.Set;
035
036public class ContributionDefImpl implements ContributionDef3
037{
038    private final String serviceId;
039
040    private final Method contributorMethod;
041
042    private final boolean optional;
043
044    private final PlasticProxyFactory proxyFactory;
045
046    private final Set<Class> markers;
047
048    private final Class serviceInterface;
049
050    private static final Class[] CONFIGURATION_TYPES = new Class[]
051            {Configuration.class, MappedConfiguration.class, OrderedConfiguration.class};
052
053    public ContributionDefImpl(String serviceId, Method contributorMethod, boolean optional, PlasticProxyFactory proxyFactory,
054                               Class serviceInterface, Set<Class> markers)
055    {
056        this.serviceId = serviceId;
057        this.contributorMethod = contributorMethod;
058        this.optional = optional;
059        this.proxyFactory = proxyFactory;
060        this.serviceInterface = serviceInterface;
061        this.markers = markers;
062    }
063
064    @Override
065    public String toString()
066    {
067        return InternalUtils.asString(contributorMethod, proxyFactory);
068    }
069
070    @Override
071    public boolean isOptional()
072    {
073        return optional;
074    }
075
076    @Override
077    public String getServiceId()
078    {
079        return serviceId;
080    }
081
082    @Override
083    public void contribute(ModuleBuilderSource moduleSource, ServiceResources resources, Configuration configuration)
084    {
085        invokeMethod(moduleSource, resources, Configuration.class, configuration);
086    }
087
088    @Override
089    public void contribute(ModuleBuilderSource moduleSource, ServiceResources resources,
090                           OrderedConfiguration configuration)
091    {
092        invokeMethod(moduleSource, resources, OrderedConfiguration.class, configuration);
093    }
094
095    @Override
096    public void contribute(ModuleBuilderSource moduleSource, ServiceResources resources,
097                           MappedConfiguration configuration)
098    {
099        invokeMethod(moduleSource, resources, MappedConfiguration.class, configuration);
100    }
101
102    private <T> void invokeMethod(ModuleBuilderSource source, ServiceResources resources, Class<T> parameterType,
103                                  T parameterValue)
104    {
105        Map<Class, Object> resourceMap = CollectionFactory.newMap();
106
107        resourceMap.put(parameterType, parameterValue);
108        resourceMap.put(ObjectLocator.class, resources);
109        resourceMap.put(Logger.class, resources.getLogger());
110
111        InjectionResources injectionResources = new MapInjectionResources(resourceMap);
112
113        // For each of the other configuration types that is not expected, add a guard.
114
115        for (Class t : CONFIGURATION_TYPES)
116        {
117            if (parameterType != t)
118            {
119                injectionResources = new DelegatingInjectionResources(new WrongConfigurationTypeGuard(
120                        resources.getServiceId(), t, parameterType), injectionResources);
121            }
122        }
123
124        Throwable fail = null;
125
126        Object moduleInstance = InternalUtils.isStatic(contributorMethod) ? null : source.getModuleBuilder();
127
128        try
129        {
130            ObjectCreator[] parameters = InternalUtils.calculateParametersForMethod(contributorMethod, resources,
131                    injectionResources, resources.getTracker());
132
133            contributorMethod.invoke(moduleInstance, InternalUtils.realizeObjects(parameters));
134        } catch (InvocationTargetException ex)
135        {
136            fail = ex.getTargetException();
137        } catch (Exception ex)
138        {
139            fail = ex;
140        }
141
142        if (fail != null)
143            throw new RuntimeException(IOCMessages.contributionMethodError(contributorMethod, fail), fail);
144    }
145
146    @Override
147    public Set<Class> getMarkers()
148    {
149        return markers;
150    }
151
152    @Override
153    public Class getServiceInterface()
154    {
155        return serviceInterface;
156    }
157}