blob: fc4ecda6fff87683f10a9e1b8230310a31ae7028 [file] [log] [blame]
/*
* 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.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import org.apache.aries.blueprint.ComponentDefinitionRegistry;
import org.apache.aries.blueprint.ExtendedBeanMetadata;
import org.apache.aries.blueprint.ExtendedBlueprintContainer;
import org.apache.aries.blueprint.PassThroughMetadata;
import org.apache.aries.blueprint.di.ArrayRecipe;
import org.apache.aries.blueprint.di.CollectionRecipe;
import org.apache.aries.blueprint.di.ComponentFactoryRecipe;
import org.apache.aries.blueprint.di.DependentComponentFactoryRecipe;
import org.apache.aries.blueprint.di.IdRefRecipe;
import org.apache.aries.blueprint.di.MapRecipe;
import org.apache.aries.blueprint.di.PassThroughRecipe;
import org.apache.aries.blueprint.di.Recipe;
import org.apache.aries.blueprint.di.RefRecipe;
import org.apache.aries.blueprint.di.ValueRecipe;
import org.apache.aries.blueprint.ext.ComponentFactoryMetadata;
import org.apache.aries.blueprint.ext.DependentComponentFactoryMetadata;
import org.apache.aries.blueprint.mutable.MutableMapMetadata;
import org.apache.aries.blueprint.reflect.MetadataUtil;
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.IdRefMetadata;
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.NullMetadata;
import org.osgi.service.blueprint.reflect.PropsMetadata;
import org.osgi.service.blueprint.reflect.RefMetadata;
import org.osgi.service.blueprint.reflect.ReferenceListMetadata;
import org.osgi.service.blueprint.reflect.ReferenceListener;
import org.osgi.service.blueprint.reflect.ReferenceMetadata;
import org.osgi.service.blueprint.reflect.RegistrationListener;
import org.osgi.service.blueprint.reflect.ServiceMetadata;
import org.osgi.service.blueprint.reflect.ValueMetadata;
/**
* TODO: javadoc
*
* @version $Rev: 985150 $, $Date: 2010-08-13 11:20:09 +0100 (Fri, 13 Aug 2010) $
*/
public class RecipeBuilder {
private final Set<String> names = new HashSet<String>();
private final ExtendedBlueprintContainer blueprintContainer;
private final ComponentDefinitionRegistry registry;
private final IdSpace recipeIdSpace;
public RecipeBuilder(ExtendedBlueprintContainer blueprintContainer, IdSpace recipeIdSpace) {
this.recipeIdSpace = recipeIdSpace;
this.blueprintContainer = blueprintContainer;
this.registry = blueprintContainer.getComponentDefinitionRegistry();
}
public BlueprintRepository createRepository() {
BlueprintRepository repository = new BlueprintRepository(blueprintContainer);
// Create component recipes
for (String name : registry.getComponentDefinitionNames()) {
ComponentMetadata component = registry.getComponentDefinition(name);
Recipe recipe = createRecipe(component);
repository.putRecipe(recipe.getName(), recipe);
}
repository.validate();
return repository;
}
public Recipe createRecipe(ComponentMetadata component) {
// Custom components should be handled before built-in ones
// in case we have a custom component that also implements a built-in metadata
if (component instanceof DependentComponentFactoryMetadata) {
return createDependentComponentFactoryMetadata((DependentComponentFactoryMetadata) component);
} else if (component instanceof ComponentFactoryMetadata) {
return createComponentFactoryMetadata((ComponentFactoryMetadata) component);
} else if (component instanceof BeanMetadata) {
return createBeanRecipe((BeanMetadata) component);
} else if (component instanceof ServiceMetadata) {
return createServiceRecipe((ServiceMetadata) component);
} else if (component instanceof ReferenceMetadata) {
return createReferenceRecipe((ReferenceMetadata) component);
} else if (component instanceof ReferenceListMetadata) {
return createReferenceListRecipe((ReferenceListMetadata) component);
} else if (component instanceof PassThroughMetadata) {
return createPassThroughRecipe((PassThroughMetadata) component);
} else {
throw new IllegalStateException("Unsupported component type " + component.getClass());
}
}
private Recipe createComponentFactoryMetadata(ComponentFactoryMetadata metadata) {
return new ComponentFactoryRecipe<ComponentFactoryMetadata>(
metadata.getId(), metadata, blueprintContainer, getDependencies(metadata));
}
private Recipe createDependentComponentFactoryMetadata(DependentComponentFactoryMetadata metadata) {
return new DependentComponentFactoryRecipe(
metadata.getId(), metadata, blueprintContainer, getDependencies(metadata));
}
private List<Recipe> getDependencies(ComponentMetadata metadata) {
List<Recipe> deps = new ArrayList<Recipe>();
for (String name : metadata.getDependsOn()) {
deps.add(new RefRecipe(getName(null), name));
}
return deps;
}
private Recipe createPassThroughRecipe(PassThroughMetadata passThroughMetadata) {
return new PassThroughRecipe(getName(passThroughMetadata.getId()),
passThroughMetadata.getObject());
}
private Recipe createReferenceListRecipe(ReferenceListMetadata metadata) {
CollectionRecipe listenersRecipe = null;
if (metadata.getReferenceListeners() != null) {
listenersRecipe = new CollectionRecipe(getName(null), ArrayList.class);
for (ReferenceListener listener : metadata.getReferenceListeners()) {
listenersRecipe.add(createRecipe(listener));
}
}
ReferenceListRecipe recipe = new ReferenceListRecipe(getName(metadata.getId()),
blueprintContainer,
metadata,
listenersRecipe,
getDependencies(metadata));
return recipe;
}
private ReferenceRecipe createReferenceRecipe(ReferenceMetadata metadata) {
CollectionRecipe listenersRecipe = null;
if (metadata.getReferenceListeners() != null) {
listenersRecipe = new CollectionRecipe(getName(null), ArrayList.class);
for (ReferenceListener listener : metadata.getReferenceListeners()) {
listenersRecipe.add(createRecipe(listener));
}
}
ReferenceRecipe recipe = new ReferenceRecipe(getName(metadata.getId()),
blueprintContainer,
metadata,
listenersRecipe,
getDependencies(metadata));
return recipe;
}
private Recipe createServiceRecipe(ServiceMetadata serviceExport) {
CollectionRecipe listenersRecipe = new CollectionRecipe(getName(null), ArrayList.class);
if (serviceExport.getRegistrationListeners() != null) {
for (RegistrationListener listener : serviceExport.getRegistrationListeners()) {
listenersRecipe.add(createRecipe(listener));
}
}
ServiceRecipe recipe = new ServiceRecipe(getName(serviceExport.getId()),
blueprintContainer,
serviceExport,
getValue(serviceExport.getServiceComponent(), null),
listenersRecipe,
getServicePropertiesRecipe(serviceExport),
getDependencies(serviceExport));
return recipe;
}
protected MapRecipe getServicePropertiesRecipe(ServiceMetadata metadata) {
List<MapEntry> properties = metadata.getServiceProperties();
if (properties != null) {
MutableMapMetadata map = MetadataUtil.createMetadata(MutableMapMetadata.class);
for (MapEntry e : properties) {
map.addEntry(e);
}
return createMapRecipe(map);
} else {
return null;
}
}
private Object getBeanClass(BeanMetadata beanMetadata) {
if (beanMetadata instanceof ExtendedBeanMetadata) {
ExtendedBeanMetadata extBeanMetadata = (ExtendedBeanMetadata) beanMetadata;
if (extBeanMetadata.getRuntimeClass() != null) {
return extBeanMetadata.getRuntimeClass();
}
}
return beanMetadata.getClassName();
}
private boolean allowsFieldInjection(BeanMetadata beanMetadata) {
if (beanMetadata instanceof ExtendedBeanMetadata) {
return ((ExtendedBeanMetadata) beanMetadata).getFieldInjection();
}
return false;
}
private BeanRecipe createBeanRecipe(BeanMetadata beanMetadata) {
BeanRecipe recipe = new BeanRecipe(
getName(beanMetadata.getId()),
blueprintContainer,
getBeanClass(beanMetadata),
allowsFieldInjection(beanMetadata));
// Create refs for explicit dependencies
recipe.setExplicitDependencies(getDependencies(beanMetadata));
recipe.setPrototype(MetadataUtil.isPrototypeScope(beanMetadata));
recipe.setInitMethod(beanMetadata.getInitMethod());
recipe.setDestroyMethod(beanMetadata.getDestroyMethod());
recipe.setInterceptorLookupKey(beanMetadata);
List<BeanArgument> beanArguments = beanMetadata.getArguments();
if (beanArguments != null && !beanArguments.isEmpty()) {
boolean hasIndex = (beanArguments.get(0).getIndex() >= 0);
if (hasIndex) {
List<BeanArgument> beanArgumentsCopy = new ArrayList<BeanArgument>(beanArguments);
Collections.sort(beanArgumentsCopy, MetadataUtil.BEAN_COMPARATOR);
beanArguments = beanArgumentsCopy;
}
List<Object> arguments = new ArrayList<Object>();
List<String> argTypes = new ArrayList<String>();
for (BeanArgument argument : beanArguments) {
Recipe value = getValue(argument.getValue(), null);
arguments.add(value);
argTypes.add(argument.getValueType());
}
recipe.setArguments(arguments);
recipe.setArgTypes(argTypes);
recipe.setReorderArguments(!hasIndex);
}
recipe.setFactoryMethod(beanMetadata.getFactoryMethod());
if (beanMetadata.getFactoryComponent() != null) {
recipe.setFactoryComponent(getValue(beanMetadata.getFactoryComponent(), null));
}
for (BeanProperty property : beanMetadata.getProperties()) {
Recipe value = getValue(property.getValue(), null);
recipe.setProperty(property.getName(), value);
}
return recipe;
}
private Recipe createRecipe(RegistrationListener listener) {
BeanRecipe recipe = new BeanRecipe(getName(null), blueprintContainer, ServiceListener.class, false);
recipe.setProperty("listener", getValue(listener.getListenerComponent(), null));
if (listener.getRegistrationMethod() != null) {
recipe.setProperty("registerMethod", listener.getRegistrationMethod());
}
if (listener.getUnregistrationMethod() != null) {
recipe.setProperty("unregisterMethod", listener.getUnregistrationMethod());
}
recipe.setProperty("blueprintContainer", blueprintContainer);
return recipe;
}
private Recipe createRecipe(ReferenceListener listener) {
BeanRecipe recipe = new BeanRecipe(getName(null), blueprintContainer, AbstractServiceReferenceRecipe.Listener.class, false);
recipe.setProperty("listener", getValue(listener.getListenerComponent(), null));
recipe.setProperty("metadata", listener);
recipe.setProperty("blueprintContainer", blueprintContainer);
return recipe;
}
private Recipe getValue(Metadata v, Object groupingType) {
if (v instanceof NullMetadata) {
return null;
} else if (v instanceof ComponentMetadata) {
return createRecipe((ComponentMetadata) v);
} else if (v instanceof ValueMetadata) {
ValueMetadata stringValue = (ValueMetadata) v;
Object type = stringValue.getType();
type = (type == null) ? groupingType : type;
ValueRecipe vr = new ValueRecipe(getName(null), stringValue, type);
return vr;
} else if (v instanceof RefMetadata) {
// TODO: make it work with property-placeholders?
String componentName = ((RefMetadata) v).getComponentId();
RefRecipe rr = new RefRecipe(getName(null), componentName);
return rr;
} else if (v instanceof CollectionMetadata) {
CollectionMetadata collectionMetadata = (CollectionMetadata) v;
Class cl = collectionMetadata.getCollectionClass();
Object type = collectionMetadata.getValueType();
if (cl == Object[].class) {
ArrayRecipe ar = new ArrayRecipe(getName(null), type);
for (Metadata lv : collectionMetadata.getValues()) {
ar.add(getValue(lv, type));
}
return ar;
} else {
CollectionRecipe cr = new CollectionRecipe(getName(null), cl != null ? cl : ArrayList.class);
for (Metadata lv : collectionMetadata.getValues()) {
cr.add(getValue(lv, type));
}
return cr;
}
} else if (v instanceof MapMetadata) {
return createMapRecipe((MapMetadata) v);
} else if (v instanceof PropsMetadata) {
PropsMetadata mapValue = (PropsMetadata) v;
MapRecipe mr = new MapRecipe(getName(null), Properties.class);
for (MapEntry entry : mapValue.getEntries()) {
Recipe key = getValue(entry.getKey(), String.class);
Recipe val = getValue(entry.getValue(), String.class);
mr.put(key, val);
}
return mr;
} else if (v instanceof IdRefMetadata) {
// TODO: make it work with property-placeholders?
String componentName = ((IdRefMetadata) v).getComponentId();
IdRefRecipe rnr = new IdRefRecipe(getName(null), componentName);
return rnr;
} else {
throw new IllegalStateException("Unsupported value: " + v.getClass().getName());
}
}
private MapRecipe createMapRecipe(MapMetadata mapValue) {
String keyType = mapValue.getKeyType();
String valueType = mapValue.getValueType();
MapRecipe mr = new MapRecipe(getName(null), HashMap.class);
for (MapEntry entry : mapValue.getEntries()) {
Recipe key = getValue(entry.getKey(), keyType);
Recipe val = getValue(entry.getValue(), valueType);
mr.put(key, val);
}
return mr;
}
private String getName(String name) {
if (name == null) {
do {
name = "#recipe-" + recipeIdSpace.nextId();
} while (names.contains(name) || registry.containsComponentDefinition(name));
}
names.add(name);
return name;
}
}