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

com.caucho.config.inject.AbstractIntrospectedBean 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.inject;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.ejb.Singleton;
import javax.ejb.Stateful;
import javax.ejb.Stateless;
import javax.enterprise.context.Dependent;
import javax.enterprise.context.NormalScope;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.Alternative;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.Specializes;
import javax.enterprise.inject.Typed;
import javax.enterprise.inject.spi.Annotated;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.enterprise.inject.spi.PassivationCapable;
import javax.inject.Named;
import javax.inject.Qualifier;
import javax.inject.Scope;

import com.caucho.config.ConfigException;
import com.caucho.config.Names;
import com.caucho.config.bytecode.ScopeAdapter;
import com.caucho.config.event.EventManager;
import com.caucho.config.reflect.BaseType;
import com.caucho.inject.Module;
import com.caucho.util.L10N;

/**
 * Common bean introspection for Produces and ManagedBean.
 */
//  implements ObjectProxy
@Module
public class AbstractIntrospectedBean extends AbstractBean
  implements PassivationCapable, PassivationSetter
{
  private static final L10N L = new L10N(AbstractIntrospectedBean.class);
  private static final Logger log
    = Logger.getLogger(AbstractIntrospectedBean.class.getName());

  private static final HashSet> _reservedTypes
    = new HashSet>();

  public static final Annotation []CURRENT_ANN
    = new Annotation[] { DefaultLiteral.DEFAULT };

  // AnnotatedType for ManagedBean, AnnotatedMethod for produces
  private Annotated _annotated;

  private BaseType _baseType;

  private Set _typeClasses;

  private ArrayList _qualifiers
    = new ArrayList();

  private Class _scope;

  private ArrayList _stereotypes
    = new ArrayList();

  private String _name;
  
  private boolean _isAlternative;

  private boolean _isPassivating;
  private String _passivationId;

  public AbstractIntrospectedBean(InjectManager manager,
                                  Type type,
                                  Annotated annotated)
  {
    super(manager);
    
    _annotated = annotated;
    
    /*
    if (type instanceof Class) {
      // ioc/024d
      _baseType = manager.createClassBaseType((Class) type);
    }
    else
      _baseType = manager.createBaseType(type);
      */
    
    _baseType = manager.createSourceBaseType(type);
    
    // Set baseTypes = _baseType.getTypeClosure(manager);
    
    // ioc/0p2d - TCK
    Set baseTypes = annotated.getTypeClosure();
    
    Typed typed = annotated.getAnnotation(Typed.class);
    
    if (typed != null) {
      _typeClasses = fillTyped(baseTypes, typed.value());
    }
    else {
      _typeClasses = baseTypes;
    }
  }

  private LinkedHashSet fillTyped(Set closure,
                                        Class []values)
  {
    LinkedHashSet typeClasses = new LinkedHashSet();
  
    for (Class cl : values) {
      fillType(typeClasses, closure, cl);
    }

    /*
    if (isClass)
      typeClasses.add(Object.class);
      */
    
    typeClasses.add(Object.class);
    
    return typeClasses;
  }
  
  private void fillType(LinkedHashSet types, 
                        Set closure,
                        Class cl)
  {
    for (Type type : closure) {
      if (type.equals(cl)) {
        types.add(type);
      }
      else if (type instanceof BaseType) {
        BaseType baseType = (BaseType) type;
        
        if (baseType.getRawClass().equals(cl))
          types.add(type);
      }
    }
  }
  
  public BaseType getBaseType()
  {
    return _baseType;
  }

  @Override
  public Class getBeanClass()
  {
    return _baseType.getRawClass();
  }

  public final Class getJavaClass()
  {
    return _baseType.getRawClass();
  }

  public Type getTargetType()
  {
    return _baseType.toType();
  }

  public String getTargetSimpleName()
  {
    return _baseType.getSimpleName();
  }

  public String getTargetName()
  {
    return _baseType.toString();
  }

  public Class getTargetClass()
  {
    return _baseType.getRawClass();
  }

  @Override
  public Annotated getAnnotated()
  {
    return _annotated;
  }

  /**
   * Gets the bean's EL qualifier name.
   */
  @Override
  public String getName()
  {
    return _name;
  }

  /**
   * Returns the bean's qualifier types
   */
  @Override
  public Set getQualifiers()
  {
    Set set = new LinkedHashSet();

    for (Annotation qualifier : _qualifiers) {
      set.add(qualifier);
    }

    return set;
  }

  @Override
  public String getId()
  {
    if (_passivationId == null && isPassivating())
      _passivationId = calculatePassivationId();

    return _passivationId;
  }

  @Override
  public void setPassivationId(String passivationId)
  {
    _passivationId = passivationId;
  }

  /**
   * Returns the bean's stereotypes
   */
  public Set> getStereotypes()
  {
    Set> set
      = new LinkedHashSet>();

    for (Annotation stereotype : _stereotypes) {
      set.add(stereotype.annotationType());
    }

    return set;
  }

  private Annotated getIntrospectedAnnotated()
  {
    return _annotated;
  }

  /**
   * Returns the scope
   */
  @Override
  public Class getScope()
  {
    return _scope;
  }

  /**
   * Returns the types that the bean implements
   */
  @Override
  public Set getTypes()
  {
    return _typeClasses;
  }
  
  @Override
  public boolean isAlternative()
  {
    return _isAlternative;
  }

  @Override
  public void introspect()
  {
    super.introspect();

    introspect(getIntrospectedAnnotated());
  }

  protected void introspect(Annotated annotated)
  {
    introspectScope(annotated);
    introspectQualifiers(annotated);
    introspectName(annotated);
    
    if (annotated.isAnnotationPresent(Alternative.class)) {
      // ioc/0618
      _isAlternative = true;
    }
    
    introspectStereotypes(annotated);
    introspectSpecializes(annotated);
    
    introspectDefault();
    
    if (getScope().isAnnotationPresent(NormalScope.class)) {
      ScopeAdapter.validateType(annotated.getBaseType());
    }
  }

  /**
   * Called for implicit introspection.
   */
  protected void introspectScope(Annotated annotated)
  {
    if (_scope != null)
      return;

    BeanManager inject = getBeanManager();

    for (Annotation ann : annotated.getAnnotations()) {
      if (inject.isScope(ann.annotationType())) {
        if (_scope != null && _scope != ann.annotationType())
          throw new ConfigException(L.l("{0}: @Scope annotation @{1} conflicts with @{2}.  Java Injection components may only have a single @Scope.",
                                        getTargetName(),
                                        _scope.getName(),
                                        ann.annotationType().getName()));

        _scope = ann.annotationType();
      }
    }
  }

  /**
   * Introspects the qualifier annotations
   */
  protected void introspectQualifiers(Annotated annotated)
  {
    if (_qualifiers.size() > 0)
      return;

    BeanManager inject = getBeanManager();

    for (Annotation ann : annotated.getAnnotations()) {
      if (inject.isQualifier(ann.annotationType())) {
        if (ann.annotationType().equals(Named.class)) {
          String namedValue = getNamedValue(ann);

          if ("".equals(namedValue)) {
            ann = Names.create(getDefaultName());
          }
        }

        _qualifiers.add(ann);
      }
    }
  }

  /**
   * Introspects the qualifier annotations
   */
  protected void introspectName(Annotated annotated)
  {
    if (_name != null)
      return;

    Annotation ann = annotated.getAnnotation(Named.class);

    if (ann != null) {
      String value = getNamedValue(ann);

      if (value == null)
        value = "";

      _name = value;
    }
  }

  /**
   * Adds the stereotypes from the bean's annotations
   */
  protected void introspectStereotypes(Annotated annotated)
  {
    Class scope = null;

    for (Annotation stereotype : annotated.getAnnotations()) {
      Class stereotypeType = stereotype.annotationType();

      Set stereotypeSet =
        getBeanManager().getStereotypeDefinition(stereotypeType);
      
      if (stereotypeSet == null)
        continue;

      _stereotypes.add(stereotype);

      for (Annotation ann : stereotypeSet) {
        Class annType = ann.annotationType();

        if (annType.isAnnotationPresent(Scope.class)
            || annType.isAnnotationPresent(NormalScope.class)) {
          if (_scope == null && scope != null && ! scope.equals(annType)) {
            throw new ConfigException(L.l("'{0}' is an invalid @Scope because a scope '{1}' has already been defined.  Only one @Scope or @NormalScope is allowed on a bean.",
                                          scope.getName(), annType.getName()));
          }

          scope = annType;
        }

        if (annType.equals(Named.class) && _name == null) {
          String namedValue = getNamedValue(ann);
          _name = "";

          if (! "".equals(namedValue))
            throw new ConfigException(L.l("@Named must not have a value in a @Stereotype definition, because @Stereotypes are used with multiple beans."));
        }

        if (annType.isAnnotationPresent(Qualifier.class)
            && ! annType.equals(Named.class)) {
          throw new ConfigException(L.l("'{0}' is not allowed on @Stereotype '{1}' because stereotypes may not have @Qualifier annotations",
                                        ann, stereotype));
        }
        
        if (annType.equals(Alternative.class))
          _isAlternative = true;
      }
    }

    if (_scope == null)
      _scope = scope;
  }
  
  /**
   * Introspects the methods for any @Observes
   */
  void introspectObservers()
  {
    EventManager eventManager = getBeanManager().getEventManager();
    
    AnnotatedType annType = getAnnotatedType();

    // ioc/0b25
    /*
    if (! getBeanManager().isIntrospectObservers(annType))
      return;
      */
    for (AnnotatedMethod beanMethod : annType.getMethods()) {
      int param = EventManager.findObserverAnnotation(beanMethod);
      
      if (param < 0)
        continue;
      
      // ioc/0b25
      /*
      Class declClass = beanMethod.getJavaMember().getDeclaringClass();
      if (declClass != annType.getJavaClass() 
          && declClass.isAssignableFrom(annType.getJavaClass())
          && ! annType.isAnnotationPresent(Specializes.class)) {
        continue;
      }
      */
      
      eventManager.addObserver(this, beanMethod);
    }
  }

  /**
   * Adds the stereotypes from the bean's annotations
   */
  protected void introspectSpecializes(Annotated annotated)
  {
    if (! annotated.isAnnotationPresent(Specializes.class))
      return;

    /*
    if (annotated.isAnnotationPresent(Named.class)) {
      throw new ConfigException(L.l("{0}: invalid @Specializes bean because it also implements @Named.",
                                    getTargetName()));
    }
    */
    
    Type baseType = annotated.getBaseType();
    
    if (! (baseType instanceof Class)) {
      throw new ConfigException(L.l("{0}: invalid @Specializes bean because '{1}' is not a class.",
                                    getTargetName(), baseType));
    }
    
    Class baseClass = (Class) baseType;
    Class parentClass = baseClass.getSuperclass();

    if (baseClass.getSuperclass() == null ||
        baseClass.getSuperclass().equals(Object.class)) {
      throw new ConfigException(L.l("{0}: invalid @Specializes bean because the superclass '{1}' is not a managed bean.",
                                    getTargetName(), baseClass.getSuperclass()));
    }
    
    if ((annotated.isAnnotationPresent(Stateless.class)
         != parentClass.isAnnotationPresent(Stateless.class))) {
      throw new ConfigException(L.l("{0}: invalid @Specializes bean because the bean is a @Stateless bean but its parent is not.",
                                    getTargetName()));
    }
    
    if ((annotated.isAnnotationPresent(Stateful.class)
         != parentClass.isAnnotationPresent(Stateful.class))) {
      throw new ConfigException(L.l("{0}: invalid @Specializes bean because the bean is a @Stateful bean but its parent is not.",
                                    getTargetName()));
    }
    
    if ((annotated.isAnnotationPresent(Singleton.class)
         != parentClass.isAnnotationPresent(Singleton.class))) {
      throw new ConfigException(L.l("{0}: invalid @Specializes bean because the bean is a @Singleton bean but its parent is not.",
                                    getTargetName()));
    }
  }

  protected void introspectDefault()
  {
    // if (_qualifiers.size() == 0)
    
    // _qualifiers.add(AnyLiteral.ANY);
    boolean isQualifier = false;
    for (Annotation ann : _qualifiers) {
      if (! Named.class.equals(ann.annotationType())
          && ! Any.class.equals(ann.annotationType())) {
        isQualifier = true;
      }
    }
    
    if (! isQualifier)
      _qualifiers.add(DefaultLiteral.DEFAULT);
      
    _qualifiers.add(AnyLiteral.ANY);

    if (_scope == null)
      _scope = Dependent.class;

    if ("".equals(_name))
      _name = getDefaultName();
  }

  protected String getDefaultName()
  {
    Class targetClass = getTargetClass();
    
    String name;
    
    if (targetClass.isAnnotationPresent(Specializes.class))
      name = targetClass.getSuperclass().getSimpleName();
    else
      name = targetClass.getSimpleName();
    
    return Character.toLowerCase(name.charAt(0)) + name.substring(1);
  }

  protected void bind()
  {
  }

  /**
   * Returns true if the bean can be null
   */
  @Override
  public boolean isNullable()
  {
    return ! getBeanClass().isPrimitive();
  }

  /**
   * Returns true if the bean is serializable
   */
  @Override
  public boolean isPassivationCapable()
  {
    return Serializable.class.isAssignableFrom(getTargetClass());
  }
  
  protected boolean isPassivating()
  {
    return (_isPassivating || isNormalScope()
            || javax.inject.Singleton.class.equals(getScope()));
  }
  
  protected boolean isNormalScope()
  {
    return getBeanManager().isNormalScope(getScope());
  }
  
  public void setPassivating(boolean isPassivating)
  {
    _isPassivating = isPassivating;
  }

  /**
   * Instantiate the bean.
   */
  @Override
  public T create(CreationalContext env)
  {
    throw new UnsupportedOperationException(getClass().getName());
  }

  /**
   * Call destroy
   */
  @Override
  public void destroy(T instance, CreationalContext env)
  {
  }

  public void dispose(T instance)
  {
  }

  /**
   * Returns the set of injection points, for validation.
   */
  @Override
  public Set getInjectionPoints()
  {
    return new HashSet();
  }

  protected String getNamedValue(Annotation ann)
  {
    try {
      if (ann instanceof Named) {
        return ((Named) ann).value();
      }

      Method method = ann.getClass().getMethod("value");
      method.setAccessible(true);

      return (String) method.invoke(ann);
    } catch (NoSuchMethodException e) {
      // ioc/0m04
      log.log(Level.FINE, e.toString(), e);

      return "";
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public String toDebugString()
  {
    StringBuilder sb = new StringBuilder();

    sb.append(getTargetSimpleName());
    sb.append("[");

    if (_name != null) {
      sb.append("name=");
      sb.append(_name);
    }

    for (Annotation qualifier : _qualifiers) {
      sb.append(",");
      sb.append(qualifier);
    }

    if (_scope != null && _scope != Dependent.class) {
      sb.append(", @");
      sb.append(_scope.getSimpleName());
    }

    sb.append("]");

    return sb.toString();
  }

  static class MethodNameComparator implements Comparator> {
    @Override
    public int compare(AnnotatedMethod a, AnnotatedMethod b)
    {
      return a.getJavaMember().getName().compareTo(b.getJavaMember().getName());
    }
  }

  static class AnnotationComparator implements Comparator {
    @Override
    public int compare(Annotation a, Annotation b)
    {
      Class annTypeA = a.annotationType();
      Class annTypeB = b.annotationType();

      return annTypeA.getName().compareTo(annTypeB.getName());
    }
  }

  static {
    _reservedTypes.add(java.io.Closeable.class);
    _reservedTypes.add(java.io.Serializable.class);
    _reservedTypes.add(Cloneable.class);
    _reservedTypes.add(Object.class);
    _reservedTypes.add(Comparable.class);

    Method namedValueMethod = null;

    try {
      namedValueMethod = Named.class.getMethod("value");
      namedValueMethod.setAccessible(true);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy