content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied.  See the License for the
   * specific language governing permissions and limitations
   * under the License.
   */
  package org.apache.aries.blueprint.container;
  
  import static org.apache.aries.blueprint.utils.ReflectionUtils.getRealCause;
  
  import java.lang.reflect.Constructor;
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.Method;
  import java.lang.reflect.Modifier;
  import java.lang.reflect.Type;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.LinkedHashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.concurrent.Callable;
  import java.util.concurrent.Semaphore;
  import java.util.concurrent.atomic.AtomicReference;
  
  import org.apache.aries.blueprint.BeanProcessor;
  import org.apache.aries.blueprint.ComponentDefinitionRegistry;
  import org.apache.aries.blueprint.Interceptor;
  import org.apache.aries.blueprint.di.AbstractRecipe;
  import org.apache.aries.blueprint.di.Recipe;
  import org.apache.aries.blueprint.proxy.ProxyUtils;
  import org.apache.aries.blueprint.services.ExtendedBlueprintContainer;
  import org.apache.aries.blueprint.utils.ReflectionUtils;
  import org.apache.aries.blueprint.utils.ReflectionUtils.PropertyDescriptor;
  import org.osgi.service.blueprint.container.ComponentDefinitionException;
  import org.osgi.service.blueprint.container.ReifiedType;
  import org.osgi.service.blueprint.reflect.BeanMetadata;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
   * A <code>Recipe</code> to create POJOs.
   *
   * @version $Rev$, $Date$
   */
  public class BeanRecipe extends AbstractRecipe {
  
      static class UnwrapperedBeanHolder {
          final Object unwrapperedBean;
          final BeanRecipe recipe;
  
          public UnwrapperedBeanHolder(Object unwrapperedBean, BeanRecipe recipe) {
              this.unwrapperedBean = unwrapperedBean;
              this.recipe = recipe;
          }
      }
  
      public class VoidableCallable implements Callable<Object>, Voidable {
  
          private final AtomicReference<Object> ref = new AtomicReference<Object>();
  
          private final Semaphore sem = new Semaphore(1);
  
          private final ThreadLocal<Object> deadlockDetector = new ThreadLocal<Object>();
  
          public void voidReference() {
              ref.set(null);
          }
  
          public Object call() throws ComponentDefinitionException {
              Object o = ref.get();
  
              if (o == null) {
                  if(deadlockDetector.get() != null) {
                      deadlockDetector.remove();
                      throw new ComponentDefinitionException("Construction cycle detected for bean " + name);
                  }
  
                  sem.acquireUninterruptibly();
                  try {
                      o = ref.get();
                      if (o == null) {
                          deadlockDetector.set(this);
                          try {
                              o = internalCreate2();
                              ref.set(o);
                          } finally {
                              deadlockDetector.remove();
                          }
                      }
                  } finally {
                      sem.release();
                  }
              }
  
              return o;
          }
  
      }
  
      private static final Logger LOGGER = LoggerFactory.getLogger(BeanRecipe.class);
  
      private final ExtendedBlueprintContainer blueprintContainer;
      private final LinkedHashMap<String,Object> properties = new LinkedHashMap<String,Object>();
      private final Object type;
  
      private String initMethod;
      private String destroyMethod;
      private List<Recipe> explicitDependencies;
  
      private Recipe factory;
      private String factoryMethod;
      private List<Object> arguments;
      private List<String> argTypes;
      private boolean reorderArguments;
      private final boolean allowsFieldInjection;
      private BeanMetadata interceptorLookupKey;
  
  
      public BeanRecipe(String name, ExtendedBlueprintContainer blueprintContainer, Object type, boolean allowsFieldInjection) {
          super(name);
          this.blueprintContainer = blueprintContainer;
          this.type = type;
          this.allowsFieldInjection = allowsFieldInjection;
      }
  
      public Object getProperty(String name) {
          return properties.get(name);
      }
  
      public Map<String, Object> getProperties() {
          return new LinkedHashMap<String, Object>(properties);
      }
  
      public void setProperty(String name, Object value) {
          properties.put(name, value);
      }
  
      public void setFactoryMethod(String method) {
          this.factoryMethod = method;
      }
  
      public void setFactoryComponent(Recipe factory) {
          this.factory = factory;
      }
  
      public void setArgTypes(List<String> argTypes) {
          this.argTypes = argTypes;
      }
  
      public void setArguments(List<Object> arguments) {
          this.arguments = arguments;
      }
  
      public void setReorderArguments(boolean reorder) {
          this.reorderArguments = reorder;
      }
  
      public void setInitMethod(String initMethod) {
          this.initMethod = initMethod;
      }
  
      public String getInitMethod() {
          return initMethod;
      }
  
      public void setDestroyMethod(String destroyMethod) {
          this.destroyMethod = destroyMethod;
      }
  
      public String getDestroyMethod() {
          return destroyMethod;
      }
  
      public List<Recipe> getExplicitDependencies() {
          return explicitDependencies;
      }
  
      public void setExplicitDependencies(List<Recipe> explicitDependencies) {
          this.explicitDependencies = explicitDependencies;
      }
  
      public void setInterceptorLookupKey(BeanMetadata metadata) {
          interceptorLookupKey = metadata;
      }
  
      @Override
      public List<Recipe> getConstructorDependencies() {
          List<Recipe> recipes = new ArrayList<Recipe>();
          if (explicitDependencies != null) {
              recipes.addAll(explicitDependencies);
          }
          if (arguments != null) {
              for (Object argument : arguments) {
                  if (argument instanceof Recipe) {
                      recipes.add((Recipe)argument);
                  }
              }
          }
          return recipes;
      }
  
      public List<Recipe> getDependencies() {
          List<Recipe> recipes = new ArrayList<Recipe>();
          for (Object o : properties.values()) {
              if (o instanceof Recipe) {
                  Recipe recipe = (Recipe) o;
                  recipes.add(recipe);
              }
          }
          recipes.addAll(getConstructorDependencies());
          return recipes;
      }
  
      private void instantiateExplicitDependencies() {
          if (explicitDependencies != null) {
              for (Recipe recipe : explicitDependencies) {
                  recipe.create();
              }
          }
      }
  
      @Override
      protected Class loadClass(String className) {
          ClassLoader loader = type instanceof Class ? ((Class) type).getClassLoader() : null;
          ReifiedType t = loadType(className, loader);
          return t != null ? t.getRawClass() : null;
      }
  
      @Override
      protected ReifiedType loadType(String className) {
          return loadType(className, type instanceof Class ? ((Class) type).getClassLoader() : null);
      }
  
      private Object getInstance() throws ComponentDefinitionException {
          Object instance;
  
          // Instanciate arguments
          List<Object> args = new ArrayList<Object>();
          List<ReifiedType> argTypes = new ArrayList<ReifiedType>();
          if (arguments != null) {
              for (int i = 0; i < arguments.size(); i++) {
                  Object arg = arguments.get(i);
                  if (arg instanceof Recipe) {
                      args.add(((Recipe) arg).create());
                  } else {
                      args.add(arg);
                  }
                  if (this.argTypes != null) {
                      argTypes.add(this.argTypes.get(i) != null ? loadType(this.argTypes.get(i)) : null);
                  }
              }
          }
  
          if (factory != null) {
              // look for instance method on factory object
              Object factoryObj = factory.create();
  
              // If the factory is a service reference, we need to get hold of the actual proxy for the service
              /* BLUEPRINT-NOOSGI
              if (factoryObj instanceof ReferenceRecipe.ServiceProxyWrapper) {
                  try {
                      factoryObj = ((ReferenceRecipe.ServiceProxyWrapper) factoryObj).convert(new ReifiedType(Object.class));
                  } catch (Exception e) {
                      throw new ComponentDefinitionException("Error when instantiating bean " + getName() + " of class " + getType(), getRealCause(e));
                  }
              } else*/ if (factoryObj instanceof UnwrapperedBeanHolder) {
                  factoryObj = wrap((UnwrapperedBeanHolder) factoryObj, Object.class);
              }
  
              // Map of matching methods
              Map<Method, List<Object>> matches = findMatchingMethods(factoryObj.getClass(), factoryMethod, true, args, argTypes);
              if (matches.size() == 1) {
                  try {
                      Map.Entry<Method, List<Object>> match = matches.entrySet().iterator().next();
                      instance = invoke(match.getKey(), factoryObj, match.getValue().toArray());
                  } catch (Throwable e) {
                      throw new ComponentDefinitionException("Error when instantiating bean " + getName() + " of class " + getType(), getRealCause(e));
                  }
              } else if (matches.size() == 0) {
                  throw new ComponentDefinitionException("Unable to find a matching factory method " + factoryMethod + " on class " + factoryObj.getClass().getName() + " for arguments " + args + " when instanciating bean " + getName());
              } else {
                  throw new ComponentDefinitionException("Multiple matching factory methods " + factoryMethod + " found on class " + factoryObj.getClass().getName() + " for arguments " + args + " when instanciating bean " + getName() + ": " + matches.keySet());
              }
          } else if (factoryMethod != null) {
              // Map of matching methods
              Map<Method, List<Object>> matches = findMatchingMethods(getType(), factoryMethod, false, args, argTypes);
              if (matches.size() == 1) {
                  try {
                      Map.Entry<Method, List<Object>> match = matches.entrySet().iterator().next();
                      instance = invoke(match.getKey(), null, match.getValue().toArray());
                  } catch (Throwable e) {
                      throw new ComponentDefinitionException("Error when instanciating bean " + getName() + " of class " + getType(), getRealCause(e));
                  }
              } else if (matches.size() == 0) {
                  throw new ComponentDefinitionException("Unable to find a matching factory method " + factoryMethod + " on class " + getType().getName() + " for arguments " + args + " when instanciating bean " + getName());
              } else {
                  throw new ComponentDefinitionException("Multiple matching factory methods " + factoryMethod + " found on class " + getType().getName() + " for arguments " + args + " when instanciating bean " + getName() + ": " + matches.keySet());
              }
          } else {
              if (getType() == null) {
                  throw new ComponentDefinitionException("No factoryMethod nor class is defined for this bean");
              }
              // Map of matching constructors
              Map<Constructor, List<Object>> matches = findMatchingConstructors(getType(), args, argTypes);
              if (matches.size() == 1) {
                  try {
                      Map.Entry<Constructor, List<Object>> match = matches.entrySet().iterator().next();
                      instance = newInstance(match.getKey(), match.getValue().toArray());
                  } catch (Throwable e) {
                      throw new ComponentDefinitionException("Error when instanciating bean " + getName() + " of class " + getType(), getRealCause(e));
                  }
              } else if (matches.size() == 0) {
                  throw new ComponentDefinitionException("Unable to find a matching constructor on class " + getType().getName() + " for arguments " + args + " when instanciating bean " + getName());
              } else {
                  throw new ComponentDefinitionException("Multiple matching constructors found on class " + getType().getName() + " for arguments " + args + " when instanciating bean " + getName() + ": " + matches.keySet());
              }
          }
  
          return instance;
      }
  
      private Map<Method, List<Object>> findMatchingMethods(Class type, String name, boolean instance, List<Object> args, List<ReifiedType> types) {
          Map<Method, List<Object>> matches = new HashMap<Method, List<Object>>();
          // Get constructors
          List<Method> methods = new ArrayList<Method>(Arrays.asList(type.getMethods()));
          // Discard any signature with wrong cardinality
          for (Iterator<Method> it = methods.iterator(); it.hasNext();) {
              Method mth = it.next();
              if (!mth.getName().equals(name)) {
                  it.remove();
              } else if (mth.getParameterTypes().length != args.size()) {
                  it.remove();
              } else if (instance ^ !Modifier.isStatic(mth.getModifiers())) {
                  it.remove();
              } else if (mth.isBridge()) {
                  it.remove();
              }
          }
  
          // on some JVMs (J9) hidden static methods are returned by Class.getMethods so we need to weed them out
          // to reduce ambiguity
          if (!instance) {
              methods = applyStaticHidingRules(methods);
          }
  
          // Find a direct match with assignment
          if (matches.size() != 1) {
              Map<Method, List<Object>> nmatches = new HashMap<Method, List<Object>>();
              for (Method mth : methods) {
                  boolean found = true;
                  List<Object> match = new ArrayList<Object>();
                  for (int i = 0; i < args.size(); i++) {
                      ReifiedType argType = new GenericType(mth.getGenericParameterTypes()[i]);
                      if (types.get(i) != null && !argType.getRawClass().equals(types.get(i).getRawClass())) {
                          found = false;
                          break;
                      }
                      //If the arg is an Unwrappered bean then we need to do the assignment check against the
                      //unwrappered bean itself.
                      Object arg = args.get(i);
                      Object argToTest = arg;
                      if(arg instanceof UnwrapperedBeanHolder)
                          argToTest = ((UnwrapperedBeanHolder)arg).unwrapperedBean;
                      if (!AggregateConverter.isAssignable(argToTest, argType)) {
                          found = false;
                          break;
                      }
                      try {
                          match.add(convert(arg, mth.getGenericParameterTypes()[i]));
                      } catch (Throwable t) {
                          found = false;
                          break;
                      }
                  }
                  if (found) {
                      nmatches.put(mth, match);
                  }
              }
              if (nmatches.size() > 0) {
                  matches = nmatches;
              }
          }
          // Find a direct match with conversion
          if (matches.size() != 1) {
              Map<Method, List<Object>> nmatches = new HashMap<Method, List<Object>>();
              for (Method mth : methods) {
                  boolean found = true;
                  List<Object> match = new ArrayList<Object>();
                  for (int i = 0; i < args.size(); i++) {
                      ReifiedType argType = new GenericType(mth.getGenericParameterTypes()[i]);
                      if (types.get(i) != null && !argType.getRawClass().equals(types.get(i).getRawClass())) {
                          found = false;
                          break;
                      }
                      try {
                          Object val = convert(args.get(i), argType);
                          match.add(val);
                      } catch (Throwable t) {
                          found = false;
                          break;
                      }
                  }
                  if (found) {
                      nmatches.put(mth, match);
                  }
              }
              if (nmatches.size() > 0) {
                  matches = nmatches;
              }
          }
          // Start reordering with assignment
          if (matches.size() != 1 && reorderArguments && args.size() > 1) {
              Map<Method, List<Object>> nmatches = new HashMap<Method, List<Object>>();
              for (Method mth : methods) {
                  ArgumentMatcher matcher = new ArgumentMatcher(mth.getGenericParameterTypes(), false);
                  List<Object> match = matcher.match(args, types);
                  if (match != null) {
                      nmatches.put(mth, match);
                  }
              }
              if (nmatches.size() > 0) {
                  matches = nmatches;
              }
          }
          // Start reordering with conversion
          if (matches.size() != 1 && reorderArguments && args.size() > 1) {
              Map<Method, List<Object>> nmatches = new HashMap<Method, List<Object>>();
              for (Method mth : methods) {
                  ArgumentMatcher matcher = new ArgumentMatcher(mth.getGenericParameterTypes(), true);
                  List<Object> match = matcher.match(args, types);
                  if (match != null) {
                      nmatches.put(mth, match);
                  }
              }
              if (nmatches.size() > 0) {
                  matches = nmatches;
              }
          }
  
          return matches;
      }
  
      private static List<Method> applyStaticHidingRules(Collection<Method> methods) {
          List<Method> result = new ArrayList<Method>(methods.size());
          for (Method m : methods) {
              boolean toBeAdded = true;
  
              Iterator<Method> it = result.iterator();
              inner: while (it.hasNext()) {
                  Method other = it.next();
                  if (hasIdenticalParameters(m, other)) {
                      Class<?> mClass = m.getDeclaringClass();
                      Class<?> otherClass = other.getDeclaringClass();
  
                      if (mClass.isAssignableFrom(otherClass)) {
                          toBeAdded = false;
                          break inner;
                      } else if (otherClass.isAssignableFrom(mClass)) {
                          it.remove();
                      }
                  }
              }
  
              if (toBeAdded) result.add(m);
          }
  
          return result;
      }
  
      private static boolean hasIdenticalParameters(Method one, Method two) {
          Class<?>[] oneTypes = one.getParameterTypes();
          Class<?>[] twoTypes = two.getParameterTypes();
  
          if (oneTypes.length != twoTypes.length) return false;
  
          for (int i=0; i<oneTypes.length; i++) {
              if (!oneTypes[i].equals(twoTypes[i])) return false;
          }
  
          return true;
      }
  
      private Map<Constructor, List<Object>> findMatchingConstructors(Class type, List<Object> args, List<ReifiedType> types) {
          Map<Constructor, List<Object>> matches = new HashMap<Constructor, List<Object>>();
          // Get constructors
          List<Constructor> constructors = new ArrayList<Constructor>(Arrays.asList(type.getConstructors()));
          // Discard any signature with wrong cardinality
          for (Iterator<Constructor> it = constructors.iterator(); it.hasNext();) {
              if (it.next().getParameterTypes().length != args.size()) {
                  it.remove();
              }
          }
          // Find a direct match with assignment
          if (matches.size() != 1) {
              Map<Constructor, List<Object>> nmatches = new HashMap<Constructor, List<Object>>();
              for (Constructor cns : constructors) {
                  boolean found = true;
                  List<Object> match = new ArrayList<Object>();
                  for (int i = 0; i < args.size(); i++) {
                      ReifiedType argType = new GenericType(cns.getGenericParameterTypes()[i]);
                      if (types.get(i) != null && !argType.getRawClass().equals(types.get(i).getRawClass())) {
                          found = false;
                          break;
                      }
                      //If the arg is an Unwrappered bean then we need to do the assignment check against the
                      //unwrappered bean itself.
                      Object arg = args.get(i);
                      Object argToTest = arg;
                      if(arg instanceof UnwrapperedBeanHolder)
                          argToTest = ((UnwrapperedBeanHolder)arg).unwrapperedBean;
                      if (!AggregateConverter.isAssignable(argToTest, argType)) {
                          found = false;
                          break;
                      }
                      try {
                          match.add(convert(arg, cns.getGenericParameterTypes()[i]));
                      } catch (Throwable t) {
                          found = false;
                          break;
                      }
                  }
                  if (found) {
                      nmatches.put(cns, match);
                  }
              }
              if (nmatches.size() > 0) {
                  matches = nmatches;
              }
          }
          // Find a direct match with conversion
          if (matches.size() != 1) {
              Map<Constructor, List<Object>> nmatches = new HashMap<Constructor, List<Object>>();
              for (Constructor cns : constructors) {
                  boolean found = true;
                  List<Object> match = new ArrayList<Object>();
                  for (int i = 0; i < args.size(); i++) {
                      ReifiedType argType = new GenericType(cns.getGenericParameterTypes()[i]);
                      if (types.get(i) != null && !argType.getRawClass().equals(types.get(i).getRawClass())) {
                          found = false;
                          break;
                      }
                      try {
                          Object val = convert(args.get(i), argType);
                          match.add(val);
                      } catch (Throwable t) {
                          found = false;
                          break;
                      }
                  }
                  if (found) {
                      nmatches.put(cns, match);
                  }
              }
              if (nmatches.size() > 0) {
                  matches = nmatches;
              }
          }
          // Start reordering with assignment
          if (matches.size() != 1 && reorderArguments && arguments.size() > 1) {
              Map<Constructor, List<Object>> nmatches = new HashMap<Constructor, List<Object>>();
              for (Constructor cns : constructors) {
                  ArgumentMatcher matcher = new ArgumentMatcher(cns.getGenericParameterTypes(), false);
                  List<Object> match = matcher.match(args, types);
                  if (match != null) {
                      nmatches.put(cns, match);
                  }
              }
              if (nmatches.size() > 0) {
                  matches = nmatches;
              }
          }
          // Start reordering with conversion
          if (matches.size() != 1 && reorderArguments && arguments.size() > 1) {
              Map<Constructor, List<Object>> nmatches = new HashMap<Constructor, List<Object>>();
              for (Constructor cns : constructors) {
                  ArgumentMatcher matcher = new ArgumentMatcher(cns.getGenericParameterTypes(), true);
                  List<Object> match = matcher.match(args, types);
                  if (match != null) {
                      nmatches.put(cns, match);
                  }
              }
              if (nmatches.size() > 0) {
                  matches = nmatches;
              }
          }
          return matches;
      }
  
      /**
       * Returns init method (if any). Throws exception if the init-method was set explicitly on the bean
       * and the method is not found on the instance.
       */
      protected Method getInitMethod(Object instance) throws ComponentDefinitionException {
          Method method = null;
          if (initMethod != null && initMethod.length() > 0) {
              method = ReflectionUtils.getLifecycleMethod(instance.getClass(), initMethod);
              if (method == null) {
                  throw new ComponentDefinitionException("Component '" + getName() + "' does not have init-method: " + initMethod);
              }
          }
          return method;
      }
  
      /**
       * Returns destroy method (if any). Throws exception if the destroy-method was set explicitly on the bean
       * and the method is not found on the instance.
       */
      public Method getDestroyMethod(Object instance) throws ComponentDefinitionException {
          Method method = null;
          if (instance != null && destroyMethod != null && destroyMethod.length() > 0) {
              method = ReflectionUtils.getLifecycleMethod(instance.getClass(), destroyMethod);
              if (method == null) {
                  throw new ComponentDefinitionException("Component '" + getName() + "' does not have destroy-method: " + destroyMethod);
              }
          }
          return method;
      }
  
      /**
       * Small helper class, to construct a chain of BeanCreators.
       * <br> 
       * Each bean creator in the chain will return a bean that has been 
       * processed by every BeanProcessor in the chain before it.
       */
      private static class BeanCreatorChain implements BeanProcessor.BeanCreator {
          public enum ChainType{Before,After};
          private final BeanProcessor.BeanCreator parentBeanCreator;
          private final BeanProcessor parentBeanProcessor;
          private final BeanMetadata beanData;
          private final String beanName;
          private final ChainType when;
          public BeanCreatorChain(BeanProcessor.BeanCreator parentBeanCreator,
                                  BeanProcessor parentBeanProcessor,
                                  BeanMetadata beanData,
                                  String beanName,
                                  ChainType when){
              this.parentBeanCreator = parentBeanCreator;
              this.parentBeanProcessor = parentBeanProcessor;
              this.beanData = beanData;
              this.beanName = beanName;
              this.when = when;
          }
  
          public Object getBean() {
              Object previousBean = parentBeanCreator.getBean();
              Object processed = null;
              switch(when){
                  case Before :
                      processed = parentBeanProcessor.beforeInit(previousBean, beanName, parentBeanCreator, beanData);
                      break;
                  case After:
                      processed = parentBeanProcessor.afterInit(previousBean, beanName, parentBeanCreator, beanData);
                      break;
              }
              return processed;
          }
      }
  
      private Object runBeanProcPreInit(Object obj){
          String beanName = getName();
          BeanMetadata beanData = (BeanMetadata) blueprintContainer
                  .getComponentDefinitionRegistry().getComponentDefinition(beanName);
          List<BeanProcessor> processors = blueprintContainer.getProcessors(BeanProcessor.class);
  
          //The start link of the chain, that provides the 
          //original, unprocessed bean to the head of the chain.
          BeanProcessor.BeanCreator initialBeanCreator = new BeanProcessor.BeanCreator() {
              public Object getBean() {
                  Object obj = getInstance();
                  //getinit, getdestroy, addpartial object don't need calling again.
                  //however, property injection does.
                  setProperties(obj);
                  return obj;
              }
          };
  
          BeanProcessor.BeanCreator currentCreator = initialBeanCreator;
          for(BeanProcessor processor : processors){
              obj = processor.beforeInit(obj, getName(), currentCreator, beanData);
              currentCreator = new BeanCreatorChain(currentCreator, processor, beanData, beanName, BeanCreatorChain.ChainType.Before);
          }
          return obj;
      }
  
      private void runBeanProcInit(Method initMethod, Object obj){
          // call init method
          if (initMethod != null) {
              try {
                  invoke(initMethod, obj, (Object[]) null);
              } catch (Throwable t) {
                  throw new ComponentDefinitionException("Unable to intialize bean " + getName(), getRealCause(t));
              }
          }
      }
  
      private Object runBeanProcPostInit(Object obj){
          String beanName = getName();
          BeanMetadata beanData = (BeanMetadata) blueprintContainer
                  .getComponentDefinitionRegistry().getComponentDefinition(beanName);
          List<BeanProcessor> processors = blueprintContainer.getProcessors(BeanProcessor.class);
  
          //The start link of the chain, that provides the 
          //original, unprocessed bean to the head of the chain.
          BeanProcessor.BeanCreator initialBeanCreator = new BeanProcessor.BeanCreator() {
              public Object getBean() {
                  Object obj = getInstance();
                  //getinit, getdestroy, addpartial object don't need calling again.
                  //however, property injection does.
                  setProperties(obj);
                  //as this is the post init chain, new beans need to go thru 
                  //the pre-init chain, and then have init called, before 
                  //being passed along the post-init chain.
                  obj = runBeanProcPreInit(obj);
                  runBeanProcInit(getInitMethod(obj), obj);
                  return obj;
              }
          };
  
          BeanProcessor.BeanCreator currentCreator = initialBeanCreator;
          for(BeanProcessor processor : processors){
              obj = processor.afterInit(obj, getName(), currentCreator, beanData);
              currentCreator = new BeanCreatorChain(currentCreator, processor, beanData, beanName, BeanCreatorChain.ChainType.After);
          }
          return obj;
      }
  
      private Object addInterceptors(final Object original, Collection<Class<?>> requiredInterfaces)
              throws ComponentDefinitionException {
  
          Object intercepted = null;
          if(requiredInterfaces.isEmpty())
              requiredInterfaces.add(original.getClass());
  
          ComponentDefinitionRegistry reg = blueprintContainer
                  .getComponentDefinitionRegistry();
          List<Interceptor> interceptors = reg.getInterceptors(interceptorLookupKey);
          if (interceptors != null && interceptors.size() > 0) {
              /* BLUEPRINT-NOOSGI
              try {
                  Bundle b = FrameworkUtil.getBundle(original.getClass());
                  if (b == null) {
                      // we have a class from the framework parent, so use our bundle for proxying.
                      b = blueprintContainer.getBundleContext().getBundle();
                  }
                  intercepted = blueprintContainer.getProxyManager().createInterceptingProxy(b,
                          requiredInterfaces, original, new Collaborator(interceptorLookupKey, interceptors));
              } catch (org.apache.aries.proxy.UnableToProxyException e) {
                  Bundle b = blueprintContainer.getBundleContext().getBundle();
                  throw new ComponentDefinitionException("Unable to create proxy for bean " + name + " in bundle " + b.getSymbolicName() + " version " + b.getVersion(), e);
              }
              */
              throw new ComponentDefinitionException("Unable to create proxy for bean " + name + ".  Not supported in blueprint-noosgi");
          } else {
              intercepted = original;
          }
          return intercepted;
      }
  
      @Override
      protected Object internalCreate() throws ComponentDefinitionException {
          /* BLUEPRINT-NOOSGI
          if (factory instanceof ReferenceRecipe) {
              ReferenceRecipe rr = (ReferenceRecipe) factory;
              if (rr.getProxyChildBeanClasses() != null) {
                  return createProxyBean(rr);
              }
          }
          */
          return new UnwrapperedBeanHolder(internalCreate2(), this);
      }
  
      /* BLUEPRINT-NOOSGI
      private Object createProxyBean(ReferenceRecipe rr) {
          try {
              VoidableCallable vc = new VoidableCallable();
              rr.addVoidableChild(vc);
              return blueprintContainer.getProxyManager().createDelegatingProxy(
                      blueprintContainer.getBundleContext().getBundle(), rr.getProxyChildBeanClasses(),
                      vc, vc.call());
          } catch (UnableToProxyException e) {
              throw new ComponentDefinitionException(e);
          }
      }
      */
  
      private Object internalCreate2() throws ComponentDefinitionException {
  
          instantiateExplicitDependencies();
  
          Object obj = getInstance();
  
          // check for init lifecycle method (if any)
          Method initMethod = getInitMethod(obj);
  
          // check for destroy lifecycle method (if any)
          getDestroyMethod(obj);
  
          // Add partially created object to the container
  //        if (initMethod == null) {
          addPartialObject(obj);
  //        }
  
          // inject properties
          setProperties(obj);
  
          obj = runBeanProcPreInit(obj);
  
          runBeanProcInit(initMethod, obj);
  
          obj = runBeanProcPostInit(obj);
  
          //Replaced by calling wrap on the UnwrapperedBeanHolder
  //        obj = addInterceptors(obj);
  
          return obj;
      }
  
      static Object wrap(UnwrapperedBeanHolder holder, Collection<Class<?>> requiredViews) {
          return holder.recipe.addInterceptors(holder.unwrapperedBean, requiredViews);
      }
  
      static Object wrap(UnwrapperedBeanHolder holder, Class<?> requiredView) {
          if(requiredView == Object.class) {
              //We don't know what we need so we have to do everything
              return holder.recipe.addInterceptors(holder.unwrapperedBean, new ArrayList<Class<?>>(1));
          } else {
              return holder.recipe.addInterceptors(holder.unwrapperedBean, ProxyUtils.asList(requiredView));
          }
      }
  
  
      @Override
      public void destroy(Object obj) {
          if (!(obj instanceof UnwrapperedBeanHolder)) {
              LOGGER.warn("Object to be destroyed is not an instance of UnwrapperedBeanHolder, type: " + obj);
              return;
          }
  
          obj = ((UnwrapperedBeanHolder)obj).unwrapperedBean;
  
          for (BeanProcessor processor : blueprintContainer.getProcessors(BeanProcessor.class)) {
              processor.beforeDestroy(obj, getName());
          }
          try {
              Method method = getDestroyMethod(obj);
              if (method != null) {
                  invoke(method, obj, (Object[]) null);
              }
          } catch (ComponentDefinitionException e) {
              // This exception occurs if the destroy method does not exist, so we just output the exception message.
              LOGGER.error(e.getMessage());
          } catch (InvocationTargetException ite) {
              /* BLUEPRINT-OSGI
              Throwable t = ite.getTargetException();
              BundleContext ctx = blueprintContainer.getBundleContext();
              Bundle b = ctx.getBundle();
              String bundleIdentifier = b.getSymbolicName() + '/' + b.getVersion();
              LOGGER.error("The blueprint bean " + getName() + " in bundle " + bundleIdentifier + " incorrectly threw an exception from its destroy method.", t);
              */
              Throwable t = ite.getTargetException();
              LOGGER.error("The blueprint bean " + getName() + " in incorrectly threw an exception from its destroy method.", t);
          } catch (Exception e) {
              /* BLUEPRINT-OSGI
              BundleContext ctx = blueprintContainer.getBundleContext();
              Bundle b = ctx.getBundle();
              String bundleIdentifier = b.getSymbolicName() + '/' + b.getVersion();
              LOGGER.error("An exception occurred while calling the destroy method of the blueprint bean " + getName() + " in bundle " + bundleIdentifier + ".", getRealCause(e));
              */
              LOGGER.error("An exception occurred while calling the destroy method of the blueprint bean " + getName() + ".", getRealCause(e));
          }
          for (BeanProcessor processor : blueprintContainer.getProcessors(BeanProcessor.class)) {
              processor.afterDestroy(obj, getName());
          }
      }
  
      public void setProperties(Object instance) throws ComponentDefinitionException {
          // clone the properties so they can be used again
          Map<String,Object> propertyValues = new LinkedHashMap<String,Object>(properties);
          setProperties(propertyValues, instance, instance.getClass());
      }
  
      public Class getType() {
          if (type instanceof Class) {
              return (Class) type;
          } else if (type instanceof String) {
              return loadClass((String) type);
          } else {
              return null;
          }
      }
  
      private void setProperties(Map<String, Object> propertyValues, Object instance, Class clazz) {
          // set remaining properties
          for (Map.Entry<String, Object> entry : propertyValues.entrySet()) {
              String propertyName = entry.getKey();
              Object propertyValue = entry.getValue();
  
              setProperty(instance, clazz, propertyName, propertyValue);
          }
  
      }
  
      private void setProperty(Object instance, Class clazz, String propertyName, Object propertyValue) {
          String[] names = propertyName.split("\\.");
          for (int i = 0; i < names.length - 1; i++) {
              PropertyDescriptor pd = getPropertyDescriptor(clazz, names[i]);
              if (pd.allowsGet()) {
                  try {
                      instance = pd.get(instance, blueprintContainer);
                  } catch (Exception e) {
                      throw new ComponentDefinitionException("Error getting property: " + names[i] + " on bean " + getName() + " when setting property " + propertyName + " on class " + clazz.getName(), getRealCause(e));
                  }
                  if (instance == null) {
                      throw new ComponentDefinitionException("Error setting compound property " + propertyName + " on bean " + getName() + ". Property " + names[i] + " is null");
                  }
                  clazz = instance.getClass();
              } else {
                  throw new ComponentDefinitionException("No getter for " + names[i] + " property on bean " + getName() + " when setting property " + propertyName + " on class " + clazz.getName());
              }
          }
  
          // Instantiate value
          if (propertyValue instanceof Recipe) {
              propertyValue = ((Recipe) propertyValue).create();
          }
  
          final PropertyDescriptor pd = getPropertyDescriptor(clazz, names[names.length - 1]);
          if (pd.allowsSet()) {
              try {
                  pd.set(instance, propertyValue, blueprintContainer);
              } catch (Exception e) {
                  throw new ComponentDefinitionException("Error setting property: " + pd, getRealCause(e));
              }
          } else {
              throw new ComponentDefinitionException("No setter for " + names[names.length - 1] + " property");
          }
      }
  
      private ReflectionUtils.PropertyDescriptor getPropertyDescriptor(Class<?> clazz, String name) {
          for (ReflectionUtils.PropertyDescriptor pd : ReflectionUtils.getPropertyDescriptors(clazz, allowsFieldInjection)) {
              if (pd.getName().equals(name)) {
                  return pd;
              }
          }
          throw new ComponentDefinitionException("Unable to find property descriptor " + name + " on class " + clazz.getName());
      }
  
      private Object invoke(Method method, Object instance, Object... args) throws Exception {
          return ReflectionUtils.invoke(blueprintContainer.getAccessControlContext(), method, instance, args);
      }
  
      private Object newInstance(Constructor constructor, Object... args) throws Exception {
          return ReflectionUtils.newInstance(blueprintContainer.getAccessControlContext(), constructor, args);
      }
  
      private static Object UNMATCHED = new Object();
  
      private class ArgumentMatcher {
  
          private final List<TypeEntry> entries;
          private final boolean convert;
  
          public ArgumentMatcher(Type[] types, boolean convert) {
              entries = new ArrayList<TypeEntry>();
              for (Type type : types) {
                  entries.add(new TypeEntry(new GenericType(type)));
              }
              this.convert = convert;
          }
  
          public List<Object> match(List<Object> arguments, List<ReifiedType> forcedTypes) {
              if (find(arguments, forcedTypes)) {
                  return getArguments();
              }
              return null;
          }
  
          private List<Object> getArguments() {
              List<Object> list = new ArrayList<Object>();
              for (TypeEntry entry : entries) {
                  if (entry.argument == UNMATCHED) {
                      throw new RuntimeException("There are unmatched types");
                  } else {
                      list.add(entry.argument);
                  }
              }
              return list;
          }
  
          private boolean find(List<Object> arguments, List<ReifiedType> forcedTypes) {
              if (entries.size() == arguments.size()) {
                  boolean matched = true;
                  for (int i = 0; i < arguments.size() && matched; i++) {
                      matched = find(arguments.get(i), forcedTypes.get(i));
                  }
                  return matched;
              }
              return false;
          }
  
          private boolean find(Object arg, ReifiedType forcedType) {
              for (TypeEntry entry : entries) {
                  Object val = arg;
                  if (entry.argument != UNMATCHED) {
                      continue;
                  }
                  if (forcedType != null) {
                      if (!forcedType.equals(entry.type)) {
                          continue;
                      }
                  } else if (arg != null) {
                      if (convert) {
  
                          if(canConvert(arg, entry.type)) {
                              try {
                                  val = convert(arg, entry.type);
                              } catch (Exception e) {
                                  throw new ComponentDefinitionException(e);
                              }
                          } else {
                              continue;
                          }
                      } else {
                          UnwrapperedBeanHolder holder = null;
                          if(arg instanceof UnwrapperedBeanHolder) {
                              holder = (UnwrapperedBeanHolder)arg;
                              arg = holder.unwrapperedBean;
                          }
                          if (!AggregateConverter.isAssignable(arg, entry.type)) {
                              continue;
                          } else if (holder != null) {
                              val = wrap(holder, entry.type.getRawClass());
                          }
                      }
                  }
                  entry.argument = val;
                  return true;
              }
              return false;
          }
  
      }
  
      private static class TypeEntry {
  
          private final ReifiedType type;
          private Object argument;
  
          public TypeEntry(ReifiedType type) {
              this.type = type;
              this.argument = UNMATCHED;
          }
  
      }
  
  }
  