
org.apache.tapestry5.ioc.internal.ModuleImpl Maven / Gradle / Ivy
The newest version!
// Copyright 2006, 2007, 2008, 2009, 2010, 2011, 2012 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.tapestry5.ioc.internal;
import org.apache.tapestry5.commons.*;
import org.apache.tapestry5.commons.internal.util.*;
import org.apache.tapestry5.commons.services.PlasticProxyFactory;
import org.apache.tapestry5.commons.services.TypeCoercer;
import org.apache.tapestry5.commons.util.CollectionFactory;
import org.apache.tapestry5.ioc.AdvisorDef;
import org.apache.tapestry5.ioc.Invokable;
import org.apache.tapestry5.ioc.Markable;
import org.apache.tapestry5.ioc.OperationTracker;
import org.apache.tapestry5.ioc.ServiceBuilderResources;
import org.apache.tapestry5.ioc.ServiceLifecycle2;
import org.apache.tapestry5.ioc.ServiceResources;
import org.apache.tapestry5.ioc.annotations.Local;
import org.apache.tapestry5.ioc.def.*;
import org.apache.tapestry5.ioc.internal.services.JustInTimeObjectCreator;
import org.apache.tapestry5.ioc.internal.util.ConcurrentBarrier;
import org.apache.tapestry5.ioc.internal.util.InjectionResources;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.ioc.internal.util.MapInjectionResources;
import org.apache.tapestry5.ioc.services.AspectDecorator;
import org.apache.tapestry5.ioc.services.Status;
import org.apache.tapestry5.plastic.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.function.Predicate;
import static java.lang.String.format;
@SuppressWarnings("all")
public class ModuleImpl implements Module
{
private final InternalRegistry registry;
private final ServiceActivityTracker tracker;
private final ModuleDef2 moduleDef;
private final PlasticProxyFactory proxyFactory;
private final Logger logger;
private static final Predicate canBeProxiedPredicate;
static
{
Predicate predicate = null;
try {
final MethodHandles.Lookup lookup = MethodHandles.lookup();
final MethodType methodType = MethodType.methodType(boolean.class);
final java.lang.invoke.MethodHandle isSealedMethodHandle =
MethodHandles.lookup().findVirtual(Class.class, "isSealed", methodType);
predicate = c -> c.isInterface() && !callIsSealed(isSealedMethodHandle, c);
}
catch (NoSuchMethodException e)
{
LoggerFactory.getLogger(ModuleImpl.class)
.info("Method Class.isSealed() not found, so we're running in a pre-15 JVM.");
}
catch (IllegalAccessException e)
{
throw new RuntimeException(e);
}
canBeProxiedPredicate = predicate != null ? predicate : c -> c.isInterface();
}
private static boolean callIsSealed(final java.lang.invoke.MethodHandle isSealedMethodHandle, Class clasz)
{
try
{
return (Boolean) isSealedMethodHandle.invoke(clasz);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
/**
* Lazily instantiated. Access is guarded by BARRIER.
*/
private Object moduleInstance;
// Set to true when invoking the module constructor. Used to
// detect endless loops caused by irresponsible dependencies in
// the constructor.
private boolean insideConstructor;
/**
* Keyed on fully qualified service id; values are instantiated services (proxies). Guarded by BARRIER.
*/
private final Map services = CollectionFactory.newCaseInsensitiveMap();
/**
* EagerLoadProxies collection into which proxies for eager loaded services are added. Guarded by BARRIER
*/
private final Collection eagerLoadProxies = CollectionFactory.newList();
private final Map serviceDefs = CollectionFactory.newCaseInsensitiveMap();
/**
* The barrier is shared by all modules, which means that creation of *any* service for any module is single
* threaded.
*/
private final static ConcurrentBarrier BARRIER = new ConcurrentBarrier();
/**
* "Magic" method related to Serializable that allows the Proxy object to replace itself with the token when being
* streamed out.
*/
private static final MethodDescription WRITE_REPLACE = new MethodDescription(Modifier.PRIVATE, "java.lang.Object",
"writeReplace", null, null, new String[]
{ObjectStreamException.class.getName()});
public ModuleImpl(InternalRegistry registry, ServiceActivityTracker tracker, ModuleDef moduleDef,
PlasticProxyFactory proxyFactory, Logger logger)
{
this.registry = registry;
this.tracker = tracker;
this.proxyFactory = proxyFactory;
this.moduleDef = InternalUtils.toModuleDef2(moduleDef);
this.logger = logger;
for (String id : moduleDef.getServiceIds())
{
ServiceDef sd = moduleDef.getServiceDef(id);
ServiceDef3 sd3 = InternalUtils.toServiceDef3(sd);
serviceDefs.put(id, sd3);
}
}
@Override
public T getService(String serviceId, Class serviceInterface)
{
assert InternalUtils.isNonBlank(serviceId);
assert serviceInterface != null;
ServiceDef3 def = getServiceDef(serviceId);
// RegistryImpl should already have checked that the service exists.
assert def != null;
Object service = findOrCreate(def);
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));
}
}
@Override
public Set findMatchingDecoratorDefs(ServiceDef serviceDef)
{
Set result = CollectionFactory.newSet();
for (DecoratorDef def : moduleDef.getDecoratorDefs())
{
if (def.matches(serviceDef) || markerMatched(serviceDef, InternalUtils.toDecoratorDef2(def)))
result.add(def);
}
return result;
}
@Override
public Set findMatchingServiceAdvisors(ServiceDef serviceDef)
{
Set result = CollectionFactory.newSet();
for (AdvisorDef def : moduleDef.getAdvisorDefs())
{
if (def.matches(serviceDef) || markerMatched(serviceDef, InternalUtils.toAdvisorDef2(def)))
result.add(def);
}
return result;
}
@Override
@SuppressWarnings("unchecked")
public Collection findServiceIdsForInterface(Class serviceInterface)
{
assert serviceInterface != null;
Collection result = CollectionFactory.newList();
for (ServiceDef2 def : serviceDefs.values())
{
if (serviceInterface.isAssignableFrom(def.getServiceInterface()))
result.add(def.getServiceId());
}
return result;
}
/**
* Locates the service proxy for a particular service (from the service definition).
*
* @param def defines the service
* @return the service proxy
*/
private Object findOrCreate(final ServiceDef3 def)
{
final String key = def.getServiceId();
final Invokable create = new Invokable()
{
@Override
public Object invoke()
{
// In a race condition, two threads may try to create the same service simulatenously.
// The second will block until after the first creates the service.
Object result = services.get(key);
// Normally, result is null, unless some other thread slipped in and created the service
// proxy.
if (result == null)
{
result = create(def);
services.put(key, result);
}
return result;
}
};
Invokable find = new Invokable()
{
@Override
public Object invoke()
{
Object result = services.get(key);
if (result == null)
result = BARRIER.withWrite(create);
return result;
}
};
return BARRIER.withRead(find);
}
@Override
public void collectEagerLoadServices(final Collection proxies)
{
Runnable work = new Runnable()
{
@Override
public void run()
{
for (ServiceDef3 def : serviceDefs.values())
{
if (def.isEagerLoad())
findOrCreate(def);
}
BARRIER.withWrite(new Runnable() {
@Override
public void run() {
proxies.addAll(eagerLoadProxies);
eagerLoadProxies.clear();
}
});
}
};
registry.run("Eager loading services", work);
}
/**
* Creates the service and updates the cache of created services.
*/
private Object create(final ServiceDef3 def)
{
final String serviceId = def.getServiceId();
final Logger logger = registry.getServiceLogger(serviceId);
final Class serviceInterface = def.getServiceInterface();
final boolean canBeProxied = canBeProxiedPredicate.test(serviceInterface);
String description = String.format("Creating %s service %s",
canBeProxied ? "proxy for" : "non-proxied instance of",
serviceId);
if (logger.isDebugEnabled())
logger.debug(description);
final Module module = this;
Invokable operation = new Invokable()
{
@Override
public Object invoke()
{
try
{
ServiceBuilderResources resources = new ServiceResourcesImpl(registry, module, def, proxyFactory,
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);
// 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.
ServiceLifecycle2 lifecycle = registry.getServiceLifecycle(def.getServiceScope());
if (!canBeProxied)
{
if (lifecycle.requiresProxy())
throw new IllegalArgumentException(
String.format(
"Service scope '%s' requires a proxy, but the service does not have a service interface (necessary to create a proxy). Provide a service interface or select a different service scope.",
def.getServiceScope()));
return creator.createObject();
}
creator = new OperationTrackingObjectCreator(registry, String.format("Instantiating service %s implementation via %s", serviceId, creator), creator);
creator = new LifecycleWrappedServiceCreator(lifecycle, resources, creator);
// Marked services (or services inside marked modules) are not decorated.
// TapestryIOCModule prevents decoration of its services. Note that all decorators will decorate
// around the aspect interceptor, which wraps around the core service implementation.
boolean allowDecoration = !def.isPreventDecoration();
if (allowDecoration)
{
creator = new AdvisorStackBuilder(def, creator, getAspectDecorator(), registry);
creator = new InterceptorStackBuilder(def, creator, registry);
}
// Add a wrapper that checks for recursion.
creator = new RecursiveServiceCreationCheckWrapper(def, creator, logger);
creator = new OperationTrackingObjectCreator(registry, "Realizing service " + serviceId, creator);
JustInTimeObjectCreator delegate = new JustInTimeObjectCreator(tracker, creator, serviceId);
Object proxy = createProxy(resources, delegate, def.isPreventDecoration());
registry.addRegistryShutdownListener(delegate);
if (def.isEagerLoad())
eagerLoadProxies.add(delegate);
tracker.setStatus(serviceId, Status.VIRTUAL);
return proxy;
} catch (Exception ex)
{
ex.printStackTrace();
throw new RuntimeException(IOCMessages.errorBuildingService(serviceId, def, ex), ex);
}
}
};
return registry.invoke(description, operation);
}
private AspectDecorator getAspectDecorator()
{
return registry.invoke("Obtaining AspectDecorator service", new Invokable()
{
@Override
public AspectDecorator invoke()
{
return registry.getService(AspectDecorator.class);
}
});
}
private final Runnable instantiateModule = new Runnable()
{
@Override
public void run()
{
moduleInstance = registry.invoke("Constructing module class " + moduleDef.getBuilderClass().getName(),
new Invokable()
{
@Override
public Object invoke()
{
return instantiateModuleInstance();
}
});
}
};
private final Invokable provideModuleInstance = new Invokable
© 2015 - 2025 Weber Informatics LLC | Privacy Policy