/* * 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.inject; import java.lang.annotation.Annotation; 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.logging.Logger; import javax.enterprise.inject.Alternative; import javax.enterprise.inject.Disposes; import javax.enterprise.inject.Produces; import javax.enterprise.inject.Specializes; import javax.enterprise.inject.spi.Annotated; import javax.enterprise.inject.spi.AnnotatedField; 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.enterprise.inject.spi.InjectionPoint; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Qualifier; import com.caucho.config.ConfigException; import com.caucho.config.Names; import com.caucho.config.program.Arg; import com.caucho.config.program.BeanArg; import com.caucho.config.reflect.AnnotatedTypeUtil; import com.caucho.config.reflect.BaseType; import com.caucho.inject.Module; import com.caucho.util.L10N; /** * Builder for produces beans. */ @Module public class ProducesBuilder { private static final Logger log = Logger.getLogger(ProducesBuilder.class.getName()); private static final L10N L = new L10N(ProducesBuilder.class); private InjectManager _manager; public ProducesBuilder(InjectManager manager) { _manager = manager; } /** * Introspects the methods for any @Produces */ public <X> void introspectProduces(Bean<X> bean, AnnotatedType<X> beanType) { HashSet<AnnotatedMethod<?>> disposesSet = new HashSet<AnnotatedMethod<?>>(); for (AnnotatedMethod<? super X> beanMethod : beanType.getMethods()) { if (beanMethod.isAnnotationPresent(Produces.class)) { AnnotatedMethod<? super X> disposesMethod = findDisposesMethod(beanType, beanMethod.getBaseType(), getQualifiers(beanMethod)); addProducesMethod(bean, beanType, beanMethod, disposesMethod); if (disposesMethod != null) disposesSet.add(disposesMethod); } } for (AnnotatedField<? super X> beanField : beanType.getFields()) { if (beanField.isAnnotationPresent(Produces.class)) { AnnotatedMethod<?> disposesMethod = findDisposesMethod(beanType, beanField.getBaseType(), getQualifiers(beanField)); addProduces(bean, beanType, beanField); if (disposesMethod != null) disposesSet.add(disposesMethod); } } for (AnnotatedMethod<? super X> beanMethod : beanType.getMethods()) { if (isDisposes(beanMethod) && ! disposesSet.contains(beanMethod)) throw new ConfigException(L.l("{0}.{1} is an invalid disposes method because it doesn't match a @Produces method", beanMethod.getJavaMember().getDeclaringClass().getName(), beanMethod.getJavaMember().getName())); } } protected <X,T> void addProducesMethod(Bean<X> bean, AnnotatedType<X> beanType, AnnotatedMethod<? super X> producesMethod, AnnotatedMethod<? super X> disposesMethod) { // ioc/07g2 vs ioc/07d0 /* // if (producesMethod.getJavaMember().getDeclaringClass() != beanType.getJavaClass() && ! beanType.isAnnotationPresent(Specializes.class)) { return; } */ Arg<? super X> []producesArgs = introspectArguments(bean, producesMethod); Arg<? super X> []disposesArgs = null; if (disposesMethod != null) disposesArgs = introspectDisposesArgs(disposesMethod, disposesMethod.getParameters()); ProducesMethodBean<X,T> producesBean = ProducesMethodBean.create(_manager, bean, producesMethod, producesArgs, disposesMethod, disposesArgs); if (producesBean.isAlternative() && ! _manager.isEnabled(producesBean)) { return; } // bean.init(); // _manager.addBean(producesBean); _manager.addProducesBean(producesBean); } protected <X> void addProduces(Bean<X> bean, AnnotatedType<X> beanType, AnnotatedField<?> beanField) { Class<?> beanClass = beanType.getJavaClass(); if (beanField.getJavaMember().getDeclaringClass() != beanClass && ! beanClass.isAnnotationPresent(Specializes.class)) return; AnnotatedMethod<? super X> disposesMethod = findDisposesMethod(beanType, beanField.getBaseType(), getQualifiers(beanField)); Arg<? super X> []disposesArgs = null; if (disposesMethod != null) disposesArgs = introspectDisposesArgs(disposesMethod, disposesMethod.getParameters()); ProducesFieldBean producesFieldBean = ProducesFieldBean.create(_manager, bean, beanField, disposesMethod, disposesArgs); // bean.init(); _manager.addProducesFieldBean(producesFieldBean); } private <X> AnnotatedMethod<? super X> findDisposesMethod(AnnotatedType<X> beanType, Type producesBaseType, Annotation []qualifiers) { for (AnnotatedMethod<? super X> beanMethod : beanType.getMethods()) { List<AnnotatedParameter<?>> params = (List) beanMethod.getParameters(); if (params.size() == 0) continue; AnnotatedParameter<?> param = params.get(0); if (! param.isAnnotationPresent(Disposes.class)) continue; if (! producesBaseType.equals(param.getBaseType())) continue; Annotation []testQualifiers = getQualifiers(param); if (! isQualifierMatch(qualifiers, testQualifiers)) continue; // XXX: check @Qualifiers Method javaMethod = beanMethod.getJavaMember(); if (beanMethod.isAnnotationPresent(Inject.class)) throw new ConfigException(L.l("{0}.{1} is an invalid @Disposes method because it has an @Inject annotation", javaMethod.getDeclaringClass().getName(), javaMethod.getName())); return beanMethod; } return null; } protected <X,T> Arg<T> []introspectArguments(Bean<X> bean, AnnotatedMethod<T> method) { List<AnnotatedParameter<T>> params = method.getParameters(); Method javaMethod = method.getJavaMember(); Arg<T> []args = new Arg[params.size()]; for (int i = 0; i < args.length; i++) { AnnotatedParameter<?> param = params.get(i); if (param.isAnnotationPresent(Disposes.class)) throw new ConfigException(L.l("'{0}.{1}' is an invalid producer method because a parameter is annotated with @Disposes", javaMethod.getDeclaringClass().getName(), javaMethod.getName())); InjectionPoint ip = new InjectionPointImpl(_manager, bean, param); if (InjectionPoint.class.equals(param.getBaseType())) args[i] = new InjectionPointArg(); else args[i] = new BeanArg(_manager, param.getBaseType(), getQualifiers(param), ip); } return args; } protected <X> Arg<X> []introspectDisposesArgs(AnnotatedMethod<?> method, List<AnnotatedParameter<X>> params) { Arg<X> []args = new Arg[params.size()]; boolean hasDisposes = false; for (int i = 0; i < args.length; i++) { AnnotatedParameter<X> param = params.get(i); InjectionPoint ip = null; if (param.isAnnotationPresent(Disposes.class)) { if (hasDisposes) throw new ConfigException(L.l("{0}.{1} is an invalid @Disposes method because two parameters are marked @Disposes", method.getJavaMember().getDeclaringClass().getName(), method.getJavaMember().getName())); hasDisposes = true; args[i] = null; } else args[i] = new BeanArg(_manager, param.getBaseType(), getQualifiers(param), ip); } return args; } protected boolean isDisposes(AnnotatedMethod<?> method) { List<AnnotatedParameter<?>> params = (List) method.getParameters(); for (int i = 0; i < params.size(); i++) { AnnotatedParameter<?> param = params.get(i); if (param.isAnnotationPresent(Disposes.class)) return true; } return false; } private boolean isQualifierMatch(Annotation []aList, Annotation []bList) { for (Annotation a : aList) { if (! isQualifierPresent(a, bList)) return false; } return true; } private boolean isQualifierPresent(Annotation a, Annotation []list) { for (Annotation ann : list) { if (! ann.annotationType().equals(a.annotationType())) continue; return true; } return false; } private Annotation []getQualifiers(Annotated annotated) { ArrayList<Annotation> qualifierList = new ArrayList<Annotation>(); for (Annotation ann : annotated.getAnnotations()) { if (ann.annotationType().equals(Named.class)) { Named named = (Named) ann; String namedValue = named.value(); if ("".equals(namedValue)) { BaseType baseType = AnnotatedTypeUtil.getBaseType(annotated); String name = baseType.getRawClass().getSimpleName(); ann = Names.create(name); } qualifierList.add(ann); } else if (ann.annotationType().isAnnotationPresent(Qualifier.class)) { qualifierList.add(ann); } } if (qualifierList.size() == 0) qualifierList.add(CurrentLiteral.CURRENT); Annotation []qualifiers = new Annotation[qualifierList.size()]; qualifierList.toArray(qualifiers); return qualifiers; } }