Package jodd.petite

Source Code of jodd.petite.PetiteBeans

// Copyright (c) 2003-2014, Jodd Team (jodd.org). All Rights Reserved.

package jodd.petite;

import jodd.introspector.ClassDescriptor;
import jodd.introspector.ClassIntrospector;
import jodd.introspector.CtorDescriptor;
import jodd.introspector.MethodDescriptor;
import jodd.introspector.PropertyDescriptor;
import jodd.petite.meta.InitMethodInvocationStrategy;
import jodd.petite.scope.Scope;
import jodd.petite.scope.SingletonScope;
import jodd.props.Props;
import jodd.util.ReflectUtil;
import jodd.util.StringPool;
import jodd.log.Logger;
import jodd.log.LoggerFactory;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
* Base layer of {@link PetiteContainer Petite Container}.
* Holds beans and scopes definitions.
*/
public abstract class PetiteBeans {

  private static final Logger log = LoggerFactory.getLogger(PetiteBeans.class);

  /**
   * Map of all beans definitions.
   */
  protected final Map<String, BeanDefinition> beans = new HashMap<String, BeanDefinition>();

  /**
   * Map of all bean scopes.
   */
  protected final Map<Class<? extends Scope>, Scope> scopes = new HashMap<Class<? extends Scope>, Scope>();

  /**
   * Map of all providers.
   */
  protected final Map<String, ProviderDefinition> providers = new HashMap<String, ProviderDefinition>();

  /**
   * Map of all bean collections.
   */
  protected final Map<Class, String[]> beanCollections = new HashMap<Class, String[]>();

  /**
   * {@link PetiteConfig Petite configuration}.
   */
  protected final PetiteConfig petiteConfig;

  /**
   * {@link InjectionPointFactory Injection point factory}.
   */
  protected final InjectionPointFactory injectionPointFactory;

  /**
   * {@link PetiteResolvers Petite resolvers}.
   */
  protected final PetiteResolvers petiteResolvers;

  /**
   * {@link ParamManager Parameters manager}.
   */
  protected final ParamManager paramManager;

  protected PetiteBeans(PetiteConfig petiteConfig) {
    this.petiteConfig = petiteConfig;
    this.injectionPointFactory = new InjectionPointFactory(petiteConfig);
    this.petiteResolvers = new PetiteResolvers(injectionPointFactory);
    this.paramManager = new ParamManager();
  }

  /**
   * Returns parameter manager.
   */
  public ParamManager getParamManager() {
    return paramManager;
  }

  /**
   * Returns {@link PetiteConfig Petite configuration}.
   * All changes on config should be done <b>before</b>
   * beans registration process starts.
   */
  public PetiteConfig getConfig() {
    return petiteConfig;
  }

  // ---------------------------------------------------------------- scopes

  /**
   * Resolves and registers scope from a scope type.
   */
  @SuppressWarnings("unchecked")
  public <S extends Scope> S resolveScope(Class<S> scopeType) {
    S scope = (S) scopes.get(scopeType);
    if (scope == null) {

      try {
        scope = PetiteUtil.newInstance(scopeType, (PetiteContainer) this);
      } catch (Exception ex) {
        throw new PetiteException("Invalid Petite scope: " + scopeType.getName(), ex);
      }

      registerScope(scopeType, scope);
      scopes.put(scopeType, scope);
    }
    return scope;
  }

  /**
   * Registers new scope. It is not necessary to manually register scopes,
   * since they become registered on first scope resolving.
   * However, it is possible to pre-register some scopes, or to <i>replace</i> one scope
   * type with another. Replacing may be important for testing purposes when
   * using container-depended scopes.
   */
  public void registerScope(Class<? extends Scope> scopeType, Scope scope) {
    scopes.put(scopeType, scope);
  }

  // ---------------------------------------------------------------- lookup beans

  /**
   * Lookups for {@link BeanDefinition bean definition}.
   * Returns <code>null</code> if bean name doesn't exist.
   */
  public BeanDefinition lookupBeanDefinition(String name) {
    return beans.get(name);
  }

