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

net.sf.javagimmicks.cdi.InjectionSpec Maven / Gradle / Ivy

There is a newer version: 0.99-alpha1
Show newest version
package net.sf.javagimmicks.cdi;

import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.UnsatisfiedResolutionException;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.inject.Named;
import javax.inject.Qualifier;

/**
 * Specifies a CDI bean injection and allows respective bean lookup from a given
 * {@link BeanManager} - allows easy building via {@link #build()} and
 * {@link #build(BeanManager)}.
 * 
 * @param 
 *           the type of beans to lookup
 */
public class InjectionSpec
{
   private final Type _type;
   private final Set _annotations;

   private final String _name;

   /**
    * Creates a new {@link Builder} for building {@link InjectionSpec}s using
    * the given {@link BeanManager}.
    * 
    * @param beanManager
    *           the {@link BeanManager} to use for bean lookup
    * @return a respective {@link Builder} to specify the injection
    *         configuration
    * @see Builder
    */
   public static  Builder build(final BeanManager beanManager)
   {
      return new Builder(beanManager);
   }

   /**
    * Convenience method for {@link #build(BeanManager)} that will use the
    * {@link BeanManager} from {@link CDIContext#getBeanManager()}.
    * 
    * @return a respective {@link Builder} to specify the injection
    *         configuration
    * @see Builder
    * @see CDIContext#getBeanManager()
    */
   public static  Builder build()
   {
      return build(null);
   }

   /**
    * Creates a new instance looking up a {@link Named} bean
    * 
    * @param name
    *           the name of the {@link Named} bean to lookup
    */
   public InjectionSpec(final String name)
   {
      if (name == null || name.length() == 0)
      {
         throw new IllegalArgumentException("Name must be specified!");
      }

      _name = name;
      _type = null;
      _annotations = null;
   }

   /**
    * Creates a new instance looking up bean by {@link Type} and the given
    * {@link Qualifier} {@link Annotation}s.
    * 
    * @param type
    *           the {@link Type} of the beans to lookup
    * @param annotations
    *           the {@link Qualifier} {@link Annotation}s the bean must have
    */
   public InjectionSpec(final Type type, final Collection annotations)
   {
      if (type == null)
      {
         throw new IllegalArgumentException("Type must be specified!");
      }

      _type = type;
      _annotations = annotations != null ? new HashSet(annotations) : Collections. emptySet();

      _name = null;
   }

   /**
    * Creates a new instance looking up bean by {@link Type} and the given
    * {@link Qualifier} {@link Annotation}s.
    * 
    * @param type
    *           the {@link Type} of the beans to lookup
    * @param annotations
    *           the {@link Qualifier} {@link Annotation}s the bean must have
    */
   public InjectionSpec(final Type type, final Annotation... annotations)
   {
      this(type, Arrays.asList(annotations));
   }

   /**
    * Creates a new instance looking up bean by an (optionally parameterized)
    * {@link Class}, the given parameter {@link Type}s and the given
    * {@link Qualifier} {@link Annotation}s.
    * 
    * @param clazz
    *           the {@link Class} of the beans to lookup
    * @param typeParameters
    *           the {@link List} of {@link Type} in case of a parameterized
    *           {@link Class}
    * @param annotations
    *           the {@link Qualifier} {@link Annotation}s the bean must have
    */
   public InjectionSpec(final Class clazz, final List typeParameters,
         final Collection annotations)
   {
      this(buildType(clazz, typeParameters), annotations);
   }

   /**
    * Creates a new instance looking up bean by {@link Class} and the given
    * {@link Qualifier} {@link Annotation}s.
    * 
    * @param clazz
    *           the {@link Class} of the beans to lookup
    * @param annotations
    *           the {@link Qualifier} {@link Annotation}s the bean must have
    */
   public InjectionSpec(final Class clazz, final Collection annotations)
   {
      this(clazz, null, annotations);
   }

   /**
    * Creates a new instance looking up bean by {@link Class} and the given
    * {@link Qualifier} {@link Annotation}s.
    * 
    * @param clazz
    *           the {@link Class} of the beans to lookup
    * @param annotations
    *           the {@link Qualifier} {@link Annotation}s the bean must have
    */
   public InjectionSpec(final Class clazz, final Annotation... annotations)
   {
      this(clazz, Arrays.asList(annotations));
   }

