You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by zo...@apache.org on 2011/02/27 21:35:49 UTC
svn commit: r1075139 [10/23] - in /aries/tags/blueprint-0.2-incubating: ./
blueprint-annotation-api/ blueprint-annotation-api/src/
blueprint-annotation-api/src/main/ blueprint-annotation-api/src/main/java/
blueprint-annotation-api/src/main/java/org/ bl...
Added: aries/tags/blueprint-0.2-incubating/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java
URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.2-incubating/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java?rev=1075139&view=auto
==============================================================================
--- aries/tags/blueprint-0.2-incubating/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java (added)
+++ aries/tags/blueprint-0.2-incubating/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java Sun Feb 27 20:35:36 2011
@@ -0,0 +1,940 @@
+/*
+ * 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.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 org.apache.aries.blueprint.BeanProcessor;
+import org.apache.aries.blueprint.ComponentDefinitionRegistry;
+import org.apache.aries.blueprint.ExtendedBlueprintContainer;
+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.AsmInterceptorWrapper;
+import org.apache.aries.blueprint.proxy.CgLibInterceptorWrapper;
+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: 978875 $, $Date: 2010-07-24 14:45:05 +0100 (Sat, 24 Jul 2010) $
+ */
+public class BeanRecipe extends AbstractRecipe {
+
+ 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
+ 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));
+ }
+ }
+
+ // 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 (!AggregateConverter.isAssignable(args.get(i), argType)) {
+ found = false;
+ break;
+ }
+ try {
+ match.add(convert(args.get(i), 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 (!AggregateConverter.isAssignable(args.get(i), argType)) {
+ found = false;
+ break;
+ }
+ try {
+ match.add(convert(args.get(i), 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 (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 BeanProcessor.BeanCreator parentBeanCreator;
+ private BeanProcessor parentBeanProcessor;
+ private BeanMetadata beanData;
+ private String beanName;
+ private 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(Object original)
+ throws ComponentDefinitionException {
+
+ Object intercepted = null;
+ ComponentDefinitionRegistry reg = blueprintContainer
+ .getComponentDefinitionRegistry();
+ List<Interceptor> interceptors = reg.getInterceptors(interceptorLookupKey);
+ if (interceptors != null && interceptors.size() > 0) {
+ boolean asmAvailable = false;
+ try {
+ // Try load load an asm class (to make sure it's actually
+ // available)
+ getClass().getClassLoader().loadClass(
+ "org.objectweb.asm.ClassVisitor");
+ LOGGER.debug("asm available for interceptors");
+ asmAvailable = true;
+ } catch (Throwable t) {
+ try {
+ // Try load load a cglib class (to make sure it's actually
+ // available)
+ getClass().getClassLoader().loadClass(
+ "net.sf.cglib.proxy.Enhancer");
+ } catch (Throwable u) {
+ throw new ComponentDefinitionException(
+ "Interceptors have been configured but neither asm nor cglib are available",
+ u);
+ }
+ }
+ if (asmAvailable) {
+ // if asm is available we can proxy the original object with the
+ // AsmInterceptorWrapper
+ intercepted = AsmInterceptorWrapper.createProxyObject(original
+ .getClass().getClassLoader(), interceptorLookupKey, interceptors,
+ original, original.getClass());
+ } else {
+ LOGGER.debug("cglib available for interceptors");
+ // otherwise we're using cglib and need to use the interfaces
+ // with the CgLibInterceptorWrapper
+ intercepted = CgLibInterceptorWrapper.createProxyObject(
+ original.getClass().getClassLoader(), interceptorLookupKey,
+ interceptors, original, original.getClass()
+ .getInterfaces());
+ }
+
+ } else {
+ intercepted = original;
+ }
+ return intercepted;
+ }
+
+ @Override
+ protected Object internalCreate() 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);
+
+ obj = addInterceptors(obj);
+
+ return obj;
+ }
+
+ @Override
+ public void destroy(Object obj) {
+ 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 (Exception e) {
+ LOGGER.info("Error invoking destroy method", 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.getAccessControlContext());
+ } 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.getAccessControlContext());
+ } 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 List<TypeEntry> entries;
+ private 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) {
+ try {
+ // TODO: call canConvert instead of convert()
+ val = convert(arg, entry.type);
+ } catch (Throwable t) {
+ continue;
+ }
+ } else {
+ if (!AggregateConverter.isAssignable(arg, entry.type)) {
+ continue;
+ }
+ }
+ }
+ 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;
+ }
+
+ }
+
+}
Added: aries/tags/blueprint-0.2-incubating/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintContainerImpl.java
URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.2-incubating/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintContainerImpl.java?rev=1075139&view=auto
==============================================================================
--- aries/tags/blueprint-0.2-incubating/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintContainerImpl.java (added)
+++ aries/tags/blueprint-0.2-incubating/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintContainerImpl.java Sun Feb 27 20:35:36 2011
@@ -0,0 +1,842 @@
+/*
+ * 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 java.io.FileNotFoundException;
+import java.net.URI;
+import java.net.URL;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.DomainCombiner;
+import java.security.Permission;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.aries.blueprint.BlueprintConstants;
+import org.apache.aries.blueprint.ComponentDefinitionRegistryProcessor;
+import org.apache.aries.blueprint.ExtendedBeanMetadata;
+import org.apache.aries.blueprint.ExtendedBlueprintContainer;
+import org.apache.aries.blueprint.NamespaceHandler;
+import org.apache.aries.blueprint.Processor;
+import org.apache.aries.blueprint.di.Recipe;
+import org.apache.aries.blueprint.di.Repository;
+import org.apache.aries.blueprint.namespace.ComponentDefinitionRegistryImpl;
+import org.apache.aries.blueprint.namespace.NamespaceHandlerRegistryImpl;
+import org.apache.aries.blueprint.reflect.MetadataUtil;
+import org.apache.aries.blueprint.reflect.PassThroughMetadataImpl;
+import org.apache.aries.blueprint.utils.HeaderParser;
+import org.apache.aries.blueprint.utils.JavaUtils;
+import org.apache.aries.blueprint.utils.HeaderParser.PathElement;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.blueprint.container.BlueprintContainer;
+import org.osgi.service.blueprint.container.BlueprintEvent;
+import org.osgi.service.blueprint.container.BlueprintListener;
+import org.osgi.service.blueprint.container.ComponentDefinitionException;
+import org.osgi.service.blueprint.container.Converter;
+import org.osgi.service.blueprint.container.NoSuchComponentException;
+import org.osgi.service.blueprint.reflect.BeanArgument;
+import org.osgi.service.blueprint.reflect.BeanMetadata;
+import org.osgi.service.blueprint.reflect.BeanProperty;
+import org.osgi.service.blueprint.reflect.CollectionMetadata;
+import org.osgi.service.blueprint.reflect.ComponentMetadata;
+import org.osgi.service.blueprint.reflect.MapEntry;
+import org.osgi.service.blueprint.reflect.MapMetadata;
+import org.osgi.service.blueprint.reflect.Metadata;
+import org.osgi.service.blueprint.reflect.PropsMetadata;
+import org.osgi.service.blueprint.reflect.RefMetadata;
+import org.osgi.service.blueprint.reflect.ReferenceListener;
+import org.osgi.service.blueprint.reflect.RegistrationListener;
+import org.osgi.service.blueprint.reflect.ServiceMetadata;
+import org.osgi.service.blueprint.reflect.ServiceReferenceMetadata;
+import org.osgi.service.blueprint.reflect.Target;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * TODO: javadoc
+ *
+ * @version $Rev: 921845 $, $Date: 2010-03-11 13:45:28 +0000 (Thu, 11 Mar 2010) $
+ */
+public class BlueprintContainerImpl implements ExtendedBlueprintContainer, NamespaceHandlerRegistry.Listener, Runnable, SatisfiableRecipe.SatisfactionListener {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BlueprintContainerImpl.class);
+
+ private enum State {
+ Unknown,
+ WaitForNamespaceHandlers,
+ Populated,
+ WaitForInitialReferences,
+ InitialReferencesSatisfied,
+ WaitForInitialReferences2,
+ Create,
+ Created,
+ Failed,
+ }
+
+ private final BundleContext bundleContext;
+ private final Bundle extenderBundle;
+ private final BlueprintListener eventDispatcher;
+ private final NamespaceHandlerRegistry handlers;
+ private final List<Object> pathList;
+ private final ComponentDefinitionRegistryImpl componentDefinitionRegistry;
+ private final AggregateConverter converter;
+ private final ScheduledExecutorService executors;
+ private Set<URI> namespaces;
+ private State state = State.Unknown;
+ private NamespaceHandlerRegistry.NamespaceHandlerSet handlerSet;
+ private boolean destroyed;
+ private Parser parser;
+ private BlueprintRepository repository;
+ private ServiceRegistration registration;
+ private List<Processor> processors;
+ private final Object satisfiablesLock = new Object();
+ private Map<String, List<SatisfiableRecipe>> satisfiables;
+ private long timeout = 5 * 60 * 1000;
+ private boolean waitForDependencies = true;
+ private boolean xmlValidation = true;
+ private ScheduledFuture timeoutFuture;
+ private final AtomicBoolean scheduled = new AtomicBoolean();
+ private final AtomicBoolean running = new AtomicBoolean();
+ private List<ServiceRecipe> services;
+ private AccessControlContext accessControlContext;
+ private final IdSpace tempRecipeIdSpace = new IdSpace();
+
+ public BlueprintContainerImpl(BundleContext bundleContext, Bundle extenderBundle, BlueprintListener eventDispatcher, NamespaceHandlerRegistry handlers, ScheduledExecutorService executors, List<Object> pathList) {
+ this.bundleContext = bundleContext;
+ this.extenderBundle = extenderBundle;
+ this.eventDispatcher = eventDispatcher;
+ this.handlers = handlers;
+ this.pathList = pathList;
+ this.converter = new AggregateConverter(this);
+ this.componentDefinitionRegistry = new ComponentDefinitionRegistryImpl();
+ this.executors = executors;
+ this.processors = new ArrayList<Processor>();
+ if (System.getSecurityManager() != null) {
+ this.accessControlContext = createAccessControlContext();
+ }
+ }
+
+ public Bundle getExtenderBundle() {
+ return extenderBundle;
+ }
+
+ public <T extends Processor> List<T> getProcessors(Class<T> clazz) {
+ List<T> p = new ArrayList<T>();
+ for (Processor processor : processors) {
+ if (clazz.isInstance(processor)) {
+ p.add(clazz.cast(processor));
+ }
+ }
+ return p;
+ }
+
+ public BlueprintListener getEventDispatcher() {
+ return eventDispatcher;
+ }
+
+ private void checkDirectives() {
+ Bundle bundle = bundleContext.getBundle();
+ Dictionary headers = bundle.getHeaders();
+ String symbolicName = (String)headers.get(Constants.BUNDLE_SYMBOLICNAME);
+ List<PathElement> paths = HeaderParser.parseHeader(symbolicName);
+
+ String timeoutDirective = paths.get(0).getDirective(BlueprintConstants.TIMEOUT_DIRECTIVE);
+ if (timeoutDirective != null) {
+ LOGGER.debug("Timeout directive: {}", timeoutDirective);
+ timeout = Integer.parseInt(timeoutDirective);
+ }
+
+ String graceperiod = paths.get(0).getDirective(BlueprintConstants.GRACE_PERIOD);
+ if (graceperiod != null) {
+ LOGGER.debug("Grace-period directive: {}", graceperiod);
+ waitForDependencies = Boolean.parseBoolean(graceperiod);
+ }
+
+ String xmlValidationDirective = paths.get(0).getDirective(BlueprintConstants.XML_VALIDATION);
+ if (xmlValidationDirective != null) {
+ LOGGER.debug("Xml-validation directive: {}", xmlValidationDirective);
+ xmlValidation = Boolean.parseBoolean(xmlValidationDirective);
+ }
+ }
+
+ public void schedule() {
+ if (scheduled.compareAndSet(false, true)) {
+ executors.submit(this);
+ }
+ }
+
+ public void run() {
+ scheduled.set(false);
+ synchronized (scheduled) {
+ synchronized (running) {
+ running.set(true);
+ try {
+ doRun();
+ } finally {
+ running.set(false);
+ running.notifyAll();
+ }
+ }
+ }
+ }
+
+ /**
+ * This method must be called inside a synchronized block to ensure this method is not run concurrently
+ */
+ private void doRun() {
+ try {
+ for (;;) {
+ if (destroyed) {
+ return;
+ }
+ LOGGER.debug("Running blueprint container for bundle {} in state {}", bundleContext.getBundle().getSymbolicName(), state);
+ switch (state) {
+ case Unknown:
+ checkDirectives();
+ eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.CREATING, getBundleContext().getBundle(), getExtenderBundle()));
+ parser = new Parser();
+ parser.parse(getResources());
+ namespaces = parser.getNamespaces();
+ handlerSet = handlers.getNamespaceHandlers(namespaces, getBundleContext().getBundle());
+ handlerSet.addListener(this);
+ state = State.WaitForNamespaceHandlers;
+ break;
+ case WaitForNamespaceHandlers:
+ {
+ List<String> missing = new ArrayList<String>();
+ for (URI ns : namespaces) {
+ if (handlerSet.getNamespaceHandler(ns) == null) {
+ missing.add("(&(" + Constants.OBJECTCLASS + "=" + NamespaceHandler.class.getName() + ")(" + NamespaceHandlerRegistryImpl.NAMESPACE + "=" + ns + "))");
+ }
+ }
+ if (missing.size() > 0) {
+ LOGGER.warn("Bundle " + bundleContext.getBundle().getSymbolicName() + " is waiting for namespace handlers " + missing);
+ eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.GRACE_PERIOD, getBundleContext().getBundle(), getExtenderBundle(), missing.toArray(new String[missing.size()])));
+ return;
+ }
+ componentDefinitionRegistry.registerComponentDefinition(new PassThroughMetadataImpl("blueprintContainer", this));
+ componentDefinitionRegistry.registerComponentDefinition(new PassThroughMetadataImpl("blueprintBundle", bundleContext.getBundle()));
+ componentDefinitionRegistry.registerComponentDefinition(new PassThroughMetadataImpl("blueprintBundleContext", bundleContext));
+ componentDefinitionRegistry.registerComponentDefinition(new PassThroughMetadataImpl("blueprintConverter", converter));
+ if (xmlValidation) {
+ parser.validate(handlerSet.getSchema());
+ }
+ parser.populate(handlerSet, componentDefinitionRegistry);
+ state = State.Populated;
+ break;
+ }
+ case Populated:
+ getRepository();
+ trackServiceReferences();
+ Runnable r = new Runnable() {
+ public void run() {
+ synchronized (scheduled) {
+ Throwable t = new TimeoutException();
+ state = State.Failed;
+ unregisterServices();
+ untrackServiceReferences();
+ destroyComponents();
+ String[] missingDependecies = getMissingDependencies();
+ LOGGER.error("Unable to start blueprint container for bundle " + bundleContext.getBundle().getSymbolicName() + " due to unresolved dependencies " + Arrays.asList(missingDependecies), t);
+ eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.FAILURE, getBundleContext().getBundle(), getExtenderBundle(), missingDependecies, t));
+ }
+ }
+ };
+ timeoutFuture = executors.schedule(r, timeout, TimeUnit.MILLISECONDS);
+ state = State.WaitForInitialReferences;
+ break;
+ case WaitForInitialReferences:
+ if (waitForDependencies) {
+ String[] missingDependencies = getMissingDependencies();
+ if (missingDependencies.length > 0) {
+ eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.GRACE_PERIOD, getBundleContext().getBundle(), getExtenderBundle(), missingDependencies));
+ return;
+ }
+ }
+ state = State.InitialReferencesSatisfied;
+ break;
+ case InitialReferencesSatisfied:
+ processTypeConverters();
+ processProcessors();
+ state = State.WaitForInitialReferences2;
+ break;
+ case WaitForInitialReferences2:
+ if (waitForDependencies) {
+ String[] missingDependencies = getMissingDependencies();
+ if (missingDependencies.length > 0) {
+ eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.GRACE_PERIOD, getBundleContext().getBundle(), getExtenderBundle(), missingDependencies));
+ return;
+ }
+ }
+ state = State.Create;
+ break;
+ case Create:
+ timeoutFuture.cancel(false);
+ registerServices();
+ instantiateEagerComponents();
+
+ // Register the BlueprintContainer in the OSGi registry
+ if (registration == null) {
+ Properties props = new Properties();
+ props.put(BlueprintConstants.CONTAINER_SYMBOLIC_NAME_PROPERTY,
+ bundleContext.getBundle().getSymbolicName());
+ props.put(BlueprintConstants.CONTAINER_VERSION_PROPERTY,
+ JavaUtils.getBundleVersion(bundleContext.getBundle()));
+ registration = registerService(new String [] { BlueprintContainer.class.getName() }, this, props);
+ eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.CREATED, getBundleContext().getBundle(), getExtenderBundle()));
+ state = State.Created;
+ }
+ break;
+ case Created:
+ case Failed:
+ return;
+ }
+ }
+ } catch (Throwable t) {
+ state = State.Failed;
+ if (timeoutFuture != null) {
+ timeoutFuture.cancel(false);
+ }
+ unregisterServices();
+ untrackServiceReferences();
+ destroyComponents();
+ LOGGER.error("Unable to start blueprint container for bundle " + bundleContext.getBundle().getSymbolicName(), t);
+ eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.FAILURE, getBundleContext().getBundle(), getExtenderBundle(), t));
+ }
+ }
+
+ private List<URL> getResources() throws FileNotFoundException {
+ List<URL> resources = new ArrayList<URL>();
+ for (Object path : pathList) {
+ if (path instanceof URL) {
+ resources.add((URL) path);
+ } else if (path instanceof String) {
+ URL url = bundleContext.getBundle().getEntry((String) path);
+ if (url == null) {
+ throw new FileNotFoundException("Unable to find configuration file for " + path);
+ } else {
+ resources.add(url);
+ }
+ } else {
+ throw new IllegalArgumentException("Unexpected path type: " + path.getClass());
+ }
+ }
+ return resources;
+ }
+
+ public Class loadClass(final String name) throws ClassNotFoundException {
+ if (accessControlContext == null) {
+ return bundleContext.getBundle().loadClass(name);
+ } else {
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<Class>() {
+ public Class run() throws Exception {
+ return bundleContext.getBundle().loadClass(name);
+ }
+ }, accessControlContext);
+ } catch (PrivilegedActionException e) {
+ Exception cause = e.getException();
+ if (cause instanceof ClassNotFoundException) {
+ throw (ClassNotFoundException) cause;
+ }
+ throw new IllegalStateException("Unexpected checked exception", cause);
+ }
+ }
+ }
+
+ public ServiceRegistration registerService(final String[] classes, final Object service, final Dictionary properties) {
+ if (accessControlContext == null) {
+ return bundleContext.registerService(classes, service, properties);
+ } else {
+ return AccessController.doPrivileged(new PrivilegedAction<ServiceRegistration>() {
+ public ServiceRegistration run() {
+ return bundleContext.registerService(classes, service, properties);
+ }
+ }, accessControlContext);
+ }
+ }
+
+ public Object getService(final ServiceReference reference) {
+ if (accessControlContext == null) {
+ return bundleContext.getService(reference);
+ } else {
+ return AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ public Object run() {
+ return bundleContext.getService(reference);
+ }
+ }, accessControlContext);
+ }
+ }
+
+ private AccessControlContext createAccessControlContext() {
+ return new AccessControlContext(AccessController.getContext(),
+ new DomainCombiner() {
+ public ProtectionDomain[] combine(ProtectionDomain[] arg0,
+ ProtectionDomain[] arg1) {
+ return new ProtectionDomain[] { new ProtectionDomain(null, null) {
+ public boolean implies(Permission permission) {
+ return bundleContext.getBundle().hasPermission(permission);
+ }
+ }
+ };
+ }
+ });
+ }
+
+ public AccessControlContext getAccessControlContext() {
+ return accessControlContext;
+ }
+
+ public BlueprintRepository getRepository() {
+ if (repository == null) {
+ repository = new RecipeBuilder(this, tempRecipeIdSpace).createRepository();
+ }
+ return repository;
+ }
+
+ private void processTypeConverters() throws Exception {
+ List<String> typeConverters = new ArrayList<String>();
+ for (Target target : componentDefinitionRegistry.getTypeConverters()) {
+ if (target instanceof ComponentMetadata) {
+ typeConverters.add(((ComponentMetadata) target).getId());
+ } else if (target instanceof RefMetadata) {
+ typeConverters.add(((RefMetadata) target).getComponentId());
+ } else {
+ throw new ComponentDefinitionException("Unexpected metadata for type converter: " + target);
+ }
+ }
+
+ Map<String, Object> objects = repository.createAll(typeConverters);
+ for (String name : typeConverters) {
+ Object obj = objects.get(name);
+ if (obj instanceof Converter) {
+ converter.registerConverter((Converter) obj);
+ } else {
+ throw new ComponentDefinitionException("Type converter " + obj + " does not implement the " + Converter.class.getName() + " interface");
+ }
+ }
+ }
+
+ private void processProcessors() throws Exception {
+ // Instanciate ComponentDefinitionRegistryProcessor and BeanProcessor
+ for (BeanMetadata bean : getMetadata(BeanMetadata.class)) {
+ if (bean instanceof ExtendedBeanMetadata && !((ExtendedBeanMetadata) bean).isProcessor()) {
+ continue;
+ }
+
+ Class clazz = null;
+ if (bean instanceof ExtendedBeanMetadata) {
+ clazz = ((ExtendedBeanMetadata) bean).getRuntimeClass();
+ }
+ if (clazz == null && bean.getClassName() != null) {
+ clazz = loadClass(bean.getClassName());
+ }
+ if (clazz == null) {
+ continue;
+ }
+
+ if (ComponentDefinitionRegistryProcessor.class.isAssignableFrom(clazz)) {
+ Object obj = repository.create(bean.getId());
+ ((ComponentDefinitionRegistryProcessor) obj).process(componentDefinitionRegistry);
+ } else if (Processor.class.isAssignableFrom(clazz)) {
+ Object obj = repository.create(bean.getId());
+ this.processors.add((Processor) obj);
+ } else {
+ continue;
+ }
+ // Update repository with recipes processed by the processors
+ untrackServiceReferences();
+ Repository tmpRepo = new RecipeBuilder(this, tempRecipeIdSpace).createRepository();
+
+ LOGGER.debug("Updating blueprint repository");
+
+ for (String name : repository.getNames()) {
+ if (repository.getInstance(name) == null) {
+ LOGGER.debug("Removing uninstantiated recipe {}", new Object[] { name });
+ repository.removeRecipe(name);
+ } else {
+ LOGGER.debug("Recipe {} is already instantiated", new Object[] { name });
+ }
+ }
+
+ for (String name : tmpRepo.getNames()) {
+ if (repository.getInstance(name) == null) {
+ LOGGER.debug("Adding new recipe {}", new Object[] { name });
+ Recipe r = tmpRepo.getRecipe(name);
+ if (r != null) {
+ repository.putRecipe(name, r);
+ }
+ } else {
+ LOGGER.debug("Recipe {} is already instantiated and cannot be updated", new Object[] { name });
+ }
+ }
+
+ getSatisfiableDependenciesMap(true);
+ trackServiceReferences();
+ }
+ }
+
+ private Map<String, List<SatisfiableRecipe>> getSatisfiableDependenciesMap() {
+ return getSatisfiableDependenciesMap(false);
+ }
+
+ private Map<String, List<SatisfiableRecipe>> getSatisfiableDependenciesMap(boolean recompute) {
+ synchronized (satisfiablesLock) {
+ if ((recompute || satisfiables == null) && repository != null) {
+ satisfiables = new HashMap<String, List<SatisfiableRecipe>>();
+ for (Recipe r : repository.getAllRecipes()) {
+ List<SatisfiableRecipe> recipes = repository.getAllRecipes(SatisfiableRecipe.class, r.getName());
+ if (!recipes.isEmpty()) {
+ satisfiables.put(r.getName(), recipes);
+ }
+ }
+ }
+ return satisfiables;
+ }
+ }
+
+ private void trackServiceReferences() {
+ Map<String, List<SatisfiableRecipe>> dependencies = getSatisfiableDependenciesMap();
+ Set<String> satisfiables = new HashSet<String>();
+ for (List<SatisfiableRecipe> recipes : dependencies.values()) {
+ for (SatisfiableRecipe satisfiable : recipes) {
+ if (satisfiables.add(satisfiable.getName())) {
+ satisfiable.start(this);
+ }
+ }
+ }
+ LOGGER.debug("Tracking service references: {}", satisfiables);
+ }
+
+ private void untrackServiceReferences() {
+ Map<String, List<SatisfiableRecipe>> dependencies = getSatisfiableDependenciesMap();
+ if (dependencies != null) {
+ Set<String> stopped = new HashSet<String>();
+ for (List<SatisfiableRecipe> recipes : dependencies.values()) {
+ for (SatisfiableRecipe satisfiable : recipes) {
+ untrackServiceReference(satisfiable, stopped, dependencies);
+ }
+ }
+ }
+ }
+
+ private void untrackServiceReference(SatisfiableRecipe recipe, Set<String> stopped, Map<String, List<SatisfiableRecipe>> dependencies) {
+ if (stopped.add(recipe.getName())) {
+ for (Map.Entry<String, List<SatisfiableRecipe>> entry : dependencies.entrySet()) {
+ if (entry.getValue().contains(recipe)) {
+ Recipe r = getRepository().getRecipe(entry.getKey());
+ if (r instanceof SatisfiableRecipe) {
+ untrackServiceReference((SatisfiableRecipe) r, stopped, dependencies);
+ }
+ }
+ }
+ recipe.stop();
+ }
+ }
+
+ public void notifySatisfaction(SatisfiableRecipe satisfiable) {
+ LOGGER.debug("Notified satisfaction {} in bundle {}: {}",
+ new Object[] { satisfiable.getName(), bundleContext.getBundle().getSymbolicName(), satisfiable.isSatisfied() });
+ if (state == State.Create || state == State.Created ) {
+ Map<String, List<SatisfiableRecipe>> dependencies = getSatisfiableDependenciesMap();
+ for (Map.Entry<String, List<SatisfiableRecipe>> entry : dependencies.entrySet()) {
+ String name = entry.getKey();
+ ComponentMetadata metadata = componentDefinitionRegistry.getComponentDefinition(name);
+ if (metadata instanceof ServiceMetadata) {
+ ServiceRecipe reg = (ServiceRecipe) repository.getRecipe(name);
+ synchronized (reg) {
+ boolean satisfied = true;
+ for (SatisfiableRecipe recipe : entry.getValue()) {
+ if (!recipe.isSatisfied()) {
+ satisfied = false;
+ break;
+ }
+ }
+ if (satisfied && !reg.isRegistered()) {
+ LOGGER.debug("Registering service {} due to satisfied references", name);
+ reg.register();
+ } else if (!satisfied && reg.isRegistered()) {
+ LOGGER.debug("Unregistering service {} due to unsatisfied references", name);
+ reg.unregister();
+ }
+ }
+ }
+ }
+ } else {
+ schedule();
+ }
+ }
+
+ private void instantiateEagerComponents() {
+ List<String> components = new ArrayList<String>();
+ for (String name : componentDefinitionRegistry.getComponentDefinitionNames()) {
+ ComponentMetadata component = componentDefinitionRegistry.getComponentDefinition(name);
+ boolean eager = component.getActivation() == ComponentMetadata.ACTIVATION_EAGER;
+ if (component instanceof BeanMetadata) {
+ BeanMetadata local = (BeanMetadata) component;
+ eager &= MetadataUtil.isSingletonScope(local);
+ }
+ if (eager) {
+ components.add(name);
+ }
+ }
+ LOGGER.debug("Instantiating components: {}", components);
+ try {
+ repository.createAll(components);
+ } catch (ComponentDefinitionException e) {
+ throw e;
+ } catch (Throwable t) {
+ throw new ComponentDefinitionException("Unable to instantiate components", t);
+ }
+ }
+
+ private void registerServices() {
+ services = repository.getAllRecipes(ServiceRecipe.class);
+ for (ServiceRecipe r : services) {
+ List<SatisfiableRecipe> dependencies = getSatisfiableDependenciesMap().get(r.getName());
+ boolean enabled = true;
+ if (dependencies != null) {
+ for (SatisfiableRecipe recipe : dependencies) {
+ if (!recipe.isSatisfied()) {
+ enabled = false;
+ break;
+ }
+ }
+ }
+ if (enabled) {
+ r.register();
+ }
+ }
+ }
+
+ private void unregisterServices() {
+ if (repository != null) {
+ List<ServiceRecipe> recipes = this.services;
+ this.services = null;
+ if (recipes != null) {
+ for (ServiceRecipe r : recipes) {
+ r.unregister();
+ }
+ }
+ }
+ }
+
+ private void destroyComponents() {
+ if (repository != null) {
+ repository.destroy();
+ }
+ }
+
+ private String[] getMissingDependencies() {
+ List<String> missing = new ArrayList<String>();
+ Map<String, List<SatisfiableRecipe>> dependencies = getSatisfiableDependenciesMap();
+ Set<SatisfiableRecipe> recipes = new HashSet<SatisfiableRecipe>();
+ for (List<SatisfiableRecipe> deps : dependencies.values()) {
+ for (SatisfiableRecipe recipe : deps) {
+ if (!recipe.isSatisfied()) {
+ recipes.add(recipe);
+ }
+ }
+ }
+ for (SatisfiableRecipe recipe : recipes) {
+ missing.add(recipe.getOsgiFilter());
+ }
+ return missing.toArray(new String[missing.size()]);
+ }
+
+ public Set<String> getComponentIds() {
+ Set<String> set = new LinkedHashSet<String>();
+ set.addAll(componentDefinitionRegistry.getComponentDefinitionNames());
+ set.add("blueprintContainer");
+ set.add("blueprintBundle");
+ set.add("blueprintBundleContext");
+ set.add("blueprintConverter");
+ return set;
+ }
+
+ public Object getComponentInstance(String id) throws NoSuchComponentException {
+ if (repository == null) {
+ throw new NoSuchComponentException(id);
+ }
+ try {
+ LOGGER.debug("Instantiating component {}", id);
+ return repository.create(id);
+ } catch (NoSuchComponentException e) {
+ throw e;
+ } catch (ComponentDefinitionException e) {
+ throw e;
+ } catch (Throwable t) {
+ throw new ComponentDefinitionException("Cound not create component instance for " + id, t);
+ }
+ }
+
+ public ComponentMetadata getComponentMetadata(String id) {
+ ComponentMetadata metadata = componentDefinitionRegistry.getComponentDefinition(id);
+ if (metadata == null) {
+ throw new NoSuchComponentException(id);
+ }
+ return metadata;
+ }
+
+ public <T extends ComponentMetadata> Collection<T> getMetadata(Class<T> clazz) {
+ Collection<T> metadatas = new ArrayList<T>();
+ for (String name : componentDefinitionRegistry.getComponentDefinitionNames()) {
+ ComponentMetadata component = componentDefinitionRegistry.getComponentDefinition(name);
+ getMetadata(clazz, component, metadatas);
+ }
+ metadatas = Collections.unmodifiableCollection(metadatas);
+ return metadatas;
+ }
+
+ private <T extends ComponentMetadata> void getMetadata(Class<T> clazz, Metadata component, Collection<T> metadatas) {
+ if (component == null) {
+ return;
+ }
+ if (clazz.isInstance(component)) {
+ metadatas.add(clazz.cast(component));
+ }
+ if (component instanceof BeanMetadata) {
+ getMetadata(clazz, ((BeanMetadata) component).getFactoryComponent(), metadatas);
+ for (BeanArgument arg : ((BeanMetadata) component).getArguments()) {
+ getMetadata(clazz, arg.getValue(), metadatas);
+ }
+ for (BeanProperty prop : ((BeanMetadata) component).getProperties()) {
+ getMetadata(clazz, prop.getValue(), metadatas);
+ }
+ }
+ if (component instanceof CollectionMetadata) {
+ for (Metadata m : ((CollectionMetadata) component).getValues()) {
+ getMetadata(clazz, m, metadatas);
+ }
+ }
+ if (component instanceof MapMetadata) {
+ for (MapEntry m : ((MapMetadata) component).getEntries()) {
+ getMetadata(clazz, m.getKey(), metadatas);
+ getMetadata(clazz, m.getValue(), metadatas);
+ }
+ }
+ if (component instanceof PropsMetadata) {
+ for (MapEntry m : ((PropsMetadata) component).getEntries()) {
+ getMetadata(clazz, m.getKey(), metadatas);
+ getMetadata(clazz, m.getValue(), metadatas);
+ }
+ }
+ if (component instanceof ServiceReferenceMetadata) {
+ for (ReferenceListener l : ((ServiceReferenceMetadata) component).getReferenceListeners()) {
+ getMetadata(clazz, l.getListenerComponent(), metadatas);
+ }
+ }
+ if (component instanceof ServiceMetadata) {
+ getMetadata(clazz, ((ServiceMetadata) component).getServiceComponent(), metadatas);
+ for (MapEntry m : ((ServiceMetadata) component).getServiceProperties()) {
+ getMetadata(clazz, m.getKey(), metadatas);
+ getMetadata(clazz, m.getValue(), metadatas);
+ }
+ for (RegistrationListener l : ((ServiceMetadata) component).getRegistrationListeners()) {
+ getMetadata(clazz, l.getListenerComponent(), metadatas);
+ }
+ }
+ }
+
+ public Converter getConverter() {
+ return converter;
+ }
+
+ public ComponentDefinitionRegistryImpl getComponentDefinitionRegistry() {
+ return componentDefinitionRegistry;
+ }
+
+ public BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+ public void destroy() {
+ destroyed = true;
+ eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.DESTROYING, getBundleContext().getBundle(), getExtenderBundle()));
+
+ if (timeoutFuture != null) {
+ timeoutFuture.cancel(false);
+ }
+ if (registration != null) {
+ registration.unregister();
+ }
+ if (handlerSet != null) {
+ handlerSet.removeListener(this);
+ handlerSet.destroy();
+ }
+ unregisterServices();
+ untrackServiceReferences();
+
+ synchronized (running) {
+ while (running.get()) {
+ try {
+ running.wait();
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+ }
+
+ destroyComponents();
+
+ eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.DESTROYED, getBundleContext().getBundle(), getExtenderBundle()));
+ LOGGER.debug("Blueprint container destroyed: {}", this.bundleContext);
+ }
+
+ public void namespaceHandlerRegistered(URI uri) {
+ if (namespaces != null && namespaces.contains(uri)) {
+ schedule();
+ }
+ }
+
+ public void namespaceHandlerUnregistered(URI uri) {
+ if (namespaces != null && namespaces.contains(uri)) {
+ unregisterServices();
+ untrackServiceReferences();
+ destroyComponents();
+ state = State.WaitForNamespaceHandlers;
+ schedule();
+ }
+ }
+
+}
+