  /**
   * Lookups for first founded {@link BeanDefinition bean definition}.
   * Returns <code>null</code> if none of the beans is found.
   */
  protected BeanDefinition lookupBeanDefinitions(String... names) {
    for (String name : names) {
      BeanDefinition beanDefinition = lookupBeanDefinition(name);
      if (beanDefinition != null) {
        return beanDefinition;
      }
    }
    return null;
  }

  /**
   * Lookups for existing {@link jodd.petite.BeanDefinition bean definition}.
   * Throws exception if bean is not found.
   */
  protected BeanDefinition lookupExistingBeanDefinition(String name) {
    BeanDefinition beanDefinition = lookupBeanDefinition(name);
    if (beanDefinition == null) {
      throw new PetiteException("Bean not found: " + name);
    }
    return beanDefinition;
  }

  /**
   * Returns <code>true</code> if bean name is registered.
   */
  public boolean isBeanNameRegistered(String name) {
    return lookupBeanDefinition(name) != null;
  }

  /**
   * Resolves bean's name from bean annotation or type name. May be used for resolving bean name
   * of base type during registration of bean subclass.
   */
  public String resolveBeanName(Class type) {
    return PetiteUtil.resolveBeanName(type, petiteConfig.getUseFullTypeNames());
  }

  // ---------------------------------------------------------------- register beans

  /**
   * Creates {@link jodd.petite.BeanDefinition} on
   * {@link #registerPetiteBean(Class, String, Class, WiringMode, boolean) bean registration}.
   * This is a hook for modifying the bean data, like passing proxifed class etc.
   * By default returns new instance of {@link jodd.petite.BeanDefinition}.
   */
  protected BeanDefinition createBeanDefinitionForRegistration(
      String name, Class type, Scope scope, WiringMode wiringMode) {

    return new BeanDefinition(name, type, scope, wiringMode);
  }

  /**
   * Registers or defines a bean.
   *
   * @param type bean type, must be specified
   * @param name bean name, if <code>null</code> it will be resolved from the class (name or annotation)
   * @param scopeType bean scope, if <code>null</code> it will be resolved from the class (annotation or default one)
   * @param wiringMode wiring mode, if <code>null</code> it will be resolved from the class (annotation or default one)
   * @param define when set to <code>true</code> bean will be defined - all injection points will be set to none
   */
  public BeanDefinition registerPetiteBean(
      Class type, String name,
      Class<? extends Scope> scopeType,
      WiringMode wiringMode,
      boolean define) {

    if (name == null) {
      name = resolveBeanName(type);
    }
    if (wiringMode == null) {
      wiringMode = PetiteUtil.resolveBeanWiringMode(type);
    }
    if (wiringMode == WiringMode.DEFAULT) {
      wiringMode = petiteConfig.getDefaultWiringMode();
    }
    if (scopeType == null) {
      scopeType = PetiteUtil.resolveBeanScopeType(type);
    }
    if (scopeType == null) {
      scopeType = SingletonScope.class;
    }

    // remove existing bean
    BeanDefinition existing = removeBean(name);
    if (existing != null) {
      if (petiteConfig.getDetectDuplicatedBeanNames()) {
        throw new PetiteException(
            "Duplicated bean name detected while registering class '" + type.getName() + "'. Petite bean class '" +
            existing.type.getName() + "' is already registered with the name: " + name);
      }
    }

    // check if type is valid
    if (type.isInterface() == true) {
      throw new PetiteException("Failed to register interface: " + type.getName());
    }

    // registration
    if (log.isDebugEnabled()) {
      log.debug("Register bean " + name +
          " of type " + type.getSimpleName() +
          " in " + scopeType.getSimpleName() +
          " using wiring mode " + wiringMode.toString());
    }

    // register
    Scope scope = resolveScope(scopeType);
    BeanDefinition beanDefinition = createBeanDefinitionForRegistration(name, type, scope, wiringMode);
    beans.put(name, beanDefinition);

    // providers
    ProviderDefinition[] providerDefinitions = petiteResolvers.resolveProviderDefinitions(beanDefinition);

    if (providerDefinitions != null) {
      for (ProviderDefinition providerDefinition : providerDefinitions) {
        providers.put(providerDefinition.name, providerDefinition);
      }
    }

    // define
    if (define) {
      beanDefinition.ctor = petiteResolvers.resolveCtorInjectionPoint(beanDefinition.getType());
      beanDefinition.properties = PropertyInjectionPoint.EMPTY;
      beanDefinition.methods = MethodInjectionPoint.EMPTY;
      beanDefinition.initMethods = InitMethodPoint.EMPTY;
      beanDefinition.destroyMethods = DestroyMethodPoint.EMPTY;
    }

    // return
    return beanDefinition;
  }