   /**
    * Checks if the current instance specifies a CDI injection on {@link Type}
    * basis.
    * 

* As an alternative, beans can be looked up by name if they are * {@link Named} annotated * * @return if the current instance specifies a CDI injection on {@link Type} * basis * @see #isNameBased() */ public boolean isTypeBased() { return _type != null; } /** * Checks if the current instance specifies a CDI injection on name basis * (i.e. looking up a {@link Named} annotated bean). *

* As an alternative, beans can be looked up on {@link Type} and * {@link Qualifier} basis. * * @return if the current instance specifies a CDI injection on name basis * @see #isTypeBased() */ public boolean isNameBased() { return _name != null; } /** * Returns the {@link Type} of beans to lookup or {@code null} if the lookup * is name based. * * @return the {@link Type} of beans to lookup * @see #isTypeBased() * @see #isNameBased() */ public Type getType() { return _type; } /** * Returns the {@link Set} of {@link Qualifier} {@link Annotation}s used for * lookup. This will be empty if the lookup is name based. * * @return the {@link Set} of {@link Qualifier} {@link Annotation}s used for * lookup * @see #isTypeBased() * @see #isNameBased() */ public Set getAnnotations() { return Collections.unmodifiableSet(_annotations); } /** * Return the name to use for lookup or {@code null} if the lookup is * {@link Type} based. * * @return the name to use for lookup * @see #isTypeBased() * @see #isNameBased() */ public String getName() { return _name; } /** * Looks up a bean instance in the given {@link BeanManager} according to the * internal injection configuration information. * * @param beanManager * the {@link BeanManager} to use for lookup * @return the resulting looked up bean * @throws UnsatisfiedResolutionException * if no (unique) bean could be looked up */ @SuppressWarnings("unchecked") public E getInstance(final BeanManager beanManager) { // Type-based case if (isTypeBased()) { final Bean bean = beanManager .resolve(beanManager.getBeans(_type, _annotations.toArray(new Annotation[0]))); if (bean == null) { throw new UnsatisfiedResolutionException("Unable to resolve a bean for " + _type + " with bindings " + _annotations); } final CreationalContext cc = beanManager.createCreationalContext(bean); return (E) beanManager.getReference(bean, _type, cc); } // Name-based case else if (isNameBased()) { final Bean bean = beanManager.resolve(beanManager.getBeans(_name)); if (bean == null) { throw new UnsatisfiedResolutionException("Unable to resolve a bean name " + _name); } final CreationalContext cc = beanManager.createCreationalContext(bean); return (E) beanManager.getReference(bean, bean.getBeanClass(), cc); } else { return null; } } private static Type buildType(final Class clazz, final Collection typeParameters) { if (clazz == null) { throw new IllegalArgumentException("Class must be specified!"); } if (typeParameters != null && !typeParameters.isEmpty()) { return new ParameterizedType() { @Override public Type getRawType() { return clazz; } @Override public Type getOwnerType() { return null; } @Override public Type[] getActualTypeArguments() { return typeParameters.toArray(new Type[0]); } }; } else { return clazz; } } /** * A builder class for creating {@link InjectionSpec} instances using a * fluent API. * * @param * the type of beans to lookup */ public static class Builder { private final BeanManager _beanManager; private Type _type; private Class _class; private final List _typeParameters = new ArrayList(); private final Set _annotations = new HashSet(); private String _name; private Builder(final BeanManager beanManager) { _beanManager = beanManager; } /** * Registers a {@link Type} literal to be used for lookup. * * @param type * the {@link Type} to use for lookup * @return the {@link Builder} itself * @throws IllegalStateException * if a {@link Class} was already registered via * {@link #setClass(Class)} or {@link Type} parameter was * already registered (e.g. via * {@link #addTypeParameters(Collection)}) or a name was * already registered via {@link #setName(String)} */ public Builder setType(final Type type) { checkHasName(); checkHasClassOrTypeParameters(); _type = type; return this; } /** * Registers a {@link Class} literal to be used for lookup. * * @param clazz * the {@link Class} to use for lookup * @return the {@link Builder} itself * @throws IllegalStateException * if a {@link Type} was already registered via * {@link #setType(Type)} or a name was already registered via * {@link #setName(String)} */ public Builder setClass(final Class clazz) { checkHasName(); checkHasType(); _class = clazz; return this; } /** * Adds the given {@link Type} parameters to use for lookup - only makes * sense in combination with a parameterized {@link Class} registered via * {@link #setClass(Class)}. * * @param typeParameters * the {@link Type} parameters to use for the registered * {@link Class} * @return the {@link Builder} itself * @throws IllegalStateException * if a {@link Type} was already registered via * {@link #setType(Type)} or a name was already registered via * {@link #setName(String)} */ public Builder addTypeParameters(final Collection typeParameters) { checkHasName(); checkHasType(); _typeParameters.addAll(typeParameters); return this; } /** * Adds the given {@link Type} parameters to use for lookup - only makes * sense in combination with a parameterized {@link Class} registered via * {@link #setClass(Class)}. * * @param typeParameters * the {@link Type} parameters to use for the registered * {@link Class} * @return the {@link Builder} itself * @throws IllegalStateException * if a {@link Type} was already registered via * {@link #setType(Type)} or a name was already registered via * {@link #setName(String)} */ public Builder addTypeParameters(final Type... typeParameters) { checkHasName(); checkHasType(); return addTypeParameters(Arrays.asList(typeParameters)); } /** * Adds the given {@link Qualifier} {@link Annotation}s to use for lookup * - only makes sense in combination with a provided {@link Type} (see * {@link #setType(Type)}) or {@link Class} (see {@link #setClass(Class)} * ). * * @param annotations * the {@link Qualifier} {@link Annotation}s to add * @return the {@link Builder} itself * @throws IllegalStateException * if a name was already registered via * {@link #setName(String)} */ public Builder addAnnotations(final Collection annotations) { checkHasName(); _annotations.addAll(annotations); return this; } /** * Adds the given {@link Qualifier} {@link Annotation}s to use for lookup * - only makes sense in combination with a provided {@link Type} (see * {@link #setType(Type)}) or {@link Class} (see {@link #setClass(Class)} * ). * * @param annotations * the {@link Qualifier} {@link Annotation}s to add * @return the {@link Builder} itself * @throws IllegalStateException * if a name was already registered via * {@link #setName(String)} */ public Builder addAnnotations(final Annotation... annotations) { return addAnnotations(Arrays.asList(annotations)); } /** * Allows registration of {@link Qualifier} {@link Annotation}s to use for * lookup via fluent API (i.e. an instance of {@link AnnotationBuilder}) - * only makes sense in combination with a provided {@link Type} (see * {@link #setType(Type)}) or {@link Class} (see {@link #setClass(Class)} * ). * * @param annotationType * the type of {@link Qualifier} {@link Annotation} to add for * lookup usage * @return an {@link AnnotationBuilder} allowing to specify * {@link Annotation} details via fluent API * @throws IllegalStateException * if a name was already registered via * {@link #setName(String)} * @see AnnotationBuilder */ public AnnotationBuilder annotation(final Class annotationType) { checkHasName(); return new AnnotationBuilder(AnnotationLiteralHelper.annotationWithMembers(annotationType)); } /** * Registers a name used for lookup of a {@link Named} bean. * * @param name * the name to use for lookup * @return the {@link Builder} itself * @throws IllegalStateException * if a {@link Class} was already registered via * {@link #setClass(Class)} or {@link Type} parameter was * already registered (e.g. via * {@link #addTypeParameters(Collection)}) or {@link Type} was * already registered via {@link #setType(Type)} */ public Builder setName(final String name) { checkHasClassOrTypeParameters(); checkHasType(); _name = name; return this; } /** * Finishes the building process and generates a respective * {@link InjectionSpec} object * * @return an {@link InjectionSpec} the conforms to the specifications * done on this builder * @throws IllegalStateException * if no or insufficient specifications information were * provided so far on this builder */ public InjectionSpec getInjection() { if (_name != null) { return new InjectionSpec(_name); } else if (_class != null) { return new InjectionSpec(_class, _typeParameters, _annotations); } else if (_type != null) { return new InjectionSpec(_type, _annotations); } else { throw new IllegalStateException("Neither a name nor a type is set in this builder!"); } } /** * Finishes the building process and looks up a respective bean instance * with in the internal {@link BeanManager} * * @throws IllegalStateException * if no or insufficient specifications information were * provided so far on this builder */ public E getInstance() { final BeanManager beanManager = _beanManager == null ? CDIContext.getBeanManager() : _beanManager; return getInjection().getInstance(beanManager); } private void checkHasName() { if (_name != null) { throw new IllegalStateException("There is already a name set on this builder!"); } } private void checkHasClassOrTypeParameters() { if (_class != null || !_typeParameters.isEmpty()) { throw new IllegalStateException( "There is already a class set or type parameters specified on this builder!"); } } private void checkHasType() { if (_type != null) { throw new IllegalStateException("There is already a type set on this builder!"); } } /** * A builder to specify additional information for {@link Qualifier} * {@link Annotation} to use for bean lookup via fluent API. * * @param * the type of {@link Annotation} to specify */ public class AnnotationBuilder { private final AnnotationLiteralHelper.Builder _delegate; private AnnotationBuilder(final AnnotationLiteralHelper.Builder delegate) { this._delegate = delegate; } /** * Specifies the value for a member of the given {@link Annotation} * * @param memberName * the member name * @param value * the value for the given member * @return the {@link AnnotationBuilder} itself * @throws IllegalArgumentException * if the given member name does not match a member of the * underlying {@link Annotation} */ public AnnotationBuilder member(final String memberName, final Object value) { _delegate.member(memberName, value); return this; } /** * Finishes the building process * * @return the {@link Builder} that originally created this * {@link AnnotationBuilder} */ public Builder add() { addAnnotations(_delegate.get()); return Builder.this; } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy