512927: Re-add spring-osgi extensions
Signed-off-by: Olaf Otto <olaf@x100.de>
diff --git a/extender/src/main/java/org/eclipse/gemini/blueprint/extender/internal/activator/ContextLoaderListener.java b/extender/src/main/java/org/eclipse/gemini/blueprint/extender/internal/activator/ContextLoaderListener.java
index 5fa3123..dec1c49 100644
--- a/extender/src/main/java/org/eclipse/gemini/blueprint/extender/internal/activator/ContextLoaderListener.java
+++ b/extender/src/main/java/org/eclipse/gemini/blueprint/extender/internal/activator/ContextLoaderListener.java
@@ -54,9 +54,8 @@
* <table border="1"> <tr> <th>Name</th> <th>Type</th> <th>Description</th> </tr> <tr>
* <td><code>shutdown.wait.time</code></td> <td>Number</td> <td>The amount of time the extender will wait for each
* application context to shutdown gracefully. Expressed in milliseconds.</td> </tr> <tr>
- * <td><code>process.annotations</code></td> <td>Boolean</td> <td>Whether or not, the extender will process SpringOSGi
- * annotations.</td> </tr> </table>
- *
+ * <td><code>process.annotations</code></td> <td>Boolean</td> <td>Whether or not, the extender will process proprietary OSGi annotations.
+ * </td> </tr> </table>
* <p/> Note: The extender configuration context is created during the bundle activation (a synchronous OSGi lifecycle
* callback) and should contain only simple bean definitions that will not delay context initialisation. </p>
*
diff --git a/extender/src/main/java/org/eclipse/gemini/blueprint/extender/internal/support/ExtenderConfiguration.java b/extender/src/main/java/org/eclipse/gemini/blueprint/extender/internal/support/ExtenderConfiguration.java
index 8370c62..ed93b3c 100644
--- a/extender/src/main/java/org/eclipse/gemini/blueprint/extender/internal/support/ExtenderConfiguration.java
+++ b/extender/src/main/java/org/eclipse/gemini/blueprint/extender/internal/support/ExtenderConfiguration.java
@@ -95,7 +95,7 @@
private static final boolean DEFAULT_SHUTDOWN_ASYNCHRONOUS = true;
private static final long DEFAULT_SHUTDOWN_WAIT = 10 * 1000;
- private static final boolean DEFAULT_PROCESS_ANNOTATION = false;
+ private static final boolean DEFAULT_PROCESS_ANNOTATION = true;
private ConfigurableOsgiBundleApplicationContext extenderConfiguration;
@@ -331,8 +331,8 @@
Class.forName(ANNOTATION_DEPENDENCY_FACTORY, false, ExtenderConfiguration.class
.getClassLoader());
} catch (ClassNotFoundException cnfe) {
- log.warn("Spring DM annotation package not found, annotation processing disabled.");
- log.debug("Spring DM annotation package not found, annotation processing disabled.", cnfe);
+ log.warn("Gemini Blueprint extensions bundle not present, annotation processing disabled.");
+ log.debug("Gemini Blueprint extensions bundle not present, annotation processing disabled.", cnfe);
return;
}
Object processor = BeanUtils.instantiateClass(annotationProcessor);
@@ -344,10 +344,10 @@
// add injection processor (first in line)
postProcessors.add(0, new OsgiAnnotationPostProcessor());
- log.info("Spring-DM annotation processing enabled");
+ log.info("Gemini Blueprint extensions annotation processing enabled");
} else {
if (debug) {
- log.debug("Spring-DM annotation processing disabled; [" + ANNOTATION_DEPENDENCY_FACTORY
+ log.debug("Gemini Blueprint extensions annotation processing disabled; [" + ANNOTATION_DEPENDENCY_FACTORY
+ "] not loaded");
}
}
diff --git a/extensions/bnd.bnd b/extensions/bnd.bnd
new file mode 100644
index 0000000..0e05784
--- /dev/null
+++ b/extensions/bnd.bnd
@@ -0,0 +1,21 @@
+Export-Package: \
+ !*.internal.*, \
+ org.eclipse.gemini.blueprint.extensions.*;version=${project.version}
+
+Bundle-Name: ${project.artifactId}
+Bundle-Version: ${project.version}
+Bundle-SymbolicName: ${spring.osgi.symbolic.name}
+Bundle-Vendor: Eclipse Foundation
+Bundle-DocURL: http://www.eclipse.org/gemini/blueprint/
+Bundle-Description: ${bundle.description}
+Bundle-License: http://www.opensource.org/licenses/eclipse-1.0.php, http://www.opensource.org/licenses/apache2.0.php
+Implementation-Title: Eclipse Gemini Blueprint
+Implementation-Version: ${project.version}
+Implementation-Vendor: Eclipse Foundation
+Implementation-Vendor-Id: org.eclipse.gemini.blueprint
+Gemini-Blueprint-Version: ${project.version}
+Spring-Version: ${spring.maven.artifact.version}
+Build-Jdk: ${java.version}
+Build-Plan: ${env.buildPlan}
+Build-Number: ${env.buildNumber}
+Git-Revision: ${git.commit.id.describe}
\ No newline at end of file
diff --git a/extensions/pom.xml b/extensions/pom.xml
new file mode 100644
index 0000000..a4f16b1
--- /dev/null
+++ b/extensions/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>gemini-blueprint</artifactId>
+ <groupId>org.eclipse.gemini.blueprint</groupId>
+ <version>2.1.0.M2-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>gemini-blueprint-extensions</artifactId>
+ <packaging>jar</packaging>
+ <name>Gemini Blueprint Extensions</name>
+ <url>http://www.eclipse.org/gemini/blueprint/</url>
+ <description>
+ Proprietary extensions not covered by the OSGi Blueprint specification
+ </description>
+
+ <properties>
+ <javadoc.loc>${basedir}/../docs/src/javadoc</javadoc.loc>
+ <spring.osgi.symbolic.name>${symName.prefix}.extensions</spring.osgi.symbolic.name>
+ <bundle.description>${project.description}</bundle.description>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.spring-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.gemini.blueprint</groupId>
+ <artifactId>gemini-blueprint-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.gemini.blueprint</groupId>
+ <artifactId>gemini-blueprint-extender</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.gemini.blueprint</groupId>
+ <artifactId>gemini-blueprint-mock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.aopalliance</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>biz.aQute.bnd</groupId>
+ <artifactId>bnd-maven-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+ </archive>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/extensions/src/main/java/org/eclipse/gemini/blueprint/extensions/annotation/ServiceReference.java b/extensions/src/main/java/org/eclipse/gemini/blueprint/extensions/annotation/ServiceReference.java
new file mode 100644
index 0000000..a9da25b
--- /dev/null
+++ b/extensions/src/main/java/org/eclipse/gemini/blueprint/extensions/annotation/ServiceReference.java
@@ -0,0 +1,75 @@
+/******************************************************************************
+ * 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.extensions.annotation;
+
+import org.eclipse.gemini.blueprint.service.importer.support.Availability;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a method (typically a JavaBean setter method) or a field as requiring an OSGi service reference.
+ *
+ * @author Andy Piper
+ */
+@Documented
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.FIELD})
+public @interface ServiceReference {
+ /**
+ * The name of the bean that backs the injected service. May be null.
+ */
+ String serviceBeanName() default "";
+
+ /**
+ * The cardinality of the service reference, defaults to mandatory.
+ */
+ Availability cardinality() default Availability.MANDATORY;
+
+ /**
+ * The invocation context classloader setting. Defalts to the classloader of the client.
+ */
+ ServiceReferenceClassLoader contextClassLoader() default ServiceReferenceClassLoader.CLIENT;
+
+ /**
+ * Timeout for service resolution in milliseconds.
+ */
+ int timeout() default 300000;
+
+ /**
+ * Interface (or class) of the service to be injected
+ */
+ Class<?>[] serviceTypes() default ServiceReference.class;
+
+ /**
+ * Whether or not to proxy greedily in collection references.
+ */
+ boolean greedyProxying() default false;
+
+ /**
+ * Whether or not to create a 'sticky' (singular) service reference.
+ */
+ boolean sticky() default true;
+
+ /**
+ * filter used to narrow service matches, may be null
+ */
+ String filter() default "";
+}
diff --git a/extensions/src/main/java/org/eclipse/gemini/blueprint/extensions/annotation/ServiceReferenceClassLoader.java b/extensions/src/main/java/org/eclipse/gemini/blueprint/extensions/annotation/ServiceReferenceClassLoader.java
new file mode 100644
index 0000000..891f73b
--- /dev/null
+++ b/extensions/src/main/java/org/eclipse/gemini/blueprint/extensions/annotation/ServiceReferenceClassLoader.java
@@ -0,0 +1,42 @@
+/******************************************************************************
+ * 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.extensions.annotation;
+
+import org.eclipse.gemini.blueprint.service.importer.support.ImportContextClassLoaderEnum;
+
+/**
+ * Spring-DM managed OSGi service <code>ClassLoader</code> property.
+ *
+ * @author Andy Piper
+ */
+public enum ServiceReferenceClassLoader {
+ CLIENT(ImportContextClassLoaderEnum.CLIENT), SERVICE_PROVIDER(ImportContextClassLoaderEnum.SERVICE_PROVIDER), UNMANAGED(
+ ImportContextClassLoaderEnum.UNMANAGED);
+
+ private ImportContextClassLoaderEnum icclValue;
+
+
+ private ServiceReferenceClassLoader(ImportContextClassLoaderEnum iccl) {
+ icclValue = iccl;
+ }
+
+ public String toString() {
+ return icclValue.name();
+ }
+
+ public ImportContextClassLoaderEnum toImportContextClassLoader() {
+ return icclValue;
+ }
+}
diff --git a/extensions/src/main/java/org/eclipse/gemini/blueprint/extensions/annotation/ServiceReferenceDependencyBeanFactoryPostProcessor.java b/extensions/src/main/java/org/eclipse/gemini/blueprint/extensions/annotation/ServiceReferenceDependencyBeanFactoryPostProcessor.java
new file mode 100644
index 0000000..23fd1c3
--- /dev/null
+++ b/extensions/src/main/java/org/eclipse/gemini/blueprint/extensions/annotation/ServiceReferenceDependencyBeanFactoryPostProcessor.java
@@ -0,0 +1,164 @@
+/******************************************************************************
+ * 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.extensions.annotation;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.eclipse.gemini.blueprint.extender.OsgiServiceDependencyFactory;
+import org.eclipse.gemini.blueprint.service.exporter.OsgiServicePropertiesResolver;
+import org.eclipse.gemini.blueprint.service.exporter.support.OsgiServiceFactoryBean;
+import org.eclipse.gemini.blueprint.service.importer.OsgiServiceDependency;
+import org.eclipse.gemini.blueprint.service.importer.support.Availability;
+import org.eclipse.gemini.blueprint.service.importer.support.OsgiServiceCollectionProxyFactoryBean;
+import org.eclipse.gemini.blueprint.service.importer.support.OsgiServiceProxyFactoryBean;
+import org.eclipse.gemini.blueprint.util.OsgiFilterUtils;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.ReflectionUtils;
+
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * Calculate service dependencies for annotation-style injected proxies.
+ *
+ * @author Andy Piper
+ */
+class ServiceReferenceDependencyBeanFactoryPostProcessor implements OsgiServiceDependencyFactory {
+ private final Log logger = LogFactory.getLog(getClass());
+
+ public Collection<OsgiServiceDependency> getServiceDependencies(BundleContext bundleContext,
+ ConfigurableListableBeanFactory beanFactory) throws BeansException, InvalidSyntaxException, BundleException {
+
+ Set<OsgiServiceDependency> dependencies = new LinkedHashSet<OsgiServiceDependency>();
+
+ String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
+ for (String definitionName : beanDefinitionNames) {
+ BeanDefinition definition = beanFactory.getBeanDefinition(definitionName);
+ String className = definition.getBeanClassName();
+ // Ignore internal stuff
+ if (className == null
+ || className.equals(OsgiServiceProxyFactoryBean.class.getName())
+ || className.equals(OsgiServiceFactoryBean.class.getName())
+ || className.equals(OsgiServiceCollectionProxyFactoryBean.class.getName())) {
+ continue;
+ }
+ try {
+ Class<?> clazz = Class.forName(className, true, beanFactory.getBeanClassLoader());
+ dependencies.addAll(getClassServiceDependencies(clazz, definitionName, definition));
+ }
+ catch (ClassNotFoundException cnfe) {
+ if (logger.isDebugEnabled())
+ logger.debug("Could not load class [" + className + "] for ["
+ + bundleContext.getBundle().getSymbolicName() + "]");
+ }
+ }
+ if (logger.isDebugEnabled())
+ logger.debug("Processing annotations for [" + bundleContext.getBundle().getSymbolicName() + "] found "
+ + dependencies);
+
+ return dependencies;
+ }
+
+ private Set<OsgiServiceDependency> getClassServiceDependencies(final Class<?> beanClass, final String beanName,
+ final BeanDefinition definition) {
+ final Set<OsgiServiceDependency> dependencies = new LinkedHashSet<OsgiServiceDependency>();
+ ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
+
+ public void doWith(final Method method) {
+ final ServiceReference s = AnnotationUtils.getAnnotation(method, ServiceReference.class);
+ if (s != null && method.getParameterTypes().length == 1
+ && !Collection.class.isAssignableFrom(method.getParameterTypes()[0])
+ // Ignore definitions overridden in the XML config
+ && !definition.getPropertyValues().contains(getPropertyName(method))) {
+ try {
+ if (logger.isDebugEnabled())
+ logger.debug("Processing annotation [" + s + "] for [" + beanClass.getName() + "."
+ + method.getName() + "()] on bean [" + beanName + "]");
+ dependencies.add(new OsgiServiceDependency() {
+
+ public Filter getServiceFilter() {
+ return getUnifiedFilter(s, method, beanName);
+ }
+
+ public boolean isMandatory() {
+ return s.cardinality() == Availability.MANDATORY;
+ }
+
+ public String getBeanName() {
+ return beanName;
+ }
+
+ public String toString() {
+ return beanName + "." + method.getName() + ": " + getServiceFilter()
+ + (isMandatory() ? " (mandatory)" : " (optional)");
+ }
+ });
+ }
+ catch (Exception e) {
+ throw new IllegalArgumentException("Error processing service annotation", e);
+ }
+ }
+ }
+ });
+ return dependencies;
+ }
+
+ private String getPropertyName(Method method) {
+ String name = method.getName();
+ if (name.startsWith("set")) {
+ return Character.toLowerCase(name.charAt(3)) + name.substring(4);
+ }
+ return name;
+ }
+
+ private Filter getUnifiedFilter(ServiceReference s, Method writeMethod, String beanName) {
+ String filter;
+ if (s.serviceTypes().length == 0 || s.serviceTypes().length == 1 && s.serviceTypes()[0].equals(ServiceReference.class)) {
+ Class<?>[] params = writeMethod.getParameterTypes();
+ if (params.length != 1) {
+ throw new IllegalArgumentException("Setter for [" + beanName + "] must have only one argument");
+ }
+ filter = OsgiFilterUtils.unifyFilter(new Class<?>[] { params[0] }, s.filter());
+ }
+ else {
+ filter = OsgiFilterUtils.unifyFilter(s.serviceTypes(), s.filter());
+ }
+
+ if (logger.isTraceEnabled())
+ logger.trace("unified classes=[" + filter + "]");
+
+ // add the serviceBeanName constraint
+ if (s.serviceBeanName().length() > 0) {
+ filter = OsgiFilterUtils.unifyFilter(OsgiServicePropertiesResolver.BEAN_NAME_PROPERTY_KEY,
+ new String[] { s.serviceBeanName() }, filter);
+ if (logger.isTraceEnabled())
+ logger.trace("unified serviceBeanName [" + ObjectUtils.nullSafeToString(s.serviceBeanName())
+ + "] and filter=[" + filter + "]");
+ }
+
+ // create (which implies validation) the actual filter
+ return OsgiFilterUtils.createFilter(filter);
+ }
+}
diff --git a/extensions/src/main/java/org/eclipse/gemini/blueprint/extensions/annotation/ServiceReferenceInjectionBeanPostProcessor.java b/extensions/src/main/java/org/eclipse/gemini/blueprint/extensions/annotation/ServiceReferenceInjectionBeanPostProcessor.java
new file mode 100644
index 0000000..8f74233
--- /dev/null
+++ b/extensions/src/main/java/org/eclipse/gemini/blueprint/extensions/annotation/ServiceReferenceInjectionBeanPostProcessor.java
@@ -0,0 +1,309 @@
+/******************************************************************************
+ * 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.extensions.annotation;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.eclipse.gemini.blueprint.context.BundleContextAware;
+import org.eclipse.gemini.blueprint.service.importer.support.*;
+import org.osgi.framework.BundleContext;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.FatalBeanException;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.PropertyValues;
+import org.springframework.beans.factory.*;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.util.ReflectionUtils;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+
+/**
+ * <code>BeanPostProcessor</code> that processed annotation to inject
+ * Spring-DM managed OSGi services.
+ *
+ * @author Andy Piper
+ */
+public class ServiceReferenceInjectionBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements
+ BundleContextAware, BeanFactoryAware, BeanClassLoaderAware {
+
+ private BundleContext bundleContext;
+
+ private static Log logger = LogFactory.getLog(ServiceReferenceInjectionBeanPostProcessor.class);
+
+ private BeanFactory beanFactory;
+
+ private ClassLoader classLoader;
+
+
+ private abstract static class ImporterCallAdapter {
+
+ static void setInterfaces(Object importer, Class<?>[] classes) {
+ if (importer instanceof OsgiServiceProxyFactoryBean)
+ ((OsgiServiceProxyFactoryBean) importer).setInterfaces(classes);
+ else
+ ((OsgiServiceCollectionProxyFactoryBean) importer).setInterfaces(classes);
+ }
+
+ static void setBundleContext(Object importer, BundleContext context) {
+ ((BundleContextAware) importer).setBundleContext(context);
+ }
+
+ static void setBeanClassLoader(Object importer, ClassLoader cl) {
+ ((BeanClassLoaderAware) importer).setBeanClassLoader(cl);
+ }
+
+ static void setCardinality(Object importer, Availability cardinality) {
+ if (importer instanceof OsgiServiceProxyFactoryBean)
+ ((OsgiServiceProxyFactoryBean) importer).setAvailability(cardinality);
+ else
+ ((OsgiServiceCollectionProxyFactoryBean) importer).setAvailability(cardinality);
+ }
+
+ static void setGreedyProxying(Object importer, boolean greedy) {
+ if (importer instanceof OsgiServiceCollectionProxyFactoryBean) {
+ ((OsgiServiceCollectionProxyFactoryBean) importer).setGreedyProxying(greedy);
+ }
+ }
+
+ static void setSticky(Object importer, boolean sticky) {
+ if (importer instanceof OsgiServiceProxyFactoryBean) {
+ ((OsgiServiceProxyFactoryBean) importer).setSticky(sticky);
+ }
+ }
+
+ static void afterPropertiesSet(Object importer) throws Exception {
+ ((InitializingBean) importer).afterPropertiesSet();
+ }
+
+ static void setFilter(Object importer, String filter) throws Exception {
+ if (importer instanceof OsgiServiceProxyFactoryBean)
+ ((OsgiServiceProxyFactoryBean) importer).setFilter(filter);
+ else
+ ((OsgiServiceCollectionProxyFactoryBean) importer).setFilter(filter);
+ }
+
+ static void setContextClassLoader(Object importer, ImportContextClassLoaderEnum ccl) {
+ if (importer instanceof OsgiServiceProxyFactoryBean)
+ ((OsgiServiceProxyFactoryBean) importer).setImportContextClassLoader(ccl);
+ else
+ ((OsgiServiceCollectionProxyFactoryBean) importer).setImportContextClassLoader(ccl);
+ }
+
+ static void setServiceBean(Object importer, String name) {
+ if (importer instanceof OsgiServiceProxyFactoryBean)
+ ((OsgiServiceProxyFactoryBean) importer).setServiceBeanName(name);
+ else
+ ((OsgiServiceCollectionProxyFactoryBean) importer).setServiceBeanName(name);
+ }
+ }
+
+
+ public void setBeanClassLoader(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ /**
+ * Perform field-based service injection as fields are not part of the
+ * {@link #postProcessPropertyValues(PropertyValues, PropertyDescriptor[], Object, String) property-based intitialization}.
+ */
+ @Override
+ public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
+ injectServicesViaAnnotatedFields(bean, beanName);
+ return bean;
+ }
+
+ /**
+ * process FactoryBean created objects, since these will not have had services injected.
+ */
+ public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
+ if (logger.isDebugEnabled())
+ logger.debug("processing [" + bean.getClass().getName() + ", " + beanName + "]");
+ // Catch FactoryBean created instances.
+ if (!(bean instanceof FactoryBean) && beanFactory.containsBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName)) {
+ injectServicesViaAnnotatedSetterMethods(bean, beanName);
+ injectServicesViaAnnotatedFields(bean, beanName);
+ }
+ return bean;
+ }
+
+ /* private version of the injector can use */
+ private void injectServicesViaAnnotatedSetterMethods(final Object bean, final String beanName) {
+ ReflectionUtils.doWithMethods(bean.getClass(), new ReflectionUtils.MethodCallback() {
+
+ public void doWith(Method method) {
+ ServiceReference s = AnnotationUtils.getAnnotation(method, ServiceReference.class);
+ if (s != null && method.getParameterTypes().length == 1) {
+ try {
+ if (logger.isDebugEnabled())
+ logger.debug("Processing annotation [" + s + "] for [" + bean.getClass().getName() + "."
+ + method.getName() + "()] on bean [" + beanName + "]");
+ method.invoke(bean, getServiceImporter(s, method, beanName).getObject());
+ }
+ catch (Exception e) {
+ throw new IllegalArgumentException("Error processing service annotation", e);
+ }
+ }
+ }
+ });
+ }
+
+ private void injectServicesViaAnnotatedFields(final Object bean, final String beanName) {
+ ReflectionUtils.doWithFields(bean.getClass(), new ReflectionUtils.FieldCallback() {
+ public void doWith(Field field) {
+ ServiceReference s = AnnotationUtils.getAnnotation(field, ServiceReference.class);
+ if (s != null && !Modifier.isStatic(field.getModifiers()) && !Modifier.isFinal(field.getModifiers())) {
+ try {
+ if (logger.isDebugEnabled())
+ logger.debug("Processing annotation [" + s + "] for [" + field + "] on bean [" + beanName + "]");
+ if (!field.isAccessible()) {
+ field.setAccessible(true);
+ }
+ ReflectionUtils.setField(field, bean, getServiceImporter(s, field.getType(), beanName).getObject());
+ }
+ catch (Exception e) {
+ throw new IllegalArgumentException("Error processing service annotation", e);
+ }
+ }
+ }
+ });
+ }
+
+ public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean,
+ String beanName) throws BeansException {
+
+ MutablePropertyValues newprops = new MutablePropertyValues(pvs);
+ for (PropertyDescriptor pd : pds) {
+ ServiceReference s = hasServiceProperty(pd);
+ if (s != null && !pvs.contains(pd.getName())) {
+ try {
+ if (logger.isDebugEnabled())
+ logger.debug("Processing annotation [" + s + "] for [" + beanName + "." + pd.getName() + "]");
+ FactoryBean importer = getServiceImporter(s, pd.getWriteMethod(), beanName);
+ // BPPs are created in stageOne(), even though they are run in stageTwo(). This check means that
+ // the call to getObject() will not fail with ServiceUnavailable. This is safe to do because
+ // ServiceReferenceDependencyBeanFactoryPostProcessor will ensure that mandatory services are
+ // satisfied before stageTwo() is run.
+ if (bean instanceof BeanPostProcessor) {
+ ImporterCallAdapter.setCardinality(importer, Availability.OPTIONAL);
+ }
+ newprops.addPropertyValue(pd.getName(), importer.getObject());
+ }
+ catch (Exception e) {
+ throw new FatalBeanException("Could not create service reference", e);
+ }
+ }
+ }
+ return newprops;
+ }
+
+ private FactoryBean getServiceImporter(ServiceReference s, Method writeMethod, String beanName) throws Exception {
+ Class<?>[] params = writeMethod.getParameterTypes();
+ if (params.length != 1) {
+ throw new IllegalArgumentException("Setter for [" + beanName + "] must have only one argument");
+ }
+ return getServiceImporter(s, params[0], beanName);
+ }
+
+ private FactoryBean getServiceImporter(ServiceReference s, Class<?> serviceType, String beanName) throws Exception {
+ // Invocations will block here, so although the ApplicationContext is created. Nothing will proceed until all the dependencies are satisfied.
+ if (Collection.class.isAssignableFrom(serviceType)) {
+ return getServiceProperty(new OsgiServiceCollectionProxyFactoryBean(), s, serviceType, beanName);
+ }
+ else {
+ return getServiceProperty(new OsgiServiceProxyFactoryBean(), s, serviceType, beanName);
+ }
+ }
+
+ private boolean impliedServiceType(ServiceReference s) {
+ return (s.serviceTypes() == null || s.serviceTypes().length == 0 || (s.serviceTypes().length == 1 && s.serviceTypes()[0].equals(ServiceReference.class)));
+ }
+
+ // Package protected for testing
+ private FactoryBean getServicePropertyInternal(FactoryBean pfb, ServiceReference s, Class<?> serviceType,
+ String beanName) throws Exception {
+ if (s.filter().length() > 0) {
+ ImporterCallAdapter.setFilter(pfb, s.filter());
+ }
+ if (impliedServiceType(s)) {
+ if (Collection.class.isAssignableFrom(serviceType)) {
+ throw new IllegalArgumentException("Cannot infer type for collection-based reference [" + beanName + "]");
+ } else {
+ ImporterCallAdapter.setInterfaces(pfb, new Class<?>[] { serviceType });
+ }
+ }
+ else {
+ ImporterCallAdapter.setInterfaces(pfb, s.serviceTypes());
+ }
+ ImporterCallAdapter.setCardinality(pfb, s.cardinality());
+ ImporterCallAdapter.setSticky(pfb, s.sticky());
+ ImporterCallAdapter.setContextClassLoader(pfb, s.contextClassLoader().toImportContextClassLoader());
+ ImporterCallAdapter.setBundleContext(pfb, bundleContext);
+
+ if (s.serviceBeanName().length() > 0) {
+ ImporterCallAdapter.setServiceBean(pfb, s.serviceBeanName());
+ }
+ ImporterCallAdapter.setBeanClassLoader(pfb, classLoader);
+ ImporterCallAdapter.afterPropertiesSet(pfb);
+ return pfb;
+ }
+
+ FactoryBean getServiceProperty(OsgiServiceProxyFactoryBean pfb, ServiceReference s,
+ Class<?> serviceType, String beanName) throws Exception {
+ pfb.setTimeout(s.timeout());
+ return getServicePropertyInternal(pfb, s, serviceType, beanName);
+ }
+
+ FactoryBean getServiceProperty(OsgiServiceCollectionProxyFactoryBean pfb, ServiceReference s,
+ Class<?> serviceType, String beanName) throws Exception {
+ if (SortedSet.class.isAssignableFrom(serviceType)) {
+ pfb.setCollectionType(CollectionType.SORTED_SET);
+ }
+ else if (Set.class.isAssignableFrom(serviceType)) {
+ pfb.setCollectionType(CollectionType.SET);
+ }
+ else if (List.class.isAssignableFrom(serviceType)) {
+ pfb.setCollectionType(CollectionType.LIST);
+ }
+ else {
+ throw new IllegalArgumentException("Setter for [" + beanName
+ + "] does not have a valid Collection type argument");
+ }
+ return getServicePropertyInternal(pfb, s, serviceType, beanName);
+ }
+
+ protected ServiceReference hasServiceProperty(PropertyDescriptor propertyDescriptor) {
+ Method setter = propertyDescriptor.getWriteMethod();
+ return setter != null ? AnnotationUtils.getAnnotation(setter, ServiceReference.class) : null;
+ }
+
+ public void setBundleContext(BundleContext context) {
+ this.bundleContext = context;
+
+ }
+
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ this.beanFactory = beanFactory;
+ }
+}
diff --git a/extensions/src/main/java/org/eclipse/gemini/blueprint/extensions/annotation/package.html b/extensions/src/main/java/org/eclipse/gemini/blueprint/extensions/annotation/package.html
new file mode 100644
index 0000000..f6592ff
--- /dev/null
+++ b/extensions/src/main/java/org/eclipse/gemini/blueprint/extensions/annotation/package.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+Extensions supporting proprietary previously provided by Spring-DM. Allows declarative injection of OSGi services through annotation.
+</body>
+</html>
diff --git a/extensions/src/test/java/org/eclipse/gemini/blueprint/extensions/annotation/AnnotatedBean.java b/extensions/src/test/java/org/eclipse/gemini/blueprint/extensions/annotation/AnnotatedBean.java
new file mode 100644
index 0000000..ad81242
--- /dev/null
+++ b/extensions/src/test/java/org/eclipse/gemini/blueprint/extensions/annotation/AnnotatedBean.java
@@ -0,0 +1,204 @@
+/******************************************************************************
+ * 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.extensions.annotation;
+
+import org.eclipse.gemini.blueprint.service.importer.support.Availability;
+import org.springframework.context.ApplicationContext;
+
+import java.util.List;
+import java.util.SortedSet;
+
+/**
+ * @author Andy Piper
+ * @since 2.1
+ */
+public class AnnotatedBean {
+
+ private String stringType;
+ private int intType;
+ private AnnotatedBean annotatedBeanTypeSimple;
+ private AnnotatedBean annotatedBeanTypeComplex;
+ private ApplicationContext applicatonContextType;
+ private AnnotatedBean annotatedBeanTypeWithCardinality1_1;
+ private AnnotatedBean annotatedBeanTypeWithCardinality0_1;
+ private List<AnnotatedBean> annotatedBeanTypeWithCardinality0_N;
+ private SortedSet<AnnotatedBean> annotatedBeanTypeWithCardinality1_N;
+ private SortedSet<AnnotatedBean> annotatedBeanErrorTypeWithCardinality1_N;
+ private AnnotatedBean annotatedBeanTypeWithClassLoaderClient;
+ private AnnotatedBean annotatedBeanTypeWithClassLoaderServiceProvider;
+ private AnnotatedBean annotatedBeanTypeWithClassLoaderUmanaged;
+ private AnnotatedBean annotatedBeanTypeWithBeanName;
+ private AnnotatedBean annotatedBeanTypeWithFilter;
+ private AnnotatedBean annotatedBeanTypeWithTimeout;
+ private AnnotatedBean annotatedBeanTypeWithServiceType;
+
+ public String getStringType() {
+ return stringType;
+ }
+
+ @ServiceReference
+ public void setStringType(String stringType) {
+ this.stringType = stringType;
+ }
+
+ public int getIntType() {
+ return intType;
+ }
+
+ @ServiceReference
+ public void setIntType(int intType) {
+ this.intType = intType;
+ }
+
+ public AnnotatedBean getAnnotatedBeanTypeSimple() {
+ return annotatedBeanTypeSimple;
+ }
+
+ @ServiceReference
+ public void setAnnotatedBeanTypeSimple(AnnotatedBean annotatedBeanTypeSimple) {
+ this.annotatedBeanTypeSimple = annotatedBeanTypeSimple;
+ }
+
+ public AnnotatedBean getAnnotatedBeanTypeComplex() {
+ return annotatedBeanTypeComplex;
+ }
+
+ @ServiceReference(serviceBeanName = "myBean", cardinality = Availability.OPTIONAL,
+ contextClassLoader = ServiceReferenceClassLoader.SERVICE_PROVIDER, timeout = 100, filter = "(id=fooey)")
+ public void setAnnotatedBeanTypeComplex(AnnotatedBean annotatedBeanTypeComplex) {
+ this.annotatedBeanTypeComplex = annotatedBeanTypeComplex;
+ }
+
+ public ApplicationContext getApplicatonContextType() {
+ return applicatonContextType;
+ }
+
+ @ServiceReference
+ public void setApplicatonContextType(ApplicationContext applicatonContextType) {
+ this.applicatonContextType = applicatonContextType;
+ }
+
+ public AnnotatedBean getAnnotatedBeanTypeWithCardinality1_1() {
+ return annotatedBeanTypeWithCardinality1_1;
+ }
+
+ @ServiceReference(cardinality = Availability.MANDATORY)
+ public void setAnnotatedBeanTypeWithCardinality1_1(AnnotatedBean annotatedBeanTypeWithCardinality1_1) {
+ this.annotatedBeanTypeWithCardinality1_1 = annotatedBeanTypeWithCardinality1_1;
+ }
+
+ public AnnotatedBean getAnnotatedBeanTypeWithCardinality0_1() {
+ return annotatedBeanTypeWithCardinality0_1;
+ }
+
+ @ServiceReference(cardinality = Availability.OPTIONAL)
+ public void setAnnotatedBeanTypeWithCardinality0_1(AnnotatedBean annotatedBeanTypeWithCardinality0_1) {
+ this.annotatedBeanTypeWithCardinality0_1 = annotatedBeanTypeWithCardinality0_1;
+ }
+
+ public List<AnnotatedBean> getAnnotatedBeanTypeWithCardinality0_N() {
+ return annotatedBeanTypeWithCardinality0_N;
+ }
+
+ @ServiceReference(cardinality = Availability.OPTIONAL, serviceTypes = AnnotatedBean.class)
+ public void setAnnotatedBeanTypeWithCardinality0_N(List<AnnotatedBean> annotatedBeanTypeWithCardinality0_N) {
+ this.annotatedBeanTypeWithCardinality0_N = annotatedBeanTypeWithCardinality0_N;
+ }
+
+ public SortedSet<AnnotatedBean> getAnnotatedBeanErrorTypeWithCardinality1_N() {
+ return annotatedBeanErrorTypeWithCardinality1_N;
+ }
+
+ @ServiceReference(cardinality = Availability.MANDATORY)
+ public void setAnnotatedBeanErrorTypeWithCardinality1_N(SortedSet<AnnotatedBean> annotatedBeanTypeWithCardinality1_N) {
+ this.annotatedBeanErrorTypeWithCardinality1_N = annotatedBeanTypeWithCardinality1_N;
+ }
+
+ public SortedSet<AnnotatedBean> getAnnotatedBeanTypeWithCardinality1_N() {
+ return annotatedBeanTypeWithCardinality1_N;
+ }
+
+ @ServiceReference(cardinality = Availability.MANDATORY, serviceTypes = AnnotatedBean.class)
+ public void setAnnotatedBeanTypeWithCardinality1_N(SortedSet<AnnotatedBean> annotatedBeanTypeWithCardinality1_N) {
+ this.annotatedBeanTypeWithCardinality1_N = annotatedBeanTypeWithCardinality1_N;
+ }
+ public AnnotatedBean getAnnotatedBeanTypeWithClassLoaderClient() {
+ return annotatedBeanTypeWithClassLoaderClient;
+ }
+
+ @ServiceReference(contextClassLoader = ServiceReferenceClassLoader.CLIENT)
+ public void setAnnotatedBeanTypeWithClassLoaderClient(AnnotatedBean annotatedBeanTypeWithClassLoaderClient) {
+ this.annotatedBeanTypeWithClassLoaderClient = annotatedBeanTypeWithClassLoaderClient;
+ }
+
+ public AnnotatedBean getAnnotatedBeanTypeWithClassLoaderServiceProvider() {
+ return annotatedBeanTypeWithClassLoaderServiceProvider;
+ }
+
+ @ServiceReference(contextClassLoader = ServiceReferenceClassLoader.SERVICE_PROVIDER)
+ public void setAnnotatedBeanTypeWithClassLoaderServiceProvider(AnnotatedBean annotatedBeanTypeWithClassLoaderServiceProvider) {
+ this.annotatedBeanTypeWithClassLoaderServiceProvider = annotatedBeanTypeWithClassLoaderServiceProvider;
+ }
+
+ public AnnotatedBean getAnnotatedBeanTypeWithClassLoaderUmanaged() {
+ return annotatedBeanTypeWithClassLoaderUmanaged;
+ }
+
+ @ServiceReference(contextClassLoader = ServiceReferenceClassLoader.UNMANAGED)
+ public void setAnnotatedBeanTypeWithClassLoaderUmanaged(AnnotatedBean annotatedBeanTypeWithClassLoaderUmanaged) {
+ this.annotatedBeanTypeWithClassLoaderUmanaged = annotatedBeanTypeWithClassLoaderUmanaged;
+ }
+
+ public AnnotatedBean getAnnotatedBeanTypeWithBeanName() {
+ return annotatedBeanTypeWithBeanName;
+ }
+
+ @ServiceReference(serviceBeanName = "myBean")
+ public void setAnnotatedBeanTypeWithBeanName(AnnotatedBean annotatedBeanTypeWithBeanName) {
+ this.annotatedBeanTypeWithBeanName = annotatedBeanTypeWithBeanName;
+ }
+
+ public AnnotatedBean getAnnotatedBeanTypeWithFilter() {
+ return annotatedBeanTypeWithFilter;
+ }
+
+ @ServiceReference(filter = "(wooey=fooo)")
+ public void setAnnotatedBeanTypeWithFilter(AnnotatedBean annotatedBeanTypeWithFilter) {
+ this.annotatedBeanTypeWithFilter = annotatedBeanTypeWithFilter;
+ }
+
+ public AnnotatedBean getAnnotatedBeanTypeWithTimeout() {
+ return annotatedBeanTypeWithTimeout;
+ }
+
+ @ServiceReference(timeout = 12345)
+ public void setAnnotatedBeanTypeWithTimeout(AnnotatedBean annotatedBeanTypeWithTimeout) {
+ this.annotatedBeanTypeWithTimeout = annotatedBeanTypeWithTimeout;
+ }
+
+ public AnnotatedBean getAnnotatedBeanTypeWithServiceType() {
+ return annotatedBeanTypeWithServiceType;
+ }
+
+ @ServiceReference(serviceTypes = Object.class)
+ public void setAnnotatedBeanTypeWithServiceType(AnnotatedBean annotatedBeanTypeWithServiceType) {
+ this.annotatedBeanTypeWithServiceType = annotatedBeanTypeWithServiceType;
+ }
+
+ @ServiceReference(serviceTypes = { Object.class, AnnotatedBean.class })
+ public void setAnnotatedBeanTypeWithMultipleServiceTypes(AnnotatedBean annotatedBeanTypeWithServiceType) {
+ this.annotatedBeanTypeWithServiceType = annotatedBeanTypeWithServiceType;
+ }
+}
diff --git a/extensions/src/test/java/org/eclipse/gemini/blueprint/extensions/annotation/MyService.java b/extensions/src/test/java/org/eclipse/gemini/blueprint/extensions/annotation/MyService.java
new file mode 100644
index 0000000..744e489
--- /dev/null
+++ b/extensions/src/test/java/org/eclipse/gemini/blueprint/extensions/annotation/MyService.java
@@ -0,0 +1,23 @@
+/******************************************************************************
+ * 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.extensions.annotation;
+
+/**
+ * @author Andy Piper
+ * @since 2.1
+ */
+public interface MyService {
+ Object getId();
+}
diff --git a/extensions/src/test/java/org/eclipse/gemini/blueprint/extensions/annotation/OsgiServiceAnnotationTest.java b/extensions/src/test/java/org/eclipse/gemini/blueprint/extensions/annotation/OsgiServiceAnnotationTest.java
new file mode 100644
index 0000000..5f0a706
--- /dev/null
+++ b/extensions/src/test/java/org/eclipse/gemini/blueprint/extensions/annotation/OsgiServiceAnnotationTest.java
@@ -0,0 +1,460 @@
+/******************************************************************************
+ * 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.extensions.annotation;
+
+import junit.framework.TestCase;
+import org.easymock.EasyMock;
+import org.easymock.internal.MocksControl;
+import org.eclipse.gemini.blueprint.mock.MockBundleContext;
+import org.eclipse.gemini.blueprint.service.importer.support.Availability;
+import org.eclipse.gemini.blueprint.service.importer.support.ImportContextClassLoaderEnum;
+import org.eclipse.gemini.blueprint.service.importer.support.OsgiServiceCollectionProxyFactoryBean;
+import org.eclipse.gemini.blueprint.service.importer.support.OsgiServiceProxyFactoryBean;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.PropertyValues;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.util.ReflectionUtils;
+import org.springframework.util.ReflectionUtils.FieldCallback;
+import org.springframework.util.ReflectionUtils.FieldFilter;
+
+import java.beans.PropertyDescriptor;
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.SortedSet;
+
+/**
+ * @author Andy Piper
+ */
+public class OsgiServiceAnnotationTest extends TestCase {
+
+ private ServiceReferenceInjectionBeanPostProcessor processor;
+
+ private BundleContext context;
+
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ processor = new ServiceReferenceInjectionBeanPostProcessor();
+ context = new MockBundleContext();
+ processor.setBundleContext(context);
+ processor.setBeanClassLoader(getClass().getClassLoader());
+ BeanFactory factory = EasyMock.createMock(BeanFactory.class);
+ processor.setBeanFactory(factory);
+ }
+
+ /**
+ * Disabled since it doesn't work as we can't proxy final classes.
+ */
+ public void tstGetServicePropertySetters() throws Exception {
+ OsgiServiceProxyFactoryBean pfb = new OsgiServiceProxyFactoryBean();
+ Method setter = AnnotatedBean.class.getMethod("setStringType", new Class<?>[] { String.class });
+ ServiceReference ref = AnnotationUtils.getAnnotation(setter, ServiceReference.class);
+
+ processor.getServiceProperty(pfb, ref, setter.getParameterTypes()[0], null);
+ Class<?>[] intfs = (Class[]) getPrivateProperty(pfb, "serviceTypes");
+ assertEquals(intfs[0], String.class);
+
+ setter = AnnotatedBean.class.getMethod("setIntType", new Class<?>[] { Integer.TYPE });
+ ref = AnnotationUtils.getAnnotation(setter, ServiceReference.class);
+
+ pfb = new OsgiServiceProxyFactoryBean();
+ processor.getServiceProperty(pfb, ref, setter.getParameterTypes()[0], null);
+ intfs = (Class[]) getPrivateProperty(pfb, "serviceTypes");
+ assertEquals(intfs[0], Integer.TYPE);
+
+ }
+
+ public void testGetServicePropertyCardinality() throws Exception {
+ OsgiServiceProxyFactoryBean pfb = new OsgiServiceProxyFactoryBean();
+ Method setter = AnnotatedBean.class.getMethod("setAnnotatedBeanTypeWithCardinality1_1",
+ new Class<?>[] { AnnotatedBean.class });
+ ServiceReference ref = AnnotationUtils.getAnnotation(setter, ServiceReference.class);
+ processor.getServiceProperty(pfb, ref, setter.getParameterTypes()[0], null);
+ assertTrue(pfb.getAvailability() == Availability.MANDATORY);
+
+ setter = AnnotatedBean.class.getMethod("setAnnotatedBeanTypeWithCardinality0_1",
+ new Class<?>[] { AnnotatedBean.class });
+ ref = AnnotationUtils.getAnnotation(setter, ServiceReference.class);
+ pfb = new OsgiServiceProxyFactoryBean();
+ processor.getServiceProperty(pfb, ref, setter.getParameterTypes()[0], null);
+ assertFalse(pfb.getAvailability() == Availability.MANDATORY);
+ }
+
+ public void testProperMultiCardinality() throws Exception {
+ OsgiServiceCollectionProxyFactoryBean pfb = new OsgiServiceCollectionProxyFactoryBean();
+
+ Method setter = AnnotatedBean.class.getMethod("setAnnotatedBeanTypeWithCardinality0_N",
+ new Class<?>[] { List.class });
+ ServiceReference ref = AnnotationUtils.getAnnotation(setter, ServiceReference.class);
+ processor.getServiceProperty(pfb, ref, setter.getParameterTypes()[0], null);
+ assertFalse(pfb.getAvailability() == Availability.MANDATORY);
+
+ setter = AnnotatedBean.class.getMethod("setAnnotatedBeanTypeWithCardinality1_N",
+ new Class<?>[] { SortedSet.class });
+ ref = AnnotationUtils.getAnnotation(setter, ServiceReference.class);
+ pfb = new OsgiServiceCollectionProxyFactoryBean();
+ processor.getServiceProperty(pfb, ref, setter.getParameterTypes()[0], null);
+ assertTrue(pfb.getAvailability() == Availability.MANDATORY);
+ }
+
+ public void testErrorMultiCardinality() throws Exception {
+ OsgiServiceCollectionProxyFactoryBean pfb = new OsgiServiceCollectionProxyFactoryBean();
+
+ Method setter = AnnotatedBean.class.getMethod("setAnnotatedBeanErrorTypeWithCardinality1_N",
+ new Class<?>[] { SortedSet.class });
+ ServiceReference ref = AnnotationUtils.getAnnotation(setter, ServiceReference.class);
+ pfb = new OsgiServiceCollectionProxyFactoryBean();
+ try {
+ processor.getServiceProperty(pfb, ref, setter.getParameterTypes()[0], null);
+ fail("IllegalArgumentException should have been thrown");
+ }
+ catch (Exception e) {
+ }
+ }
+
+ public void testGetServicePropertyClassloader() throws Exception {
+ OsgiServiceProxyFactoryBean pfb = new OsgiServiceProxyFactoryBean();
+ Method setter = AnnotatedBean.class.getMethod("setAnnotatedBeanTypeWithClassLoaderClient",
+ new Class<?>[] { AnnotatedBean.class });
+ ServiceReference ref = AnnotationUtils.getAnnotation(setter, ServiceReference.class);
+ processor.getServiceProperty(pfb, ref, setter.getParameterTypes()[0], null);
+ assertEquals(pfb.getImportContextClassLoader(), ImportContextClassLoaderEnum.CLIENT);
+
+ pfb = new OsgiServiceProxyFactoryBean();
+ setter = AnnotatedBean.class.getMethod("setAnnotatedBeanTypeWithClassLoaderUmanaged",
+ new Class<?>[] { AnnotatedBean.class });
+ ref = AnnotationUtils.getAnnotation(setter, ServiceReference.class);
+ processor.getServiceProperty(pfb, ref, setter.getParameterTypes()[0], null);
+
+ assertEquals(pfb.getImportContextClassLoader(), ImportContextClassLoaderEnum.UNMANAGED);
+
+ pfb = new OsgiServiceProxyFactoryBean();
+ setter = AnnotatedBean.class.getMethod("setAnnotatedBeanTypeWithClassLoaderServiceProvider",
+ new Class<?>[] { AnnotatedBean.class });
+ ref = AnnotationUtils.getAnnotation(setter, ServiceReference.class);
+ processor.getServiceProperty(pfb, ref, setter.getParameterTypes()[0], null);
+ assertEquals(pfb.getImportContextClassLoader(), ImportContextClassLoaderEnum.SERVICE_PROVIDER);
+ }
+
+ public void testGetServicePropertyBeanName() throws Exception {
+ OsgiServiceProxyFactoryBean pfb = new OsgiServiceProxyFactoryBean();
+ Method setter = AnnotatedBean.class.getMethod("setAnnotatedBeanTypeWithBeanName",
+ new Class<?>[] { AnnotatedBean.class });
+ ServiceReference ref = AnnotationUtils.getAnnotation(setter, ServiceReference.class);
+ processor.getServiceProperty(pfb, ref, setter.getParameterTypes()[0], null);
+ String beanName = (String) getPrivateProperty(pfb, "serviceBeanName");
+ ;
+ assertEquals(beanName, "myBean");
+ }
+
+ public void testGetServicePropertyFilter() throws Exception {
+ OsgiServiceProxyFactoryBean pfb = new OsgiServiceProxyFactoryBean();
+ Method setter = AnnotatedBean.class.getMethod("setAnnotatedBeanTypeWithFilter",
+ new Class<?>[] { AnnotatedBean.class });
+ ServiceReference ref = AnnotationUtils.getAnnotation(setter, ServiceReference.class);
+ processor.getServiceProperty(pfb, ref, setter.getParameterTypes()[0], null);
+ String filter = (String) getPrivateProperty(pfb, "filter");
+ ;
+ assertEquals(filter, "(wooey=fooo)");
+ }
+
+ public void testGetServicePropertyServiceClass() throws Exception {
+ OsgiServiceProxyFactoryBean pfb = new OsgiServiceProxyFactoryBean();
+ Method setter = AnnotatedBean.class.getMethod("setAnnotatedBeanTypeWithServiceType",
+ new Class<?>[] { AnnotatedBean.class });
+ ServiceReference ref = AnnotationUtils.getAnnotation(setter, ServiceReference.class);
+ processor.getServiceProperty(pfb, ref, setter.getParameterTypes()[0], null);
+ Class<?>[] intfs = (Class[]) getPrivateProperty(pfb, "interfaces");
+ assertEquals(intfs[0], Object.class);
+ }
+
+ public void testGetServicePropertyComplex() throws Exception {
+ OsgiServiceProxyFactoryBean pfb = new OsgiServiceProxyFactoryBean();
+ Method setter = AnnotatedBean.class.getMethod("setAnnotatedBeanTypeComplex",
+ new Class<?>[] { AnnotatedBean.class });
+ ServiceReference ref = AnnotationUtils.getAnnotation(setter, ServiceReference.class);
+ processor.getServiceProperty(pfb, ref, setter.getParameterTypes()[0], null);
+ Class<?>[] intfs = (Class[]) getPrivateProperty(pfb, "interfaces");
+ String filter = (String) getPrivateProperty(pfb, "filter");
+ String beanName = (String) getPrivateProperty(pfb, "serviceBeanName");
+ assertEquals(intfs[0], AnnotatedBean.class);
+ assertFalse(pfb.getAvailability() == Availability.MANDATORY);
+ assertEquals(ImportContextClassLoaderEnum.SERVICE_PROVIDER, pfb.getImportContextClassLoader());
+ assertEquals(filter, "(id=fooey)");
+ assertEquals(beanName, "myBean");
+ }
+
+ public void testServiceBeanInjection() throws Exception {
+ ServiceBean bean = new ServiceBean();
+ final MyService bean1 = new MyService() {
+
+ public Object getId() {
+ return this;
+ }
+ };
+ final Serializable bean2 = new Serializable() {
+
+ public String toString() {
+ return "bean2";
+ }
+ };
+
+ BundleContext context = new MockBundleContext() {
+
+ public Object getService(org.osgi.framework.ServiceReference reference) {
+ String clazz = ((String[]) reference.getProperty(Constants.OBJECTCLASS))[0];
+ if (clazz == null)
+ return null;
+ else if (clazz.equals(MyService.class.getName())) {
+ return bean1;
+ }
+ else if (clazz.equals(Serializable.class.getName())) {
+ return bean2;
+ }
+ return null;
+ }
+
+ };
+
+ ServiceReferenceInjectionBeanPostProcessor p = new ServiceReferenceInjectionBeanPostProcessor();
+ p.setBundleContext(context);
+ p.setBeanClassLoader(getClass().getClassLoader());
+ BeanFactory factory = EasyMock.createMock(BeanFactory.class);
+ MocksControl factoryControl = MocksControl.getControl(factory);
+ EasyMock.expect(factory.containsBean("&myBean")).andReturn(true);
+ EasyMock.replay(factory);
+ p.setBeanFactory(factory);
+
+ p.postProcessAfterInitialization(bean, "myBean");
+ assertSame(bean1.getId(), bean.getServiceBean().getId());
+ assertSame(bean2.toString(), bean.getSerializableBean().toString());
+
+ factoryControl.verify();
+ }
+
+ public void testServiceBeanWithAnnotatedFieldsInjection() throws Exception {
+ ServiceBeanWithAnnotatedFields bean = new ServiceBeanWithAnnotatedFields();
+ final MyService bean1 = new MyService() {
+
+ public Object getId() {
+ return this;
+ }
+ };
+ final Serializable bean2 = new Serializable() {
+
+ public String toString() {
+ return "bean2";
+ }
+ };
+
+ BundleContext context = new MockBundleContext() {
+
+ public Object getService(org.osgi.framework.ServiceReference reference) {
+ String clazz = ((String[]) reference.getProperty(Constants.OBJECTCLASS))[0];
+ if (clazz == null)
+ return null;
+ else if (clazz.equals(MyService.class.getName())) {
+ return bean1;
+ }
+ else if (clazz.equals(Serializable.class.getName())) {
+ return bean2;
+ }
+ return null;
+ }
+
+ };
+
+ ServiceReferenceInjectionBeanPostProcessor p = new ServiceReferenceInjectionBeanPostProcessor();
+ p.setBundleContext(context);
+ p.setBeanClassLoader(getClass().getClassLoader());
+ BeanFactory factory = EasyMock.createMock(BeanFactory.class);
+ EasyMock.replay(factory);
+ p.setBeanFactory(factory);
+
+ p.postProcessBeforeInitialization(bean, "myBean");
+ assertSame(bean1.getId(), bean.getServiceBean().getId());
+ assertSame(bean2.toString(), bean.getSerializableBean().toString());
+ }
+
+ public void testServiceFactoryBeanNotInjected() throws Exception {
+ ServiceFactoryBean bean = new ServiceFactoryBean();
+ final MyService bean1 = new MyService() {
+
+ public Object getId() {
+ return this;
+ }
+ };
+ final Serializable bean2 = new Serializable() {
+
+ public String toString() {
+ return "bean2";
+ }
+ };
+
+ BundleContext context = new MockBundleContext() {
+
+ public Object getService(org.osgi.framework.ServiceReference reference) {
+ String clazz = ((String[]) reference.getProperty(Constants.OBJECTCLASS))[0];
+ if (clazz == null)
+ return null;
+ else if (clazz.equals(MyService.class.getName())) {
+ return bean1;
+ }
+ else if (clazz.equals(Serializable.class.getName())) {
+ return bean2;
+ }
+ return null;
+ }
+
+ };
+
+ ServiceReferenceInjectionBeanPostProcessor p = new ServiceReferenceInjectionBeanPostProcessor();
+ p.setBundleContext(context);
+ p.postProcessAfterInitialization(bean, "myBean");
+ assertNull(bean.getServiceBean());
+ assertNull(bean.getSerializableBean());
+ }
+
+ public void testServiceFactoryBeanInjected() throws Exception {
+ ServiceFactoryBean bean = new ServiceFactoryBean();
+ final MyService bean1 = new MyService() {
+
+ public Object getId() {
+ return this;
+ }
+ };
+ final Serializable bean2 = new Serializable() {
+
+ public String toString() {
+ return "bean2";
+ }
+ };
+
+ BundleContext context = new MockBundleContext() {
+
+ public Object getService(org.osgi.framework.ServiceReference reference) {
+ String clazz = ((String[]) reference.getProperty(Constants.OBJECTCLASS))[0];
+ if (clazz == null)
+ return null;
+ else if (clazz.equals(MyService.class.getName())) {
+ return bean1;
+ }
+ else if (clazz.equals(Serializable.class.getName())) {
+ return bean2;
+ }
+ return null;
+ }
+
+ };
+
+ ServiceReferenceInjectionBeanPostProcessor p = new ServiceReferenceInjectionBeanPostProcessor();
+ p.setBundleContext(context);
+ p.setBeanClassLoader(getClass().getClassLoader());
+ PropertyValues pvs = p.postProcessPropertyValues(new MutablePropertyValues(), new PropertyDescriptor[] {
+ new PropertyDescriptor("serviceBean", ServiceFactoryBean.class),
+ new PropertyDescriptor("serializableBean", ServiceFactoryBean.class) }, bean, "myBean");
+
+ MyService msb = (MyService) pvs.getPropertyValue("serviceBean").getValue();
+ Serializable ssb = (Serializable) pvs.getPropertyValue("serializableBean").getValue();
+
+ assertNotNull(msb);
+ assertNotNull(ssb);
+
+ assertSame(bean1.getId(), msb.getId());
+ assertSame(bean2.toString(), ssb.toString());
+ }
+
+ public void testServiceBeanInjectedValues() throws Exception {
+ ServiceBean bean = new ServiceBean();
+ final MyService bean1 = new MyService() {
+
+ public Object getId() {
+ return this;
+ }
+ };
+ final Serializable bean2 = new Serializable() {
+
+ public String toString() {
+ return "bean2";
+ }
+ };
+
+ BundleContext context = new MockBundleContext() {
+
+ public Object getService(org.osgi.framework.ServiceReference reference) {
+ String clazz = ((String[]) reference.getProperty(Constants.OBJECTCLASS))[0];
+ if (clazz == null)
+ return null;
+ else if (clazz.equals(MyService.class.getName())) {
+ return bean1;
+ }
+ else if (clazz.equals(Serializable.class.getName())) {
+ return bean2;
+ }
+ return null;
+ }
+
+ };
+
+ ServiceReferenceInjectionBeanPostProcessor p = new ServiceReferenceInjectionBeanPostProcessor();
+ p.setBundleContext(context);
+ p.setBeanClassLoader(getClass().getClassLoader());
+ PropertyValues pvs = p.postProcessPropertyValues(new MutablePropertyValues(), new PropertyDescriptor[] {
+ new PropertyDescriptor("serviceBean", ServiceBean.class),
+ new PropertyDescriptor("serializableBean", ServiceBean.class) }, bean, "myBean");
+
+ MyService msb = (MyService) pvs.getPropertyValue("serviceBean").getValue();
+ Serializable ssb = (Serializable) pvs.getPropertyValue("serializableBean").getValue();
+
+ assertNotNull(msb);
+ assertNotNull(ssb);
+
+ assertSame(bean1.getId(), msb.getId());
+ assertSame(bean2.toString(), ssb.toString());
+ }
+
+ protected Object getPrivateProperty(final Object target, final String fieldName) {
+ final Field foundField[] = new Field[1];
+
+ ReflectionUtils.doWithFields(target.getClass(), new FieldCallback() {
+
+ public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
+ field.setAccessible(true);
+ foundField[0] = field;
+ }
+
+ }, new FieldFilter() {
+
+ public boolean matches(Field field) {
+ return fieldName.equals(field.getName());
+ }
+
+ });
+
+ try {
+ return foundField[0].get(target);
+ }
+ catch (Exception ex) {
+ // translate
+ throw new RuntimeException(ex);
+ }
+ }
+}
\ No newline at end of file
diff --git a/extensions/src/test/java/org/eclipse/gemini/blueprint/extensions/annotation/ServiceBean.java b/extensions/src/test/java/org/eclipse/gemini/blueprint/extensions/annotation/ServiceBean.java
new file mode 100644
index 0000000..ec52ded
--- /dev/null
+++ b/extensions/src/test/java/org/eclipse/gemini/blueprint/extensions/annotation/ServiceBean.java
@@ -0,0 +1,46 @@
+/******************************************************************************
+ * 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.extensions.annotation;
+
+import java.io.Serializable;
+
+/**
+ * @author Andy Piper
+ * @since 2.1
+ */
+public class ServiceBean {
+
+ private MyService serviceBean;
+
+ public MyService getServiceBean() {
+ return serviceBean;
+ }
+
+ @ServiceReference
+ public void setServiceBean(MyService serviceBean) {
+ this.serviceBean = serviceBean;
+ }
+
+ public Serializable getSerializableBean() {
+ return serializableBean;
+ }
+
+ @ServiceReference
+ public void setSerializableBean(Serializable serializableBean) {
+ this.serializableBean = serializableBean;
+ }
+
+ private Serializable serializableBean;
+}
diff --git a/extensions/src/test/java/org/eclipse/gemini/blueprint/extensions/annotation/ServiceBeanWithAnnotatedFields.java b/extensions/src/test/java/org/eclipse/gemini/blueprint/extensions/annotation/ServiceBeanWithAnnotatedFields.java
new file mode 100644
index 0000000..572d47e
--- /dev/null
+++ b/extensions/src/test/java/org/eclipse/gemini/blueprint/extensions/annotation/ServiceBeanWithAnnotatedFields.java
@@ -0,0 +1,36 @@
+/******************************************************************************
+ * 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.extensions.annotation;
+
+import java.io.Serializable;
+
+/**
+ * @author Andy Piper
+ * @since 2.1
+ */
+public class ServiceBeanWithAnnotatedFields {
+ @ServiceReference
+ private MyService serviceBean;
+ @ServiceReference
+ private Serializable serializableBean;
+
+ public MyService getServiceBean() {
+ return serviceBean;
+ }
+
+ public Serializable getSerializableBean() {
+ return serializableBean;
+ }
+}
diff --git a/extensions/src/test/java/org/eclipse/gemini/blueprint/extensions/annotation/ServiceFactoryBean.java b/extensions/src/test/java/org/eclipse/gemini/blueprint/extensions/annotation/ServiceFactoryBean.java
new file mode 100644
index 0000000..0a4e0d2
--- /dev/null
+++ b/extensions/src/test/java/org/eclipse/gemini/blueprint/extensions/annotation/ServiceFactoryBean.java
@@ -0,0 +1,35 @@
+/******************************************************************************
+ * 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.extensions.annotation;
+
+import org.springframework.beans.factory.FactoryBean;
+
+/**
+ * @author Andy Piper
+ * @since 2.1
+ */
+public class ServiceFactoryBean extends ServiceBean implements FactoryBean {
+ public Object getObject() throws Exception {
+ return new ServiceBean();
+ }
+
+ public Class<?> getObjectType() {
+ return ServiceBean.class;
+ }
+
+ public boolean isSingleton() {
+ return true;
+ }
+}
diff --git a/extensions/src/test/resources/logback-test.xml b/extensions/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..3b9df81
--- /dev/null
+++ b/extensions/src/test/resources/logback-test.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+ <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%p [%c] - %m%n</pattern>
+ </encoder>
+ </appender>
+ <logger name="org.springframework.osgi" level="TRACE"/>
+ <root level="WARN">
+ <appender-ref ref="stdout"/>
+ </root>
+</configuration>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 80872cd..5cda637 100644
--- a/pom.xml
+++ b/pom.xml
@@ -174,6 +174,7 @@
<module>io</module>
<module>core</module>
<module>extender</module>
+ <module>extensions</module>
<module>test-support</module>
</modules>
@@ -687,7 +688,6 @@
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
- <version>2.1.5</version>
<executions>
<execution>
<goals>
@@ -889,6 +889,7 @@
<modules>
<module>core</module>
<module>extender</module>
+ <module>extensions</module>
<module>integration-tests</module>
<module>io</module>
<module>mock</module>
diff --git a/src/assembly/no-dependencies.xml b/src/assembly/no-dependencies.xml
index d584c87..8e245f3 100644
--- a/src/assembly/no-dependencies.xml
+++ b/src/assembly/no-dependencies.xml
@@ -17,7 +17,8 @@
<include>org.eclipse.gemini.blueprint:gemini-blueprint-io</include>
<include>org.eclipse.gemini.blueprint:gemini-blueprint-mock</include>
<include>org.eclipse.gemini.blueprint:gemini-blueprint-core</include>
- <include>org.eclipse.gemini.blueprint:gemini-blueprint-extender</include>
+ <include>org.eclipse.gemini.blueprint:gemini-blueprint-extender</include>
+ <include>org.eclipse.gemini.blueprint:gemini-blueprint-extensions</include>
<include>org.eclipse.gemini.blueprint:gemini-blueprint-test</include>
</includes>
<!-- the .jar and -sources.jar will be included here -->