  /**
   * Removes all petite beans of provided type. Bean name is not resolved from a type!
   * Instead, all beans are iterated and only beans with equal types are removed.
   * @see #removeBean(String)
   */
  public void removeBean(Class type) {
    // collect bean names
    Set<String> beanNames = new HashSet<String>();

    for (BeanDefinition def : beans.values()) {
      if (def.type.equals(type)) {
        beanNames.add(def.name);
      }
    }

    // remove collected bean names
    for (String beanName : beanNames) {
      removeBean(beanName);
    }
  }

  /**
   * Removes bean and returns definition of removed bean.
   * All resolvers references are deleted, too.
   * Returns bean definition of removed bean or <code>null</code>.
   */
  public BeanDefinition removeBean(String name) {
    BeanDefinition bd = beans.remove(name);
    if (bd == null) {
      return null;
    }
    bd.scopeRemove();
    return bd;
  }

  // ---------------------------------------------------------------- bean collections

  /**
   * Resolves bean names for give type.
   */
  protected String[] resolveBeanNamesForType(Class type) {
    String[] beanNames = beanCollections.get(type);
    if (beanNames != null) {
      return beanNames;
    }

    ArrayList<String> list = new ArrayList<String>();

    for (Map.Entry<String, BeanDefinition> entry : beans.entrySet()) {
      BeanDefinition beanDefinition = entry.getValue();

      if (ReflectUtil.isTypeOf(beanDefinition.type, type)) {
        String beanName = entry.getKey();
        list.add(beanName);
      }
    }

    if (list.isEmpty()) {
      beanNames = StringPool.EMPTY_ARRAY;
    } else {
      beanNames = list.toArray(new String[list.size()]);
    }

    beanCollections.put(type, beanNames);
    return beanNames;
  }

  // ---------------------------------------------------------------- injection points

  /**
   * Registers constructor injection point.
   *
   * @param beanName bean name
   * @param paramTypes constructor parameter types, may be <code>null</code>
   * @param references references for arguments
   */
  public void registerPetiteCtorInjectionPoint(String beanName, Class[] paramTypes, String[] references) {
    BeanDefinition beanDefinition = lookupExistingBeanDefinition(beanName);
    String[][] ref = PetiteUtil.convertRefToReferences(references);

    ClassDescriptor cd = ClassIntrospector.lookup(beanDefinition.type);
    Constructor constructor = null;

    if (paramTypes == null) {
      CtorDescriptor[] ctors = cd.getAllCtorDescriptors();
      if (ctors != null && ctors.length > 0) {
        if (ctors.length > 1) {
          throw new PetiteException(ctors.length + " suitable constructor found as injection point for: " + beanDefinition.type.getName());
        }
        constructor = ctors[0].getConstructor();
      }
    } else {
      CtorDescriptor ctorDescriptor = cd.getCtorDescriptor(paramTypes, true);

      if (ctorDescriptor != null) {
        constructor = ctorDescriptor.getConstructor();
      }
    }

    if (constructor == null) {
      throw new PetiteException("Constructor not found: " + beanDefinition.type.getName());
    }

    beanDefinition.ctor = injectionPointFactory.createCtorInjectionPoint(constructor, ref);
  }

