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

org.apache.tapestry.ioc.internal.ModuleImpl 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.internal;

import org.apache.tapestry.ioc.*;
import org.apache.tapestry.ioc.def.ContributionDef;
import org.apache.tapestry.ioc.def.DecoratorDef;
import org.apache.tapestry.ioc.def.ModuleDef;
import org.apache.tapestry.ioc.def.ServiceDef;
import org.apache.tapestry.ioc.internal.services.JustInTimeObjectCreator;
import static org.apache.tapestry.ioc.internal.util.CollectionFactory.*;
import static org.apache.tapestry.ioc.internal.util.Defense.notBlank;
import static org.apache.tapestry.ioc.internal.util.Defense.notNull;
import org.apache.tapestry.ioc.internal.util.InternalUtils;
import org.apache.tapestry.ioc.services.*;
import org.slf4j.Logger;

import java.io.ObjectStreamException;
import java.io.Serializable;
import static java.lang.String.format;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.*;

public class ModuleImpl implements Module
{
    private final InternalRegistry _registry;

    private final ServiceActivityTracker _tracker;

    private final ModuleDef _moduleDef;

    private final ClassFactory _classFactory;

    private final Logger _logger;

    // Guarded by MUTEX
    private Object _moduleBuilder;

    /**
     * A single mutex, shared by all modules, that serializes creation of services across all
     * threads. This is a bit draconian, but appears to be necessary. Fortunately, service creation
     * is a very tiny part of any individual service's lifecycle.
     */
    private static final Object MUTEX = new Object();

    // Set to true when invoking the module constructor. Used to
    // detect endless loops caused by irresponsible dependencies in
    // the constructor. Guarded by MUTEX.
    private boolean _insideConstructor;

    /**
     * Keyed on fully qualified service id; values are instantiated services (proxies).
     */
    private final Map _services = newCaseInsensitiveMap();

    public ModuleImpl(InternalRegistry registry, ServiceActivityTracker tracker, ModuleDef moduleDef,
                      ClassFactory classFactory, Logger logger)
    {
        _registry = registry;
        _tracker = tracker;
        _moduleDef = moduleDef;
        _classFactory = classFactory;
        _logger = logger;
    }

    public  T getService(String serviceId, Class serviceInterface)
    {
        notBlank(serviceId, "serviceId");
        notNull(serviceInterface, "serviceInterface");
        // module may be null.

        ServiceDef def = _moduleDef.getServiceDef(serviceId);

        // RegistryImpl should already have checked that the service exists.
        assert def != null;

        Object service = findOrCreate(def, null);

        try
        {
            return serviceInterface.cast(service);
        }
        catch (ClassCastException ex)
        {
            // This may be overkill: I don't know how this could happen
            // given that the return type of the method determines
            // the service interface.

            throw new RuntimeException(IOCMessages.serviceWrongInterface(serviceId, def
                    .getServiceInterface(), serviceInterface));
        }
    }

    public Set findMatchingDecoratorDefs(ServiceDef serviceDef)
    {
        Set result = newSet();

        for (DecoratorDef def : _moduleDef.getDecoratorDefs())
        {
            if (def.matches(serviceDef)) result.add(def);
        }

        return result;
    }

    public List findDecoratorsForService(String serviceId)
    {
        ServiceDef sd = _moduleDef.getServiceDef(serviceId);

        return _registry.findDecoratorsForService(sd);
    }

    @SuppressWarnings("unchecked")
    public Collection findServiceIdsForInterface(Class serviceInterface)
    {
        notNull(serviceInterface, "serviceInterface");

        Collection result = newList();

        for (String id : _moduleDef.getServiceIds())
        {
            ServiceDef def = _moduleDef.getServiceDef(id);

            if (serviceInterface.isAssignableFrom(def.getServiceInterface())) result.add(id);
        }

        return result;
    }

