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 -->