blob: ed5c819b1a167b628f9c5f39adc04e519eeb9162 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2006, 2010 VMware Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0
* is available at http://www.opensource.org/licenses/apache2.0.php.
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
* VMware Inc.
*****************************************************************************/
package org.eclipse.gemini.blueprint.blueprint.reflect;
import org.eclipse.gemini.blueprint.config.internal.AbstractReferenceDefinitionParser;
import org.eclipse.gemini.blueprint.util.BeanReferenceFactoryBean;
import org.osgi.service.blueprint.reflect.ComponentMetadata;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.Mergeable;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import java.util.*;
import java.util.regex.Pattern;
/**
* Internal class used for adapting Spring's bean definition to OSGi Blueprint metadata. Used by {@link MetadataFactory}
* which acts as a facade.
*
* @author Adrian Colyer
* @author Costin Leau
*/
class ComponentMetadataFactory implements MetadataConstants {
private static final String BEAN_REF_FB_CLASS_NAME = BeanReferenceFactoryBean.class.getName();
private static final String GENERATED_REF = AbstractReferenceDefinitionParser.GENERATED_REF;
private static final String PROMOTED_REF = AbstractReferenceDefinitionParser.PROMOTED_REF;
private static final String REGEX =
"\\.org\\.springframework\\.osgi\\.service\\.importer\\.support\\.OsgiService(?:Collection)*ProxyFactoryBean#\\d+#\\d+";
private static final Pattern PATTERN = Pattern.compile(REGEX);
private static final String GENERATED_END = "#generated";
private static final String GENERATED_START = ".org.eclipse.gemini.blueprint.service.importer.support.OsgiService";
private static final String GENERATED_MIDDLE = "ProxyFactoryBean#";
/**
* Builds a component metadata from the given bean definition.
*
* @param name bean name
* @param beanDefinition
* @return
*/
static ComponentMetadata buildMetadata(String name, BeanDefinition beanDefinition) {
// shortcut (to avoid re-re-wrapping)
Object metadata = beanDefinition.getAttribute(COMPONENT_METADATA_ATTRIBUTE);
if (metadata instanceof ComponentMetadata)
return (ComponentMetadata) metadata;
// if no name has been given, look for one
if (name == null) {
name = (String) beanDefinition.getAttribute(COMPONENT_NAME);
}
if (isServiceExporter(beanDefinition)) {
return new SimpleServiceExportComponentMetadata(name, beanDefinition);
}
if (isSingleServiceImporter(beanDefinition)) {
return new SimpleReferenceMetadata(name, beanDefinition);
}
if (isCollectionImporter(beanDefinition)) {
return new SimpleReferenceListMetadata(name, beanDefinition);
}
BeanDefinition original = unwrapImporterReference(beanDefinition);
if (original != null) {
return buildMetadata(null, original);
}
if (isEnvironmentManager(beanDefinition)) {
return new EnvironmentManagerMetadata(name);
}
return new SimpleBeanMetadata(name, beanDefinition);
}
private static boolean isServiceExporter(BeanDefinition beanDefinition) {
return checkBeanDefinitionClassCompatibility(beanDefinition, EXPORTER_CLASS);
}
private static boolean isSingleServiceImporter(BeanDefinition beanDefinition) {
return checkBeanDefinitionClassCompatibility(beanDefinition, SINGLE_SERVICE_IMPORTER_CLASS);
}
private static boolean isCollectionImporter(BeanDefinition beanDefinition) {
return checkBeanDefinitionClassCompatibility(beanDefinition, MULTI_SERVICE_IMPORTER_CLASS);
}
static BeanDefinition unwrapImporterReference(BeanDefinition beanDefinition) {
if (BEAN_REF_FB_CLASS_NAME.equals(beanDefinition.getBeanClassName())) {
// check special DM case of nested mandatory
// references being promoted to top level beans
if (beanDefinition instanceof AbstractBeanDefinition) {
AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDefinition;
if (abd.isSynthetic() && abd.hasAttribute(GENERATED_REF)) {
BeanDefinition actual = abd.getOriginatingBeanDefinition();
return actual;
}
}
}
return null;
}
private static boolean isEnvironmentManager(BeanDefinition beanDefinition) {
return checkBeanDefinitionClassCompatibility(beanDefinition, ENV_FB_CLASS);
}
private static boolean checkBeanDefinitionClassCompatibility(BeanDefinition definition, Class<?> clazz) {
if (definition instanceof AbstractBeanDefinition) {
AbstractBeanDefinition abstractDefinition = (AbstractBeanDefinition) definition;
if (abstractDefinition.hasBeanClass()) {
Class<?> beanClass = abstractDefinition.getBeanClass();
return clazz.isAssignableFrom(beanClass);
}
}
return (clazz.getName().equals(definition.getBeanClassName()));
}
static Collection<ComponentMetadata> buildNestedMetadata(BeanDefinition beanDefinition) {
List<ComponentMetadata> col = new ArrayList<ComponentMetadata>(4);
processBeanDefinition(beanDefinition, col);
// remove the first definition
col.remove(0);
return col;
}
private static void processBeanMetadata(BeanMetadataElement metadata, Collection<ComponentMetadata> to) {
if (metadata instanceof BeanDefinition) {
processBeanDefinition((BeanDefinition) metadata, to);
}
else if (metadata instanceof BeanDefinitionHolder) {
BeanDefinitionHolder bh = (BeanDefinitionHolder) metadata;
processBeanDefinition(bh.getBeanDefinition(), to);
}
else if (metadata instanceof Mergeable && metadata instanceof Iterable) {
processIterable((Iterable) metadata, to);
}
}
private static void processBeanDefinition(BeanDefinition definition, Collection<ComponentMetadata> to) {
to.add(buildMetadata(null, definition));
// start with constructors
ConstructorArgumentValues cavs = definition.getConstructorArgumentValues();
// generic values
List<ValueHolder> genericValues = cavs.getGenericArgumentValues();
for (ValueHolder valueHolder : genericValues) {
Object value = MetadataUtils.getValue(valueHolder);
if (value instanceof BeanMetadataElement) {
processBeanMetadata((BeanMetadataElement) value, to);
}
}
// indexed ones
Map<Integer, ValueHolder> indexedValues = cavs.getIndexedArgumentValues();
for (ValueHolder valueHolder : indexedValues.values()) {
Object value = MetadataUtils.getValue(valueHolder);
if (value instanceof BeanMetadataElement) {
processBeanMetadata((BeanMetadataElement) value, to);
}
}
// now property values
PropertyValues pvs = definition.getPropertyValues();
for (PropertyValue pv : pvs.getPropertyValues()) {
Object value = MetadataUtils.getValue(pv);
if (value instanceof BeanMetadataElement) {
processBeanMetadata((BeanMetadataElement) value, to);
}
}
}
private static void processIterable(Iterable iterableMetadata, Collection<ComponentMetadata> to) {
for (Object value : iterableMetadata) {
if (value instanceof BeanMetadataElement) {
processBeanMetadata((BeanMetadataElement) value, to);
}
}
}
public static List<ComponentMetadata> buildComponentMetadataFor(ConfigurableListableBeanFactory factory) {
List<ComponentMetadata> metadata = new ArrayList<ComponentMetadata>();
String[] components = factory.getBeanDefinitionNames();
for (String beanName : components) {
BeanDefinition definition = factory.getBeanDefinition(beanName);
// filter generated definitions
if (!definition.hasAttribute(PROMOTED_REF)) {
// add metadata for top-level definitions
metadata.add(MetadataFactory.buildComponentMetadataFor(beanName, definition));
// look for nested ones
metadata.addAll(MetadataFactory.buildNestedComponentMetadataFor(definition));
}
}
return metadata;
}
// eliminate the names of promoted importers
public static Set<String> filterIds(Set<String> components) {
// search for pattern "
// .org.eclipse.gemini.blueprint.service.importer.support.OsgiServiceProxyFactoryBean#N#N and
// .org.eclipse.gemini.blueprint.service.importer.support.OsgiServiceCollectionProxyFactoryBean#N#N
Set<String> filtered = new LinkedHashSet<String>(components.size());
for (String string : components) {
if (!(string.startsWith(GENERATED_START) && string.endsWith(GENERATED_END) && string
.contains(GENERATED_MIDDLE))) {
filtered.add(string);
}
}
return filtered;
}
}