  /**
   * Registers property injection point.
   *
   * @param beanName bean name
   * @param property property name
   * @param reference explicit injection reference, may be <code>null</code>
   */
  public void registerPetitePropertyInjectionPoint(String beanName, String property, String reference) {
    BeanDefinition beanDefinition = lookupExistingBeanDefinition(beanName);
    String[] references = reference == null ? null : new String[] {reference};

    ClassDescriptor cd = ClassIntrospector.lookup(beanDefinition.type);
    PropertyDescriptor propertyDescriptor = cd.getPropertyDescriptor(property, true);
    if (propertyDescriptor == null) {
      throw new PetiteException("Property not found: " + beanDefinition.type.getName() + '#' + property);
    }

    PropertyInjectionPoint pip =
        injectionPointFactory.createPropertyInjectionPoint(propertyDescriptor, references);

    beanDefinition.addPropertyInjectionPoint(pip);
  }

  /**
   * Registers set injection point.
   *
   * @param beanName bean name
   * @param property set property name
   */
  public void registerPetiteSetInjectionPoint(String beanName, String property) {
    BeanDefinition beanDefinition = lookupExistingBeanDefinition(beanName);
    ClassDescriptor cd = ClassIntrospector.lookup(beanDefinition.type);

    PropertyDescriptor propertyDescriptor = cd.getPropertyDescriptor(property, true);

    if (propertyDescriptor == null) {
      throw new PetiteException("Property not found: " + beanDefinition.type.getName() + '#' + property);
    }

    SetInjectionPoint sip = injectionPointFactory.createSetInjectionPoint(propertyDescriptor);

    beanDefinition.addSetInjectionPoint(sip);
  }

  /**
   * Registers method injection point.
   *
   * @param beanName bean name
   * @param methodName method name
   * @param arguments method arguments, may be <code>null</code>
   * @param references injection references
   */
  public void registerPetiteMethodInjectionPoint(String beanName, String methodName, Class[] arguments, String[] references) {
    BeanDefinition beanDefinition = lookupExistingBeanDefinition(beanName);
    String[][] ref = PetiteUtil.convertRefToReferences(references);
    ClassDescriptor cd = ClassIntrospector.lookup(beanDefinition.type);

    Method method = null;
    if (arguments == null) {
      MethodDescriptor[] methods = cd.getAllMethodDescriptors(methodName);
      if (methods != null && methods.length > 0) {
        if (methods.length > 1) {
          throw new PetiteException(methods.length + " suitable methods found as injection points for: " + beanDefinition.type.getName() + '#' + methodName);
        }
        method = methods[0].getMethod();
      }
    } else {
      MethodDescriptor md = cd.getMethodDescriptor(methodName, arguments, true);
      if (md != null) {
        method = md.getMethod();
      }
    }
    if (method == null) {
      throw new PetiteException("Method not found: " + beanDefinition.type.getName() + '#' + methodName);
    }
    MethodInjectionPoint mip = injectionPointFactory.createMethodInjectionPoint(method, ref);

    beanDefinition.addMethodInjectionPoint(mip);
  }

  /**
   * Registers init method.
   *
   * @param beanName bean name
   * @param invocationStrategy moment of invocation
   * @param initMethodNames init method names
   */
  public void registerPetiteInitMethods(String beanName, InitMethodInvocationStrategy invocationStrategy, String... initMethodNames) {
    BeanDefinition beanDefinition = lookupExistingBeanDefinition(beanName);

    ClassDescriptor cd = ClassIntrospector.lookup(beanDefinition.type);
    if (initMethodNames == null) {
      initMethodNames = StringPool.EMPTY_ARRAY;
    }

    int total = initMethodNames.length;
    InitMethodPoint[] initMethodPoints = new InitMethodPoint[total];

    int i;
    for (i = 0; i < initMethodNames.length; i++) {
      MethodDescriptor md = cd.getMethodDescriptor(initMethodNames[i], ReflectUtil.NO_PARAMETERS, true);
      if (md == null) {
        throw new PetiteException("Init method not found: " + beanDefinition.type.getName() + '#' + initMethodNames[i]);
      }
      initMethodPoints[i] = new InitMethodPoint(md.getMethod(), i, invocationStrategy);
    }

    beanDefinition.addInitMethodPoints(initMethodPoints);
  }

