/****************************************************************************** | |
* 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; | |
} | |
} |