com.caucho.config.extension.ExtensionManager Maven / Gradle / Ivy
/*
* Copyright (c) 1998-2018 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.config.extension;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.InjectionException;
import javax.enterprise.inject.spi.Annotated;
import javax.enterprise.inject.spi.AnnotatedField;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.enterprise.inject.spi.InjectionTarget;
import javax.enterprise.inject.spi.ObserverMethod;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import javax.enterprise.inject.spi.ProcessBean;
import javax.enterprise.inject.spi.ProcessInjectionTarget;
import javax.enterprise.inject.spi.ProcessProducer;
import javax.enterprise.inject.spi.Producer;
import com.caucho.config.ConfigException;
import com.caucho.config.event.AbstractObserverMethod;
import com.caucho.config.event.EventManager;
import com.caucho.config.inject.BeanBuilder;
import com.caucho.config.inject.DefaultLiteral;
import com.caucho.config.inject.InjectManager;
import com.caucho.config.inject.ManagedBeanImpl;
import com.caucho.config.inject.ProducesFieldBean;
import com.caucho.config.inject.ProducesMethodBean;
import com.caucho.config.program.BeanArg;
import com.caucho.config.reflect.BaseType;
import com.caucho.inject.LazyExtension;
import com.caucho.inject.Module;
import com.caucho.loader.DynamicClassLoader;
import com.caucho.util.ConcurrentArrayList;
import com.caucho.util.IoUtil;
import com.caucho.util.L10N;
import com.caucho.vfs.ReadStream;
import com.caucho.vfs.Vfs;
/**
* Manages custom extensions for the inject manager.
*/
@Module
public class ExtensionManager
{
private static final L10N L = new L10N(ExtensionManager.class);
private static final Logger log
= Logger.getLogger(ExtensionManager.class.getName());
private final InjectManager _cdiManager;
private String _classLoaderHash = "";
private HashSet _extensionSet = new HashSet();
private HashMap,ExtensionItem> _extensionMap
= new HashMap,ExtensionItem>();
private ConcurrentArrayList _pendingEventList
= new ConcurrentArrayList(PendingEvent.class);
private boolean _isCustomExtension;
public ExtensionManager(InjectManager cdiManager)
{
_cdiManager = cdiManager;
}
boolean isCustomExtension()
{
return _isCustomExtension;
}
public void updateExtensions()
{
try {
ClassLoader loader = _cdiManager.getClassLoader();
if (loader == null)
return;
String hash = DynamicClassLoader.getHash(loader);
if (_classLoaderHash.equals(hash))
return;
_classLoaderHash = hash;
Enumeration e = loader.getResources("META-INF/services/" + Extension.class.getName());
while (e.hasMoreElements()) {
URL url = (URL) e.nextElement();
if (_extensionSet.contains(url))
continue;
_extensionSet.add(url);
InputStream is = null;
try {
is = url.openStream();
ReadStream in = Vfs.openRead(is);
String line;
while ((line = in.readLine()) != null) {
int p = line.indexOf('#');
if (p >= 0)
line = line.substring(0, p);
line = line.trim();
if (line.length() > 0) {
loadExtension(line);
}
}
in.close();
} catch (IOException e1) {
log.log(Level.WARNING, e1.toString(), e1);
} finally {
IoUtil.close(is);
}
}
} catch (IOException e) {
log.log(Level.WARNING, e.toString(), e);
}
}
public void createExtension(String className)
{
try {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class> cl = Class.forName(className, false, loader);
Constructor> ctor = cl.getConstructor(new Class[] { InjectManager.class });
Extension extension = (Extension) ctor.newInstance(_cdiManager);
addExtension(extension);
} catch (Exception e) {
log.log(Level.FINEST, e.toString(), e);
}
}
void loadExtension(String className)
{
try {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class> cl = Class.forName(className, false, loader);
if (! Extension.class.isAssignableFrom(cl))
throw new InjectionException(L.l("'{0}' is not a valid extension because it does not implement {1}",
cl, Extension.class.getName()));
Extension extension = null;
for (Constructor> ctor : cl.getDeclaredConstructors()) {
if (ctor.getParameterTypes().length == 0) {
ctor.setAccessible(true);
extension = (Extension) ctor.newInstance();
}
}
if (extension == null)
extension = (Extension) cl.newInstance();
addExtension(extension);
} catch (Exception e) {
log.log(Level.WARNING, e.toString(), e);
}
}
public void addExtension(Extension ext)
{
if (log.isLoggable(Level.FINER))
log.finer(this + " add extension " + ext);
// Register extension instance with CDI.
// TODO Is this correct or should a managed bean be created?
BeanBuilder extends Extension> beanBuilder = _cdiManager
.createBeanFactory(ext.getClass());
// TODO Is singleton equivalent to ApplicationScoped? That is what the
// extension is supposed to be as per CDI specification.
_cdiManager.addBean(beanBuilder.singleton(ext));
ExtensionItem item = introspect(ext.getClass());
for (ExtensionMethod method : item.getExtensionMethods()) {
Method javaMethod = method.getMethod();
Class> rawType = method.getBaseType().getRawClass();
ExtensionObserver observer;
observer = new ExtensionObserver(ext,
method.getMethod(),
method.getArgs());
BaseType baseType = method.getBaseType();
// #5531, convert Process to Process extends Foo>
baseType = baseType.extendGenericType();
_cdiManager.getEventManager().addExtensionObserver(observer,
baseType,
method.getQualifiers());
if ((ProcessAnnotatedType.class.isAssignableFrom(rawType))
&& ! javaMethod.isAnnotationPresent(LazyExtension.class)) {
_cdiManager.setIsCustomExtension(true);
}
if ((ProcessBean.class.isAssignableFrom(rawType))
&& ! javaMethod.isAnnotationPresent(LazyExtension.class)) {
_cdiManager.setIsCustomExtension(true);
}
if ((ProcessInjectionTarget.class.isAssignableFrom(rawType))
&& ! javaMethod.isAnnotationPresent(LazyExtension.class)) {
_cdiManager.setIsCustomExtension(true);
}
if ((ProcessProducer.class.isAssignableFrom(rawType))
&& ! javaMethod.isAnnotationPresent(LazyExtension.class)) {
_cdiManager.setIsCustomExtension(true);
}
}
}
private ExtensionItem introspect(Class> cl)
{
ExtensionItem item = _extensionMap.get(cl);
if (item == null) {
item = new ExtensionItem(cl);
_extensionMap.put(cl, item);
}
return item;
}
public Bean processBean(Bean bean, ProcessBean processBean)
{
InjectManager cdi = _cdiManager;
BaseType baseType = cdi.createTargetBaseType(processBean.getClass());
baseType = baseType.fill(cdi.createTargetBaseType(bean.getBeanClass()));
_pendingEventList.add(new PendingEvent(processBean, baseType));
return processBean.getBean();
}
@Module
public Bean processBean(Bean bean, Annotated ann)
{
InjectManager cdi = _cdiManager;
if (ann == null)
ann = cdi.createAnnotatedType(bean.getBeanClass());
ProcessBeanImpl event = new ProcessBeanImpl(_cdiManager, bean, ann);
BaseType baseType = cdi.createTargetBaseType(event.getClass());
baseType = baseType.fill(cdi.createTargetBaseType(bean.getBeanClass()));
_pendingEventList.add(new PendingEvent(event, baseType));
return event.getBean();
}
@Module
public Bean processManagedBean(ManagedBeanImpl bean, Annotated ann)
{
InjectManager cdi = _cdiManager;
ProcessManagedBeanImpl event
= new ProcessManagedBeanImpl(_cdiManager, bean, ann);
BaseType baseType = cdi.createTargetBaseType(event.getClass());
baseType = baseType.fill(cdi.createTargetBaseType(bean.getBeanClass()));
_pendingEventList.add(new PendingEvent(event, baseType));
/*
getEventManager().fireExtensionEvent(event, baseType);
if (event.isVeto())
return null;
else
return event.getBean();
*/
return event.getBean();
}
@Module
public Bean processProducerMethod(ProducesMethodBean bean)
{
InjectManager cdi = _cdiManager;
ProcessProducerMethodImpl event
= new ProcessProducerMethodImpl(_cdiManager, bean);
AnnotatedMethod super T> method = bean.getProducesMethod();
Bean> producerBean = bean.getProducerBean();
BaseType baseType = cdi.createTargetBaseType(event.getClass());
baseType = baseType.fill(cdi.createTargetBaseType(method.getBaseType()),
cdi.createTargetBaseType(producerBean.getBeanClass()));
_pendingEventList.add(new PendingEvent(event, baseType));
/*
getEventManager().fireExtensionEvent(event, baseType);
if (event.isVeto())
return null;
else
return event.getBean();
*/
return event.getBean();
}
@Module
public Bean processProducerField(ProducesFieldBean bean)
{
InjectManager cdi = _cdiManager;
ProcessProducerFieldImpl event
= new ProcessProducerFieldImpl(_cdiManager, bean);
AnnotatedField super T> field = bean.getField();
BaseType baseType = cdi.createTargetBaseType(event.getClass());
baseType = baseType.fill(cdi.createTargetBaseType(field.getBaseType()),
cdi.createTargetBaseType(bean.getProducerBean().getBeanClass()));
_pendingEventList.add(new PendingEvent(event, baseType));
/*
getEventManager().fireExtensionEvent(event, baseType);
if (event.isVeto())
return null;
else
return event.getBean();
*/
return event.getBean();
}
public void processPendingEvents()
{
while (_pendingEventList.size() > 0) {
PendingEvent event = _pendingEventList.remove(0);
getEventManager().fireExtensionEvent(event.getEvent(), event.getType());
}
}
/**
* Processes the discovered InjectionTarget
*/
public InjectionTarget
processInjectionTarget(InjectionTarget target,
AnnotatedType annotatedType)
{
InjectManager cdi = _cdiManager;
ProcessInjectionTargetImpl processTarget
= new ProcessInjectionTargetImpl(_cdiManager, target, annotatedType);
BaseType eventType = cdi.createTargetBaseType(ProcessInjectionTargetImpl.class);
eventType = eventType.fill(cdi.createTargetBaseType(annotatedType.getBaseType()));
getEventManager().fireExtensionEvent(processTarget, eventType);
return (InjectionTarget) processTarget.getInjectionTarget();
}
/**
* Processes the discovered method producer
*/
public Producer
processProducer(AnnotatedMethod producesMethod,
Producer producer)
{
InjectManager cdi = _cdiManager;
ProcessProducerImpl event
= new ProcessProducerImpl(producesMethod, producer);
AnnotatedType> declaringType = producesMethod.getDeclaringType();
Type declaringClass;
if (declaringType != null)
declaringClass = declaringType.getBaseType();
else
declaringClass = producesMethod.getJavaMember().getDeclaringClass();
BaseType eventType = cdi.createTargetBaseType(ProcessProducerImpl.class);
eventType = eventType.fill(cdi.createTargetBaseType(producesMethod.getBaseType()),
cdi.createTargetBaseType(declaringClass));
getEventManager().fireExtensionEvent(event, eventType);
return event.getProducer();
}
/**
* Processes the discovered method producer
*/
public Producer
processProducer(AnnotatedField producesField,
Producer producer)
{
InjectManager cdi = _cdiManager;
ProcessProducerImpl event
= new ProcessProducerImpl(producesField, producer);
AnnotatedType declaringType = producesField.getDeclaringType();
BaseType eventType = cdi.createTargetBaseType(ProcessProducerImpl.class);
eventType = eventType.fill(cdi.createTargetBaseType(producesField.getBaseType()),
cdi.createTargetBaseType(declaringType.getBaseType()));
getEventManager().fireExtensionEvent(event, eventType);
return event.getProducer();
}
/**
* Processes the observer.
*/
public void processObserver(ObserverMethod observer,
AnnotatedMethod method)
{
ProcessObserverImpl event
= new ProcessObserverImpl(_cdiManager, observer, method);
AnnotatedMethod annotatedMethod = event.getAnnotatedMethod();
AnnotatedType declaringType = annotatedMethod.getDeclaringType();
ObserverMethod observerMethod = event.getObserverMethod();
Type observedType = observerMethod.getObservedType();
BaseType eventType = _cdiManager.createTargetBaseType(ProcessObserverImpl.class);
eventType = eventType.fill(_cdiManager.createTargetBaseType(observedType),
_cdiManager.createTargetBaseType(declaringType.getBaseType()));
getEventManager().fireExtensionEvent(event, eventType);
}
public void fireBeforeBeanDiscovery()
{
getEventManager().fireExtensionEvent(new BeforeBeanDiscoveryImpl(_cdiManager));
}
public void fireAfterBeanDiscovery()
{
getEventManager().fireExtensionEvent(new AfterBeanDiscoveryImpl(_cdiManager));
}
public void fireAfterDeploymentValidation()
{
AfterDeploymentValidationImpl event
= new AfterDeploymentValidationImpl(_cdiManager);
getEventManager().fireExtensionEvent(event);
/*
if (event.getDeploymentProblem() != null)
throw ConfigException.create(event.getDeploymentProblem());
*/
}
/**
* Creates a discovered annotated type.
*/
public AnnotatedType processAnnotatedType(AnnotatedType type)
{
InjectManager cdi = _cdiManager;
ProcessAnnotatedTypeImpl processType
= new ProcessAnnotatedTypeImpl(type);
BaseType baseType = cdi.createTargetBaseType(ProcessAnnotatedTypeImpl.class);
baseType = baseType.fill(cdi.createTargetBaseType(type.getBaseType()));
getEventManager().fireExtensionEvent(processType, baseType);
if (processType.isVeto()) {
return null;
}
type = processType.getAnnotatedType();
return type;
}
private EventManager getEventManager()
{
return _cdiManager.getEventManager();
}
public String toString()
{
return getClass().getSimpleName() + "[" + _cdiManager + "]";
}
class ExtensionItem {
private ArrayList _observers
= new ArrayList();
ExtensionItem(Class> cl)
{
for (Method method : cl.getDeclaredMethods()) {
ExtensionMethod extMethod = bindObserver(cl, method);
if (extMethod != null)
_observers.add(extMethod);
}
}
private ArrayList getExtensionMethods()
{
return _observers;
}
@SuppressWarnings("unchecked")
private ExtensionMethod bindObserver(Class> cl, Method method)
{
Type []param = method.getGenericParameterTypes();
if (param.length < 1)
return null;
Annotation [][]paramAnn = method.getParameterAnnotations();
if (! hasObserver(paramAnn))
return null;
InjectManager inject = _cdiManager;
BeanArg> []args = new BeanArg[param.length];
for (int i = 1; i < param.length; i++) {
Annotation []bindings = inject.getQualifiers(paramAnn[i]);
if (bindings.length == 0)
bindings = new Annotation[] { DefaultLiteral.DEFAULT };
InjectionPoint ip = null;
args[i] = new BeanArg(inject, param[i], bindings, ip);
}
BaseType baseType = inject.createTargetBaseType(param[0]);
return new ExtensionMethod(method, baseType,
inject.getQualifiers(paramAnn[0]),
args);
}
private boolean hasObserver(Annotation [][]paramAnn)
{
for (int i = 0; i < paramAnn.length; i++) {
for (int j = 0; j < paramAnn[i].length; j++) {
if (paramAnn[i][j].annotationType().equals(Observes.class))
return true;
}
}
return false;
}
}
static class ExtensionMethod {
private final Method _method;
private final BaseType _type;
private final Annotation []_qualifiers;
private final BeanArg> []_args;
ExtensionMethod(Method method,
BaseType type,
Annotation []qualifiers,
BeanArg> []args)
{
_method = method;
method.setAccessible(true);
_type = type;
_qualifiers = qualifiers;
_args = args;
}
public Method getMethod()
{
return _method;
}
public BeanArg> []getArgs()
{
return _args;
}
public BaseType getBaseType()
{
return _type;
}
public Annotation []getQualifiers()
{
return _qualifiers;
}
public String toString()
{
return getClass().getSimpleName() + "[" + _method + "]";
}
}
static class ExtensionObserver extends AbstractObserverMethod
© 2015 - 2025 Weber Informatics LLC | Privacy Policy