  /**
   * Registers destroy method.
   *
   * @param beanName bean name
   * @param destroyMethodNames destroy method names
   */
  public void registerPetiteDestroyMethods(String beanName, String... destroyMethodNames) {
    BeanDefinition beanDefinition = lookupExistingBeanDefinition(beanName);

    ClassDescriptor cd = ClassIntrospector.lookup(beanDefinition.type);
    if (destroyMethodNames == null) {
      destroyMethodNames = StringPool.EMPTY_ARRAY;
    }

    int total = destroyMethodNames.length;
    DestroyMethodPoint[] destroyMethodPoints = new DestroyMethodPoint[total];

    int i;
    for (i = 0; i < destroyMethodNames.length; i++) {
      MethodDescriptor md = cd.getMethodDescriptor(destroyMethodNames[i], ReflectUtil.NO_PARAMETERS, true);
      if (md == null) {
        throw new PetiteException("Destroy method not found: " + beanDefinition.type.getName() + '#' + destroyMethodNames[i]);
      }
      destroyMethodPoints[i] = new DestroyMethodPoint(md.getMethod());
    }

    beanDefinition.addDestroyMethodPoints(destroyMethodPoints);
  }

  // ---------------------------------------------------------------- providers

  /**
   * Registers instance method provider.
   *
   * @param providerName provider name
   * @param beanName bean name
   * @param methodName instance method name
   * @param arguments method argument types, may be <code>null</code>
   */
  public void registerPetiteProvider(String providerName, String beanName, String methodName, Class[] arguments) {
    BeanDefinition beanDefinition = lookupBeanDefinition(beanName);

    if (beanDefinition == null) {
      throw new PetiteException("Bean not found: " + beanName);
    }

    Class beanType = beanDefinition.type;

    ClassDescriptor cd = ClassIntrospector.lookup(beanType);
    MethodDescriptor md = cd.getMethodDescriptor(methodName, arguments, true);

    if (md == null) {
      throw new PetiteException("Provider method not found: " + methodName);
    }

    ProviderDefinition providerDefinition = new ProviderDefinition(providerName, beanName, md.getMethod());

    providers.put(providerName, providerDefinition);
  }

  /**
   * Registers static method provider.
   *
   * @param providerName provider name
   * @param type class type
   * @param staticMethodName static method name
   * @param arguments method argument types, may be <code>null</code>
   */
  public void registerPetiteProvider(String providerName, Class type, String staticMethodName, Class[] arguments) {
    ClassDescriptor cd = ClassIntrospector.lookup(type);
    MethodDescriptor md = cd.getMethodDescriptor(staticMethodName, arguments, true);

    if (md == null) {
      throw new PetiteException("Provider method not found: " + staticMethodName);
    }

    ProviderDefinition providerDefinition = new ProviderDefinition(providerName, md.getMethod());

    providers.put(providerName, providerDefinition);
  }

  // ---------------------------------------------------------------- statistics

  /**
   * Returns total number of registered beans.
   */
  public int getTotalBeans() {
    return beans.size();
  }

  /**
   * Returns total number of used scopes.
   */
  public int getTotalScopes() {
    return scopes.size();
  }

  /**
   * Returns set of all bean names.
   */
  public Set<String> getBeanNames() {
    return beans.keySet();
  }

  // ---------------------------------------------------------------- params

  /**
   * Defines new parameter. Parameters with same name will be replaced.
   */
  public void defineParameter(String name, Object value) {
    paramManager.put(name, value);
  }

  /**
   * Returns defined parameter.
   */
  public Object getParameter(String name) {
    return paramManager.get(name);
  }

  /**
   * Prepares list of all bean parameters and optionally resolves inner references.
   */
  protected String[] resolveBeanParams(String name, boolean resolveReferenceParams) {
    return paramManager.resolve(name, resolveReferenceParams);
  }

  /**
   * Defines many parameters at once.
   */
  public void defineParameters(Map<?, ?> properties) {
    for (Map.Entry<?, ?> entry : properties.entrySet()) {
      defineParameter(entry.getKey().toString(), entry.getValue());
    }
  }

  /**
   * Defines many parameters at once from {@link jodd.props.Props}.
   */
  public void defineParameters(Props props) {
    Map<?, ?> map = new HashMap<Object, Object>();
    props.extractProps(map);
    defineParameters(map);
  }

}
TOP

Related Classes of jodd.petite.PetiteBeans

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.