/* * Copyright (c) 1998-2011 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.xml; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.annotation.PostConstruct; import javax.enterprise.context.NormalScope; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.inject.Produces; import javax.enterprise.inject.spi.AnnotatedConstructor; import javax.enterprise.inject.spi.AnnotatedMethod; import javax.enterprise.inject.spi.AnnotatedParameter; import javax.enterprise.inject.spi.AnnotatedType; import javax.enterprise.inject.spi.Bean; import javax.inject.Qualifier; import javax.inject.Scope; import javax.interceptor.InterceptorBinding; import org.w3c.dom.Node; import com.caucho.config.ConfigException; import com.caucho.config.ConfiguredLiteral; import com.caucho.config.cfg.BeansConfig; import com.caucho.config.inject.InjectManager; import com.caucho.config.inject.ManagedBeanImpl; import com.caucho.config.program.Arg; import com.caucho.config.program.ConfigProgram; import com.caucho.config.program.ContainerProgram; import com.caucho.config.program.NodeBuilderChildProgram; import com.caucho.config.reflect.AnnotatedElementImpl; import com.caucho.config.reflect.AnnotatedMethodImpl; import com.caucho.config.reflect.AnnotatedTypeImpl; import com.caucho.config.reflect.BaseType; import com.caucho.config.reflect.ReflectionAnnotatedFactory; import com.caucho.config.type.ConfigType; import com.caucho.config.type.TypeFactory; import com.caucho.util.L10N; import com.caucho.xml.QName; /** * Custom bean configured by namespace */ public class XmlBeanConfig<T> { private static final L10N L = new L10N(XmlBeanConfig.class); private static final String RESIN_NS = "http://caucho.com/ns/resin"; private InjectManager _cdiManager; private Class<T> _class; private AnnotatedTypeImpl<T> _annotatedType; private Bean<T> _bean; private ConfigType<T> _configType; private ArrayList<ConfigProgram> _args; private QName _name; private String _filename; private int _line; private ContainerProgram _init; private boolean _hasBindings; private boolean _hasInterceptorBindings; private boolean _isInlineBean; private boolean _isBeansXml; public XmlBeanConfig(QName name, Class<T> cl, Object parent) { _name = name; _class = cl; _cdiManager = InjectManager.create(); // XXX: // _component = new SimpleBean(cl); // _component.setScopeClass(Dependent.class); // ioc/2601 BaseType baseType = _cdiManager.createSourceBaseType(cl); AnnotatedType<T> annType = ReflectionAnnotatedFactory.introspectType(baseType); _annotatedType = new AnnotatedTypeImpl<T>(annType); _cdiManager.addConfiguredBean(cl.getName()); _configType = TypeFactory.getCustomBeanType(cl); // defaults to @Configured // clearAnnotations(_annotatedType, DeploymentType.class); _annotatedType.addAnnotation(ConfiguredLiteral.create()); for (AnnotatedMethod<?> method : _annotatedType.getMethods()) { // ioc/0614 AnnotatedMethodImpl<?> methodImpl = (AnnotatedMethodImpl<?>) method; if (methodImpl.isAnnotationPresent(Produces.class)) methodImpl.addAnnotation(ConfiguredLiteral.create()); } if (parent instanceof BeansConfig) _isBeansXml = true; } public ConfigType<T> getConfigType() { return _configType; } public Class<T> getClassType() { return _class; } public void setConfigLocation(String filename, int line) { _filename = filename; _line = line; } public String getFilename() { return _filename; } public int getLine() { return _line; } public void setInlineBean(boolean isInline) { _isInlineBean = isInline; } public void setBeansXml(boolean isBeansXml) { _isBeansXml = isBeansXml; } public void addArg(ConfigProgram arg) { if (_args == null) _args = new ArrayList<ConfigProgram>(); _args.add(arg); } public void addArgs(ArrayList<ConfigProgram> args) { _args = new ArrayList<ConfigProgram>(args); } /* public void setNew(ConfigProgram []args) { if (_args == null) _args = new ArrayList<ConfigProgram>(); for (ConfigProgram arg : args) { _args.add(arg); } } */ public void addAdd(ConfigProgram add) { addInitProgram(add); } public void addInitProgram(ConfigProgram program) { if (_init == null) { _init = new ContainerProgram(); /* if (_component != null) _component.setInit(_init); */ } _init.addProgram(program); } public void addBuilderProgram(ConfigProgram program) { QName name = program.getQName(); if (name == null) { addInitProgram(program); return; } Class<?> cl = createClass(name); if (cl == null) { } else if (Annotation.class.isAssignableFrom(cl)) { ConfigType<?> type = TypeFactory.getType(cl); Object bean = type.create(null, name); Node node = getProgramNode(program); if (node != null) XmlConfigContext.getCurrent().configureNode(node, bean, type); Annotation ann = (Annotation) type.replaceObject(bean); addAnnotation(ann); return; } if (name.getNamespaceURI().equals(_name.getNamespaceURI())) { if (_configType.getAttribute(name) != null) addInitProgram(program); else { throw new ConfigException(L.l("'{0}' is an unknown field for '{1}'", name.getLocalName(), _class.getName())); } } else throw new ConfigException(L.l("'{0}' is an unknown field name. Fields must belong to the same namespace as the class", name.getCanonicalName())); } private Node getProgramNode(ConfigProgram program) { if (program instanceof NodeBuilderChildProgram) return ((NodeBuilderChildProgram) program).getNode(); return null; } public void addAnnotation(Annotation ann) { // XXX: some annotations also remove other annotations if (ann.annotationType().isAnnotationPresent(Qualifier.class) && ! _hasBindings) { _hasBindings = true; clearBindings(_annotatedType); } if (ann.annotationType().isAnnotationPresent(InterceptorBinding.class) && ! _hasInterceptorBindings) { _hasInterceptorBindings = true; clearAnnotations(_annotatedType, InterceptorBinding.class); } if (ann.annotationType().isAnnotationPresent(Scope.class) || ann.annotationType().isAnnotationPresent(NormalScope.class)) { clearAnnotations(_annotatedType, Scope.class); clearAnnotations(_annotatedType, NormalScope.class); } _annotatedType.addAnnotation(ann); } public void addMethod(XmlBeanMethodConfig methodConfig) { Method method = methodConfig.getMethod(); Annotation []annList = methodConfig.getAnnotations(); AnnotatedMethod<?> annMethod = _annotatedType.createMethod(method); if (annMethod instanceof AnnotatedMethodImpl<?>) { AnnotatedMethodImpl<?> methodImpl = (AnnotatedMethodImpl<?>) annMethod; // ioc/0c64 methodImpl.clearAnnotations(); addAnnotations(methodImpl, annList); } //_component.addMethod(new SimpleBeanMethod(method, annList)); } private void addAnnotations(AnnotatedElementImpl annotated, Annotation []annList) { for (Annotation ann : annList) { annotated.addAnnotation(ann); } } public void addField(XmlBeanFieldConfig fieldConfig) { // Field field = fieldConfig.getField(); // Annotation []annList = fieldConfig.getAnnotations(); //_component.addField(new SimpleBeanField(field, annList)); } private void clearBindings(AnnotatedTypeImpl<?> beanType) { HashSet<Annotation> annSet = new HashSet<Annotation>(beanType.getAnnotations()); for (Annotation ann : annSet) { if (ann.annotationType().isAnnotationPresent(Qualifier.class)) beanType.removeAnnotation(ann); } } private void clearAnnotations(AnnotatedTypeImpl<?> beanType, Class<? extends Annotation> annType) { HashSet<Annotation> annSet = new HashSet<Annotation>(beanType.getAnnotations()); for (Annotation ann : annSet) { if (ann.annotationType().isAnnotationPresent(annType)) beanType.removeAnnotation(ann); } } private Class<?> createClass(QName name) { String uri = name.getNamespaceURI(); String localName = name.getLocalName(); if (uri.equals(RESIN_NS)) { return createResinClass(localName); } if (! uri.startsWith("urn:java:")) return null; String pkg = uri.substring("urn:java:".length()); return TypeFactory.loadClass(pkg, name.getLocalName()); } private Class<?> createResinClass(String name) { Class<?> cl = TypeFactory.loadClass("ee", name); if (cl != null) return cl; cl = TypeFactory.loadClass("com.caucho.config", name); if (cl != null) return cl; return null; } @PostConstruct public void init() { if (_annotatedType != null) { initComponent(); } } public void initComponent() { /* XXX: constructor if (_args != null) _component.setNewArgs(_args); */ Arg<?> []newProgram = null; Constructor<?> javaCtor = null; if (_args != null) { AnnotatedConstructor<T> ctor = null; for (AnnotatedConstructor<T> testCtor : _annotatedType.getConstructors()) { if (testCtor.getParameters().size() == _args.size()) ctor = testCtor; } if (ctor == null) { throw new ConfigException(L.l("No matching constructor found for '{0}' with {1} arguments.", _annotatedType, _args.size())); } javaCtor = ctor.getJavaMember(); ArrayList<ConfigProgram> newList = _args; newProgram = new Arg[newList.size()]; Type []genericParam = javaCtor.getGenericParameterTypes(); List<AnnotatedParameter<T>> parameters = (List<AnnotatedParameter<T>>) ctor.getParameters(); String loc = null; for (int i = 0; i < _args.size(); i++) { ConfigProgram argProgram = _args.get(i); ConfigType<?> type = TypeFactory.getType(genericParam[i]); if (argProgram != null) newProgram[i] = new ProgramArg(type, argProgram); else newProgram[i] = new BeanArg(loc, genericParam[i], parameters.get(i).getAnnotations()); } } else newProgram = new Arg[0]; ConfigProgram []injectProgram; if (_init != null) { ArrayList<ConfigProgram> programList = _init.getProgramList(); injectProgram = new ConfigProgram[programList.size()]; programList.toArray(injectProgram); } else injectProgram = new ConfigProgram[0]; XmlCookie xmlCookie = _cdiManager.generateXmlCookie(); _annotatedType.addAnnotation(xmlCookie); XmlManagedBeanImpl<T> managedBean = new XmlManagedBeanImpl<T>(_cdiManager, _annotatedType, false, _name, _filename, _line); managedBean.introspect(); XmlInjectionTarget<T> injectionTarget = new XmlInjectionTarget(managedBean, javaCtor, newProgram, injectProgram); _bean = new XmlBean<T>(managedBean, injectionTarget); _cdiManager.addXmlInjectionTarget(xmlCookie.value(), injectionTarget); if (! _isInlineBean) { _cdiManager.discoverBean(_annotatedType); // ioc/23n3, ioc/0603 if (! _isBeansXml) { _cdiManager.update(); } } //beanManager.addBean(_bean); //managedBean.introspectProduces(); /* for (Bean producesBean : managedBean.getProducerBeans()) { beanManager.addBean(producesBean); } */ } protected Bean bindParameter(String loc, Type type, Set<Annotation> bindingSet) { Annotation []bindings = new Annotation[bindingSet.size()]; bindingSet.toArray(bindings); Set<Bean<?>> set = _cdiManager.getBeans(type, bindings); if (set == null || set.size() == 0) return null; return _cdiManager.resolve(set); } public Object toObject() { if (_bean == null) init(); InjectManager beanManager = InjectManager.create(); CreationalContext<?> env = beanManager.createCreationalContext(_bean); Class<?> type = _bean.getBeanClass(); return InjectManager.create().getReference(_bean, type, env); } public String toString() { return getClass().getSimpleName() + "[" + _class.getSimpleName() + "]"; } class BeanArg extends Arg<T> { private String _loc; private Constructor<T> _ctor; private Type _type; private Set<Annotation> _bindings; private Bean<T> _bean; BeanArg(String loc, Type type, Set<Annotation> bindings) { _loc = loc; _type = type; _bindings = bindings; bind(); } public void bind() { if (_bean == null) { _bean = bindParameter(_loc, _type, _bindings); if (_bean == null) throw new ConfigException(L.l("{0}: {1} does not have valid arguments", _loc, _ctor)); } } public Object eval(CreationalContext<T> env) { if (_bean == null) bind(); // XXX: getInstance for injection? Type type = null; return _cdiManager.getReference(_bean, type, env); } } static class ProgramArg<T> extends Arg<T> { private ConfigType<T> _type; private ConfigProgram _program; ProgramArg(ConfigType<T> type, ConfigProgram program) { _type = type; _program = program; } public Object eval(CreationalContext<T> creationalContext) { // ConfigContext env = ConfigContext.create(); // env.setCreationalContext(creationalContext); return _program.create(_type, creationalContext); } } }