    /**
     * Locates the service proxy for a particular service (from the service definition).
     * 

* Access is synchronized via {@link #MUTEX}. * * @param def defines the service * @param eagerLoadProxies TODO * @return the service proxy */ private Object findOrCreate(ServiceDef def, List eagerLoadProxies) { synchronized (MUTEX) { String key = def.getServiceId(); Object result = _services.get(key); if (result == null) { result = create(def, eagerLoadProxies); _services.put(key, result); } return result; } } public void eagerLoadServices() { List proxies = newList(); synchronized (MUTEX) { for (String serviceId : _moduleDef.getServiceIds()) { ServiceDef def = _moduleDef.getServiceDef(serviceId); if (def.isEagerLoad()) findOrCreate(def, proxies); } for (EagerLoadServiceProxy proxy : proxies) proxy.eagerLoadService(); } } /** * Creates the service and updates the cache of created services. Access is synchronized via * {@link #MUTEX}. * * @param eagerLoadProxies a list into which any eager loaded proxies should be added */ private Object create(ServiceDef def, List eagerLoadProxies) { String serviceId = def.getServiceId(); Logger logger = _registry.getServiceLogger(serviceId); if (logger.isDebugEnabled()) logger.debug(IOCMessages.creatingService(serviceId)); try { ServiceBuilderResources resources = new ServiceResourcesImpl(_registry, this, def, _classFactory, logger); // Build up a stack of operations that will be needed to realize the service // (by the proxy, at a later date). ObjectCreator creator = def.createServiceCreator(resources); Class serviceInterface = def.getServiceInterface(); // For non-proxyable services, we immediately create the service implementation // and return it. There's no interface to proxy, which throws out the possibility of // deferred instantiation, service lifecycles, and decorators. if (!serviceInterface.isInterface()) return creator.createObject(); creator = new LifecycleWrappedServiceCreator(_registry, def.getServiceScope(), resources, creator); // Don't allow the core IOC services services to be decorated. if (!TapestryIOCModule.class.equals(_moduleDef.getBuilderClass())) creator = new InterceptorStackBuilder(this, serviceId, creator); // Add a wrapper that checks for recursion. creator = new RecursiveServiceCreationCheckWrapper(def, creator, logger); JustInTimeObjectCreator delegate = new JustInTimeObjectCreator(_tracker, creator, serviceId); Object proxy = createProxy(resources, delegate); _registry.addRegistryShutdownListener(delegate); // Occasionally service A may invoke service B from its service builder method; if // service B // is eager loaded, we'll hit this method but eagerLoadProxies will be null. That's OK // ... service B // is being realized anyway. if (def.isEagerLoad() && eagerLoadProxies != null) eagerLoadProxies.add(delegate); _tracker.setStatus(serviceId, Status.VIRTUAL); return proxy; } catch (Exception ex) { throw new RuntimeException(IOCMessages.errorBuildingService(serviceId, def, ex), ex); } } public Object getModuleBuilder() { synchronized (MUTEX) { if (_moduleBuilder == null) _moduleBuilder = instantiateModuleBuilder(); return _moduleBuilder; } } /** * Access synchronized by MUTEX. */ private Object instantiateModuleBuilder() { Class builderClass = _moduleDef.getBuilderClass(); Constructor[] constructors = builderClass.getConstructors(); if (constructors.length == 0) throw new RuntimeException(IOCMessages.noPublicConstructors(builderClass)); if (constructors.length > 1) { // Sort the constructors ascending by number of parameters (descending); this is really // just to allow the test suite to work properly across different JVMs (which will // often order the constructors differently). Comparator comparator = new Comparator() { public int compare(Constructor c1, Constructor c2) { return c2.getParameterTypes().length - c1.getParameterTypes().length; } }; Arrays.sort(constructors, comparator); _logger.warn(IOCMessages.tooManyPublicConstructors(builderClass, constructors[0])); } Constructor constructor = constructors[0]; if (_insideConstructor) throw new RuntimeException(IOCMessages.recursiveModuleConstructor(builderClass, constructor)); ObjectLocator locator = new ObjectLocatorImpl(_registry, this); Map parameterDefaults = newMap(); parameterDefaults.put(Logger.class, _logger); parameterDefaults.put(ObjectLocator.class, locator); Throwable fail = null; try { _insideConstructor = true; Object[] parameterValues = InternalUtils.calculateParameters(locator, parameterDefaults, constructor.getParameterTypes(), constructor.getParameterAnnotations()); return constructor.newInstance(parameterValues); } catch (InvocationTargetException ex) { fail = ex.getTargetException(); } catch (Exception ex) { fail = ex; } finally { _insideConstructor = false; } throw new RuntimeException(IOCMessages.instantiateBuilderError(builderClass, fail), fail); } private Object createProxy(ServiceResources resources, ObjectCreator creator) { String serviceId = resources.getServiceId(); Class serviceInterface = resources.getServiceInterface(); String toString = format("", serviceId, serviceInterface.getName()); return createProxyInstance(creator, serviceId, serviceInterface, toString); } private Object createProxyInstance(ObjectCreator creator, String serviceId, Class serviceInterface, String description) { ServiceProxyToken token = SerializationSupport.createToken(serviceId); ClassFab classFab = _registry.newClass(serviceInterface); classFab.addField("_creator", Modifier.PRIVATE | Modifier.FINAL, ObjectCreator.class); classFab.addField("_token", Modifier.PRIVATE | Modifier.FINAL, ServiceProxyToken.class); classFab.addConstructor(new Class[]{ObjectCreator.class, ServiceProxyToken.class}, null, "{ _creator = $1; _token = $2; }"); // Make proxies serializable by writing the token to the stream. classFab.addInterface(Serializable.class); // This is the "magic" signature that allows an object to substitute some other // object for itself. MethodSignature writeReplaceSig = new MethodSignature(Object.class, "writeReplace", null, new Class[]{ObjectStreamException.class}); classFab.addMethod(Modifier.PRIVATE, writeReplaceSig, "return _token;"); // Now delegate all the methods. String body = format("return (%s) _creator.createObject();", serviceInterface.getName()); MethodSignature sig = new MethodSignature(serviceInterface, "_delegate", null, null); classFab.addMethod(Modifier.PRIVATE, sig, body); classFab.proxyMethodsToDelegate(serviceInterface, "_delegate()", description); Class proxyClass = classFab.createClass(); try { return proxyClass.getConstructors()[0].newInstance(creator, token); } catch (Exception ex) { // Exceptions should not happen. throw new RuntimeException(ex.getMessage(), ex); } } public Set getContributorDefsForService(String serviceId) { Set result = newSet(); for (ContributionDef def : _moduleDef.getContributionDefs()) { if (def.getServiceId().equals(serviceId)) result.add(def); } return result; } public ServiceDef getServiceDef(String serviceId) { return _moduleDef.getServiceDef(serviceId); } public String getLoggerName() { return _moduleDef.getLoggerName(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy