Merge pull request #32 from rotty3000/master

README details about custom Aries CDI features
diff --git a/README.md b/README.md
index 5e66497..15552c9 100644
--- a/README.md
+++ b/README.md
@@ -192,4 +192,67 @@
 
   - The behaviour of this container should be to start the `@ApplicationScoped` context immediately. This allows for services from the container component to be published right away.
 
-Check out the many questions and answers in the [FAQ](faq.md).
\ No newline at end of file
+Check out the many questions and answers in the [FAQ](faq.md).
+
+## Aries CDI Extension SPI
+
+Aries CDI enables a number of custom features for [OSGi CDI Portable Extensions](https://osgi.org/specification/osgi.enterprise/7.0.0/service.cdi.html#service.cdi-portable.extensions) (further referred to as Extensions).
+
+#### Implicit Extensions
+
+**Implicit Extensions** declare the custom service property `aries.cdi.extension.mode` whose value is `implicit`. When deployed into a framework, such extensions will bind to all CDI Bundles automatically.
+
+#### Transitive Extensions
+
+Extensions that define requirements on other extensions (using the same `osgi.cdi.extension` requirement a CDI Bundle would), will cause Aries CDI to bind those to the CDI Bundle automatically in a transitive fashion.
+
+#### Annotated Types provided by Extensions
+
+There are two ways an extension can load annotated types:
+
+1. it can use the CDI SPI (this is the standard way.)
+For example:
+
+   ```java
+   public void addBeans(@Observes BeforeBeanDiscovery bbd, BeanManager bm) {
+      bbd.addAnnotatedType(bm.createAnnotatedType(Foo.class));
+   }
+   ```
+
+2. it can use the custom attribute `aries.cdi.extension.bean.classes` on the `osgi.cdi.extension`  capability provided by the extension.
+   For example:
+
+   ```properties
+   Provide-Capability: \
+      osgi.cdi.extension;\
+         osgi.cdi.extension='foo';\
+         version:Version='1.3.0';\
+         aries.cdi.extension.bean.classes:List<String>='org.acme.Foo'
+   ```
+
+#### Adapting Annotated Types as Services
+
+A common scenario with OSGi CDI Portable Extensions is for extensions to adapt annotated types originating in the CDI Bundle as OSGi services (or with more service types).
+
+Aries CDI's Extension SPI provides a convenience mechanism in the form of a carrier annotation `@AdaptedService` and a helper utility `Adapted.withServiceTypes(AnnotatedTypeConfigurator<X>, Class<?>...)`.
+
+The following will make the annotated type produce a service of type `javax.servlet.Servlet`:
+
+```java
+if (!Adapted.withServiceTypes(configurator, javax.servlet.Servlet.class)) {
+   // was not adapted because it was already marked with @Service
+   return;
+}
+```
+
+
+
+The dependency for the Extension SPI is:
+
+```xml
+<dependency>
+   <groupId>org.apache.aries.cdi</groupId>
+   <artifactId>org.apache.aries.cdi.extension.spi</artifactId>
+   <version>...</version>
+</dependency>
+```
diff --git a/cdi-bom/pom.xml b/cdi-bom/pom.xml
index f86b2d6..5045ba3 100644
--- a/cdi-bom/pom.xml
+++ b/cdi-bom/pom.xml
@@ -59,21 +59,15 @@
 			</dependency>
 			<dependency>
 				<groupId>org.apache.aries.cdi</groupId>
-				<artifactId>org.apache.aries.cdi.extension.jndi</artifactId>
+				<artifactId>org.apache.aries.cdi.extension.jaxrs</artifactId>
 				<version>${project.version}</version>
 				<scope>runtime</scope>
 			</dependency>
 			<dependency>
 				<groupId>org.apache.aries.cdi</groupId>
-				<artifactId>org.apache.aries.cdi.extension.mp-config</artifactId>
+				<artifactId>org.apache.aries.cdi.extension.jndi</artifactId>
 				<version>${project.version}</version>
-				<scope>compile</scope>
-			</dependency>
-			<dependency>
-				<groupId>org.apache.aries.cdi</groupId>
-				<artifactId>org.apache.aries.cdi.extension.mp-metrics</artifactId>
-				<version>${project.version}</version>
-				<scope>compile</scope>
+				<scope>runtime</scope>
 			</dependency>
 			<dependency>
 				<groupId>org.apache.aries.cdi</groupId>
@@ -95,6 +89,12 @@
 			</dependency>
 			<dependency>
 				<groupId>org.apache.aries.cdi</groupId>
+				<artifactId>org.apache.aries.cdi.extension.spi</artifactId>
+				<version>${project.version}</version>
+				<scope>compile</scope>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.aries.cdi</groupId>
 				<artifactId>org.apache.aries.cdi.extra</artifactId>
 				<version>${project.version}</version>
 				<scope>compile</scope>
@@ -120,7 +120,7 @@
 			<dependency>
 				<groupId>org.apache.aries.jax.rs</groupId>
 				<artifactId>org.apache.aries.jax.rs.whiteboard</artifactId>
-				<version>1.0.6</version>
+				<version>${jax.rs.whiteboard.version}</version>
 				<scope>runtime</scope>
 			</dependency>
 			<dependency>
@@ -167,4 +167,53 @@
 			</dependency>
 		</dependencies>
 	</dependencyManagement>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>biz.aQute.bnd</groupId>
+				<artifactId>bnd-baseline-maven-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>baseline</id>
+						<phase>none</phase>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+
+	<profiles>
+		<profile>
+			<id>experimental</id>
+			<dependencyManagement>
+				<dependencies>
+					<dependency>
+						<groupId>org.apache.aries.cdi</groupId>
+						<artifactId>org.apache.aries.cdi.extension.mp-config</artifactId>
+						<version>${project.version}</version>
+						<scope>compile</scope>
+					</dependency>
+					<dependency>
+						<groupId>org.apache.aries.cdi</groupId>
+						<artifactId>org.apache.aries.cdi.extension.mp-jwt-auth</artifactId>
+						<version>${project.version}</version>
+						<scope>compile</scope>
+					</dependency>
+					<dependency>
+						<groupId>org.apache.aries.cdi</groupId>
+						<artifactId>org.apache.aries.cdi.extension.mp-metrics</artifactId>
+						<version>${project.version}</version>
+						<scope>compile</scope>
+					</dependency>
+					<dependency>
+						<groupId>org.apache.johnzon</groupId>
+						<artifactId>johnzon-osgi</artifactId>
+						<version>${johnzon.version}</version>
+						<scope>runtime</scope>
+					</dependency>
+				</dependencies>
+			</dependencyManagement>
+		</profile>
+	</profiles>
 </project>
diff --git a/cdi-build-tools/pom.xml b/cdi-build-tools/pom.xml
index d37a461..a24e3b5 100644
--- a/cdi-build-tools/pom.xml
+++ b/cdi-build-tools/pom.xml
@@ -30,11 +30,6 @@
 	<name>Apache Aries CDI - Tools used during build</name>
 	<description>Apache Aries CDI - Tools used during build</description>
 
-	<properties>
-		<gpg.skip>true</gpg.skip>
-		<maven.deploy.skip>true</maven.deploy.skip>
-	</properties>
-
 	<dependencies>
 		<dependency>
 			<groupId>org.osgi</groupId>
@@ -58,6 +53,34 @@
 				<groupId>biz.aQute.bnd</groupId>
 				<artifactId>bnd-maven-plugin</artifactId>
 			</plugin>
+			<plugin>
+				<groupId>biz.aQute.bnd</groupId>
+				<artifactId>bnd-baseline-maven-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>baseline</id>
+						<phase>none</phase>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<artifactId>maven-deploy-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>default-deploy</id>
+						<phase>none</phase>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<artifactId>maven-gpg-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>default-deploy</id>
+						<phase>none</phase>
+					</execution>
+				</executions>
+			</plugin>
 		</plugins>
 	</build>
 </project>
\ No newline at end of file
diff --git a/cdi-build-tools/src/main/java/org/apache/aries/cdi/build/tools/AddExtensionRequirement.java b/cdi-build-tools/src/main/java/org/apache/aries/cdi/build/tools/AddExtensionRequirement.java
index 988f35e..3a71db5 100644
--- a/cdi-build-tools/src/main/java/org/apache/aries/cdi/build/tools/AddExtensionRequirement.java
+++ b/cdi-build-tools/src/main/java/org/apache/aries/cdi/build/tools/AddExtensionRequirement.java
@@ -14,14 +14,20 @@
 
 package org.apache.aries.cdi.build.tools;
 
+import static java.lang.String.format;
+import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
+
 import java.io.IOException;
+import java.util.Arrays;
 
 import org.osgi.annotation.bundle.Requirement;
+import org.osgi.annotation.bundle.Requirements;
 import org.osgi.service.cdi.CDIConstants;
 
 import net.bytebuddy.build.BuildLogger;
 import net.bytebuddy.build.Plugin;
 import net.bytebuddy.description.annotation.AnnotationDescription;
+import net.bytebuddy.description.method.MethodDescription;
 import net.bytebuddy.description.type.TypeDescription;
 import net.bytebuddy.dynamic.ClassFileLocator;
 import net.bytebuddy.dynamic.DynamicType.Builder;
@@ -51,10 +57,12 @@
 public class AddExtensionRequirement implements Plugin {
 
 	private final BuildLogger buildLogger;
-	private final String extension;
-	private final String version;
 	private final String name;
 	private final Match match;
+	private final AnnotationDescription annotationDescription;
+
+	private final TypeDescription requirementsDescription = TypeDescription.ForLoadedType.of(Requirements.class);
+	private final MethodDescription.InDefinedShape requirementsValue = requirementsDescription.getDeclaredMethods().getOnly();
 
 	private enum Match {
 		CLASS, PACKAGE, PREFIX
@@ -64,8 +72,12 @@
 		BuildLogger buildLogger, String extension, String version, String glob) {
 
 		this.buildLogger = buildLogger;
-		this.extension = extension;
-		this.version = version;
+
+		this.annotationDescription = AnnotationDescription.Builder.ofType(Requirement.class)
+			.define("namespace", CDIConstants.CDI_EXTENSION_PROPERTY)
+			.define("name", extension)
+			.define("version", version)
+			.build();
 
 		String name = glob;
 
@@ -86,9 +98,21 @@
 	}
 
 	@Override
-	public boolean matches(TypeDescription target) {
-		String className = target.getName();
-		String packageName = target.getPackage().getName();
+	public boolean matches(TypeDescription typeDescription) {
+		if (typeDescription.isPackageType()) {
+			return false;
+		}
+
+		if (isAnnotatedWith(Requirements.class).matches(typeDescription)) {
+			AnnotationDescription[] annotationDescriptions = typeDescription.getDeclaredAnnotations().ofType(Requirements.class).getValue(requirementsValue).resolve(AnnotationDescription[].class);
+
+			if (Arrays.asList(annotationDescriptions).contains(annotationDescription)) {
+				return false;
+			}
+		}
+
+		String className = typeDescription.getName();
+		String packageName = typeDescription.getPackage().getName();
 
 		boolean matches = false;
 		switch (match) {
@@ -110,13 +134,27 @@
 
 	@Override
 	public Builder<?> apply(Builder<?> builder, TypeDescription typeDescription, ClassFileLocator cfl) {
-		buildLogger.info("Processing class: " + typeDescription.getActualName());
+		buildLogger.info(format("Adding requirement %s on type %s", annotationDescription, typeDescription.getActualName()));
+
+		AnnotationDescription[] annotationDescriptions = new AnnotationDescription[1];
+
+		// This isn't quite working like I'd expect because the builder cannot see previous transformations.
+		// We need some kind of merging operation to occur.
+		if (isAnnotatedWith(requirementsDescription).matches(typeDescription)) {
+			annotationDescriptions = typeDescription.getDeclaredAnnotations().ofType(Requirements.class).getValue(requirementsValue).resolve(AnnotationDescription[].class);
+
+			buildLogger.info(format("Requirements were found on %s, %s", typeDescription.getActualName(), annotationDescriptions));
+
+			annotationDescriptions = Arrays.copyOf(annotationDescriptions, annotationDescriptions.length + 1);
+
+			builder = builder.visit(new RequirementsAnnotationRemover());
+		}
+
+		annotationDescriptions[annotationDescriptions.length - 1] = annotationDescription;
 
 		return builder.annotateType(
-			AnnotationDescription.Builder.ofType(Requirement.class)
-				.define("namespace", CDIConstants.CDI_EXTENSION_PROPERTY)
-				.define("name", extension)
-				.define("version", version)
+			AnnotationDescription.Builder.ofType(Requirements.class)
+				.defineAnnotationArray("value", annotationDescription.getAnnotationType(), annotationDescriptions)
 				.build());
 	}
 
diff --git a/cdi-build-tools/src/main/java/org/apache/aries/cdi/build/tools/RequirementsAnnotationRemover.java b/cdi-build-tools/src/main/java/org/apache/aries/cdi/build/tools/RequirementsAnnotationRemover.java
new file mode 100644
index 0000000..bc5305c
--- /dev/null
+++ b/cdi-build-tools/src/main/java/org/apache/aries/cdi/build/tools/RequirementsAnnotationRemover.java
@@ -0,0 +1,63 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.build.tools;
+
+import static net.bytebuddy.jar.asm.Opcodes.ASM4;
+
+import org.osgi.annotation.bundle.Requirements;
+
+import net.bytebuddy.asm.AsmVisitorWrapper;
+import net.bytebuddy.description.field.FieldDescription;
+import net.bytebuddy.description.field.FieldList;
+import net.bytebuddy.description.method.MethodList;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.implementation.Implementation;
+import net.bytebuddy.jar.asm.AnnotationVisitor;
+import net.bytebuddy.jar.asm.ClassVisitor;
+import net.bytebuddy.jar.asm.Type;
+import net.bytebuddy.pool.TypePool;
+
+public class RequirementsAnnotationRemover implements AsmVisitorWrapper {
+	private final String requirementsDescriptor = Type.getDescriptor(Requirements.class);
+
+	@Override
+	public int mergeWriter(int flags) {
+		return 0;
+	}
+
+	@Override
+	public int mergeReader(int flags) {
+		return 0;
+	}
+
+	@Override
+	public ClassVisitor wrap(
+		TypeDescription instrumentedType, ClassVisitor classVisitor, Implementation.Context implementationContext,
+		TypePool typePool, FieldList<FieldDescription.InDefinedShape> fields, MethodList<?> methods,
+		int writerFlags, int readerFlags) {
+
+		return new ClassVisitor(ASM4, classVisitor) {
+			@Override
+			public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+				if (descriptor.equals(requirementsDescriptor)) {
+					return null;
+				}
+
+				return super.visitAnnotation(descriptor, visible);
+			}
+		};
+	}
+
+}
\ No newline at end of file
diff --git a/cdi-executable/base.bndrun b/cdi-executable/base.bndrun
index 0e23676..6216eda 100644
--- a/cdi-executable/base.bndrun
+++ b/cdi-executable/base.bndrun
@@ -36,4 +36,8 @@
 	org.slf4j.event;version=1.7.28,\
 	org.slf4j.helpers;version=1.7.28,\
 	org.slf4j.spi;version=1.7.28,\
+	sun.invoke,\
 	sun.misc
+
+-runblacklist.base: \
+	osgi.identity;filter:='(osgi.identity=biz.aQute.bndlib)'
diff --git a/cdi-executable/owb-executable.bndrun b/cdi-executable/owb-executable.bndrun
index 4557b7c..b2af497 100644
--- a/cdi-executable/owb-executable.bndrun
+++ b/cdi-executable/owb-executable.bndrun
@@ -24,6 +24,7 @@
 	openwebbeans-impl;version='[2.0.13,2.0.14)',\
 	openwebbeans-spi;version='[2.0.13,2.0.14)',\
 	org.apache.aries.cdi.extender;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.spi;version='[1.1.0,1.1.1)',\
 	org.apache.aries.cdi.owb;version='[1.1.0,1.1.1)',\
 	org.apache.aries.cdi.spi;version='[1.1.0,1.1.1)',\
 	org.apache.aries.spifly.dynamic.framework.extension;version='[1.2.3,1.2.4)',\
diff --git a/cdi-executable/pom.xml b/cdi-executable/pom.xml
index 57e17d0..31b834c 100644
--- a/cdi-executable/pom.xml
+++ b/cdi-executable/pom.xml
@@ -47,12 +47,6 @@
 		<url>https://github.com/apache/aries-cdi</url>
 	</scm>
 
-	<properties>
-		<gpg.skip>true</gpg.skip>
-		<maven.deploy.skip>true</maven.deploy.skip>
-		<maven.install.skip>true</maven.install.skip>
-	</properties>
-
 	<dependencyManagement>
 		<dependencies>
 			<dependency>
@@ -156,6 +150,43 @@
 					</execution>
 				</executions>
 			</plugin>
+			<plugin>
+				<groupId>biz.aQute.bnd</groupId>
+				<artifactId>bnd-baseline-maven-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>baseline</id>
+						<phase>none</phase>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<artifactId>maven-install-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>default-install</id>
+						<phase>none</phase>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<artifactId>maven-deploy-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>default-deploy</id>
+						<phase>none</phase>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<artifactId>maven-gpg-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>default-deploy</id>
+						<phase>none</phase>
+					</execution>
+				</executions>
+			</plugin>
 		</plugins>
 	</build>
 
diff --git a/cdi-executable/weld-executable.bndrun b/cdi-executable/weld-executable.bndrun
index 4103d24..dd58d9f 100644
--- a/cdi-executable/weld-executable.bndrun
+++ b/cdi-executable/weld-executable.bndrun
@@ -27,6 +27,7 @@
 	javax.transaction-api;version='[1.2.0,1.2.1)',\
 	jboss-classfilewriter;version='[1.2.3,1.2.4)',\
 	org.apache.aries.cdi.extender;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.spi;version='[1.1.0,1.1.1)',\
 	org.apache.aries.cdi.spi;version='[1.1.0,1.1.1)',\
 	org.apache.aries.cdi.weld;version='[1.1.0,1.1.1)',\
 	org.apache.aries.spifly.dynamic.framework.extension;version='[1.2.3,1.2.4)',\
diff --git a/cdi-extender/pom.xml b/cdi-extender/pom.xml
index 0821749..9e47dbf 100644
--- a/cdi-extender/pom.xml
+++ b/cdi-extender/pom.xml
@@ -58,12 +58,44 @@
 					]]></bnd>
 				</configuration>
 			</plugin>
+			<plugin>
+				<artifactId>maven-resources-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>default-testResources</id>
+						<phase>process-test-resources</phase>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>default-testCompile</id>
+						<phase>test-compile</phase>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<artifactId>maven-surefire-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>default-test</id>
+						<phase>test</phase>
+					</execution>
+				</executions>
+			</plugin>
 		</plugins>
 	</build>
 
 	<dependencies>
 		<dependency>
 			<groupId>org.apache.aries.cdi</groupId>
+			<artifactId>org.apache.aries.cdi.extension.spi</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.aries.cdi</groupId>
 			<artifactId>org.apache.aries.cdi.spi</artifactId>
 			<version>${project.version}</version>
 		</dependency>
diff --git a/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/container/BundleContextExtension.java b/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/container/BundleContextExtension.java
index 23cd786..f412d48 100644
--- a/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/container/BundleContextExtension.java
+++ b/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/container/BundleContextExtension.java
@@ -14,9 +14,13 @@
 
 package org.apache.aries.cdi.container.internal.container;
 
+import javax.annotation.Priority;
 import javax.enterprise.event.Observes;
 import javax.enterprise.inject.spi.AfterBeanDiscovery;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.enterprise.inject.spi.BeforeBeanDiscovery;
 import javax.enterprise.inject.spi.Extension;
+import javax.interceptor.Interceptor;
 
 import org.osgi.framework.BundleContext;
 
@@ -26,6 +30,13 @@
 		_bundleContext = bundleContext;
 	}
 
+	void init(
+		@Priority(Interceptor.Priority.PLATFORM_BEFORE)
+		@Observes BeforeBeanDiscovery bbd, BeanManager beanManager) {
+
+		beanManager.fireEvent(_bundleContext);
+	}
+
 	void afterBeanDiscovery(@Observes AfterBeanDiscovery abd) {
 		abd.addBean()
 			.addType(BundleContext.class)
diff --git a/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/container/ContainerBootstrap.java b/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/container/ContainerBootstrap.java
index 02afa09..e3ab0ed 100644
--- a/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/container/ContainerBootstrap.java
+++ b/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/container/ContainerBootstrap.java
@@ -14,9 +14,16 @@
 
 package org.apache.aries.cdi.container.internal.container;
 
+import static aQute.lib.exceptions.FunctionWithException.asFunction;
 import static java.util.Objects.requireNonNull;
+import static java.util.Optional.ofNullable;
+import static org.osgi.service.cdi.CDIConstants.CDI_EXTENSION_PROPERTY;
 
 import java.net.URL;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Map;
 import java.util.ServiceLoader;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
@@ -40,6 +47,9 @@
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceObjects;
 import org.osgi.framework.ServiceReference;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.resource.Capability;
 import org.osgi.service.cdi.runtime.dto.ExtensionDTO;
 import org.osgi.service.log.Logger;
 import org.osgi.util.tracker.ServiceTracker;
@@ -188,18 +198,41 @@
 		for (ExtensionDTO extensionDTO : containerState.containerDTO().extensions) {
 			ExtendedExtensionDTO extendedExtensionDTO = (ExtendedExtensionDTO)extensionDTO;
 
+			Dictionary<String,Object> properties = extendedExtensionDTO.extension.getServiceReference().getProperties();
+
 			initializer.addExtension(
-				extendedExtensionDTO.extension.getService(),
-				Maps.of(extendedExtensionDTO.extension.getServiceReference().getProperties()));
+				extendedExtensionDTO.extension.getService(), Maps.of(properties));
 
 			Bundle extensionBundle = extendedExtensionDTO.extension.getServiceReference().getBundle();
 
+			getClassesFromExtensionCapability(properties, extensionBundle, initializer);
+
 			if (!loader.getBundles().contains(extensionBundle)) {
 				loader.getBundles().add(extensionBundle);
 			}
 		}
 	}
 
+	private void getClassesFromExtensionCapability(Dictionary<String,Object> properties, Bundle extensionBundle, CDIContainerInitializer initializer) {
+		List<BundleCapability> capabilities = extensionBundle.adapt(BundleWiring.class).getCapabilities(CDI_EXTENSION_PROPERTY);
+
+		if (capabilities.isEmpty()) {
+			return;
+		}
+
+		Map<String, Object> attributes = capabilities.stream().map(Capability::getAttributes).filter(
+			map -> map.get(CDI_EXTENSION_PROPERTY).equals(properties.get(CDI_EXTENSION_PROPERTY))
+		).findFirst().orElseGet(Collections::emptyMap);
+
+		ofNullable(
+			attributes.get("aries.cdi.extension.bean.classes")
+		).map(List.class::cast).map(List<String>::stream).orElseGet(Stream::empty).map(
+			asFunction(extensionBundle::loadClass)
+		).forEach(
+			initializer::addBeanClasses
+		);
+	}
+
 	private void withListeners(final Consumer<ContainerListener> action) {
 		final ServiceReference<ContainerListener>[] refs = _listeners.getServiceReferences();
 		if (refs != null && refs.length > 0) {
diff --git a/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/container/ContainerState.java b/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/container/ContainerState.java
index 538e0cf..3097b16 100644
--- a/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/container/ContainerState.java
+++ b/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/container/ContainerState.java
@@ -14,14 +14,24 @@
 
 package org.apache.aries.cdi.container.internal.container;
 
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.unmodifiableMap;
+import static java.util.Optional.empty;
+import static java.util.Optional.ofNullable;
 import static org.apache.aries.cdi.container.internal.util.Filters.asFilter;
+import static org.apache.aries.cdi.container.internal.util.Throw.asString;
+import static org.apache.aries.cdi.container.internal.util.Throw.exception;
+import static org.osgi.framework.Constants.SERVICE_BUNDLEID;
 import static org.osgi.namespace.extender.ExtenderNamespace.EXTENDER_NAMESPACE;
+import static org.osgi.resource.Namespace.REQUIREMENT_FILTER_DIRECTIVE;
 import static org.osgi.service.cdi.CDIConstants.CDI_CAPABILITY_NAME;
 import static org.osgi.service.cdi.CDIConstants.CDI_CONTAINER_ID;
 import static org.osgi.service.cdi.CDIConstants.CDI_EXTENSION_PROPERTY;
+import static org.osgi.service.cdi.ComponentType.CONTAINER;
+import static org.osgi.service.cdi.ConfigurationPolicy.OPTIONAL;
+import static org.osgi.service.cdi.MaximumCardinality.ONE;
 
 import java.lang.reflect.InvocationTargetException;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -44,16 +54,11 @@
 import org.apache.aries.cdi.container.internal.util.Throw;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
 import org.osgi.framework.dto.BundleDTO;
 import org.osgi.framework.wiring.BundleCapability;
 import org.osgi.framework.wiring.BundleRequirement;
 import org.osgi.framework.wiring.BundleWire;
 import org.osgi.framework.wiring.BundleWiring;
-import org.osgi.resource.Namespace;
-import org.osgi.service.cdi.ComponentType;
-import org.osgi.service.cdi.ConfigurationPolicy;
-import org.osgi.service.cdi.MaximumCardinality;
 import org.osgi.service.cdi.runtime.dto.ContainerDTO;
 import org.osgi.service.cdi.runtime.dto.template.ComponentTemplateDTO;
 import org.osgi.service.cdi.runtime.dto.template.ContainerTemplateDTO;
@@ -90,7 +95,7 @@
 
 		BundleWiring bundleWiring = _bundle.adapt(BundleWiring.class);
 
-		Map<String, Object> cdiAttributes = Collections.emptyMap();
+		Map<String, Object> cdiAttributes = emptyMap();
 
 		for (BundleWire wire : bundleWiring.getRequiredWires(EXTENDER_NAMESPACE)) {
 			BundleCapability capability = wire.getCapability();
@@ -104,7 +109,7 @@
 			}
 		}
 
-		_cdiAttributes = Collections.unmodifiableMap(cdiAttributes);
+		_cdiAttributes = unmodifiableMap(cdiAttributes);
 
 		Set<String> extensionRequirements = new HashSet<>();
 
@@ -119,7 +124,7 @@
 		_containerDTO.template = new ContainerTemplateDTO();
 		_containerDTO.template.components = new CopyOnWriteArrayList<>();
 		_containerDTO.template.extensions = new CopyOnWriteArrayList<>();
-		_containerDTO.template.id = Optional.ofNullable(
+		_containerDTO.template.id = ofNullable(
 			(String)_cdiAttributes.get(CDI_CONTAINER_ID)
 		).orElse(
 			_bundle.getSymbolicName()
@@ -136,7 +141,7 @@
 					_containerDTO.template.extensions.add(extensionTemplateDTO);
 				}
 				catch (Exception e) {
-					_containerDTO.errors.add(Throw.asString(e));
+					_containerDTO.errors.add(asString(e));
 				}
 			}
 		);
@@ -146,20 +151,20 @@
 		_containerComponentTemplateDTO.beans = new CopyOnWriteArrayList<>();
 		_containerComponentTemplateDTO.configurations = new CopyOnWriteArrayList<>();
 		_containerComponentTemplateDTO.name = _containerDTO.template.id;
-		_containerComponentTemplateDTO.properties = Collections.emptyMap();
+		_containerComponentTemplateDTO.properties = emptyMap();
 		_containerComponentTemplateDTO.references = new CopyOnWriteArrayList<>();
-		_containerComponentTemplateDTO.type = ComponentType.CONTAINER;
+		_containerComponentTemplateDTO.type = CONTAINER;
 
 		ExtendedConfigurationTemplateDTO configurationTemplate = new ExtendedConfigurationTemplateDTO();
-		configurationTemplate.maximumCardinality = MaximumCardinality.ONE;
-		configurationTemplate.pid = Optional.ofNullable(
+		configurationTemplate.maximumCardinality = ONE;
+		configurationTemplate.pid = ofNullable(
 			(String)_cdiAttributes.get(CDI_CONTAINER_ID)
 		).map(
 			s -> s.replaceAll("-", ".")
 		).orElse(
 			"osgi.cdi." + _bundle.getSymbolicName().replaceAll("-", ".")
 		);
-		configurationTemplate.policy = ConfigurationPolicy.OPTIONAL;
+		configurationTemplate.policy = OPTIONAL;
 
 		_containerComponentTemplateDTO.configurations.add(configurationTemplate);
 
@@ -175,7 +180,7 @@
 		catch (Exception e) {
 			_log.error(l -> l.error("CCR Discovery resulted in errors on {}", bundle, e));
 
-			_containerDTO.errors.add(Throw.asString(e));
+			_containerDTO.errors.add(asString(e));
 		}
 
 		_beanManagerDeferred = _promiseFactory.deferred();
@@ -191,7 +196,7 @@
 		try {
 			return _beanManagerDeferred.getPromise().timeout(5000).getValue();
 		} catch (InvocationTargetException | InterruptedException e) {
-			return Throw.exception(e);
+			return exception(e);
 		}
 	}
 
@@ -280,15 +285,15 @@
 			if (cm == null) {
 				_log.error(l -> l.error("CCR unexpected error fetching configuration admin for {}", pid));
 
-				return Optional.empty();
+				return empty();
 			}
 
-			return Optional.ofNullable(cm.listConfigurations(query));
+			return ofNullable(cm.listConfigurations(query));
 		}
 		catch (Exception e) {
 			_log.warn(l -> l.warn("CCR unexpected error fetching configuration for {}", pid, e));
 
-			return Optional.empty();
+			return empty();
 		}
 	}
 
@@ -353,7 +358,7 @@
 	private void collectExtensionRequirements(BundleWiring bundleWiring, Set<String> extensionRequirements) {
 		for (BundleWire wire : bundleWiring.getRequiredWires(CDI_EXTENSION_PROPERTY)) {
 			String filter = wire.getRequirement().getDirectives().get(
-				Namespace.REQUIREMENT_FILTER_DIRECTIVE);
+				REQUIREMENT_FILTER_DIRECTIVE);
 			Bundle extensionProvider = wire.getProvider().getBundle();
 
 			StringBuilder sb = new StringBuilder();
@@ -361,7 +366,7 @@
 			sb.append("(&");
 			sb.append(filter);
 			sb.append("(");
-			sb.append(Constants.SERVICE_BUNDLEID);
+			sb.append(SERVICE_BUNDLEID);
 			sb.append("=");
 			sb.append(extensionProvider.getBundleId());
 			sb.append("))");
diff --git a/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/util/Annotates.java b/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/util/Annotates.java
index 9239d4a..584076b 100644
--- a/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/util/Annotates.java
+++ b/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/util/Annotates.java
@@ -14,6 +14,8 @@
 
 package org.apache.aries.cdi.container.internal.util;
 
+import static java.util.Arrays.asList;
+import static java.util.Optional.ofNullable;
 import static org.apache.aries.cdi.container.internal.util.Reflection.getRawType;
 
 import java.lang.annotation.Annotation;
@@ -26,10 +28,10 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.Set;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import javax.decorator.Decorator;
 import javax.enterprise.context.ApplicationScoped;
@@ -56,6 +58,7 @@
 import javax.inject.Scope;
 import javax.interceptor.Interceptor;
 
+import org.apache.aries.cdi.extension.spi.annotation.AdaptedService;
 import org.osgi.service.cdi.ServiceScope;
 import org.osgi.service.cdi.annotations.Service;
 import org.osgi.service.cdi.annotations.ServiceInstance;
@@ -185,11 +188,11 @@
 
 		if (annotated instanceof AnnotatedType) {
 			Class<?> annotatedClass = ((AnnotatedType<?>)annotated).getJavaClass();
-			Optional.ofNullable(annotatedClass.getAnnotatedSuperclass()).ifPresent(at -> ats.add(at));
-			ats.addAll(Arrays.asList(annotatedClass.getAnnotatedInterfaces()));
+			ofNullable(annotatedClass.getAnnotatedSuperclass()).ifPresent(at -> ats.add(at));
+			ats.addAll(asList(annotatedClass.getAnnotatedInterfaces()));
 
 			for (java.lang.reflect.AnnotatedType at : ats) {
-				Optional.ofNullable(at.getAnnotation(Service.class)).ifPresent(
+				ofNullable(at.getAnnotation(Service.class)).ifPresent(
 					service -> {
 						if (service.value().length > 0) {
 							throw new IllegalArgumentException(
@@ -212,6 +215,10 @@
 				);
 			}
 
+			ofNullable(
+				annotated.getAnnotation(AdaptedService.class)
+			).map(AdaptedService::value).map(Stream::of).orElseGet(Stream::empty).forEach(serviceTypes::add);
+
 			Service service = annotated.getAnnotation(Service.class);
 
 			if (service == null) {
diff --git a/cdi-extension-jaxrs/LICENSE b/cdi-extension-jaxrs/LICENSE
new file mode 100644
index 0000000..6b0b127
--- /dev/null
+++ b/cdi-extension-jaxrs/LICENSE
@@ -0,0 +1,203 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
diff --git a/cdi-extension-jaxrs/NOTICE b/cdi-extension-jaxrs/NOTICE
new file mode 100644
index 0000000..424644d
--- /dev/null
+++ b/cdi-extension-jaxrs/NOTICE
@@ -0,0 +1,8 @@
+
+Apache Aries
+Copyright 2009-2011 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+
diff --git a/cdi-extension-jaxrs/pom.xml b/cdi-extension-jaxrs/pom.xml
new file mode 100644
index 0000000..772d78a
--- /dev/null
+++ b/cdi-extension-jaxrs/pom.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<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>
+		<groupId>org.apache.aries.cdi</groupId>
+		<artifactId>org.apache.aries.cdi</artifactId>
+		<version>1.1.0-SNAPSHOT</version>
+		<relativePath>..</relativePath>
+	</parent>
+
+	<artifactId>org.apache.aries.cdi.extension.jaxrs</artifactId>
+	<name>Apache Aries CDI - JAX-RS Extension</name>
+	<description>
+		Provides support to CDI bundles for JAX-RS components.
+	</description>
+
+	<licenses>
+		<license>
+			<name>ASL 2.0</name>
+			<url>https://www.apache.org/licenses/LICENSE-2.0</url>
+		</license>
+	</licenses>
+
+	<scm>
+		<connection>scm:git:git@github.com:apache/aries-cdi.git</connection>
+		<developerConnection>scm:git:git@github.com:apache/aries-cdi.git</developerConnection>
+		<tag>HEAD</tag>
+		<url>https://github.com/apache/aries-cdi</url>
+	</scm>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>biz.aQute.bnd</groupId>
+				<artifactId>bnd-maven-plugin</artifactId>
+			</plugin>
+		</plugins>
+	</build>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.apache.aries.cdi</groupId>
+			<artifactId>org.apache.aries.cdi.extension.spi</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.aries.cdi</groupId>
+			<artifactId>org.apache.aries.cdi.extra</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.aries.cdi</groupId>
+			<artifactId>org.apache.aries.cdi.spi</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.geronimo.specs</groupId>
+			<artifactId>geronimo-annotation_1.3_spec</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.geronimo.specs</groupId>
+			<artifactId>geronimo-atinject_1.0_spec</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.geronimo.specs</groupId>
+			<artifactId>geronimo-interceptor_1.2_spec</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.geronimo.specs</groupId>
+			<artifactId>geronimo-jaxrs_2.1_spec</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.geronimo.specs</groupId>
+			<artifactId>geronimo-jcdi_2.0_spec</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.namespace.extender</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.namespace.implementation</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.namespace.service</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.service.cdi</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.service.jaxrs</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>osgi.annotation</artifactId>
+		</dependency>
+			<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>osgi.core</artifactId>
+		</dependency>
+	</dependencies>
+</project>
diff --git a/cdi-extension-jaxrs/src/main/java/org/apache/aries/cdi/extension/jaxrs/JaxrsActivator.java b/cdi-extension-jaxrs/src/main/java/org/apache/aries/cdi/extension/jaxrs/JaxrsActivator.java
new file mode 100644
index 0000000..cf80ad2
--- /dev/null
+++ b/cdi-extension-jaxrs/src/main/java/org/apache/aries/cdi/extension/jaxrs/JaxrsActivator.java
@@ -0,0 +1,53 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.extension.jaxrs;
+
+import static org.osgi.framework.Constants.BUNDLE_ACTIVATOR;
+import static org.osgi.framework.Constants.SERVICE_DESCRIPTION;
+import static org.osgi.framework.Constants.SERVICE_VENDOR;
+import static org.osgi.service.cdi.CDIConstants.CDI_EXTENSION_PROPERTY;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import javax.enterprise.inject.spi.Extension;
+
+import org.osgi.annotation.bundle.Header;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+@Header(name = BUNDLE_ACTIVATOR, value = "${@class}")
+public class JaxrsActivator implements BundleActivator {
+
+	@Override
+	public void start(BundleContext context) throws Exception {
+		Dictionary<String, Object> properties = new Hashtable<>();
+		properties.put(CDI_EXTENSION_PROPERTY, "aries.cdi.jaxrs");
+		properties.put(SERVICE_DESCRIPTION, "Apache Aries CDI - JAX-RS Extension");
+		properties.put(SERVICE_VENDOR, "Apache Software Foundation");
+
+		_serviceRegistration = context.registerService(
+			Extension.class, new JaxrsExtensionFactory(), properties);
+	}
+
+	@Override
+	public void stop(BundleContext context) throws Exception {
+		_serviceRegistration.unregister();
+	}
+
+	private ServiceRegistration<Extension> _serviceRegistration;
+
+}
diff --git a/cdi-extension-jaxrs/src/main/java/org/apache/aries/cdi/extension/jaxrs/JaxrsCDIExtension.java b/cdi-extension-jaxrs/src/main/java/org/apache/aries/cdi/extension/jaxrs/JaxrsCDIExtension.java
new file mode 100644
index 0000000..8915d70
--- /dev/null
+++ b/cdi-extension-jaxrs/src/main/java/org/apache/aries/cdi/extension/jaxrs/JaxrsCDIExtension.java
@@ -0,0 +1,360 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.extension.jaxrs;
+
+import static java.util.Optional.ofNullable;
+
+import java.lang.annotation.Annotation;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.enterprise.context.Dependent;
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.spi.AfterDeploymentValidation;
+import javax.enterprise.inject.spi.AnnotatedType;
+import javax.enterprise.inject.spi.DeploymentException;
+import javax.enterprise.inject.spi.Extension;
+import javax.enterprise.inject.spi.ProcessAnnotatedType;
+import javax.enterprise.inject.spi.WithAnnotations;
+import javax.enterprise.inject.spi.configurator.AnnotatedTypeConfigurator;
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HEAD;
+import javax.ws.rs.OPTIONS;
+import javax.ws.rs.PATCH;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.container.DynamicFeature;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.ParamConverterProvider;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.WriterInterceptor;
+
+import org.apache.aries.cdi.extension.spi.adapt.Adapted;
+import org.apache.aries.cdi.extra.propertytypes.JaxrsApplicationBase;
+import org.apache.aries.cdi.extra.propertytypes.JaxrsApplicationSelect;
+import org.apache.aries.cdi.extra.propertytypes.JaxrsExtension;
+import org.apache.aries.cdi.extra.propertytypes.JaxrsExtensionSelect;
+import org.apache.aries.cdi.extra.propertytypes.JaxrsName;
+import org.apache.aries.cdi.extra.propertytypes.JaxrsResource;
+import org.apache.aries.cdi.extra.propertytypes.JaxrsWhiteboardTarget;
+import org.apache.aries.cdi.spi.configuration.Configuration;
+import org.osgi.service.cdi.ServiceScope;
+import org.osgi.service.cdi.annotations.ServiceInstance;
+import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
+
+public class JaxrsCDIExtension implements Extension {
+
+	private volatile Configuration configuration;
+	private final List<AnnotatedType<? extends Application>> applications = new CopyOnWriteArrayList<>();
+
+
+	void getConfiguration(@Observes Configuration configuration) {
+		this.configuration = configuration;
+	}
+
+	void application(
+		@Observes @WithAnnotations(ApplicationPath.class)
+		ProcessAnnotatedType<? extends Application> pat) {
+
+		AnnotatedType<? extends Application> annotatedType = pat.getAnnotatedType();
+
+		applications.add(annotatedType);
+
+		AnnotatedTypeConfigurator<? extends Application> configurator = pat.configureAnnotatedType();
+
+		if (!commonProperties(annotatedType, configurator, Application.class, true)) {
+			return;
+		}
+
+		if (!annotatedType.isAnnotationPresent(JaxrsApplicationBase.class)) {
+			configurator.add(
+				JaxrsApplicationBase.Literal.of(
+					annotatedType.getAnnotation(ApplicationPath.class).value()));
+		}
+	}
+
+	<X> void resource(
+		@Observes @WithAnnotations({Path.class, DELETE.class, GET.class, HEAD.class, OPTIONS.class, PATCH.class, POST.class, PUT.class})
+		ProcessAnnotatedType<X> pat) {
+
+		AnnotatedType<X> annotatedType = pat.getAnnotatedType();
+
+		AnnotatedTypeConfigurator<X> configurator = pat.configureAnnotatedType();
+
+		if (!commonProperties(annotatedType, configurator, Object.class, false)) {
+			return;
+		}
+
+		if (!annotatedType.isAnnotationPresent(JaxrsResource.class)) {
+			configurator.add(JaxrsResource.Literal.INSTANCE);
+		}
+	}
+
+	void containerRequestFilter(
+		@Observes ProcessAnnotatedType<? extends ContainerRequestFilter> pat) {
+
+		AnnotatedType<? extends ContainerRequestFilter> annotatedType = pat.getAnnotatedType();
+
+		AnnotatedTypeConfigurator<? extends ContainerRequestFilter> configurator = pat.configureAnnotatedType();
+
+		if (!commonProperties(annotatedType, configurator, ContainerRequestFilter.class, false)) {
+			return;
+		}
+
+		if (!annotatedType.isAnnotationPresent(JaxrsExtension.class)) {
+			configurator.add(JaxrsExtension.Literal.INSTANCE);
+		}
+	}
+
+	void containerResponseFilter(
+		@Observes ProcessAnnotatedType<? extends ContainerResponseFilter> pat) {
+
+		AnnotatedType<? extends ContainerResponseFilter> annotatedType = pat.getAnnotatedType();
+
+		AnnotatedTypeConfigurator<? extends ContainerResponseFilter> configurator = pat.configureAnnotatedType();
+
+		if (!commonProperties(annotatedType, configurator, ContainerResponseFilter.class, false)) {
+			return;
+		}
+
+		if (!annotatedType.isAnnotationPresent(JaxrsExtension.class)) {
+			configurator.add(JaxrsExtension.Literal.INSTANCE);
+		}
+	}
+
+	void readerInterceptor(
+		@Observes ProcessAnnotatedType<? extends ReaderInterceptor> pat) {
+
+		AnnotatedType<? extends ReaderInterceptor> annotatedType = pat.getAnnotatedType();
+
+		AnnotatedTypeConfigurator<? extends ReaderInterceptor> configurator = pat.configureAnnotatedType();
+
+		if (!commonProperties(annotatedType, configurator, ReaderInterceptor.class, false)) {
+			return;
+		}
+
+		if (!annotatedType.isAnnotationPresent(JaxrsExtension.class)) {
+			configurator.add(JaxrsExtension.Literal.INSTANCE);
+		}
+	}
+
+	void writerInterceptor(
+		@Observes ProcessAnnotatedType<? extends WriterInterceptor> pat) {
+
+		AnnotatedType<? extends WriterInterceptor> annotatedType = pat.getAnnotatedType();
+
+		AnnotatedTypeConfigurator<? extends WriterInterceptor> configurator = pat.configureAnnotatedType();
+
+		if (!commonProperties(annotatedType, configurator, WriterInterceptor.class, false)) {
+			return;
+		}
+
+		if (!annotatedType.isAnnotationPresent(JaxrsExtension.class)) {
+			configurator.add(JaxrsExtension.Literal.INSTANCE);
+		}
+	}
+
+	@SuppressWarnings("rawtypes")
+	void messageBodyReader(
+		@Observes ProcessAnnotatedType<? extends MessageBodyReader> pat) {
+
+		AnnotatedType<? extends MessageBodyReader> annotatedType = pat.getAnnotatedType();
+
+		AnnotatedTypeConfigurator<? extends MessageBodyReader> configurator = pat.configureAnnotatedType();
+
+		if (!commonProperties(annotatedType, configurator, MessageBodyReader.class, false)) {
+			return;
+		}
+
+		if (!annotatedType.isAnnotationPresent(JaxrsExtension.class)) {
+			configurator.add(JaxrsExtension.Literal.INSTANCE);
+		}
+	}
+
+	@SuppressWarnings("rawtypes")
+	void messageBodyWriter(
+		@Observes ProcessAnnotatedType<? extends MessageBodyWriter> pat) {
+
+		AnnotatedType<? extends MessageBodyWriter> annotatedType = pat.getAnnotatedType();
+
+		AnnotatedTypeConfigurator<? extends MessageBodyWriter> configurator = pat.configureAnnotatedType();
+
+		if (!commonProperties(annotatedType, configurator, MessageBodyWriter.class, false)) {
+			return;
+		}
+
+		if (!annotatedType.isAnnotationPresent(JaxrsExtension.class)) {
+			configurator.add(JaxrsExtension.Literal.INSTANCE);
+		}
+	}
+
+	@SuppressWarnings("rawtypes")
+	void contextResolver(
+		@Observes ProcessAnnotatedType<? extends ContextResolver> pat) {
+
+		AnnotatedType<? extends ContextResolver> annotatedType = pat.getAnnotatedType();
+
+		AnnotatedTypeConfigurator<? extends ContextResolver> configurator = pat.configureAnnotatedType();
+
+		if (!commonProperties(annotatedType, configurator, ContextResolver.class, false)) {
+			return;
+		}
+
+		if (!annotatedType.isAnnotationPresent(JaxrsExtension.class)) {
+			configurator.add(JaxrsExtension.Literal.INSTANCE);
+		}
+	}
+
+	@SuppressWarnings("rawtypes")
+	void exceptionMapper(
+		@Observes ProcessAnnotatedType<? extends ExceptionMapper> pat) {
+
+		AnnotatedType<? extends ExceptionMapper> annotatedType = pat.getAnnotatedType();
+
+		AnnotatedTypeConfigurator<? extends ExceptionMapper> configurator = pat.configureAnnotatedType();
+
+		if (!commonProperties(annotatedType, configurator, ExceptionMapper.class, false)) {
+			return;
+		}
+
+		if (!annotatedType.isAnnotationPresent(JaxrsExtension.class)) {
+			configurator.add(JaxrsExtension.Literal.INSTANCE);
+		}
+	}
+
+	void paramConverterProvider(
+		@Observes ProcessAnnotatedType<? extends ParamConverterProvider> pat) {
+
+		AnnotatedType<? extends ParamConverterProvider> annotatedType = pat.getAnnotatedType();
+
+		AnnotatedTypeConfigurator<? extends ParamConverterProvider> configurator = pat.configureAnnotatedType();
+
+		if (!commonProperties(annotatedType, configurator, ParamConverterProvider.class, false)) {
+			return;
+		}
+
+		if (!annotatedType.isAnnotationPresent(JaxrsExtension.class)) {
+			configurator.add(JaxrsExtension.Literal.INSTANCE);
+		}
+	}
+
+	void feature(
+		@Observes ProcessAnnotatedType<? extends Feature> pat) {
+
+		AnnotatedType<? extends Feature> annotatedType = pat.getAnnotatedType();
+
+		AnnotatedTypeConfigurator<? extends Feature> configurator = pat.configureAnnotatedType();
+
+		if (!commonProperties(annotatedType, configurator, Feature.class, false)) {
+			return;
+		}
+
+		if (!annotatedType.isAnnotationPresent(JaxrsExtension.class)) {
+			configurator.add(JaxrsExtension.Literal.INSTANCE);
+		}
+	}
+
+	void dynamicFeature(
+		@Observes ProcessAnnotatedType<? extends DynamicFeature> pat) {
+
+		AnnotatedType<? extends DynamicFeature> annotatedType = pat.getAnnotatedType();
+
+		AnnotatedTypeConfigurator<? extends DynamicFeature> configurator = pat.configureAnnotatedType();
+
+		if (!commonProperties(annotatedType, configurator, DynamicFeature.class, false)) {
+			return;
+		}
+
+		if (!annotatedType.isAnnotationPresent(JaxrsExtension.class)) {
+			configurator.add(JaxrsExtension.Literal.INSTANCE);
+		}
+	}
+
+	/*
+	 * @return true if common properties were added (i.e. if no @Service was found)
+	 */
+	boolean commonProperties(
+		AnnotatedType<?> annotatedType, AnnotatedTypeConfigurator<?> configurator,
+		Class<?> serviceType, boolean application) {
+
+		if (!Adapted.withServiceTypes(configurator, serviceType)) {
+			return false;
+		}
+
+		if (!annotatedType.isAnnotationPresent(JaxrsName.class)) {
+			if (application) {
+				configurator.add(
+					JaxrsName.Literal.of(
+						ofNullable((String)configuration.get(JaxrsWhiteboardConstants.JAX_RS_NAME)).orElse(
+							JaxrsWhiteboardConstants.JAX_RS_DEFAULT_APPLICATION
+						)
+					)
+				);
+			}
+			else {
+				configurator.add(JaxrsName.Literal.of(annotatedType.getJavaClass().getSimpleName()));
+			}
+		}
+
+		if (!application && !annotatedType.isAnnotationPresent(JaxrsApplicationSelect.class)) {
+			ofNullable((String)configuration.get(JaxrsWhiteboardConstants.JAX_RS_APPLICATION_SELECT)).ifPresent(
+				select -> configurator.add(JaxrsApplicationSelect.Literal.of(select))
+			);
+		}
+
+		if (!annotatedType.isAnnotationPresent(JaxrsExtensionSelect.class)) {
+			ofNullable((String[])configuration.get(JaxrsWhiteboardConstants.JAX_RS_EXTENSION_SELECT)).ifPresent(selects -> {
+				if (selects.length > 0) {
+					configurator.add(JaxrsExtensionSelect.Literal.of(selects));
+				}
+			});
+		}
+
+		if (!annotatedType.isAnnotationPresent(JaxrsWhiteboardTarget.class)) {
+			ofNullable((String)configuration.get(JaxrsWhiteboardConstants.JAX_RS_WHITEBOARD_TARGET)).ifPresent(
+				target -> configurator.add(JaxrsWhiteboardTarget.Literal.of(target))
+			);
+		}
+
+		if (!annotatedType.isAnnotationPresent(ServiceInstance.class)) {
+			Class<? extends Annotation> beanScope = Util.beanScope(annotatedType, Dependent.class);
+
+			if (Dependent.class.equals(beanScope)) {
+				configurator.add(ServiceInstance.Literal.of(ServiceScope.PROTOTYPE));
+			}
+		}
+
+		return true;
+	}
+
+	void afterDeploymentValidation(@Observes AfterDeploymentValidation adv) {
+		if (applications.size() > 1) {
+			adv.addDeploymentProblem(
+				new DeploymentException(
+					"More than one javax.ws.rs.core.Application annotated types were found in the CDI bundle."));
+		}
+	}
+
+}
diff --git a/cdi-extension-jaxrs/src/main/java/org/apache/aries/cdi/extension/jaxrs/JaxrsExtensionFactory.java b/cdi-extension-jaxrs/src/main/java/org/apache/aries/cdi/extension/jaxrs/JaxrsExtensionFactory.java
new file mode 100644
index 0000000..d71fa5b
--- /dev/null
+++ b/cdi-extension-jaxrs/src/main/java/org/apache/aries/cdi/extension/jaxrs/JaxrsExtensionFactory.java
@@ -0,0 +1,37 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.extension.jaxrs;
+
+import javax.enterprise.inject.spi.Extension;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.PrototypeServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+
+public class JaxrsExtensionFactory implements PrototypeServiceFactory<Extension> {
+
+	@Override
+	public Extension getService(
+		Bundle bundle, ServiceRegistration<Extension> registration) {
+
+		return new JaxrsCDIExtension();
+	}
+
+	@Override
+	public void ungetService(
+		Bundle bundle, ServiceRegistration<Extension> registration, Extension service) {
+	}
+
+}
diff --git a/cdi-extension-jaxrs/src/main/java/org/apache/aries/cdi/extension/jaxrs/Util.java b/cdi-extension-jaxrs/src/main/java/org/apache/aries/cdi/extension/jaxrs/Util.java
new file mode 100644
index 0000000..c03e358
--- /dev/null
+++ b/cdi-extension-jaxrs/src/main/java/org/apache/aries/cdi/extension/jaxrs/Util.java
@@ -0,0 +1,61 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.extension.jaxrs;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Predicate;
+
+import javax.enterprise.context.NormalScope;
+import javax.enterprise.inject.spi.Annotated;
+import javax.inject.Scope;
+
+public class Util {
+
+	private static final Predicate<Annotation> isScope = annotation ->
+		annotation.annotationType().isAnnotationPresent(Scope.class) ||
+		annotation.annotationType().isAnnotationPresent(NormalScope.class);
+
+	public static Class<? extends Annotation> beanScope(Annotated annotated, Class<? extends Annotation> defaultValue) {
+		Class<? extends Annotation> scope = collect(annotated.getAnnotations()).stream().filter(isScope).map(Annotation::annotationType).findFirst().orElse(null);
+
+		return (scope == null) ? defaultValue : scope;
+	}
+
+	private static List<Annotation> collect(Collection<Annotation> annotations) {
+		List<Annotation> list = new ArrayList<>();
+		for (Annotation a1 : annotations) {
+			if (a1.annotationType().getName().startsWith("java.lang.annotation.")) continue;
+			list.add(a1);
+		}
+		list.addAll(inherit(list));
+		return list;
+	}
+
+	private static List<Annotation> inherit(Collection<Annotation> annotations) {
+		List<Annotation> list = new ArrayList<>();
+		for (Annotation a1 : annotations) {
+			for (Annotation a2 : collect(Arrays.asList(a1.annotationType().getAnnotations()))) {
+				if (list.contains(a2)) continue;
+				list.add(a2);
+			}
+		}
+		return list;
+	}
+
+}
diff --git a/cdi-extension-jaxrs/src/main/java/org/apache/aries/cdi/extension/jaxrs/package-info.java b/cdi-extension-jaxrs/src/main/java/org/apache/aries/cdi/extension/jaxrs/package-info.java
new file mode 100644
index 0000000..3dec230
--- /dev/null
+++ b/cdi-extension-jaxrs/src/main/java/org/apache/aries/cdi/extension/jaxrs/package-info.java
@@ -0,0 +1,39 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@Capability(
+	attribute = "objectClass:List<String>=javax.enterprise.inject.spi.Extension",
+	namespace = SERVICE_NAMESPACE
+)
+@Capability(
+	namespace = CDI_EXTENSION_PROPERTY,
+	name = "aries.cdi.jaxrs",
+	uses= {
+		javax.enterprise.inject.spi.Extension.class,
+		javax.ws.rs.Path.class,
+		javax.ws.rs.core.Application.class,
+		javax.ws.rs.ext.Provider.class
+	},
+	version = "1.0.0"
+)
+@RequireCDIImplementation
+@RequireJaxrsWhiteboard
+package org.apache.aries.cdi.extension.jaxrs;
+
+import static org.osgi.namespace.service.ServiceNamespace.SERVICE_NAMESPACE;
+import static org.osgi.service.cdi.CDIConstants.CDI_EXTENSION_PROPERTY;
+
+import org.osgi.annotation.bundle.Capability;
+import org.osgi.service.cdi.annotations.RequireCDIImplementation;
+import org.osgi.service.jaxrs.whiteboard.annotations.RequireJaxrsWhiteboard;
diff --git a/cdi-extension-mp-config/base-itest.bndrun b/cdi-extension-mp-config/base-itest.bndrun
new file mode 100644
index 0000000..b12de6f
--- /dev/null
+++ b/cdi-extension-mp-config/base-itest.bndrun
@@ -0,0 +1,50 @@
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+
+#-runjdb: 8000
+
+-standalone: true
+-runee: JavaSE-1.8
+-runfw: org.eclipse.osgi
+-runproperties: \
+	eclipse.log.enabled=false,\
+	logback.configurationFile=file:${.}/logback.xml,\
+	org.osgi.service.http.port=0,\
+	osgi.console=,\
+	tck.config.test.javaconfig.converter.stringvalues=foo,\
+	test.property.a=blah,\
+	test.property.b=,\
+	org.apache.felix.http.host=localhost
+
+-resolve.effective: resolve, active
+
+-runpath: \
+	ch.qos.logback.classic,\
+	ch.qos.logback.core,\
+	org.apache.felix.logback,\
+	slf4j.api
+
+-runsystempackages: \
+	org.slf4j;version=1.7.25,\
+	org.slf4j.event;version=1.7.25,\
+	org.slf4j.helpers;version=1.7.25,\
+	org.slf4j.spi;version=1.7.25,\
+	sun.invoke,\
+	sun.misc
+
+-runrequires.base: \
+	osgi.identity;filter:='(osgi.identity=${project.artifactId})',\
+	osgi.identity;filter:='(osgi.identity=${project.artifactId}-tests)',\
+	osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.command)'
+
+-runblacklist.base: \
+	osgi.identity;filter:='(osgi.identity=biz.aQute.bndlib)'
\ No newline at end of file
diff --git a/cdi-itests/bnd/tb22.bnd b/cdi-extension-mp-config/bnd/tb01.bnd
similarity index 91%
copy from cdi-itests/bnd/tb22.bnd
copy to cdi-extension-mp-config/bnd/tb01.bnd
index 1ec52c6..61c7009 100644
--- a/cdi-itests/bnd/tb22.bnd
+++ b/cdi-extension-mp-config/bnd/tb01.bnd
@@ -10,4 +10,4 @@
 #    See the License for the specific language governing permissions and
 #    limitations under the License.
 
-Export-Package: ${p}.tb22.*;-split-package:=first
+Export-Package: ${p}.tb01.*;-split-package:=first
diff --git a/cdi-extension-mp-config/logback.xml b/cdi-extension-mp-config/logback.xml
new file mode 100644
index 0000000..32104c5
--- /dev/null
+++ b/cdi-extension-mp-config/logback.xml
@@ -0,0 +1,41 @@
+<!--
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<configuration>
+	<!--  scan="true" scanPeriod="5 seconds" debug="true"> -->
+	<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
+		<resetJUL>true</resetJUL>
+	</contextListener>
+
+	<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<!-- <pattern>%d{HH:mm:ss.SSS} [%.15thread] %-5level %logger{36}:%line - %msg%n</pattern> -->
+			<pattern>[%.15thread] %-5level %logger{36}:%line - %msg%n</pattern>
+		</encoder>
+	</appender>
+
+	<!-- <logger name="Events.Service.cdi-itests" level="INFO"/> -->
+	<!-- <logger name="Events.Service.org.apache.aries.cdi" level="INFO"/> -->
+	<!-- <logger name="org.apache.aries.cdi" level="DEBUG"/> -->
+
+	<logger name="LogService.org.apache.aries.cdi.itests" level="OFF"/>
+	<logger name="org.eclipse" level="ERROR"/>
+	<logger name="org.jboss" level="ERROR"/>
+
+	<root level="ERROR">
+		<appender-ref ref="STDOUT" />
+	</root>
+</configuration>
diff --git a/cdi-extension-mp-config/owb-itest.bndrun b/cdi-extension-mp-config/owb-itest.bndrun
new file mode 100644
index 0000000..93d9975
--- /dev/null
+++ b/cdi-extension-mp-config/owb-itest.bndrun
@@ -0,0 +1,51 @@
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+
+-include: base-itest.bndrun
+#-runjdb: 8000
+
+-runrequires: \
+	osgi.identity;filter:='(osgi.identity=org.apache.aries.cdi.owb)'
+
+-runblacklist: \
+	osgi.identity;filter:='(osgi.identity=*weld*)'
+
+-runbundles: \
+	openwebbeans-impl;version='[2.0.13,2.0.14)',\
+	openwebbeans-spi;version='[2.0.13,2.0.14)',\
+	org.apache.aries.cdi.extender;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.mp-config-tests;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.mp-config;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.spi;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.owb;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.spi;version='[1.1.0,1.1.1)',\
+	org.apache.aries.spifly.dynamic.framework.extension;version='[1.2.3,1.2.4)',\
+	org.apache.felix.configadmin;version='[1.9.10,1.9.11)',\
+	org.apache.felix.converter;version='[1.0.12,1.0.13)',\
+	org.apache.felix.gogo.command;version='[1.1.0,1.1.1)',\
+	org.apache.felix.gogo.runtime;version='[1.1.2,1.1.3)',\
+	org.apache.felix.gogo.shell;version='[1.1.2,1.1.3)',\
+	org.apache.geronimo.specs.geronimo-annotation_1.3_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-atinject_1.0_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-el_2.2_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-interceptor_1.2_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-jcdi_2.0_spec;version='[1.1.0,1.1.1)',\
+	org.apache.servicemix.bundles.junit;version='[4.12.0,4.12.1)',\
+	org.apache.xbean.asm7-shaded;version='[4.13.0,4.13.1)',\
+	org.apache.xbean.bundleutils;version='[4.15.0,4.15.1)',\
+	org.apache.xbean.finder-shaded;version='[4.13.0,4.13.1)',\
+	org.assertj.core;version='[3.13.2,3.13.3)',\
+	org.osgi.service.cdi;version='[1.0.0,1.0.1)',\
+	org.osgi.test.common;version='[1.0.0,1.0.1)',\
+	org.osgi.test.junit4;version='[1.0.0,1.0.1)',\
+	org.osgi.util.function;version='[1.1.0,1.1.1)',\
+	org.osgi.util.promise;version='[1.1.0,1.1.1)'
diff --git a/cdi-extension-mp-config/pom.xml b/cdi-extension-mp-config/pom.xml
index d54d303..34ce7c3 100644
--- a/cdi-extension-mp-config/pom.xml
+++ b/cdi-extension-mp-config/pom.xml
@@ -49,15 +49,23 @@
 			<plugin>
 				<groupId>biz.aQute.bnd</groupId>
 				<artifactId>bnd-maven-plugin</artifactId>
-				<configuration>
-					<bnd><![CDATA[
-						Export-Package: org.eclipse.microprofile.config.*
-						-includepackage: org.apache.geronimo.config.*
-						-cdiannotations:
-						-noclassforname: true
-						-fixupmessages: "Split package...";is:=ignore
-					]]></bnd>
-				</configuration>
+				<executions>
+					<execution>
+						<id>bnd-process</id>
+						<configuration>
+							<bnd><![CDATA[
+								Export-Package: org.eclipse.microprofile.config.*
+								-includepackage: org.apache.geronimo.config.*
+								-cdiannotations:
+								-noclassforname: true
+								-fixupmessages: "Split package...";is:=ignore
+								-bundleannotations: \
+									!org.eclipse.microprofile.config.*,\
+									org.apache.aries.cdi.extension.mp.config.*
+							]]></bnd>
+						</configuration>
+					</execution>
+				</executions>
 			</plugin>
 			<plugin>
 				<groupId>net.bytebuddy</groupId>
@@ -140,6 +148,14 @@
 		</dependency>
 		<dependency>
 			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.namespace.extender</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.namespace.implementation</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
 			<artifactId>org.osgi.namespace.service</artifactId>
 		</dependency>
 		<dependency>
@@ -156,4 +172,238 @@
 		</dependency>
 	</dependencies>
 
+	<dependencyManagement>
+		<dependencies>
+			<dependency>
+				<groupId>org.apache.aries.cdi</groupId>
+				<artifactId>org.apache.aries.cdi.bom</artifactId>
+				<version>${project.version}</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.felix</groupId>
+				<artifactId>org.apache.felix.gogo.bom</artifactId>
+				<version>1.0.2</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
+		</dependencies>
+	</dependencyManagement>
+
+	<profiles>
+		<profile>
+			<id>experimental</id>
+
+			<properties>
+				<maven.test.skip>false</maven.test.skip>
+			</properties>
+
+			<dependencies>
+				<dependency>
+					<groupId>org.apache.aries.cdi</groupId>
+					<artifactId>org.apache.aries.cdi.extra</artifactId>
+				</dependency>
+				<dependency>
+					<groupId>org.osgi</groupId>
+					<artifactId>org.osgi.test.assertj</artifactId>
+					<version>1.0.0-SNAPSHOT</version>
+					<scope>test</scope>
+				</dependency>
+				<dependency>
+					<groupId>org.osgi</groupId>
+					<artifactId>org.osgi.test.junit4</artifactId>
+					<version>1.0.0-SNAPSHOT</version>
+					<scope>test</scope>
+				</dependency>
+			</dependencies>
+
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>biz.aQute.bnd</groupId>
+						<artifactId>bnd-maven-plugin</artifactId>
+						<executions>
+							<!-- Integration Test Configuration -->
+							<execution>
+								<id>bnd-process-test</id>
+								<goals>
+									<goal>bnd-process-test</goal>
+								</goals>
+								<configuration>
+									<bnd><![CDATA[
+										p = org.apache.aries.cdi.extension.mp.config.test
+										Export-Package: !${p}.tb*,${p}.*
+										-make: (*).(jar); type=bnd; recipe="${.}/bnd/$1.bnd"
+										-includeresource: \
+											tb01.jar
+									]]></bnd>
+									<testCases>junit4</testCases>
+									<includeClassesDir>false</includeClassesDir>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+					<!-- This dynamically calculates all the things we need to run our code. -->
+					<plugin>
+						<groupId>biz.aQute.bnd</groupId>
+						<artifactId>bnd-resolver-maven-plugin</artifactId>
+						<configuration>
+							<bundles>
+								<bundle>target/${project.build.finalName}-tests.jar</bundle>
+							</bundles>
+							<failOnChanges>false</failOnChanges>
+							<includeDependencyManagement>true</includeDependencyManagement>
+							<reportOptional>false</reportOptional>
+							<scopes>
+								<scope>compile</scope>
+								<scope>runtime</scope>
+								<scope>test</scope>
+							</scopes>
+						</configuration>
+						<executions>
+							<execution>
+								<id>resolve-test-owb</id>
+								<phase>pre-integration-test</phase>
+								<goals>
+									<goal>resolve</goal>
+								</goals>
+								<configuration>
+									<bndruns>
+										<bndrun>owb-itest.bndrun</bndrun>
+									</bndruns>
+								</configuration>
+							</execution>
+							<execution>
+								<id>resolve-test-weld</id>
+								<phase>pre-integration-test</phase>
+								<goals>
+									<goal>resolve</goal>
+								</goals>
+								<configuration>
+									<bndruns>
+										<bndrun>weld-itest.bndrun</bndrun>
+									</bndruns>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+					<!-- This is the plugin runs the OSGi integration tests. -->
+					<plugin>
+						<groupId>biz.aQute.bnd</groupId>
+						<artifactId>bnd-testing-maven-plugin</artifactId>
+						<configuration>
+							<bundles>
+								<bundle>target/${project.build.finalName}-tests.jar</bundle>
+							</bundles>
+							<failOnChanges>false</failOnChanges>
+							<includeDependencyManagement>true</includeDependencyManagement>
+							<resolve>false</resolve>
+							<scopes>
+								<scope>compile</scope>
+								<scope>runtime</scope>
+								<scope>test</scope>
+							</scopes>
+						</configuration>
+						<executions>
+							<execution>
+								<id>testing-owb</id>
+								<goals>
+									<goal>testing</goal>
+								</goals>
+								<configuration>
+									<bndruns>
+										<bndrun>owb-itest.bndrun</bndrun>
+									</bndruns>
+								</configuration>
+							</execution>
+							<execution>
+								<id>testing-weld</id>
+								<goals>
+									<goal>testing</goal>
+								</goals>
+								<configuration>
+									<bndruns>
+										<bndrun>weld-itest.bndrun</bndrun>
+									</bndruns>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+					<!-- <plugin>
+						<groupId>biz.aQute.bnd</groupId>
+						<artifactId>bnd-run-maven-plugin</artifactId>
+						<configuration>
+							<bundles>
+								<bundle>target/${project.build.finalName}-tests.jar</bundle>
+							</bundles>
+							<includeDependencyManagement>true</includeDependencyManagement>
+							<scopes>
+								<scope>compile</scope>
+								<scope>runtime</scope>
+								<scope>test</scope>
+							</scopes>
+						</configuration>
+						<executions>
+							<execution>
+								<id>run-owb</id>
+								<goals>
+									<goal>run</goal>
+								</goals>
+								<configuration>
+									<bndrun>owb-itest.bndrun</bndrun>
+								</configuration>
+							</execution>
+							<execution>
+								<id>run-weld</id>
+								<goals>
+									<goal>run</goal>
+								</goals>
+								<configuration>
+									<bndrun>weld-itest.bndrun</bndrun>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin> -->
+					<plugin>
+						<artifactId>maven-resources-plugin</artifactId>
+						<executions>
+							<execution>
+								<id>default-testResources</id>
+								<phase>process-test-resources</phase>
+							</execution>
+						</executions>
+					</plugin>
+					<plugin>
+						<artifactId>maven-compiler-plugin</artifactId>
+						<executions>
+							<execution>
+								<id>default-testCompile</id>
+								<phase>test-compile</phase>
+							</execution>
+						</executions>
+					</plugin>
+					<plugin>
+						<artifactId>maven-jar-plugin</artifactId>
+						<executions>
+							<execution>
+								<id>test-jar</id>
+								<goals>
+									<goal>test-jar</goal>
+								</goals>
+								<configuration>
+									<archive>
+										<manifestFile>${project.build.testOutputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+									</archive>
+									<excludes>
+										<exclude>org/apache/aries/cdi/extension/mp/config/test/tb*/**</exclude>
+									</excludes>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+	</profiles>
 </project>
diff --git a/cdi-extension-mp-config/src/main/java/org/apache/aries/cdi/extension/mp/config/StubExtension.java b/cdi-extension-mp-config/src/main/java/org/apache/aries/cdi/extension/mp/config/MPConfigExtension.java
similarity index 60%
rename from cdi-extension-mp-config/src/main/java/org/apache/aries/cdi/extension/mp/config/StubExtension.java
rename to cdi-extension-mp-config/src/main/java/org/apache/aries/cdi/extension/mp/config/MPConfigExtension.java
index 7c0cf3b..cea5295 100644
--- a/cdi-extension-mp-config/src/main/java/org/apache/aries/cdi/extension/mp/config/StubExtension.java
+++ b/cdi-extension-mp-config/src/main/java/org/apache/aries/cdi/extension/mp/config/MPConfigExtension.java
@@ -14,36 +14,32 @@
 
 package org.apache.aries.cdi.extension.mp.config;
 
-import static org.apache.aries.cdi.extension.mp.config.StubExtension.EXTENSION_NAME;
+import static org.apache.aries.cdi.extension.mp.config.MPConfigExtension.EXTENSION_NAME;
+import static org.apache.aries.cdi.extension.mp.config.MPConfigExtension.EXTENSION_VERSION;
+import static org.osgi.framework.Constants.SCOPE_PROTOTYPE;
+import static org.osgi.framework.Constants.SERVICE_SCOPE;
+import static org.osgi.framework.Constants.SERVICE_VENDOR;
 import static org.osgi.service.cdi.CDIConstants.CDI_EXTENSION_PROPERTY;
 
-import javax.enterprise.event.Observes;
-import javax.enterprise.inject.spi.BeanManager;
-import javax.enterprise.inject.spi.BeforeBeanDiscovery;
 import javax.enterprise.inject.spi.Extension;
 
 import org.apache.geronimo.config.cdi.ConfigExtension;
-import org.apache.geronimo.config.cdi.ConfigInjectionProducer;
 
 import aQute.bnd.annotation.spi.ServiceProvider;
 
 @ServiceProvider(
 	attribute = {
-		CDI_EXTENSION_PROPERTY + "=" + EXTENSION_NAME,
-		"service.scope=prototype",
-		"service.vendor=Apache Software Foundation",
-		"version:Version=1.3.0"
+		CDI_EXTENSION_PROPERTY + '=' + EXTENSION_NAME,
+		SERVICE_SCOPE + '=' + SCOPE_PROTOTYPE,
+		SERVICE_VENDOR + "=Apache Software Foundation",
+		"version:Version=" + EXTENSION_VERSION
 	},
-	effective = "active",
 	uses = Extension.class,
 	value = Extension.class
 )
-public class StubExtension extends ConfigExtension {
+public class MPConfigExtension extends ConfigExtension {
 
 	public final static String EXTENSION_NAME = "eclipse.microprofile.config";
-
-	public void addBeans(@Observes BeforeBeanDiscovery bbd, BeanManager bm) {
-		bbd.addAnnotatedType(bm.createAnnotatedType(ConfigInjectionProducer.class));
-	}
+	public final static String EXTENSION_VERSION = "1.3.0";
 
 }
diff --git a/cdi-extension-mp-config/src/main/java/org/apache/aries/cdi/extension/mp/config/package-info.java b/cdi-extension-mp-config/src/main/java/org/apache/aries/cdi/extension/mp/config/package-info.java
index 1980043..581c1d1 100644
--- a/cdi-extension-mp-config/src/main/java/org/apache/aries/cdi/extension/mp/config/package-info.java
+++ b/cdi-extension-mp-config/src/main/java/org/apache/aries/cdi/extension/mp/config/package-info.java
@@ -24,12 +24,16 @@
 		javax.enterprise.event.Observes.class,
 		Extension.class
 	},
-	version = "1.3" // TODO ?maybe read this from pom property?
+	version = EXTENSION_VERSION,
+	attribute = {
+		"aries.cdi.extension.bean.classes:List<String>='org.apache.geronimo.config.cdi.ConfigInjectionProducer'"
+	}
 )
 @RequireCDIExtender
 package org.apache.aries.cdi.extension.mp.config;
 
-import static org.apache.aries.cdi.extension.mp.config.StubExtension.EXTENSION_NAME;
+import static org.apache.aries.cdi.extension.mp.config.MPConfigExtension.EXTENSION_NAME;
+import static org.apache.aries.cdi.extension.mp.config.MPConfigExtension.EXTENSION_VERSION;
 import static org.osgi.namespace.service.ServiceNamespace.SERVICE_NAMESPACE;
 import static org.osgi.service.cdi.CDIConstants.CDI_EXTENSION_PROPERTY;
 
diff --git a/cdi-extension-mp-config/src/test/java/org/apache/aries/cdi/extension/mp/config/test/BaseTestCase.java b/cdi-extension-mp-config/src/test/java/org/apache/aries/cdi/extension/mp/config/test/BaseTestCase.java
new file mode 100644
index 0000000..ad8165d
--- /dev/null
+++ b/cdi-extension-mp-config/src/test/java/org/apache/aries/cdi/extension/mp/config/test/BaseTestCase.java
@@ -0,0 +1,131 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.extension.mp.config.test;
+
+import static java.util.Optional.ofNullable;
+import static org.osgi.test.common.filter.Filters.format;
+
+import java.util.function.Supplier;
+
+import org.apache.aries.cdi.extra.RequireCDIExtension;
+import org.junit.Rule;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cdi.runtime.CDIComponentRuntime;
+import org.osgi.test.junit4.context.BundleContextRule;
+import org.osgi.test.junit4.service.ServiceUseRule;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+@RequireCDIExtension("eclipse.microprofile.config")
+public abstract class BaseTestCase {
+
+	public static final long timeout = 500;
+
+	@Rule
+	public BundleContextRule bcr = new BundleContextRule();
+	@Rule
+	public ServiceUseRule<CDIComponentRuntime> ccrr = new ServiceUseRule.Builder<CDIComponentRuntime>(CDIComponentRuntime.class, bcr).build();
+
+	@Rule
+	public TestWatcher watchman= new TestWatcher() {
+		@Override
+		protected void failed(Throwable e, Description description) {
+			System.out.printf("--------- TEST: %s#%s [%s]%n", description.getTestClass(), description.getMethodName(), "FAILED");
+		}
+
+		@Override
+		protected void succeeded(Description description) {
+			System.out.printf("--------- TEST: %s#%s [%s]%n", description.getTestClass(), description.getMethodName(), "PASSED");
+		}
+	};
+
+	public <S,T> CloseableTracker<S, T> track(Filter filter) {
+		CloseableTracker<S, T> tracker = new CloseableTracker<>(bcr.getBundleContext(), filter);
+		tracker.open();
+		return tracker;
+	}
+
+	public <S,T> CloseableTracker<S, T> track(String pattern, Object... objects) {
+		return track(format(pattern, objects));
+	}
+
+	public <S> CloseableTracker<S, ServiceReference<S>> trackSR(String pattern, Object... objects) {
+		return trackSR(format(pattern, objects));
+	}
+
+	public <S> CloseableTracker<S, ServiceReference<S>> trackSR(Filter filter) {
+		CloseableTracker<S, ServiceReference<S>> tracker = new CloseableTracker<>(bcr.getBundleContext(), filter, new ServiceTrackerCustomizer<S, ServiceReference<S>>() {
+
+			@Override
+			public ServiceReference<S> addingService(ServiceReference<S> reference) {
+				return reference;
+			}
+
+			@Override
+			public void modifiedService(ServiceReference<S> reference, ServiceReference<S> service) {
+			}
+
+			@Override
+			public void removedService(ServiceReference<S> reference, ServiceReference<S> service) {
+			}
+
+		});
+		tracker.open();
+		return tracker;
+	}
+
+	public long getChangeCount(ServiceReference<?> reference) {
+		return ofNullable(
+			reference.getProperty("service.changecount")
+		).map(
+			Long.class::cast
+		).orElseGet(
+			() -> new Long(-1l)
+		).longValue();
+	}
+
+	public static <T> T tccl(ClassLoader classLoader, Supplier<T> supplier) {
+		Thread currentThread = Thread.currentThread();
+		ClassLoader original = currentThread.getContextClassLoader();
+		try {
+			currentThread.setContextClassLoader(classLoader);
+			return supplier.get();
+		}
+		finally {
+			currentThread.setContextClassLoader(original);
+		}
+	}
+
+	public static class CloseableTracker<S, T> extends ServiceTracker<S, T> implements AutoCloseable {
+
+		public CloseableTracker(BundleContext context, Filter filter) {
+			super(context, filter, null);
+		}
+
+		public CloseableTracker(BundleContext context, Filter filter, ServiceTrackerCustomizer<S, T> customizer) {
+			super(context, filter, customizer);
+		}
+
+		@Override
+		public void close() {
+			super.close();
+		}
+
+	}
+}
diff --git a/cdi-extension-mp-config/src/test/java/org/apache/aries/cdi/extension/mp/config/test/MpConfigTests.java b/cdi-extension-mp-config/src/test/java/org/apache/aries/cdi/extension/mp/config/test/MpConfigTests.java
new file mode 100644
index 0000000..917179e
--- /dev/null
+++ b/cdi-extension-mp-config/src/test/java/org/apache/aries/cdi/extension/mp/config/test/MpConfigTests.java
@@ -0,0 +1,35 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.extension.mp.config.test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.aries.cdi.extension.mp.config.test.interfaces.Pojo;
+import org.junit.Test;
+
+public class MpConfigTests extends BaseTestCase {
+
+	@Test
+	public void testConfigIsSet() throws Exception {
+		bcr.installBundle("tb01.jar");
+
+		try (CloseableTracker<Pojo, Pojo> tracker = track("(objectClass=%s)", Pojo.class.getName())) {
+			Pojo pojo = tracker.waitForService(timeout);
+
+			assertThat(pojo).extracting(it -> it.foo(null)).isEqualTo("[foo]");
+		}
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java b/cdi-extension-mp-config/src/test/java/org/apache/aries/cdi/extension/mp/config/test/interfaces/Pojo.java
similarity index 65%
copy from cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java
copy to cdi-extension-mp-config/src/test/java/org/apache/aries/cdi/extension/mp/config/test/interfaces/Pojo.java
index 315634c..77728e2 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java
+++ b/cdi-extension-mp-config/src/test/java/org/apache/aries/cdi/extension/mp/config/test/interfaces/Pojo.java
@@ -12,5 +12,19 @@
  * limitations under the License.
  */
 
-@org.osgi.service.cdi.annotations.Beans
-package org.apache.aries.cdi.test.tb16;
+package org.apache.aries.cdi.extension.mp.config.test.interfaces;
+
+import java.util.Collections;
+import java.util.Map;
+
+public interface Pojo {
+
+	public String foo(String fooInput);
+
+	public int getCount();
+
+	default Map<String, Object> getMap() {
+		return Collections.emptyMap();
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/ConfiguredBean.java b/cdi-extension-mp-config/src/test/java/org/apache/aries/cdi/extension/mp/config/test/tb01/ConfiguredBean.java
similarity index 88%
rename from cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/ConfiguredBean.java
rename to cdi-extension-mp-config/src/test/java/org/apache/aries/cdi/extension/mp/config/test/tb01/ConfiguredBean.java
index 478caa9..7db13a4 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/ConfiguredBean.java
+++ b/cdi-extension-mp-config/src/test/java/org/apache/aries/cdi/extension/mp/config/test/tb01/ConfiguredBean.java
@@ -12,13 +12,13 @@
  * limitations under the License.
  */
 
-package org.apache.aries.cdi.test.tb16;
+package org.apache.aries.cdi.extension.mp.config.test.tb01;
 
 import java.util.List;
 
 import javax.inject.Inject;
 
-import org.apache.aries.cdi.test.interfaces.Pojo;
+import org.apache.aries.cdi.extension.mp.config.test.interfaces.Pojo;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
 import org.osgi.service.cdi.annotations.Service;
 
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java b/cdi-extension-mp-config/src/test/java/org/apache/aries/cdi/extension/mp/config/test/tb01/package-info.java
similarity index 82%
copy from cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java
copy to cdi-extension-mp-config/src/test/java/org/apache/aries/cdi/extension/mp/config/test/tb01/package-info.java
index 315634c..b6daac4 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java
+++ b/cdi-extension-mp-config/src/test/java/org/apache/aries/cdi/extension/mp/config/test/tb01/package-info.java
@@ -12,5 +12,7 @@
  * limitations under the License.
  */
 
-@org.osgi.service.cdi.annotations.Beans
-package org.apache.aries.cdi.test.tb16;
+@Beans
+package org.apache.aries.cdi.extension.mp.config.test.tb01;
+
+import org.osgi.service.cdi.annotations.Beans;
diff --git a/cdi-extension-mp-config/weld-itest.bndrun b/cdi-extension-mp-config/weld-itest.bndrun
new file mode 100644
index 0000000..6d5f641
--- /dev/null
+++ b/cdi-extension-mp-config/weld-itest.bndrun
@@ -0,0 +1,53 @@
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+
+-include: base-itest.bndrun
+#-runjdb: 8000
+
+-runrequires: \
+	osgi.identity;filter:='(osgi.identity=javax.ejb-api)',\
+	osgi.identity;filter:='(osgi.identity=javax.transaction-api)',\
+	osgi.identity;filter:='(osgi.identity=org.apache.aries.cdi.weld)'
+
+-runblacklist: \
+	osgi.identity;filter:='(osgi.identity=*owb*)'
+
+-runbundles: \
+	javax.ejb-api;version='[3.2.0,3.2.1)',\
+	javax.transaction-api;version='[1.2.0,1.2.1)',\
+	jboss-classfilewriter;version='[1.2.3,1.2.4)',\
+	org.apache.aries.cdi.extender;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.mp-config-tests;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.mp-config;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.spi;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.spi;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.weld;version='[1.1.0,1.1.1)',\
+	org.apache.aries.spifly.dynamic.framework.extension;version='[1.2.3,1.2.4)',\
+	org.apache.felix.configadmin;version='[1.9.10,1.9.11)',\
+	org.apache.felix.converter;version='[1.0.12,1.0.13)',\
+	org.apache.felix.gogo.command;version='[1.1.0,1.1.1)',\
+	org.apache.felix.gogo.runtime;version='[1.1.2,1.1.3)',\
+	org.apache.felix.gogo.shell;version='[1.1.2,1.1.3)',\
+	org.apache.geronimo.specs.geronimo-annotation_1.3_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-atinject_1.0_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-el_2.2_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-interceptor_1.2_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-jcdi_2.0_spec;version='[1.1.0,1.1.1)',\
+	org.apache.servicemix.bundles.junit;version='[4.12.0,4.12.1)',\
+	org.assertj.core;version='[3.13.2,3.13.3)',\
+	org.jboss.logging.jboss-logging;version='[3.3.2,3.3.3)',\
+	org.jboss.weld.osgi-bundle;version='[3.0.5,3.0.6)',\
+	org.osgi.service.cdi;version='[1.0.0,1.0.1)',\
+	org.osgi.test.common;version='[1.0.0,1.0.1)',\
+	org.osgi.test.junit4;version='[1.0.0,1.0.1)',\
+	org.osgi.util.function;version='[1.1.0,1.1.1)',\
+	org.osgi.util.promise;version='[1.1.0,1.1.1)'
diff --git a/cdi-extension-mp-jwt-auth/base-itest.bndrun b/cdi-extension-mp-jwt-auth/base-itest.bndrun
new file mode 100644
index 0000000..b12de6f
--- /dev/null
+++ b/cdi-extension-mp-jwt-auth/base-itest.bndrun
@@ -0,0 +1,50 @@
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+
+#-runjdb: 8000
+
+-standalone: true
+-runee: JavaSE-1.8
+-runfw: org.eclipse.osgi
+-runproperties: \
+	eclipse.log.enabled=false,\
+	logback.configurationFile=file:${.}/logback.xml,\
+	org.osgi.service.http.port=0,\
+	osgi.console=,\
+	tck.config.test.javaconfig.converter.stringvalues=foo,\
+	test.property.a=blah,\
+	test.property.b=,\
+	org.apache.felix.http.host=localhost
+
+-resolve.effective: resolve, active
+
+-runpath: \
+	ch.qos.logback.classic,\
+	ch.qos.logback.core,\
+	org.apache.felix.logback,\
+	slf4j.api
+
+-runsystempackages: \
+	org.slf4j;version=1.7.25,\
+	org.slf4j.event;version=1.7.25,\
+	org.slf4j.helpers;version=1.7.25,\
+	org.slf4j.spi;version=1.7.25,\
+	sun.invoke,\
+	sun.misc
+
+-runrequires.base: \
+	osgi.identity;filter:='(osgi.identity=${project.artifactId})',\
+	osgi.identity;filter:='(osgi.identity=${project.artifactId}-tests)',\
+	osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.command)'
+
+-runblacklist.base: \
+	osgi.identity;filter:='(osgi.identity=biz.aQute.bndlib)'
\ No newline at end of file
diff --git a/cdi-itests/bnd/tb22.bnd b/cdi-extension-mp-jwt-auth/bnd/tb01.bnd
similarity index 62%
copy from cdi-itests/bnd/tb22.bnd
copy to cdi-extension-mp-jwt-auth/bnd/tb01.bnd
index 1ec52c6..465c064 100644
--- a/cdi-itests/bnd/tb22.bnd
+++ b/cdi-extension-mp-jwt-auth/bnd/tb01.bnd
@@ -10,4 +10,9 @@
 #    See the License for the specific language governing permissions and
 #    limitations under the License.
 
-Export-Package: ${p}.tb22.*;-split-package:=first
+Export-Package: ${p}.tb01.*;-split-package:=first
+-includeresource: \
+	@microprofile-jwt-auth-tck-[0-9.]*-tests.jar!/publicKey.pem,\
+	@microprofile-jwt-auth-tck-[0-9.]*-tests.jar!/publicKey4k.pem,\
+	META-INF/microprofile-config.properties=bnd/tb01.properties,\
+	META-INF/geronimo/microprofile/jwt-auth.properties=bnd/tb01.properties
\ No newline at end of file
diff --git a/cdi-extension-mp-jwt-auth/bnd/tb01.properties b/cdi-extension-mp-jwt-auth/bnd/tb01.properties
new file mode 100644
index 0000000..e55c962
--- /dev/null
+++ b/cdi-extension-mp-jwt-auth/bnd/tb01.properties
@@ -0,0 +1,36 @@
+#Licensed to the Apache Software Foundation (ASF) under one
+#or more contributor license agreements.  See the NOTICE file
+#distributed with this work for additional information
+#regarding copyright ownership.  The ASF licenses this file
+#to you under the Apache License, Version 2.0 (the
+#"License"); you may not use this file except in compliance
+#with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+#Unless required by applicable law or agreed to in writing,
+#software distributed under the License is distributed on an
+#"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#KIND, either express or implied.  See the License for the
+#specific language governing permissions and limitations
+#under the License.
+## COPIED FROM: geronimo-jwt-auth/src/test/resources/META-INF/geronimo/microprofile/jwt-auth.properties
+geronimo.jwt-auth.groups.mapping = \
+Group1MappedRole = group1, Group1MappedRole
+
+geronimo.jwt-auth.issuer.default = https://server.example.com
+
+geronimo.jwt-auth.kids.key.mapping = \
+/privateKey.pem = /publicKey.pem
+
+## COPIED FROM: microprofile-jwt-auth-tck-[0-9.]*-tests.jar!/META-INF/microprofile-config.properties
+# The publicKey4k.pem contents
+mp.jwt.verify.publickey=-----BEGIN PUBLIC KEY-----\n\
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtL6HShqY5H4y56rsCo7VdhT9/eLQwsJpKWg66j98XsB/qc5ZxkJ25GXCzpjR0ZvzAxMNlj1hrMORaKVzz2/5axZgF1eZfzgrNyQ9rtGaBtMNAB20jLsoYp5psRTaYxKeOiLHPr3956ukSRUF9YfJGSamrvGOwC8h6zbq6uaydv+FVJXijlMD/iCggUfoirtVOWK/X1IzV7covxcGzT0X019/4RbtjLdnvqZnGqmpHQpBEItI+4gNvaKR8NDWUxAjO/v+oOKR5nEUnDWcQSCxKmyQrVJtHr9PBwWrHzTSx4k1L1hLf+AWXAdy/r6c0Lzgt5knmZTyWDG2+n8SlrXxHHxFO1Wz8H/OKBzTAf8zIuj2lkXYo+M6aoJM7qQmTys80dtYvnaHGSl+jpe2plMbS9RS4XcHH7vCqJc9acBnp9CvLgjOmA0b5Rc0WyN4sn1SDFYe6HZcVo4YGTbtTTlwgu/ozQ1x+xpTAaU0mWkHMwT0CO79rPORjhDXokEuduvtp6VUiAaoFF6Y3QQLf6O3P9p8yghpBBLb460lEQqOHQQGP0EK46cU81dlcD5lYE0TayDzb9pZZWUyjIE4ElzyW7wgI4xw7czdBalN+IhXKfGUCqIDVh7X7JpmskZMaRixf424yBcZLntEejZy59yLDSssHMc/bqnBraXuo8JBEPkCAwEAAQ==\n\
+-----END PUBLIC KEY-----
+mp.jwt.verify.issuer=https://server.example.com
+
+## COPIED FROM: microprofile-jwt-auth-tck-[0-9.]*-tests.jar!/META-INF/microprofile-config-location.properties
+# A reference to the publicKey4k.pem contents embedded location
+mp.jwt.verify.publickey.location=/publicKey4k.pem
+mp.jwt.verify.publickey.issuer=https://server.example.com
diff --git a/cdi-extension-mp-jwt-auth/logback.xml b/cdi-extension-mp-jwt-auth/logback.xml
new file mode 100644
index 0000000..32104c5
--- /dev/null
+++ b/cdi-extension-mp-jwt-auth/logback.xml
@@ -0,0 +1,41 @@
+<!--
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<configuration>
+	<!--  scan="true" scanPeriod="5 seconds" debug="true"> -->
+	<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
+		<resetJUL>true</resetJUL>
+	</contextListener>
+
+	<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<!-- <pattern>%d{HH:mm:ss.SSS} [%.15thread] %-5level %logger{36}:%line - %msg%n</pattern> -->
+			<pattern>[%.15thread] %-5level %logger{36}:%line - %msg%n</pattern>
+		</encoder>
+	</appender>
+
+	<!-- <logger name="Events.Service.cdi-itests" level="INFO"/> -->
+	<!-- <logger name="Events.Service.org.apache.aries.cdi" level="INFO"/> -->
+	<!-- <logger name="org.apache.aries.cdi" level="DEBUG"/> -->
+
+	<logger name="LogService.org.apache.aries.cdi.itests" level="OFF"/>
+	<logger name="org.eclipse" level="ERROR"/>
+	<logger name="org.jboss" level="ERROR"/>
+
+	<root level="ERROR">
+		<appender-ref ref="STDOUT" />
+	</root>
+</configuration>
diff --git a/cdi-extension-mp-jwt-auth/owb-itest.bndrun b/cdi-extension-mp-jwt-auth/owb-itest.bndrun
new file mode 100644
index 0000000..2b9dd4e
--- /dev/null
+++ b/cdi-extension-mp-jwt-auth/owb-itest.bndrun
@@ -0,0 +1,73 @@
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+
+-include: base-itest.bndrun
+#-runjdb: 8000
+
+-runrequires: \
+	osgi.identity;filter:='(osgi.identity=org.apache.aries.cdi.owb)'
+
+-runblacklist: \
+	osgi.identity;filter:='(osgi.identity=*weld*)'
+
+-runbundles: \
+	javax.servlet.jsp-api;version='[2.3.3,2.3.4)',\
+	openwebbeans-impl;version='[2.0.13,2.0.14)',\
+	openwebbeans-spi;version='[2.0.13,2.0.14)',\
+	openwebbeans-web;version='[2.0.13,2.0.14)',\
+	org.apache.aries.cdi.extender;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.jaxrs;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.mp-config;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.mp-jwt-auth-tests;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.mp-jwt-auth;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.servlet.common;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.servlet.owb;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.spi;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extra;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.owb;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.spi;version='[1.1.0,1.1.1)',\
+	org.apache.aries.jax.rs.whiteboard;version='[1.0.7,1.0.8)',\
+	org.apache.aries.spifly.dynamic.framework.extension;version='[1.2.3,1.2.4)',\
+	org.apache.commons.io;version='[2.6.0,2.6.1)',\
+	org.apache.commons.logging;version='[1.2.0,1.2.1)',\
+	org.apache.felix.configadmin;version='[1.9.10,1.9.11)',\
+	org.apache.felix.converter;version='[1.0.12,1.0.13)',\
+	org.apache.felix.gogo.command;version='[1.1.0,1.1.1)',\
+	org.apache.felix.gogo.runtime;version='[1.1.2,1.1.3)',\
+	org.apache.felix.gogo.shell;version='[1.1.2,1.1.3)',\
+	org.apache.felix.http.jetty;version='[4.0.14,4.0.15)',\
+	org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
+	org.apache.geronimo.specs.geronimo-annotation_1.3_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-atinject_1.0_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-el_2.2_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-interceptor_1.2_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-jaxrs_2.1_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-jcdi_2.0_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-json_1.1_spec;version='[1.3.0,1.3.1)',\
+	org.apache.geronimo.specs.geronimo-jsonb_1.0_spec;version='[1.2.0,1.2.1)',\
+	org.apache.httpcomponents.httpclient;version='[4.5.3,4.5.4)',\
+	org.apache.httpcomponents.httpcore;version='[4.4.6,4.4.7)',\
+	org.apache.johnzon.core;version='[1.2.3,1.2.4)',\
+	org.apache.johnzon.jsonb;version='[1.2.3,1.2.4)',\
+	org.apache.johnzon.mapper;version='[1.2.3,1.2.4)',\
+	org.apache.johnzon.osgi;version='[1.2.3,1.2.4)',\
+	org.apache.servicemix.bundles.junit;version='[4.12.0,4.12.1)',\
+	org.apache.xbean.asm7-shaded;version='[4.13.0,4.13.1)',\
+	org.apache.xbean.bundleutils;version='[4.15.0,4.15.1)',\
+	org.apache.xbean.finder-shaded;version='[4.13.0,4.13.1)',\
+	org.assertj.core;version='[3.13.2,3.13.3)',\
+	org.osgi.service.cdi;version='[1.0.0,1.0.1)',\
+	org.osgi.service.jaxrs;version='[1.0.0,1.0.1)',\
+	org.osgi.test.common;version='[1.0.0,1.0.1)',\
+	org.osgi.test.junit4;version='[1.0.0,1.0.1)',\
+	org.osgi.util.function;version='[1.1.0,1.1.1)',\
+	org.osgi.util.promise;version='[1.1.0,1.1.1)'
diff --git a/cdi-extension-mp-jwt-auth/pom.xml b/cdi-extension-mp-jwt-auth/pom.xml
new file mode 100644
index 0000000..42da435
--- /dev/null
+++ b/cdi-extension-mp-jwt-auth/pom.xml
@@ -0,0 +1,558 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<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>
+		<groupId>org.apache.aries.cdi</groupId>
+		<artifactId>org.apache.aries.cdi</artifactId>
+		<version>1.1.0-SNAPSHOT</version>
+		<relativePath>..</relativePath>
+	</parent>
+
+	<artifactId>org.apache.aries.cdi.extension.mp-jwt-auth</artifactId>
+	<name>Apache Aries CDI - MicroProfile JWT Auth ${mp.jwt.auth.version} Using Apache Geronimo</name>
+	<description>Apache Aries CDI - MicroProfile JWT Auth ${mp.jwt.auth.version} Using Apache Geronimo</description>
+
+	<properties>
+		<gpg.skip>true</gpg.skip>
+		<maven.deploy.skip>true</maven.deploy.skip>
+		<maven.install.skip>true</maven.install.skip>
+	</properties>
+
+	<licenses>
+		<license>
+			<name>ASL 2.0</name>
+			<url>https://www.apache.org/licenses/LICENSE-2.0</url>
+		</license>
+	</licenses>
+
+	<scm>
+		<connection>scm:git:git@github.com:apache/aries-cdi.git</connection>
+		<developerConnection>scm:git:git@github.com:apache/aries-cdi.git</developerConnection>
+		<tag>HEAD</tag>
+		<url>https://github.com/apache/aries-cdi</url>
+	</scm>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>biz.aQute.bnd</groupId>
+				<artifactId>bnd-maven-plugin</artifactId>
+				<configuration>
+					<bnd><![CDATA[
+						Export-Package: \
+							org.eclipse.microprofile.auth.*,\
+							org.eclipse.microprofile.jwt.*
+						-includepackage: org.apache.geronimo.microprofile.impl.jwtauth.*
+						-cdiannotations:
+						-noclassforname: true
+						-fixupmessages: "Split package...";is:=ignore
+						-bundleannotations: \
+							!org.eclipse.microprofile.*,\
+							org.apache.aries.cdi.extension.mp.jwt.*
+					]]></bnd>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>net.bytebuddy</groupId>
+				<artifactId>byte-buddy-maven-plugin</artifactId>
+				<version>${byte.buddy.version}</version>
+				<executions>
+					<execution>
+						<goals>
+							<goal>transform</goal>
+						</goals>
+					</execution>
+				</executions>
+				<configuration>
+					<transformations>
+						<transformation>
+							<plugin>org.apache.aries.cdi.build.tools.AddExtensionRequirement</plugin>
+							<arguments>
+								<argument>
+									<index>1</index>
+									<value>eclipse.microprofile.jwt-auth</value>
+								</argument>
+								<argument>
+									<index>2</index>
+									<value>${mp.jwt.auth.version}</value>
+								</argument>
+								<argument>
+									<index>3</index>
+									<value>org.eclipse.microprofile.**</value>
+								</argument>
+							</arguments>
+						</transformation>
+					</transformations>
+				</configuration>
+				<dependencies>
+					<dependency>
+						<groupId>org.osgi</groupId>
+						<artifactId>osgi.annotation</artifactId>
+						<version>7.0.0</version>
+					</dependency>
+					<dependency>
+						<groupId>org.apache.aries.cdi</groupId>
+						<artifactId>org.apache.aries.cdi.build.tools</artifactId>
+						<version>${project.version}</version>
+					</dependency>
+				</dependencies>
+			</plugin>
+		</plugins>
+	</build>
+
+	<dependencies>
+		<dependency>
+			<groupId>biz.aQute.bnd</groupId>
+			<artifactId>biz.aQute.bnd.annotation</artifactId>
+			<version>${bnd.version}</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.aries.cdi</groupId>
+			<artifactId>org.apache.aries.cdi.extra</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.aries.cdi</groupId>
+			<artifactId>org.apache.aries.cdi.extension.mp-config</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.aries.cdi</groupId>
+			<artifactId>org.apache.aries.cdi.spi</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.felix</groupId>
+			<artifactId>org.apache.felix.http.servlet-api</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.geronimo.specs</groupId>
+			<artifactId>geronimo-annotation_1.3_spec</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.geronimo.specs</groupId>
+			<artifactId>geronimo-atinject_1.0_spec</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.geronimo.specs</groupId>
+			<artifactId>geronimo-jaxrs_2.1_spec</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.geronimo.specs</groupId>
+			<artifactId>geronimo-jcdi_2.0_spec</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.geronimo.specs</groupId>
+			<artifactId>geronimo-json_1.1_spec</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.eclipse.microprofile.jwt</groupId>
+			<artifactId>microprofile-jwt-auth-api</artifactId>
+			<version>${mp.jwt.auth.version}</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.geronimo</groupId>
+			<artifactId>geronimo-jwt-auth</artifactId>
+			<version>1.0.4-SNAPSHOT</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.namespace.extender</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.namespace.implementation</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.namespace.service</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.service.cdi</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.service.http.whiteboard</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.service.jaxrs</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>osgi.annotation</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>osgi.core</artifactId>
+		</dependency>
+	</dependencies>
+
+	<dependencyManagement>
+		<dependencies>
+			<dependency>
+				<groupId>org.apache.aries.cdi</groupId>
+				<artifactId>org.apache.aries.cdi.bom</artifactId>
+				<version>${project.version}</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.felix</groupId>
+				<artifactId>org.apache.felix.gogo.bom</artifactId>
+				<version>1.0.2</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
+		</dependencies>
+	</dependencyManagement>
+
+	<profiles>
+		<profile>
+			<id>experimental</id>
+
+			<properties>
+				<maven.test.skip>false</maven.test.skip>
+				<johnzon.version>1.2.3-SNAPSHOT</johnzon.version>
+				<jax.rs.whiteboard.version>1.0.7-SNAPSHOT</jax.rs.whiteboard.version>
+			</properties>
+
+			<dependencies>
+				<dependency>
+					<groupId>org.apache.johnzon</groupId>
+					<artifactId>johnzon-osgi</artifactId>
+					<version>${johnzon.version}</version>
+					<scope>runtime</scope>
+				</dependency>
+				<dependency>
+					<groupId>org.apache.aries.jax.rs</groupId>
+					<artifactId>org.apache.aries.jax.rs.whiteboard</artifactId>
+					<version>${jax.rs.whiteboard.version}</version><!--$NO-MVN-MAN-VER$-->
+					<scope>runtime</scope>
+				</dependency>
+
+				<dependency>
+					<groupId>org.eclipse.microprofile.jwt</groupId>
+					<artifactId>microprofile-jwt-auth-tck</artifactId>
+					<version>${mp.jwt.auth.version}</version>
+					<scope>test</scope>
+					<exclusions>
+						<exclusion>
+							<groupId>javax.enterprise</groupId>
+							<artifactId>cdi-api</artifactId>
+						</exclusion>
+						<exclusion>
+							<groupId>org.testng</groupId>
+							<artifactId>testng</artifactId>
+						</exclusion>
+						<exclusion>
+							<groupId>org.jboss.shrinkwrap</groupId>
+							<artifactId>shrinkwrap-api</artifactId>
+						</exclusion>
+						<exclusion>
+							<groupId>org.jboss.arquillian.container</groupId>
+							<artifactId>arquillian-container-spi</artifactId>
+						</exclusion>
+						<exclusion>
+							<groupId>org.jboss.arquillian.container</groupId>
+							<artifactId>
+								arquillian-container-test-spi
+							</artifactId>
+						</exclusion>
+						<exclusion>
+							<groupId>org.jboss.arquillian.testng</groupId>
+							<artifactId>arquillian-testng-container</artifactId>
+						</exclusion>
+						<exclusion>
+							<groupId>org.jboss.shrinkwrap.resolver</groupId>
+							<artifactId>
+								shrinkwrap-resolver-depchain
+							</artifactId>
+						</exclusion>
+					</exclusions>
+				</dependency>
+				<dependency>
+					<groupId>org.eclipse.microprofile.jwt</groupId>
+					<artifactId>microprofile-jwt-auth-tck</artifactId>
+					<version>${mp.jwt.auth.version}</version>
+					<type>test-jar</type>
+					<scope>test</scope>
+					<exclusions>
+						<exclusion>
+							<groupId>javax.enterprise</groupId>
+							<artifactId>cdi-api</artifactId>
+						</exclusion>
+						<exclusion>
+							<groupId>org.testng</groupId>
+							<artifactId>testng</artifactId>
+						</exclusion>
+						<exclusion>
+							<groupId>org.jboss.shrinkwrap.resolver</groupId>
+							<artifactId>shrinkwrap-resolver-depchain</artifactId>
+						</exclusion>
+						<exclusion>
+							<groupId>org.jboss.shrinkwrap</groupId>
+							<artifactId>shrinkwrap-api</artifactId>
+						</exclusion>
+						<exclusion>
+							<groupId>org.jboss.arquillian.container</groupId>
+							<artifactId>arquillian-container-spi</artifactId>
+						</exclusion>
+						<exclusion>
+							<groupId>org.jboss.arquillian.container</groupId>
+							<artifactId>arquillian-container-test-spi</artifactId>
+						</exclusion>
+						<exclusion>
+							<groupId>org.jboss.arquillian.testng</groupId>
+							<artifactId>arquillian-testng-container</artifactId>
+						</exclusion>
+					</exclusions>
+				</dependency>
+
+				<dependency>
+					<groupId>org.osgi</groupId>
+					<artifactId>org.osgi.test.assertj</artifactId>
+					<version>1.0.0-SNAPSHOT</version>
+					<scope>test</scope>
+				</dependency>
+				<dependency>
+					<groupId>org.osgi</groupId>
+					<artifactId>org.osgi.test.junit4</artifactId>
+					<version>1.0.0-SNAPSHOT</version>
+					<scope>test</scope>
+				</dependency>
+				<dependency>
+					<groupId>org.apache.httpcomponents</groupId>
+					<artifactId>httpclient-osgi</artifactId>
+					<version>4.5.3</version>
+					<scope>test</scope>
+				</dependency>
+				<dependency>
+					<groupId>org.apache.httpcomponents</groupId>
+					<artifactId>httpcore-osgi</artifactId>
+					<version>4.4.6</version>
+					<scope>test</scope>
+				</dependency>
+			</dependencies>
+
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>biz.aQute.bnd</groupId>
+						<artifactId>bnd-maven-plugin</artifactId>
+						<executions>
+							<!-- Integration Test Configuration -->
+							<execution>
+								<id>bnd-process-test</id>
+								<goals>
+									<goal>bnd-process-test</goal>
+								</goals>
+								<configuration>
+									<bnd><![CDATA[
+										p = org.apache.aries.cdi.extension.mp.jwt.test
+										Export-Package: !${p}.tb*,${p}.*
+										Import-Package:	\
+											!net.jcip.annotations.*,\
+											!org.testng.*,\
+											!org.bouncycastle.*,\
+											*
+										Private-Package: \
+											com.nimbusds.*,\
+											net.minidev.json.*,\
+											org.eclipse.microprofile.jwt.tck.util
+										-make: (*).(jar); type=bnd; recipe="${.}/bnd/$1.bnd"
+										-includeresource: \
+											@microprofile-jwt-auth-tck-[0-9.]*-tests.jar!/privateKey.pem,\
+											@microprofile-jwt-auth-tck-[0-9.]*-tests.jar!/Token2.json,\
+											tb01.jar
+										-fixupmessages: "Split package...";is:=ignore
+									]]></bnd>
+									<testCases>junit4</testCases>
+									<includeClassesDir>false</includeClassesDir>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+					<!-- This dynamically calculates all the things we need to run our code. -->
+					<plugin>
+						<groupId>biz.aQute.bnd</groupId>
+						<artifactId>bnd-resolver-maven-plugin</artifactId>
+						<configuration>
+							<bundles>
+								<bundle>target/${project.build.finalName}-tests.jar</bundle>
+							</bundles>
+							<failOnChanges>false</failOnChanges>
+							<includeDependencyManagement>true</includeDependencyManagement>
+							<reportOptional>false</reportOptional>
+							<scopes>
+								<scope>compile</scope>
+								<scope>runtime</scope>
+								<scope>test</scope>
+							</scopes>
+						</configuration>
+						<executions>
+							<execution>
+								<id>resolve-test-owb</id>
+								<phase>pre-integration-test</phase>
+								<goals>
+									<goal>resolve</goal>
+								</goals>
+								<configuration>
+									<bndruns>
+										<bndrun>owb-itest.bndrun</bndrun>
+									</bndruns>
+								</configuration>
+							</execution>
+							<execution>
+								<id>resolve-test-weld</id>
+								<phase>pre-integration-test</phase>
+								<goals>
+									<goal>resolve</goal>
+								</goals>
+								<configuration>
+									<bndruns>
+										<bndrun>weld-itest.bndrun</bndrun>
+									</bndruns>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+					<!-- This is the plugin runs the OSGi integration tests. -->
+					<plugin>
+						<groupId>biz.aQute.bnd</groupId>
+						<artifactId>bnd-testing-maven-plugin</artifactId>
+						<configuration>
+							<bundles>
+								<bundle>target/${project.build.finalName}-tests.jar</bundle>
+							</bundles>
+							<failOnChanges>false</failOnChanges>
+							<includeDependencyManagement>true</includeDependencyManagement>
+							<resolve>false</resolve>
+							<scopes>
+								<scope>compile</scope>
+								<scope>runtime</scope>
+								<scope>test</scope>
+							</scopes>
+						</configuration>
+						<executions>
+							<execution>
+								<id>testing-owb</id>
+								<goals>
+									<goal>testing</goal>
+								</goals>
+								<configuration>
+									<bndruns>
+										<bndrun>owb-itest.bndrun</bndrun>
+									</bndruns>
+								</configuration>
+							</execution>
+							<execution>
+								<id>testing-weld</id>
+								<goals>
+									<goal>testing</goal>
+								</goals>
+								<configuration>
+									<bndruns>
+										<bndrun>weld-itest.bndrun</bndrun>
+									</bndruns>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+					<!-- <plugin>
+						<groupId>biz.aQute.bnd</groupId>
+						<artifactId>bnd-run-maven-plugin</artifactId>
+						<configuration>
+							<bundles>
+								<bundle>target/${project.build.finalName}-tests.jar</bundle>
+							</bundles>
+							<includeDependencyManagement>true</includeDependencyManagement>
+							<scopes>
+								<scope>compile</scope>
+								<scope>runtime</scope>
+								<scope>test</scope>
+							</scopes>
+						</configuration>
+						<executions>
+							<execution>
+								<id>run-owb</id>
+								<goals>
+									<goal>run</goal>
+								</goals>
+								<configuration>
+									<bndrun>owb-itest.bndrun</bndrun>
+								</configuration>
+							</execution>
+							<execution>
+								<id>run-weld</id>
+								<goals>
+									<goal>run</goal>
+								</goals>
+								<configuration>
+									<bndrun>weld-itest.bndrun</bndrun>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin> -->
+					<plugin>
+						<artifactId>maven-resources-plugin</artifactId>
+						<executions>
+							<execution>
+								<id>default-testResources</id>
+								<phase>process-test-resources</phase>
+							</execution>
+						</executions>
+					</plugin>
+					<plugin>
+						<artifactId>maven-compiler-plugin</artifactId>
+						<executions>
+							<execution>
+								<id>default-testCompile</id>
+								<phase>test-compile</phase>
+							</execution>
+						</executions>
+					</plugin>
+					<plugin>
+						<artifactId>maven-jar-plugin</artifactId>
+						<executions>
+							<execution>
+								<id>test-jar</id>
+								<goals>
+									<goal>test-jar</goal>
+								</goals>
+								<configuration>
+									<archive>
+										<manifestFile>${project.build.testOutputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+									</archive>
+									<excludes>
+										<exclude>org/apache/aries/cdi/extension/mp/jwt/test/tb*/**</exclude>
+									</excludes>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+	</profiles>
+</project>
diff --git a/cdi-extension-mp-jwt-auth/src/main/java/org/apache/aries/cdi/extension/mp/jwt/MPJwtAuthExtension.java b/cdi-extension-mp-jwt-auth/src/main/java/org/apache/aries/cdi/extension/mp/jwt/MPJwtAuthExtension.java
new file mode 100644
index 0000000..0ad32a1
--- /dev/null
+++ b/cdi-extension-mp-jwt-auth/src/main/java/org/apache/aries/cdi/extension/mp/jwt/MPJwtAuthExtension.java
@@ -0,0 +1,348 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.extension.mp.jwt;
+
+import static java.lang.String.format;
+import static java.util.Optional.ofNullable;
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static org.apache.aries.cdi.extension.mp.jwt.MPJwtAuthExtension.EXTENSION_NAME;
+import static org.apache.aries.cdi.extension.mp.jwt.MPJwtAuthExtension.EXTENSION_VERSION;
+import static org.osgi.framework.Constants.SCOPE_PROTOTYPE;
+import static org.osgi.framework.Constants.SERVICE_BUNDLEID;
+import static org.osgi.framework.Constants.SERVICE_DESCRIPTION;
+import static org.osgi.framework.Constants.SERVICE_SCOPE;
+import static org.osgi.framework.Constants.SERVICE_VENDOR;
+import static org.osgi.service.cdi.CDIConstants.CDI_EXTENSION_PROPERTY;
+import static org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_ASYNC_SUPPORTED;
+import static org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_NAME;
+import static org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN;
+import static org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants.JAX_RS_APPLICATION_SELECT;
+import static org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants.JAX_RS_EXTENSION;
+import static org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants.JAX_RS_MEDIA_TYPE;
+import static org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants.JAX_RS_NAME;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.BiConsumer;
+
+import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.Any;
+import javax.enterprise.inject.spi.AfterDeploymentValidation;
+import javax.enterprise.inject.spi.AnnotatedType;
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.enterprise.inject.spi.BeforeShutdown;
+import javax.enterprise.inject.spi.Extension;
+import javax.enterprise.inject.spi.ProcessAnnotatedType;
+import javax.enterprise.inject.spi.WithAnnotations;
+import javax.enterprise.inject.spi.configurator.AnnotatedTypeConfigurator;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.DynamicFeature;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+
+import org.apache.aries.cdi.extra.propertytypes.JaxrsExtensionSelect;
+import org.apache.aries.cdi.spi.configuration.Configuration;
+import org.apache.geronimo.microprofile.impl.jwtauth.cdi.GeronimoJwtAuthExtension;
+import org.apache.geronimo.microprofile.impl.jwtauth.config.GeronimoJwtAuthConfig;
+import org.apache.geronimo.microprofile.impl.jwtauth.jaxrs.GeronimoJwtAuthExceptionMapper;
+import org.apache.geronimo.microprofile.impl.jwtauth.jaxrs.JAXRSRequestForwarder;
+import org.apache.geronimo.microprofile.impl.jwtauth.jaxrs.RolesAllowedFeature;
+import org.apache.geronimo.microprofile.impl.jwtauth.servlet.GeronimoJwtAuthFilter;
+import org.eclipse.microprofile.auth.LoginConfig;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
+
+import aQute.bnd.annotation.spi.ServiceProvider;
+
+@ServiceProvider(
+	attribute = {
+		CDI_EXTENSION_PROPERTY + '=' + EXTENSION_NAME,
+		SERVICE_SCOPE + '=' + SCOPE_PROTOTYPE,
+		SERVICE_VENDOR + "=Apache Software Foundation",
+		"version:Version=" + EXTENSION_VERSION
+	},
+	uses = Extension.class,
+	value = Extension.class
+)
+public class MPJwtAuthExtension extends GeronimoJwtAuthExtension implements BiConsumer<HttpServletRequest, Runnable> {
+
+	public final static String EXTENSION_NAME = "eclipse.microprofile.jwt-auth";
+	public final static String EXTENSION_VERSION = "1.1.1";
+
+	@SuppressWarnings("serial")
+	private final static Set<String> defaultSelects = new HashSet<String>() {{
+		add(format("(%s=%s)", JAX_RS_NAME, "jwt.roles.allowed"));
+		add(format("(%s=%s)", JAX_RS_NAME, "jwt.request.forwarder"));
+		add(format("(%s=%s)", JAX_RS_NAME, "jwt.exception.mapper"));
+		add(format("(&(objectClass=%s)(%s=%s))", MessageBodyReader.class.getName(), JAX_RS_MEDIA_TYPE, APPLICATION_JSON));
+		add(format("(&(objectClass=%s)(%s=%s))", MessageBodyWriter.class.getName(), JAX_RS_MEDIA_TYPE, APPLICATION_JSON));
+	}};
+
+	private volatile BundleContext bundleContext;
+	private volatile Configuration configuration;
+
+	void getBundleContext(@Observes BundleContext bundleContext) {
+		this.bundleContext = bundleContext;
+	}
+
+	void getConfiguration(@Observes Configuration configuration) {
+		this.configuration = configuration;
+	}
+
+	final List<AnnotatedType<? extends Application>> applications = new CopyOnWriteArrayList<>();
+
+	void addLoginConfigs(@Observes @WithAnnotations(LoginConfig.class) ProcessAnnotatedType<? extends Application> pat) {
+		AnnotatedType<? extends Application> annotatedType = pat.getAnnotatedType();
+
+		LoginConfig loginConfig = annotatedType.getAnnotation(LoginConfig.class);
+
+		if ("MP-JWT".equalsIgnoreCase(loginConfig.authMethod())) {
+			applications.add(pat.getAnnotatedType());
+
+			AnnotatedTypeConfigurator<? extends Application> configurator = pat.configureAnnotatedType();
+
+			Set<String> selectSet = ofNullable((String[])configuration.get(JaxrsWhiteboardConstants.JAX_RS_EXTENSION_SELECT)).map(selects -> {
+				Set<String> mergedSelects = new HashSet<>(defaultSelects);
+				if (selects.length > 0) {
+					mergedSelects.addAll(Arrays.asList(selects));
+				}
+				return mergedSelects;
+			}).orElse(defaultSelects);
+
+			JaxrsExtensionSelect jaxrsExtensionSelect = annotatedType.getAnnotation(JaxrsExtensionSelect.class);
+
+			if (jaxrsExtensionSelect != null) {
+				Arrays.asList(jaxrsExtensionSelect.value()).forEach(selectSet::add);
+				configurator.remove(jaxrsExtensionSelect::equals);
+			}
+
+			configurator.add(JaxrsExtensionSelect.Literal.of(selectSet.toArray(new String[0])));
+		}
+	}
+
+	@Override
+	public void accept(final HttpServletRequest t, final Runnable u) {
+		execute(t, new ServletRunnable() {
+			@Override
+			public void run() throws ServletException, IOException {
+				u.run();
+			}
+		});
+	}
+
+	void registerSecurityExtensions(
+		@Observes AfterDeploymentValidation adv, BeanManager beanManager) {
+
+		try {
+			registerHttpWhiteboardJwtAuthFilter(beanManager);
+			registerJaxrsExceptionMapper(beanManager);
+			registerJaxrsRequestForwarder(beanManager);
+			registerJaxrsRolesAllowed(beanManager);
+		}
+		catch (Throwable t) {
+			adv.addDeploymentProblem(t);
+		}
+	}
+
+	void beforeShutdown(@Observes BeforeShutdown bs) {
+		unregister(_exceptionMapperRegistration);
+		unregister(_requestForwarderRegistration);
+		unregister(_rolesAllowedRegistration);
+		unregister(_jwtAuthFilterRegistration);
+		_cccs.forEach(CreationalContext::release);
+	}
+
+	void registerHttpWhiteboardJwtAuthFilter(BeanManager beanManager) {
+		final GeronimoJwtAuthConfig config = GeronimoJwtAuthConfig.create();
+		final boolean forceSetup = "true".equalsIgnoreCase(config.read("filter.active", "false"));
+		if (forceSetup || !applications.isEmpty()) {
+			registerJwtAuthFilter(config, beanManager);
+		}
+	}
+
+	void registerJwtAuthFilter(GeronimoJwtAuthConfig config, BeanManager beanManager) {
+		Dictionary<String, Object> properties = new Hashtable<>();
+
+		properties.put(SERVICE_DESCRIPTION, "Aries CDI - MP JWT Auth Servlet Filter");
+		properties.put(SERVICE_VENDOR, "Apache Software Foundation");
+		properties.put(HTTP_WHITEBOARD_FILTER_NAME, "geronimo-microprofile-jwt-auth-filter");
+		properties.put(HTTP_WHITEBOARD_FILTER_PATTERN, config.read("filter.mapping.default", "/*"));
+		properties.put(HTTP_WHITEBOARD_FILTER_ASYNC_SUPPORTED, true);
+		properties.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE - 1000);
+
+		final GeronimoJwtAuthFilter jwtAuthFilter = get(GeronimoJwtAuthFilter.class, beanManager);
+
+		_jwtAuthFilterRegistration = bundleContext.registerService(
+			Filter.class,
+			new Filter() {
+				final Filter delegate = jwtAuthFilter;
+				final ClassLoader loader = bundleContext.getBundle().adapt(BundleWiring.class).getClassLoader();
+
+				@Override
+				public void init(FilterConfig arg0) throws ServletException {
+					Thread currentThread = Thread.currentThread();
+					ClassLoader current = currentThread.getContextClassLoader();
+
+					try {
+						currentThread.setContextClassLoader(loader);
+						delegate.init(arg0);
+					}
+					finally {
+						currentThread.setContextClassLoader(current);
+					}
+				}
+
+				@Override
+				public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
+						throws IOException, ServletException {
+
+					Thread currentThread = Thread.currentThread();
+					ClassLoader current = currentThread.getContextClassLoader();
+
+					try {
+						currentThread.setContextClassLoader(loader);
+						delegate.doFilter(arg0, arg1, arg2);
+					}
+					finally {
+						currentThread.setContextClassLoader(current);
+					}
+				}
+
+				@Override
+				public void destroy() {
+					Thread currentThread = Thread.currentThread();
+					ClassLoader current = currentThread.getContextClassLoader();
+
+					try {
+						currentThread.setContextClassLoader(loader);
+						delegate.destroy();
+					}
+					finally {
+						currentThread.setContextClassLoader(current);
+					}
+				}
+
+			}, properties);
+	}
+
+	void registerJaxrsRolesAllowed(BeanManager beanManager) {
+		Dictionary<String, Object> properties = new Hashtable<>();
+
+		properties.put(SERVICE_DESCRIPTION, "Aries CDI - MP JWT Auth Roles Allowed");
+		properties.put(SERVICE_VENDOR, "Apache Software Foundation");
+		properties.put(JAX_RS_APPLICATION_SELECT, applicationSelectFilter("(!(jwt.roles.allowed=false))"));
+		properties.put(JAX_RS_EXTENSION, Boolean.TRUE);
+		properties.put(JAX_RS_NAME, "jwt.roles.allowed");
+
+		RolesAllowedFeature rolesAllowed = get(RolesAllowedFeature.class, beanManager);
+
+		_rolesAllowedRegistration = bundleContext.registerService(
+			DynamicFeature.class, rolesAllowed, properties);
+	}
+
+	void registerJaxrsRequestForwarder(BeanManager beanManager) {
+		Dictionary<String, Object> properties = new Hashtable<>();
+
+		properties.put(SERVICE_DESCRIPTION, "Aries CDI - MP JWT Auth Request Forwarder");
+		properties.put(SERVICE_VENDOR, "Apache Software Foundation");
+		properties.put(JAX_RS_APPLICATION_SELECT, applicationSelectFilter("(!(jwt.request.forwarder=false))"));
+		properties.put(JAX_RS_EXTENSION, Boolean.TRUE);
+		properties.put(JAX_RS_NAME, "jwt.request.forwarder");
+
+		JAXRSRequestForwarder requestForwarder = get(JAXRSRequestForwarder.class, beanManager);
+
+		_requestForwarderRegistration = bundleContext.registerService(
+			ContainerRequestFilter.class, requestForwarder, properties);
+	}
+
+	void registerJaxrsExceptionMapper(BeanManager beanManager) {
+		Dictionary<String, Object> properties = new Hashtable<>();
+
+		properties.put(SERVICE_DESCRIPTION, "Aries CDI - MP JWT Auth Exception Mapper");
+		properties.put(SERVICE_VENDOR, "Apache Software Foundation");
+		properties.put(JAX_RS_APPLICATION_SELECT, applicationSelectFilter("(!(jwt.exception.mapper=false))"));
+		properties.put(JAX_RS_EXTENSION, Boolean.TRUE);
+		properties.put(JAX_RS_NAME, "jwt.exception.mapper");
+
+		GeronimoJwtAuthExceptionMapper exceptionMapper = get(GeronimoJwtAuthExceptionMapper.class, beanManager);
+
+		_exceptionMapperRegistration = bundleContext.registerService(
+			ExceptionMapper.class, exceptionMapper, properties);
+	}
+
+	String applicationSelectFilter(String defaultValue) {
+		return ofNullable(
+			configuration.get(JAX_RS_APPLICATION_SELECT)
+		).map(
+			String.class::cast
+		).orElse(
+			format("(&(%s=%s)%s)", SERVICE_BUNDLEID, bundleContext.getBundle().getBundleId(), defaultValue)
+		);
+	}
+
+	static void unregister(ServiceRegistration<?> reg) {
+		if (reg != null) {
+			try {
+				reg.unregister();
+			}
+			catch (IllegalStateException ise) {
+				//
+			}
+		}
+	}
+
+	<T> T get(Class<T> clazz, BeanManager beanManager) {
+		Set<Bean<?>> beans = beanManager.getBeans(clazz, Any.Literal.INSTANCE);
+		@SuppressWarnings("unchecked")
+		Bean<T> bean = (Bean<T>)beanManager.resolve(beans);
+		CreationalContext<T> ccc = beanManager.createCreationalContext(bean);
+		try {
+			return beanManager.getContext(bean.getScope()).get(bean, ccc);
+		}
+		finally {
+			if (!beanManager.isNormalScope(bean.getScope())) {
+				_cccs.add(ccc);
+			}
+		}
+	}
+
+	private final List<CreationalContext<?>> _cccs = new CopyOnWriteArrayList<>();
+
+	private volatile ServiceRegistration<?> _exceptionMapperRegistration;
+	private volatile ServiceRegistration<?> _jwtAuthFilterRegistration;
+	private volatile ServiceRegistration<?> _requestForwarderRegistration;
+	private volatile ServiceRegistration<?> _rolesAllowedRegistration;
+
+}
diff --git a/cdi-extension-mp-jwt-auth/src/main/java/org/apache/aries/cdi/extension/mp/jwt/package-info.java b/cdi-extension-mp-jwt-auth/src/main/java/org/apache/aries/cdi/extension/mp/jwt/package-info.java
new file mode 100644
index 0000000..1f269e3
--- /dev/null
+++ b/cdi-extension-mp-jwt-auth/src/main/java/org/apache/aries/cdi/extension/mp/jwt/package-info.java
@@ -0,0 +1,62 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@Capability(
+	attribute = "objectClass:List<String>=javax.enterprise.inject.spi.Extension",
+	namespace = SERVICE_NAMESPACE
+)
+@Capability(
+	namespace = CDI_EXTENSION_PROPERTY,
+	name = EXTENSION_NAME,
+	uses= {
+		javax.annotation.Priority.class,
+		javax.enterprise.event.Observes.class,
+		javax.enterprise.inject.spi.Extension.class
+	},
+	version = EXTENSION_VERSION,
+	attribute = {
+		"aries.cdi.extension.bean.classes:List<String>='"
+			+ "org.apache.geronimo.microprofile.impl.jwtauth.jwt.ContextualJsonWebToken,"
+			+ "org.apache.geronimo.microprofile.impl.jwtauth.jwt.DateValidator,"
+			+ "org.apache.geronimo.microprofile.impl.jwtauth.config.GeronimoJwtAuthConfig,"
+			+ "org.apache.geronimo.microprofile.impl.jwtauth.jaxrs.GeronimoJwtAuthExceptionMapper,"
+			+ "org.apache.geronimo.microprofile.impl.jwtauth.servlet.GeronimoJwtAuthFilter,"
+			+ "org.apache.geronimo.microprofile.impl.jwtauth.jaxrs.GroupMapper,"
+			+ "org.apache.geronimo.microprofile.impl.jwtauth.jaxrs.JAXRSRequestForwarder,"
+			+ "org.apache.geronimo.microprofile.impl.jwtauth.jwt.JwtParser,"
+			+ "org.apache.geronimo.microprofile.impl.jwtauth.servlet.JwtRequest,"
+			+ "org.apache.geronimo.microprofile.impl.jwtauth.jwt.KidMapper,"
+			+ "org.apache.geronimo.microprofile.impl.jwtauth.jaxrs.ResponseBuilder,"
+			+ "org.apache.geronimo.microprofile.impl.jwtauth.jaxrs.RolesAllowedFeature,"
+			+ "org.apache.geronimo.microprofile.impl.jwtauth.jwt.SignatureValidator,"
+			+ "org.apache.geronimo.microprofile.impl.jwtauth.servlet.TokenAccessor'"
+	}
+)
+@JSONRequired
+@RequireCDIExtension("aries.cdi.http")
+@RequireCDIExtension("aries.cdi.jaxrs")
+@RequireCDIExtender
+@RequireJaxrsWhiteboard
+package org.apache.aries.cdi.extension.mp.jwt;
+
+import static org.apache.aries.cdi.extension.mp.jwt.MPJwtAuthExtension.EXTENSION_NAME;
+import static org.apache.aries.cdi.extension.mp.jwt.MPJwtAuthExtension.EXTENSION_VERSION;
+import static org.osgi.namespace.service.ServiceNamespace.SERVICE_NAMESPACE;
+import static org.osgi.service.cdi.CDIConstants.CDI_EXTENSION_PROPERTY;
+
+import org.apache.aries.cdi.extra.RequireCDIExtension;
+import org.apache.aries.cdi.extra.propertytypes.JSONRequired;
+import org.osgi.annotation.bundle.Capability;
+import org.osgi.service.cdi.annotations.RequireCDIExtender;
+import org.osgi.service.jaxrs.whiteboard.annotations.RequireJaxrsWhiteboard;
diff --git a/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/BaseTestCase.java b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/BaseTestCase.java
new file mode 100644
index 0000000..b5f6d0f
--- /dev/null
+++ b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/BaseTestCase.java
@@ -0,0 +1,129 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.extension.mp.jwt.test;
+
+import static java.util.Optional.ofNullable;
+import static org.osgi.test.common.filter.Filters.format;
+
+import java.util.function.Supplier;
+
+import org.junit.Rule;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cdi.runtime.CDIComponentRuntime;
+import org.osgi.test.junit4.context.BundleContextRule;
+import org.osgi.test.junit4.service.ServiceUseRule;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+public abstract class BaseTestCase {
+
+	public static final long timeout = 500;
+
+	@Rule
+	public BundleContextRule bcr = new BundleContextRule();
+	@Rule
+	public ServiceUseRule<CDIComponentRuntime> ccrr = new ServiceUseRule.Builder<CDIComponentRuntime>(CDIComponentRuntime.class, bcr).build();
+
+	@Rule
+	public TestWatcher watchman= new TestWatcher() {
+		@Override
+		protected void failed(Throwable e, Description description) {
+			System.out.printf("--------- TEST: %s#%s [%s]%n", description.getTestClass(), description.getMethodName(), "FAILED");
+		}
+
+		@Override
+		protected void succeeded(Description description) {
+			System.out.printf("--------- TEST: %s#%s [%s]%n", description.getTestClass(), description.getMethodName(), "PASSED");
+		}
+	};
+
+	public <S,T> CloseableTracker<S, T> track(Filter filter) {
+		CloseableTracker<S, T> tracker = new CloseableTracker<>(bcr.getBundleContext(), filter);
+		tracker.open();
+		return tracker;
+	}
+
+	public <S,T> CloseableTracker<S, T> track(String pattern, Object... objects) {
+		return track(format(pattern, objects));
+	}
+
+	public <S> CloseableTracker<S, ServiceReference<S>> trackSR(String pattern, Object... objects) {
+		return trackSR(format(pattern, objects));
+	}
+
+	public <S> CloseableTracker<S, ServiceReference<S>> trackSR(Filter filter) {
+		CloseableTracker<S, ServiceReference<S>> tracker = new CloseableTracker<>(bcr.getBundleContext(), filter, new ServiceTrackerCustomizer<S, ServiceReference<S>>() {
+
+			@Override
+			public ServiceReference<S> addingService(ServiceReference<S> reference) {
+				return reference;
+			}
+
+			@Override
+			public void modifiedService(ServiceReference<S> reference, ServiceReference<S> service) {
+			}
+
+			@Override
+			public void removedService(ServiceReference<S> reference, ServiceReference<S> service) {
+			}
+
+		});
+		tracker.open();
+		return tracker;
+	}
+
+	public long getChangeCount(ServiceReference<?> reference) {
+		return ofNullable(
+			reference.getProperty("service.changecount")
+		).map(
+			Long.class::cast
+		).orElseGet(
+			() -> new Long(-1l)
+		).longValue();
+	}
+
+	public static <T> T tccl(ClassLoader classLoader, Supplier<T> supplier) {
+		Thread currentThread = Thread.currentThread();
+		ClassLoader original = currentThread.getContextClassLoader();
+		try {
+			currentThread.setContextClassLoader(classLoader);
+			return supplier.get();
+		}
+		finally {
+			currentThread.setContextClassLoader(original);
+		}
+	}
+
+	public static class CloseableTracker<S, T> extends ServiceTracker<S, T> implements AutoCloseable {
+
+		public CloseableTracker(BundleContext context, Filter filter) {
+			super(context, filter, null);
+		}
+
+		public CloseableTracker(BundleContext context, Filter filter, ServiceTrackerCustomizer<S, T> customizer) {
+			super(context, filter, customizer);
+		}
+
+		@Override
+		public void close() {
+			super.close();
+		}
+
+	}
+}
diff --git a/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/CookieTest.java b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/CookieTest.java
new file mode 100644
index 0000000..4df619c
--- /dev/null
+++ b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/CookieTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.aries.cdi.extension.mp.jwt.test;
+
+import static javax.ws.rs.core.MediaType.TEXT_PLAIN_TYPE;
+import static org.junit.Assert.assertEquals;
+
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.Cookie;
+
+import org.eclipse.microprofile.jwt.tck.util.TokenUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.osgi.test.junit4.service.ServiceUseRule;
+
+// NOTE: reuses tck resources and token generation
+public class CookieTest extends MpJwtAuthTests {
+	@Rule
+	public ServiceUseRule<ClientBuilder> cbr = new ServiceUseRule.Builder<>(ClientBuilder.class) //
+		.build();
+
+	@Test
+	public void test() throws Exception {
+		final ClientBuilder cb = cbr.getService();
+		cb.connectTimeout(1000, TimeUnit.SECONDS);
+		cb.readTimeout(1000, TimeUnit.SECONDS);
+
+		final Client client = cb.build();
+
+		try {
+			final String token = TokenUtils.generateTokenString("/Token2.json");
+			final String serverToken = client.target(getJaxrsEndpoint())
+					.path("passthrough")
+					.request(TEXT_PLAIN_TYPE)
+					.cookie(new Cookie("Bearer", token))
+					.get(String.class);
+			assertEquals(serverToken, token);
+		} finally {
+			client.close();
+		}
+	}
+}
diff --git a/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/HttpBaseTestCase.java b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/HttpBaseTestCase.java
new file mode 100644
index 0000000..6383425
--- /dev/null
+++ b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/HttpBaseTestCase.java
@@ -0,0 +1,86 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.extension.mp.jwt.test;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.InputStream;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.osgi.services.HttpClientBuilderFactory;
+import org.assertj.core.util.Arrays;
+import org.junit.Rule;
+import org.osgi.service.http.runtime.HttpServiceRuntime;
+import org.osgi.service.http.runtime.dto.ServletContextDTO;
+import org.osgi.service.http.runtime.dto.ServletDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+import org.osgi.test.junit4.service.ServiceUseRule;
+
+public abstract class HttpBaseTestCase extends BaseTestCase {
+
+	@Rule
+	public ServiceUseRule<HttpServiceRuntime> hsrr = new ServiceUseRule.Builder<HttpServiceRuntime>(HttpServiceRuntime.class, bcr).build();
+	@Rule
+	public ServiceUseRule<HttpClientBuilderFactory> hcbfr = new ServiceUseRule.Builder<HttpClientBuilderFactory>(HttpClientBuilderFactory.class, bcr).build();
+
+	public String getHttpEndpoint() {
+		String[] endpoints = (String[])hsrr.getServiceReference().getProperty("osgi.http.endpoint");
+
+		if (endpoints == null || endpoints.length == 0) {
+			String port = (String)hsrr.getServiceReference().getProperty("org.osgi.service.http.port");
+			return "http://localhost:" + port;
+		}
+
+		return endpoints[0];
+	}
+
+	public String read(HttpEntity entity) throws Exception {
+		if (entity == null) {
+			return null;
+		}
+
+		try (InputStream in = entity.getContent();
+			java.util.Scanner s = new java.util.Scanner(in)) {
+
+			s.useDelimiter("\\A");
+			return s.hasNext() ? s.next() : "";
+		}
+	}
+
+	public ServletDTO waitFor(String path) throws InterruptedException {
+		return waitFor(path, 20);
+	}
+
+	public ServletDTO waitFor(String path, int intervals) throws InterruptedException {
+		for (int j = intervals; j > 0; j--) {
+			for (ServletContextDTO scDTO : hsrr.getService().getRuntimeDTO().servletContextDTOs) {
+				if (scDTO.name.equals(HttpWhiteboardConstants.HTTP_WHITEBOARD_DEFAULT_CONTEXT_NAME)) {
+					for (ServletDTO sDTO : scDTO.servletDTOs) {
+						if (Arrays.asList(sDTO.patterns).contains(path)) {
+							return sDTO;
+						}
+					}
+				}
+			}
+
+			Thread.sleep(50);
+		}
+
+		assertTrue(String.format("%s not found in time", path), false);
+
+		return null;
+	}
+
+}
diff --git a/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/JaxRsAsyncTest.java b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/JaxRsAsyncTest.java
new file mode 100644
index 0000000..24ffe7f
--- /dev/null
+++ b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/JaxRsAsyncTest.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.aries.cdi.extension.mp.jwt.test;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
+import static org.junit.Assert.assertEquals;
+import static org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants.JAX_RS_MEDIA_TYPE;
+
+import java.util.concurrent.TimeUnit;
+
+import javax.json.JsonObject;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.ext.MessageBodyReader;
+
+import org.eclipse.microprofile.jwt.tck.util.TokenUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.osgi.test.junit4.service.ServiceUseRule;
+
+// NOTE: reuses tck resources and token generation
+public class JaxRsAsyncTest extends MpJwtAuthTests {
+	@Rule
+	@SuppressWarnings("rawtypes")
+	public ServiceUseRule<MessageBodyReader> mbr = new ServiceUseRule.Builder<>(MessageBodyReader.class) //
+		.filter("(%s=%s)", JAX_RS_MEDIA_TYPE, APPLICATION_JSON)
+		.build();
+	@Rule
+	public ServiceUseRule<ClientBuilder> cbr = new ServiceUseRule.Builder<>(ClientBuilder.class) //
+		.build();
+
+	@Test
+	public void runAsync() throws Exception {
+		final ClientBuilder cb = cbr.getService();
+		cb.register(mbr.getService());
+		cb.connectTimeout(1000, TimeUnit.SECONDS);
+		cb.readTimeout(1000, TimeUnit.SECONDS);
+
+		final Client client = cb.build();
+
+		try {
+			final String token = TokenUtils.generateTokenString("/Token2.json");
+			final JsonObject object = client.target(getJaxrsEndpoint())
+					.path("test/async")
+					.request(APPLICATION_JSON_TYPE)
+					.header("Authorization", "bearer " + token)
+					.get(JsonObject.class);
+			assertEquals(object.toString(), "{\"before\":\"sync=" + token + "\",\"after\":\"async=" + token + "\"}");
+		} finally {
+			client.close();
+		}
+	}
+}
diff --git a/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/JaxrsBaseTestCase.java b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/JaxrsBaseTestCase.java
new file mode 100644
index 0000000..1f1f9c1
--- /dev/null
+++ b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/JaxrsBaseTestCase.java
@@ -0,0 +1,50 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.extension.mp.jwt.test;
+
+import java.util.Collection;
+
+import javax.ws.rs.client.ClientBuilder;
+
+import org.junit.Rule;
+import org.osgi.service.jaxrs.runtime.JaxrsServiceRuntime;
+import org.osgi.service.jaxrs.runtime.JaxrsServiceRuntimeConstants;
+import org.osgi.test.junit4.service.ServiceUseRule;
+
+public abstract class JaxrsBaseTestCase extends HttpBaseTestCase {
+
+	@Rule
+	public ServiceUseRule<JaxrsServiceRuntime> jsrr = new ServiceUseRule.Builder<JaxrsServiceRuntime>(JaxrsServiceRuntime.class, bcr).build();
+	@Rule
+	public ServiceUseRule<ClientBuilder> cbr = new ServiceUseRule.Builder<ClientBuilder>(ClientBuilder.class, bcr).build();
+
+	public String getJaxrsEndpoint() {
+		Object endpointsObj = jsrr.getServiceReference().getProperty(
+			JaxrsServiceRuntimeConstants.JAX_RS_SERVICE_ENDPOINT);
+
+		if (endpointsObj instanceof String) {
+			return String.valueOf(endpointsObj);
+		}
+		else if (endpointsObj instanceof String[]) {
+			return ((String[])endpointsObj)[0];
+		}
+		else if (endpointsObj instanceof Collection) {
+			return String.valueOf(((Collection<?>)endpointsObj).iterator().next());
+		}
+
+		return null;
+	}
+
+}
diff --git a/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/MpJwtAuthTests.java b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/MpJwtAuthTests.java
new file mode 100644
index 0000000..5bb9094
--- /dev/null
+++ b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/MpJwtAuthTests.java
@@ -0,0 +1,39 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.extension.mp.jwt.test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.Before;
+import org.osgi.service.jaxrs.runtime.dto.RuntimeDTO;
+
+public class MpJwtAuthTests extends JaxrsBaseTestCase {
+
+	@Before
+	public void setUp() throws Exception {
+		bcr.installBundle("tb01.jar");
+
+		int count = 100;
+		RuntimeDTO runtimeDTO;
+		while ((runtimeDTO = jsrr.getService().getRuntimeDTO()).defaultApplication.resourceDTOs.length < 3 && (count > 0)) {
+			count--;
+			Thread.sleep(100);
+		}
+
+		assertThat(runtimeDTO.defaultApplication.resourceDTOs).extracting("name").contains(
+			"AsyncEndpoint", "PassthroughEndpoint", "TokenInspector");
+	}
+
+}
diff --git a/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/PreProvidedTokenTest.java b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/PreProvidedTokenTest.java
new file mode 100644
index 0000000..3fbf61d
--- /dev/null
+++ b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/PreProvidedTokenTest.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.aries.cdi.extension.mp.jwt.test;
+
+import static javax.ws.rs.core.MediaType.TEXT_PLAIN_TYPE;
+import static org.junit.Assert.assertEquals;
+
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+
+import org.junit.Test;
+import org.osgi.framework.ServiceObjects;
+
+// NOTE: reuses tck resources and token generation
+public class PreProvidedTokenTest extends MpJwtAuthTests {
+	@Test
+	public void runAsync() {
+		ServiceObjects<ClientBuilder> cbSO = bcr.getBundleContext().getServiceObjects(cbr.getServiceReference());
+		final ClientBuilder cb = cbSO.getService();
+		cb.connectTimeout(100, TimeUnit.SECONDS);
+		cb.readTimeout(100, TimeUnit.SECONDS);
+
+		final Client client = cb.build();
+
+		try {
+			final String value = client.target(getJaxrsEndpoint())
+					.path("inspector")
+					.queryParam("claim", "name")
+					.request(TEXT_PLAIN_TYPE)
+					.get(String.class)
+					.trim();
+			assertEquals("run-as", value);
+		} finally {
+			client.close();
+			cbSO.ungetService(cb);
+		}
+	}
+}
diff --git a/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/tb01/AsyncEndpoint.java b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/tb01/AsyncEndpoint.java
new file mode 100644
index 0000000..27b4f35
--- /dev/null
+++ b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/tb01/AsyncEndpoint.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.aries.cdi.extension.mp.jwt.test.tb01;
+
+import static java.util.concurrent.TimeUnit.MINUTES;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BiConsumer;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.json.Json;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+
+import org.eclipse.microprofile.jwt.JsonWebToken;
+
+@Path("test")
+@ApplicationScoped
+public class AsyncEndpoint {
+	@Inject
+	private JsonWebToken token;
+
+	@Inject // not perfect but CDI doesn't have any real propagation here
+	private BiConsumer<HttpServletRequest, Runnable> extension;
+
+	@GET
+	@Path("async")
+	@Produces(MediaType.APPLICATION_JSON)
+	public void async(@Suspended final AsyncResponse response,
+					@Context final HttpServletRequest request) {
+		final CountDownLatch latchBefore = new CountDownLatch(1);
+		final CountDownLatch latchResponse = new CountDownLatch(1);
+		final AtomicReference<String> before = new AtomicReference<>();
+
+		new Thread(() -> extension.accept(request, () -> {
+			try {
+				final String after = capture("async");
+				latchBefore.countDown();
+				try {
+					latchResponse.await(1, MINUTES);
+				} catch (final InterruptedException e) {
+					Thread.currentThread().interrupt();
+				}
+				response.resume(Json.createObjectBuilder()
+						.add("before", before.get())
+						.add("after", after).build());
+			} catch (final Exception e) {
+				latchBefore.countDown();
+				response.resume(e);
+			}
+		})).start();
+
+		try {
+			latchBefore.await(1, MINUTES);
+		} catch (final InterruptedException e) {
+			Thread.currentThread().interrupt();
+		}
+		before.set(capture("sync"));
+		latchResponse.countDown();
+	}
+
+	private String capture(final String marker) {
+		return marker + "=" + token.getRawToken();
+	}
+}
diff --git a/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/tb01/PassthroughEndpoint.java b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/tb01/PassthroughEndpoint.java
new file mode 100644
index 0000000..b2bdf3a
--- /dev/null
+++ b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/tb01/PassthroughEndpoint.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.aries.cdi.extension.mp.jwt.test.tb01;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.eclipse.microprofile.jwt.JsonWebToken;
+
+@Path("passthrough")
+@ApplicationScoped
+public class PassthroughEndpoint {
+	@Inject
+	private JsonWebToken token;
+
+	@GET
+	@Produces(MediaType.TEXT_PLAIN)
+	public String passthrough() {
+		return token.getRawToken();
+	}
+}
diff --git a/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/tb01/RunAsFilter.java b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/tb01/RunAsFilter.java
new file mode 100644
index 0000000..311dc5e
--- /dev/null
+++ b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/tb01/RunAsFilter.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.aries.cdi.extension.mp.jwt.test.tb01;
+
+import static java.util.Collections.singleton;
+
+import java.io.IOException;
+import java.util.Set;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.annotation.WebFilter;
+
+import org.eclipse.microprofile.jwt.JsonWebToken;
+
+@WebFilter("/inspector")
+public class RunAsFilter implements Filter {
+	@Override
+	public void doFilter(final ServletRequest request, final ServletResponse response,
+						final FilterChain chain) throws IOException, ServletException {
+		request.setAttribute(JsonWebToken.class.getName(), new JsonWebToken() {
+			@Override
+			public String getName() {
+				return "run-as";
+			}
+
+			@Override
+			public Set<String> getClaimNames() {
+				return singleton("name");
+			}
+
+			@Override
+			@SuppressWarnings("unchecked")
+			public <T> T getClaim(final String s) {
+				return "name".equals(s) ? (T) "the-name" : null;
+			}
+		});
+		chain.doFilter(request, response);
+	}
+
+	@Override
+	public void destroy() {
+	}
+
+	@Override
+	public void init(FilterConfig arg0) throws ServletException {
+	}
+}
diff --git a/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/tb01/TCKApplication.java b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/tb01/TCKApplication.java
new file mode 100644
index 0000000..bef37b3
--- /dev/null
+++ b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/tb01/TCKApplication.java
@@ -0,0 +1,33 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.extension.mp.jwt.test.tb01;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.ApplicationScoped;
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+import org.eclipse.microprofile.auth.LoginConfig;
+
+@ApplicationPath("/")
+@ApplicationScoped
+@LoginConfig(authMethod = "MP-JWT", realmName = "TCK-MP-JWT")
+public class TCKApplication extends Application {
+
+	@PostConstruct
+	public void init() {
+		System.out.println("here");
+	}
+}
diff --git a/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/tb01/TokenInspector.java b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/tb01/TokenInspector.java
new file mode 100644
index 0000000..2be5ef3
--- /dev/null
+++ b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/tb01/TokenInspector.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.aries.cdi.extension.mp.jwt.test.tb01;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+
+import org.eclipse.microprofile.jwt.JsonWebToken;
+
+@Path("inspector")
+@ApplicationScoped
+public class TokenInspector {
+	@Inject
+	private JsonWebToken token;
+
+	@GET
+	@Produces(MediaType.TEXT_PLAIN)
+	public String inspect(@QueryParam("claim") final String name) {
+		return "name".equals(name) ? token.getName() : token.getClaim(name);
+	}
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/tb01/package-info.java
similarity index 83%
rename from cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java
rename to cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/tb01/package-info.java
index 315634c..06524be 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java
+++ b/cdi-extension-mp-jwt-auth/src/test/java/org/apache/aries/cdi/extension/mp/jwt/test/tb01/package-info.java
@@ -12,5 +12,7 @@
  * limitations under the License.
  */
 
-@org.osgi.service.cdi.annotations.Beans
-package org.apache.aries.cdi.test.tb16;
+@Beans
+package org.apache.aries.cdi.extension.mp.jwt.test.tb01;
+
+import org.osgi.service.cdi.annotations.Beans;
diff --git a/cdi-extension-mp-jwt-auth/weld-itest.bndrun b/cdi-extension-mp-jwt-auth/weld-itest.bndrun
new file mode 100644
index 0000000..3c96ec4
--- /dev/null
+++ b/cdi-extension-mp-jwt-auth/weld-itest.bndrun
@@ -0,0 +1,73 @@
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+
+-include: base-itest.bndrun
+#-runjdb: 8000
+
+-runrequires: \
+	osgi.identity;filter:='(osgi.identity=javax.ejb-api)',\
+	osgi.identity;filter:='(osgi.identity=javax.transaction-api)',\
+	osgi.identity;filter:='(osgi.identity=org.apache.aries.cdi.weld)'
+
+-runblacklist: \
+	osgi.identity;filter:='(osgi.identity=*owb*)'
+
+-runbundles: \
+	javax.ejb-api;version='[3.2.0,3.2.1)',\
+	javax.transaction-api;version='[1.2.0,1.2.1)',\
+	jboss-classfilewriter;version='[1.2.3,1.2.4)',\
+	org.apache.aries.cdi.extender;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.jaxrs;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.mp-config;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.mp-jwt-auth-tests;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.mp-jwt-auth;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.servlet.common;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.servlet.weld;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.spi;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extra;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.spi;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.weld;version='[1.1.0,1.1.1)',\
+	org.apache.aries.jax.rs.whiteboard;version='[1.0.7,1.0.8)',\
+	org.apache.aries.spifly.dynamic.framework.extension;version='[1.2.3,1.2.4)',\
+	org.apache.commons.io;version='[2.6.0,2.6.1)',\
+	org.apache.commons.logging;version='[1.2.0,1.2.1)',\
+	org.apache.felix.configadmin;version='[1.9.10,1.9.11)',\
+	org.apache.felix.converter;version='[1.0.12,1.0.13)',\
+	org.apache.felix.gogo.command;version='[1.1.0,1.1.1)',\
+	org.apache.felix.gogo.runtime;version='[1.1.2,1.1.3)',\
+	org.apache.felix.gogo.shell;version='[1.1.2,1.1.3)',\
+	org.apache.felix.http.jetty;version='[4.0.14,4.0.15)',\
+	org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
+	org.apache.geronimo.specs.geronimo-annotation_1.3_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-atinject_1.0_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-el_2.2_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-interceptor_1.2_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-jaxrs_2.1_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-jcdi_2.0_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-json_1.1_spec;version='[1.3.0,1.3.1)',\
+	org.apache.geronimo.specs.geronimo-jsonb_1.0_spec;version='[1.2.0,1.2.1)',\
+	org.apache.httpcomponents.httpclient;version='[4.5.3,4.5.4)',\
+	org.apache.httpcomponents.httpcore;version='[4.4.6,4.4.7)',\
+	org.apache.johnzon.core;version='[1.2.3,1.2.4)',\
+	org.apache.johnzon.jsonb;version='[1.2.3,1.2.4)',\
+	org.apache.johnzon.mapper;version='[1.2.3,1.2.4)',\
+	org.apache.johnzon.osgi;version='[1.2.3,1.2.4)',\
+	org.apache.servicemix.bundles.junit;version='[4.12.0,4.12.1)',\
+	org.assertj.core;version='[3.13.2,3.13.3)',\
+	org.jboss.logging.jboss-logging;version='[3.3.2,3.3.3)',\
+	org.jboss.weld.osgi-bundle;version='[3.0.5,3.0.6)',\
+	org.osgi.service.cdi;version='[1.0.0,1.0.1)',\
+	org.osgi.service.jaxrs;version='[1.0.0,1.0.1)',\
+	org.osgi.test.common;version='[1.0.0,1.0.1)',\
+	org.osgi.test.junit4;version='[1.0.0,1.0.1)',\
+	org.osgi.util.function;version='[1.1.0,1.1.1)',\
+	org.osgi.util.promise;version='[1.1.0,1.1.1)'
diff --git a/cdi-extension-mp-metrics/base-itest.bndrun b/cdi-extension-mp-metrics/base-itest.bndrun
new file mode 100644
index 0000000..b12de6f
--- /dev/null
+++ b/cdi-extension-mp-metrics/base-itest.bndrun
@@ -0,0 +1,50 @@
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+
+#-runjdb: 8000
+
+-standalone: true
+-runee: JavaSE-1.8
+-runfw: org.eclipse.osgi
+-runproperties: \
+	eclipse.log.enabled=false,\
+	logback.configurationFile=file:${.}/logback.xml,\
+	org.osgi.service.http.port=0,\
+	osgi.console=,\
+	tck.config.test.javaconfig.converter.stringvalues=foo,\
+	test.property.a=blah,\
+	test.property.b=,\
+	org.apache.felix.http.host=localhost
+
+-resolve.effective: resolve, active
+
+-runpath: \
+	ch.qos.logback.classic,\
+	ch.qos.logback.core,\
+	org.apache.felix.logback,\
+	slf4j.api
+
+-runsystempackages: \
+	org.slf4j;version=1.7.25,\
+	org.slf4j.event;version=1.7.25,\
+	org.slf4j.helpers;version=1.7.25,\
+	org.slf4j.spi;version=1.7.25,\
+	sun.invoke,\
+	sun.misc
+
+-runrequires.base: \
+	osgi.identity;filter:='(osgi.identity=${project.artifactId})',\
+	osgi.identity;filter:='(osgi.identity=${project.artifactId}-tests)',\
+	osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.command)'
+
+-runblacklist.base: \
+	osgi.identity;filter:='(osgi.identity=biz.aQute.bndlib)'
\ No newline at end of file
diff --git a/cdi-itests/bnd/tb22.bnd b/cdi-extension-mp-metrics/bnd/tb01.bnd
similarity index 91%
copy from cdi-itests/bnd/tb22.bnd
copy to cdi-extension-mp-metrics/bnd/tb01.bnd
index 1ec52c6..61c7009 100644
--- a/cdi-itests/bnd/tb22.bnd
+++ b/cdi-extension-mp-metrics/bnd/tb01.bnd
@@ -10,4 +10,4 @@
 #    See the License for the specific language governing permissions and
 #    limitations under the License.
 
-Export-Package: ${p}.tb22.*;-split-package:=first
+Export-Package: ${p}.tb01.*;-split-package:=first
diff --git a/cdi-extension-mp-metrics/logback.xml b/cdi-extension-mp-metrics/logback.xml
new file mode 100644
index 0000000..32104c5
--- /dev/null
+++ b/cdi-extension-mp-metrics/logback.xml
@@ -0,0 +1,41 @@
+<!--
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<configuration>
+	<!--  scan="true" scanPeriod="5 seconds" debug="true"> -->
+	<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
+		<resetJUL>true</resetJUL>
+	</contextListener>
+
+	<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<!-- <pattern>%d{HH:mm:ss.SSS} [%.15thread] %-5level %logger{36}:%line - %msg%n</pattern> -->
+			<pattern>[%.15thread] %-5level %logger{36}:%line - %msg%n</pattern>
+		</encoder>
+	</appender>
+
+	<!-- <logger name="Events.Service.cdi-itests" level="INFO"/> -->
+	<!-- <logger name="Events.Service.org.apache.aries.cdi" level="INFO"/> -->
+	<!-- <logger name="org.apache.aries.cdi" level="DEBUG"/> -->
+
+	<logger name="LogService.org.apache.aries.cdi.itests" level="OFF"/>
+	<logger name="org.eclipse" level="ERROR"/>
+	<logger name="org.jboss" level="ERROR"/>
+
+	<root level="ERROR">
+		<appender-ref ref="STDOUT" />
+	</root>
+</configuration>
diff --git a/cdi-extension-mp-metrics/owb-itest.bndrun b/cdi-extension-mp-metrics/owb-itest.bndrun
new file mode 100644
index 0000000..e7be03c
--- /dev/null
+++ b/cdi-extension-mp-metrics/owb-itest.bndrun
@@ -0,0 +1,66 @@
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+
+-include: base-itest.bndrun
+#-runjdb: 8000
+
+-runrequires: \
+	osgi.identity;filter:='(osgi.identity=org.apache.aries.cdi.owb)'
+
+-runblacklist: \
+	osgi.identity;filter:='(osgi.identity=*weld*)'
+
+-runbundles: \
+	openwebbeans-impl;version='[2.0.13,2.0.14)',\
+	openwebbeans-spi;version='[2.0.13,2.0.14)',\
+	org.apache.aries.cdi.extender;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.mp-metrics-tests;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.mp-metrics;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.spi;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extra;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.owb;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.spi;version='[1.1.0,1.1.1)',\
+	org.apache.aries.jax.rs.whiteboard;version='[1.0.7,1.0.8)',\
+	org.apache.aries.spifly.dynamic.framework.extension;version='[1.2.3,1.2.4)',\
+	org.apache.commons.logging;version='[1.2.0,1.2.1)',\
+	org.apache.felix.configadmin;version='[1.9.10,1.9.11)',\
+	org.apache.felix.converter;version='[1.0.12,1.0.13)',\
+	org.apache.felix.gogo.command;version='[1.1.0,1.1.1)',\
+	org.apache.felix.gogo.runtime;version='[1.1.2,1.1.3)',\
+	org.apache.felix.gogo.shell;version='[1.1.2,1.1.3)',\
+	org.apache.felix.http.jetty;version='[4.0.14,4.0.15)',\
+	org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
+	org.apache.geronimo.specs.geronimo-annotation_1.3_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-atinject_1.0_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-el_2.2_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-interceptor_1.2_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-jaxrs_2.1_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-jcdi_2.0_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-json_1.1_spec;version='[1.3.0,1.3.1)',\
+	org.apache.geronimo.specs.geronimo-jsonb_1.0_spec;version='[1.2.0,1.2.1)',\
+	org.apache.httpcomponents.httpclient;version='[4.5.3,4.5.4)',\
+	org.apache.httpcomponents.httpcore;version='[4.4.6,4.4.7)',\
+	org.apache.johnzon.core;version='[1.2.3,1.2.4)',\
+	org.apache.johnzon.jsonb;version='[1.2.3,1.2.4)',\
+	org.apache.johnzon.mapper;version='[1.2.3,1.2.4)',\
+	org.apache.johnzon.osgi;version='[1.2.3,1.2.4)',\
+	org.apache.servicemix.bundles.junit;version='[4.12.0,4.12.1)',\
+	org.apache.xbean.asm7-shaded;version='[4.13.0,4.13.1)',\
+	org.apache.xbean.bundleutils;version='[4.15.0,4.15.1)',\
+	org.apache.xbean.finder-shaded;version='[4.13.0,4.13.1)',\
+	org.assertj.core;version='[3.13.2,3.13.3)',\
+	org.osgi.service.cdi;version='[1.0.0,1.0.1)',\
+	org.osgi.service.jaxrs;version='[1.0.0,1.0.1)',\
+	org.osgi.test.common;version='[1.0.0,1.0.1)',\
+	org.osgi.test.junit4;version='[1.0.0,1.0.1)',\
+	org.osgi.util.function;version='[1.1.0,1.1.1)',\
+	org.osgi.util.promise;version='[1.1.0,1.1.1)'
diff --git a/cdi-extension-mp-metrics/pom.xml b/cdi-extension-mp-metrics/pom.xml
index dadbe7e..7b808d0 100644
--- a/cdi-extension-mp-metrics/pom.xml
+++ b/cdi-extension-mp-metrics/pom.xml
@@ -56,6 +56,9 @@
 						-cdiannotations:
 						-noclassforname: true
 						-fixupmessages: "Split package...";is:=ignore
+						-bundleannotations: \
+							!org.eclipse.microprofile.metrics.*,\
+							org.apache.aries.cdi.extension.mp.metrics.*
 					]]></bnd>
 				</configuration>
 			</plugin>
@@ -109,22 +112,20 @@
 
 	<dependencies>
 		<dependency>
-			<groupId>org.apache.aries.cdi</groupId>
-			<artifactId>org.apache.aries.cdi.extra</artifactId>
-			<version>${project.version}</version>
-		</dependency>
-		<dependency>
-			<groupId>org.apache.aries.cdi</groupId>
-			<artifactId>org.apache.aries.cdi.spi</artifactId>
-			<version>${project.version}</version>
-		</dependency>
-		<dependency>
 			<groupId>biz.aQute.bnd</groupId>
 			<artifactId>biz.aQute.bnd.annotation</artifactId>
 			<version>${bnd.version}</version>
 			<scope>provided</scope>
 		</dependency>
 		<dependency>
+			<groupId>org.apache.aries.cdi</groupId>
+			<artifactId>org.apache.aries.cdi.extra</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.aries.cdi</groupId>
+			<artifactId>org.apache.aries.cdi.spi</artifactId>
+		</dependency>
+		<dependency>
 			<groupId>org.apache.geronimo.specs</groupId>
 			<artifactId>geronimo-annotation_1.3_spec</artifactId>
 		</dependency>
@@ -145,24 +146,6 @@
 			<artifactId>geronimo-jcdi_2.0_spec</artifactId>
 		</dependency>
 		<dependency>
-			<groupId>org.apache.geronimo.specs</groupId>
-			<artifactId>geronimo-jsonb_1.0_spec</artifactId>
-		</dependency>
-		<dependency>
-			<groupId>org.apache.geronimo.specs</groupId>
-			<artifactId>geronimo-json_1.1_spec</artifactId>
-		</dependency>
-		<dependency>
-			<groupId>org.apache.johnzon</groupId>
-			<artifactId>johnzon-core</artifactId>
-			<version>1.2.2</version>
-		</dependency>
-		<dependency>
-			<groupId>org.apache.johnzon</groupId>
-			<artifactId>johnzon-jsonb</artifactId>
-			<version>1.2.2</version>
-		</dependency>
-		<dependency>
 			<groupId>org.eclipse.microprofile.metrics</groupId>
 			<artifactId>microprofile-metrics-api</artifactId>
 			<version>${mp.metrics.version}</version>
@@ -176,6 +159,14 @@
 		</dependency>
 		<dependency>
 			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.namespace.extender</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.namespace.implementation</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
 			<artifactId>org.osgi.namespace.service</artifactId>
 		</dependency>
 		<dependency>
@@ -196,4 +187,266 @@
 		</dependency>
 	</dependencies>
 
+	<dependencyManagement>
+		<dependencies>
+			<dependency>
+				<groupId>org.apache.aries.cdi</groupId>
+				<artifactId>org.apache.aries.cdi.bom</artifactId>
+				<version>${project.version}</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.felix</groupId>
+				<artifactId>org.apache.felix.gogo.bom</artifactId>
+				<version>1.0.2</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
+		</dependencies>
+	</dependencyManagement>
+
+	<profiles>
+		<profile>
+			<id>experimental</id>
+
+			<properties>
+				<maven.test.skip>false</maven.test.skip>
+				<johnzon.version>1.2.3-SNAPSHOT</johnzon.version>
+				<jax.rs.whiteboard.version>1.0.7-SNAPSHOT</jax.rs.whiteboard.version>
+			</properties>
+
+			<dependencies>
+				<dependency>
+					<groupId>org.apache.johnzon</groupId>
+					<artifactId>johnzon-osgi</artifactId>
+					<version>${johnzon.version}</version>
+					<scope>runtime</scope>
+				</dependency>
+				<dependency>
+					<groupId>org.apache.aries.jax.rs</groupId>
+					<artifactId>org.apache.aries.jax.rs.whiteboard</artifactId>
+					<version>${jax.rs.whiteboard.version}</version><!--$NO-MVN-MAN-VER$-->
+					<scope>runtime</scope>
+				</dependency>
+
+				<dependency>
+					<groupId>org.osgi</groupId>
+					<artifactId>org.osgi.service.http.whiteboard</artifactId>
+				</dependency>
+				<dependency>
+					<groupId>org.osgi</groupId>
+					<artifactId>org.osgi.test.assertj</artifactId>
+					<version>1.0.0-SNAPSHOT</version>
+					<scope>test</scope>
+				</dependency>
+				<dependency>
+					<groupId>org.osgi</groupId>
+					<artifactId>org.osgi.test.junit4</artifactId>
+					<version>1.0.0-SNAPSHOT</version>
+					<scope>test</scope>
+				</dependency>
+				<dependency>
+					<groupId>org.apache.httpcomponents</groupId>
+					<artifactId>httpclient-osgi</artifactId>
+					<version>4.5.3</version>
+					<scope>test</scope>
+				</dependency>
+				<dependency>
+					<groupId>org.apache.httpcomponents</groupId>
+					<artifactId>httpcore-osgi</artifactId>
+					<version>4.4.6</version>
+					<scope>test</scope>
+				</dependency>
+			</dependencies>
+
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>biz.aQute.bnd</groupId>
+						<artifactId>bnd-maven-plugin</artifactId>
+						<executions>
+							<!-- Integration Test Configuration -->
+							<execution>
+								<id>bnd-process-test</id>
+								<goals>
+									<goal>bnd-process-test</goal>
+								</goals>
+								<configuration>
+									<bnd><![CDATA[
+										p = org.apache.aries.cdi.extension.mp.metrics.test
+										Export-Package: !${p}.tb*,${p}.*
+										-make: (*).(jar); type=bnd; recipe="${.}/bnd/$1.bnd"
+										-includeresource: \
+											tb01.jar
+										-fixupmessages: "Split package...";is:=ignore
+									]]></bnd>
+									<testCases>junit4</testCases>
+									<includeClassesDir>false</includeClassesDir>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+					<!-- This dynamically calculates all the things we need to run our code. -->
+					<plugin>
+						<groupId>biz.aQute.bnd</groupId>
+						<artifactId>bnd-resolver-maven-plugin</artifactId>
+						<configuration>
+							<bundles>
+								<bundle>target/${project.build.finalName}-tests.jar</bundle>
+							</bundles>
+							<failOnChanges>false</failOnChanges>
+							<includeDependencyManagement>true</includeDependencyManagement>
+							<reportOptional>false</reportOptional>
+							<scopes>
+								<scope>compile</scope>
+								<scope>runtime</scope>
+								<scope>test</scope>
+							</scopes>
+						</configuration>
+						<executions>
+							<execution>
+								<id>resolve-test-owb</id>
+								<phase>pre-integration-test</phase>
+								<goals>
+									<goal>resolve</goal>
+								</goals>
+								<configuration>
+									<bndruns>
+										<bndrun>owb-itest.bndrun</bndrun>
+									</bndruns>
+								</configuration>
+							</execution>
+							<execution>
+								<id>resolve-test-weld</id>
+								<phase>pre-integration-test</phase>
+								<goals>
+									<goal>resolve</goal>
+								</goals>
+								<configuration>
+									<bndruns>
+										<bndrun>weld-itest.bndrun</bndrun>
+									</bndruns>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+					<!-- This is the plugin runs the OSGi integration tests. -->
+					<plugin>
+						<groupId>biz.aQute.bnd</groupId>
+						<artifactId>bnd-testing-maven-plugin</artifactId>
+						<configuration>
+							<bundles>
+								<bundle>target/${project.build.finalName}-tests.jar</bundle>
+							</bundles>
+							<failOnChanges>false</failOnChanges>
+							<includeDependencyManagement>true</includeDependencyManagement>
+							<resolve>false</resolve>
+							<scopes>
+								<scope>compile</scope>
+								<scope>runtime</scope>
+								<scope>test</scope>
+							</scopes>
+						</configuration>
+						<executions>
+							<execution>
+								<id>testing-owb</id>
+								<goals>
+									<goal>testing</goal>
+								</goals>
+								<configuration>
+									<bndruns>
+										<bndrun>owb-itest.bndrun</bndrun>
+									</bndruns>
+								</configuration>
+							</execution>
+							<execution>
+								<id>testing-weld</id>
+								<goals>
+									<goal>testing</goal>
+								</goals>
+								<configuration>
+									<bndruns>
+										<bndrun>weld-itest.bndrun</bndrun>
+									</bndruns>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+					<!-- <plugin>
+						<groupId>biz.aQute.bnd</groupId>
+						<artifactId>bnd-run-maven-plugin</artifactId>
+						<configuration>
+							<bundles>
+								<bundle>target/${project.build.finalName}-tests.jar</bundle>
+							</bundles>
+							<includeDependencyManagement>true</includeDependencyManagement>
+							<scopes>
+								<scope>compile</scope>
+								<scope>runtime</scope>
+								<scope>test</scope>
+							</scopes>
+						</configuration>
+						<executions>
+							<execution>
+								<id>run-owb</id>
+								<goals>
+									<goal>run</goal>
+								</goals>
+								<configuration>
+									<bndrun>owb-itest.bndrun</bndrun>
+								</configuration>
+							</execution>
+							<execution>
+								<id>run-weld</id>
+								<goals>
+									<goal>run</goal>
+								</goals>
+								<configuration>
+									<bndrun>weld-itest.bndrun</bndrun>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin> -->
+					<plugin>
+						<artifactId>maven-resources-plugin</artifactId>
+						<executions>
+							<execution>
+								<id>default-testResources</id>
+								<phase>process-test-resources</phase>
+							</execution>
+						</executions>
+					</plugin>
+					<plugin>
+						<artifactId>maven-compiler-plugin</artifactId>
+						<executions>
+							<execution>
+								<id>default-testCompile</id>
+								<phase>test-compile</phase>
+							</execution>
+						</executions>
+					</plugin>
+					<plugin>
+						<artifactId>maven-jar-plugin</artifactId>
+						<executions>
+							<execution>
+								<id>test-jar</id>
+								<goals>
+									<goal>test-jar</goal>
+								</goals>
+								<configuration>
+									<archive>
+										<manifestFile>${project.build.testOutputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+									</archive>
+									<excludes>
+										<exclude>org/apache/aries/cdi/extension/mp/metrics/test/tb*/**</exclude>
+									</excludes>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+	</profiles>
 </project>
diff --git a/cdi-extension-mp-metrics/src/main/java/org/apache/aries/cdi/extension/mp/metrics/StubExtension.java b/cdi-extension-mp-metrics/src/main/java/org/apache/aries/cdi/extension/mp/metrics/MPMetricsExtension.java
similarity index 60%
rename from cdi-extension-mp-metrics/src/main/java/org/apache/aries/cdi/extension/mp/metrics/StubExtension.java
rename to cdi-extension-mp-metrics/src/main/java/org/apache/aries/cdi/extension/mp/metrics/MPMetricsExtension.java
index f6eda9f..4aeef39 100644
--- a/cdi-extension-mp-metrics/src/main/java/org/apache/aries/cdi/extension/mp/metrics/StubExtension.java
+++ b/cdi-extension-mp-metrics/src/main/java/org/apache/aries/cdi/extension/mp/metrics/MPMetricsExtension.java
@@ -14,17 +14,20 @@
 
 package org.apache.aries.cdi.extension.mp.metrics;
 
+import static java.lang.String.format;
 import static javax.interceptor.Interceptor.Priority.LIBRARY_AFTER;
-import static org.apache.aries.cdi.extension.mp.metrics.StubExtension.EXTENSION_NAME;
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static org.apache.aries.cdi.extension.mp.metrics.MPMetricsExtension.EXTENSION_NAME;
+import static org.apache.aries.cdi.extension.mp.metrics.MPMetricsExtension.EXTENSION_VERSION;
+import static org.osgi.framework.Constants.SCOPE_PROTOTYPE;
 import static org.osgi.framework.Constants.SERVICE_DESCRIPTION;
 import static org.osgi.framework.Constants.SERVICE_RANKING;
+import static org.osgi.framework.Constants.SERVICE_SCOPE;
 import static org.osgi.framework.Constants.SERVICE_VENDOR;
 import static org.osgi.service.cdi.CDIConstants.CDI_EXTENSION_PROPERTY;
 import static org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants.JAX_RS_APPLICATION_SELECT;
-import static org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants.JAX_RS_EXTENSION;
 import static org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants.JAX_RS_EXTENSION_SELECT;
 import static org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants.JAX_RS_MEDIA_TYPE;
-import static org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants.JAX_RS_NAME;
 import static org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants.JAX_RS_RESOURCE;
 
 import java.util.Dictionary;
@@ -32,26 +35,19 @@
 
 import javax.annotation.Priority;
 import javax.enterprise.event.Observes;
-import javax.enterprise.event.ObservesAsync;
 import javax.enterprise.inject.spi.AfterDeploymentValidation;
 import javax.enterprise.inject.spi.AnnotatedType;
 import javax.enterprise.inject.spi.Bean;
 import javax.enterprise.inject.spi.BeanManager;
-import javax.enterprise.inject.spi.BeforeBeanDiscovery;
 import javax.enterprise.inject.spi.BeforeShutdown;
 import javax.enterprise.inject.spi.Extension;
 import javax.enterprise.inject.spi.InjectionTargetFactory;
-import javax.ws.rs.core.MediaType;
 import javax.ws.rs.ext.MessageBodyReader;
 import javax.ws.rs.ext.MessageBodyWriter;
 
 import org.apache.aries.cdi.spi.configuration.Configuration;
-import org.apache.geronimo.microprofile.metrics.cdi.CountedInterceptor;
-import org.apache.geronimo.microprofile.metrics.cdi.MeteredInterceptor;
 import org.apache.geronimo.microprofile.metrics.cdi.MetricsExtension;
-import org.apache.geronimo.microprofile.metrics.cdi.TimedInterceptor;
 import org.apache.geronimo.microprofile.metrics.jaxrs.CdiMetricsEndpoints;
-import org.apache.johnzon.jaxrs.jsonb.jaxrs.JsonbJaxrsProvider;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceRegistration;
 
@@ -59,63 +55,48 @@
 
 @ServiceProvider(
 	attribute = {
-		CDI_EXTENSION_PROPERTY + "=" + EXTENSION_NAME,
-		"service.scope=prototype",
-		"service.vendor=Apache Software Foundation",
-		"version:Version=1.1.1"
+		CDI_EXTENSION_PROPERTY + '=' + EXTENSION_NAME,
+		SERVICE_SCOPE + '=' + SCOPE_PROTOTYPE,
+		SERVICE_VENDOR + "=Apache Software Foundation",
+		"version:Version=" + EXTENSION_VERSION
 	},
-	effective = "active",
 	uses = Extension.class,
 	value = Extension.class
 )
-public class StubExtension extends MetricsExtension {
+public class MPMetricsExtension extends MetricsExtension {
 
 	public final static String EXTENSION_NAME = "eclipse.microprofile.metrics";
+	public final static String EXTENSION_VERSION = "1.1.1";
 
+	private volatile BundleContext bundleContext;
 	private volatile Configuration configuration;
 
+	void getBundleContext(@Observes BundleContext bundleContext) {
+		this.bundleContext = bundleContext;
+	}
+
 	void getConfiguration(@Observes Configuration configuration) {
 		this.configuration = configuration;
 	}
 
-	public void addBeans(@Observes BeforeBeanDiscovery bbd, BeanManager bm) {
-		bbd.addAnnotatedType(bm.createAnnotatedType(CountedInterceptor.class));
-		bbd.addAnnotatedType(bm.createAnnotatedType(MeteredInterceptor.class));
-		bbd.addAnnotatedType(bm.createAnnotatedType(TimedInterceptor.class));
-		bbd.addAnnotatedType(bm.createAnnotatedType(CdiMetricsEndpoints.class));
-	}
-
 	void registerMetricsEndpoint(
 		@Observes @Priority(LIBRARY_AFTER + 800)
 		AfterDeploymentValidation adv, BeanManager beanManager) {
 
-		beanManager.getEvent().fireAsync(new Ready());
-	}
+		if (bundleContext == null || configuration == null) {
+			return;
+		}
 
-	void registerMetricsEndpoint0(
-		@ObservesAsync Ready ready, BeanManager beanManager, BundleContext bundleContext) {
 		Dictionary<String, Object> properties = new Hashtable<>();
 
-		properties.put(SERVICE_DESCRIPTION, "Aries CDI - MP Metrics JSON Provider");
-		properties.put(SERVICE_VENDOR, "Apache Software Foundation");
-		properties.put(JAX_RS_APPLICATION_SELECT, configuration.get(JAX_RS_APPLICATION_SELECT));
-		properties.put(JAX_RS_EXTENSION, Boolean.TRUE.toString());
-		properties.put(JAX_RS_MEDIA_TYPE, MediaType.APPLICATION_JSON);
-		properties.put(JAX_RS_NAME, "metrics.json.provider");
-		properties.put(SERVICE_RANKING, Integer.MAX_VALUE - 100);
-
-		JsonbJaxrsProvider<?> johnzonProvider = new JsonbJaxrsProvider<>();
-
-		_jsonProviderRegistration = bundleContext.registerService(
-			new String[] {MessageBodyReader.class.getName(), MessageBodyWriter.class.getName()}, johnzonProvider, properties);
-
-		properties = new Hashtable<>();
-
 		properties.put(SERVICE_DESCRIPTION, "Aries CDI - MP Metrics Portable Extension Endpoint");
 		properties.put(SERVICE_VENDOR, "Apache Software Foundation");
 		properties.put(JAX_RS_APPLICATION_SELECT, configuration.get(JAX_RS_APPLICATION_SELECT));
 		properties.put(JAX_RS_RESOURCE, Boolean.TRUE.toString());
-		properties.put(JAX_RS_EXTENSION_SELECT, "(osgi.jaxrs.name=metrics.json.provider)");
+		properties.put(JAX_RS_EXTENSION_SELECT, new String[] {
+			format("(&(objectClass=%s)(%s=%s))", MessageBodyReader.class.getName(), JAX_RS_MEDIA_TYPE, APPLICATION_JSON),
+			format("(&(objectClass=%s)(%s=%s))", MessageBodyWriter.class.getName(), JAX_RS_MEDIA_TYPE, APPLICATION_JSON)
+		});
 		properties.put(SERVICE_RANKING, Integer.MAX_VALUE - 100);
 
 		AnnotatedType<CdiMetricsEndpoints> annotatedType = beanManager.createAnnotatedType(CdiMetricsEndpoints.class);
@@ -134,22 +115,11 @@
 				_endpointRegistration.unregister();
 			}
 			catch (IllegalStateException ise) {
-				// the service was already unregistered.
-			}
-		}
-		if (_jsonProviderRegistration != null) {
-			try {
-				_jsonProviderRegistration.unregister();
-			}
-			catch (IllegalStateException ise) {
-				// the service was already unregistered.
+				//
 			}
 		}
 	}
 
-	public static class Ready {}
-
-	private volatile ServiceRegistration<?> _jsonProviderRegistration;
 	private volatile ServiceRegistration<?> _endpointRegistration;
 
 }
diff --git a/cdi-extension-mp-metrics/src/main/java/org/apache/aries/cdi/extension/mp/metrics/StubJsonProvider.java b/cdi-extension-mp-metrics/src/main/java/org/apache/aries/cdi/extension/mp/metrics/StubJsonProvider.java
deleted file mode 100644
index 389fc1a..0000000
--- a/cdi-extension-mp-metrics/src/main/java/org/apache/aries/cdi/extension/mp/metrics/StubJsonProvider.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.aries.cdi.extension.mp.metrics;
-
-import javax.json.spi.JsonProvider;
-
-import org.apache.johnzon.core.JsonProviderImpl;
-
-import aQute.bnd.annotation.spi.ServiceProvider;
-
-@ServiceProvider(JsonProvider.class)
-@SuppressWarnings("serial")
-public class StubJsonProvider extends JsonProviderImpl {
-}
diff --git a/cdi-extension-mp-metrics/src/main/java/org/apache/aries/cdi/extension/mp/metrics/package-info.java b/cdi-extension-mp-metrics/src/main/java/org/apache/aries/cdi/extension/mp/metrics/package-info.java
index 9303ff8..31dd171 100644
--- a/cdi-extension-mp-metrics/src/main/java/org/apache/aries/cdi/extension/mp/metrics/package-info.java
+++ b/cdi-extension-mp-metrics/src/main/java/org/apache/aries/cdi/extension/mp/metrics/package-info.java
@@ -24,16 +24,25 @@
 		javax.enterprise.event.Observes.class,
 		javax.enterprise.inject.spi.Extension.class
 	},
-	version = "1.1.1" // TODO ?maybe read this from pom property?
+	version = EXTENSION_VERSION,
+	attribute = {
+		"aries.cdi.extension.bean.classes:List<String>='"
+			+ "org.apache.geronimo.microprofile.metrics.cdi.CountedInterceptor,"
+			+ "org.apache.geronimo.microprofile.metrics.cdi.MeteredInterceptor,"
+			+ "org.apache.geronimo.microprofile.metrics.cdi.TimedInterceptor'"
+	}
 )
+@JSONRequired
 @RequireCDIExtender
 @RequireJaxrsWhiteboard
 package org.apache.aries.cdi.extension.mp.metrics;
 
-import static org.apache.aries.cdi.extension.mp.metrics.StubExtension.EXTENSION_NAME;
+import static org.apache.aries.cdi.extension.mp.metrics.MPMetricsExtension.EXTENSION_NAME;
+import static org.apache.aries.cdi.extension.mp.metrics.MPMetricsExtension.EXTENSION_VERSION;
 import static org.osgi.namespace.service.ServiceNamespace.SERVICE_NAMESPACE;
 import static org.osgi.service.cdi.CDIConstants.CDI_EXTENSION_PROPERTY;
 
+import org.apache.aries.cdi.extra.propertytypes.JSONRequired;
 import org.osgi.annotation.bundle.Capability;
 import org.osgi.service.cdi.annotations.RequireCDIExtender;
 import org.osgi.service.jaxrs.whiteboard.annotations.RequireJaxrsWhiteboard;
diff --git a/cdi-extension-mp-metrics/src/test/java/org/apache/aries/cdi/extension/mp/metrics/test/BaseTestCase.java b/cdi-extension-mp-metrics/src/test/java/org/apache/aries/cdi/extension/mp/metrics/test/BaseTestCase.java
new file mode 100644
index 0000000..392c44d
--- /dev/null
+++ b/cdi-extension-mp-metrics/src/test/java/org/apache/aries/cdi/extension/mp/metrics/test/BaseTestCase.java
@@ -0,0 +1,129 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.extension.mp.metrics.test;
+
+import static java.util.Optional.ofNullable;
+import static org.osgi.test.common.filter.Filters.format;
+
+import java.util.function.Supplier;
+
+import org.junit.Rule;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cdi.runtime.CDIComponentRuntime;
+import org.osgi.test.junit4.context.BundleContextRule;
+import org.osgi.test.junit4.service.ServiceUseRule;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+public abstract class BaseTestCase {
+
+	public static final long timeout = 500;
+
+	@Rule
+	public BundleContextRule bcr = new BundleContextRule();
+	@Rule
+	public ServiceUseRule<CDIComponentRuntime> ccrr = new ServiceUseRule.Builder<CDIComponentRuntime>(CDIComponentRuntime.class, bcr).build();
+
+	@Rule
+	public TestWatcher watchman= new TestWatcher() {
+		@Override
+		protected void failed(Throwable e, Description description) {
+			System.out.printf("--------- TEST: %s#%s [%s]%n", description.getTestClass(), description.getMethodName(), "FAILED");
+		}
+
+		@Override
+		protected void succeeded(Description description) {
+			System.out.printf("--------- TEST: %s#%s [%s]%n", description.getTestClass(), description.getMethodName(), "PASSED");
+		}
+	};
+
+	public <S,T> CloseableTracker<S, T> track(Filter filter) {
+		CloseableTracker<S, T> tracker = new CloseableTracker<>(bcr.getBundleContext(), filter);
+		tracker.open();
+		return tracker;
+	}
+
+	public <S,T> CloseableTracker<S, T> track(String pattern, Object... objects) {
+		return track(format(pattern, objects));
+	}
+
+	public <S> CloseableTracker<S, ServiceReference<S>> trackSR(String pattern, Object... objects) {
+		return trackSR(format(pattern, objects));
+	}
+
+	public <S> CloseableTracker<S, ServiceReference<S>> trackSR(Filter filter) {
+		CloseableTracker<S, ServiceReference<S>> tracker = new CloseableTracker<>(bcr.getBundleContext(), filter, new ServiceTrackerCustomizer<S, ServiceReference<S>>() {
+
+			@Override
+			public ServiceReference<S> addingService(ServiceReference<S> reference) {
+				return reference;
+			}
+
+			@Override
+			public void modifiedService(ServiceReference<S> reference, ServiceReference<S> service) {
+			}
+
+			@Override
+			public void removedService(ServiceReference<S> reference, ServiceReference<S> service) {
+			}
+
+		});
+		tracker.open();
+		return tracker;
+	}
+
+	public long getChangeCount(ServiceReference<?> reference) {
+		return ofNullable(
+			reference.getProperty("service.changecount")
+		).map(
+			Long.class::cast
+		).orElseGet(
+			() -> new Long(-1l)
+		).longValue();
+	}
+
+	public static <T> T tccl(ClassLoader classLoader, Supplier<T> supplier) {
+		Thread currentThread = Thread.currentThread();
+		ClassLoader original = currentThread.getContextClassLoader();
+		try {
+			currentThread.setContextClassLoader(classLoader);
+			return supplier.get();
+		}
+		finally {
+			currentThread.setContextClassLoader(original);
+		}
+	}
+
+	public static class CloseableTracker<S, T> extends ServiceTracker<S, T> implements AutoCloseable {
+
+		public CloseableTracker(BundleContext context, Filter filter) {
+			super(context, filter, null);
+		}
+
+		public CloseableTracker(BundleContext context, Filter filter, ServiceTrackerCustomizer<S, T> customizer) {
+			super(context, filter, customizer);
+		}
+
+		@Override
+		public void close() {
+			super.close();
+		}
+
+	}
+}
diff --git a/cdi-extension-mp-metrics/src/test/java/org/apache/aries/cdi/extension/mp/metrics/test/HttpBaseTestCase.java b/cdi-extension-mp-metrics/src/test/java/org/apache/aries/cdi/extension/mp/metrics/test/HttpBaseTestCase.java
new file mode 100644
index 0000000..9bec5f6
--- /dev/null
+++ b/cdi-extension-mp-metrics/src/test/java/org/apache/aries/cdi/extension/mp/metrics/test/HttpBaseTestCase.java
@@ -0,0 +1,86 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.extension.mp.metrics.test;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.InputStream;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.osgi.services.HttpClientBuilderFactory;
+import org.assertj.core.util.Arrays;
+import org.junit.Rule;
+import org.osgi.service.http.runtime.HttpServiceRuntime;
+import org.osgi.service.http.runtime.dto.ServletContextDTO;
+import org.osgi.service.http.runtime.dto.ServletDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+import org.osgi.test.junit4.service.ServiceUseRule;
+
+public abstract class HttpBaseTestCase extends BaseTestCase {
+
+	@Rule
+	public ServiceUseRule<HttpServiceRuntime> hsrr = new ServiceUseRule.Builder<HttpServiceRuntime>(HttpServiceRuntime.class, bcr).build();
+	@Rule
+	public ServiceUseRule<HttpClientBuilderFactory> hcbfr = new ServiceUseRule.Builder<HttpClientBuilderFactory>(HttpClientBuilderFactory.class, bcr).build();
+
+	public String getHttpEndpoint() {
+		String[] endpoints = (String[])hsrr.getServiceReference().getProperty("osgi.http.endpoint");
+
+		if (endpoints == null || endpoints.length == 0) {
+			String port = (String)hsrr.getServiceReference().getProperty("org.osgi.service.http.port");
+			return "http://localhost:" + port;
+		}
+
+		return endpoints[0];
+	}
+
+	public String read(HttpEntity entity) throws Exception {
+		if (entity == null) {
+			return null;
+		}
+
+		try (InputStream in = entity.getContent();
+			java.util.Scanner s = new java.util.Scanner(in)) {
+
+			s.useDelimiter("\\A");
+			return s.hasNext() ? s.next() : "";
+		}
+	}
+
+	public ServletDTO waitFor(String path) throws InterruptedException {
+		return waitFor(path, 20);
+	}
+
+	public ServletDTO waitFor(String path, int intervals) throws InterruptedException {
+		for (int j = intervals; j > 0; j--) {
+			for (ServletContextDTO scDTO : hsrr.getService().getRuntimeDTO().servletContextDTOs) {
+				if (scDTO.name.equals(HttpWhiteboardConstants.HTTP_WHITEBOARD_DEFAULT_CONTEXT_NAME)) {
+					for (ServletDTO sDTO : scDTO.servletDTOs) {
+						if (Arrays.asList(sDTO.patterns).contains(path)) {
+							return sDTO;
+						}
+					}
+				}
+			}
+
+			Thread.sleep(50);
+		}
+
+		assertTrue(String.format("%s not found in time", path), false);
+
+		return null;
+	}
+
+}
diff --git a/cdi-extension-mp-metrics/src/test/java/org/apache/aries/cdi/extension/mp/metrics/test/JaxrsBaseTestCase.java b/cdi-extension-mp-metrics/src/test/java/org/apache/aries/cdi/extension/mp/metrics/test/JaxrsBaseTestCase.java
new file mode 100644
index 0000000..b1ea0f9
--- /dev/null
+++ b/cdi-extension-mp-metrics/src/test/java/org/apache/aries/cdi/extension/mp/metrics/test/JaxrsBaseTestCase.java
@@ -0,0 +1,50 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.extension.mp.metrics.test;
+
+import java.util.Collection;
+
+import javax.ws.rs.client.ClientBuilder;
+
+import org.junit.Rule;
+import org.osgi.service.jaxrs.runtime.JaxrsServiceRuntime;
+import org.osgi.service.jaxrs.runtime.JaxrsServiceRuntimeConstants;
+import org.osgi.test.junit4.service.ServiceUseRule;
+
+public abstract class JaxrsBaseTestCase extends HttpBaseTestCase {
+
+	@Rule
+	public ServiceUseRule<JaxrsServiceRuntime> jsrr = new ServiceUseRule.Builder<JaxrsServiceRuntime>(JaxrsServiceRuntime.class, bcr).build();
+	@Rule
+	public ServiceUseRule<ClientBuilder> cbr = new ServiceUseRule.Builder<ClientBuilder>(ClientBuilder.class, bcr).build();
+
+	public String getJaxrsEndpoint() {
+		Object endpointsObj = jsrr.getServiceReference().getProperty(
+			JaxrsServiceRuntimeConstants.JAX_RS_SERVICE_ENDPOINT);
+
+		if (endpointsObj instanceof String) {
+			return String.valueOf(endpointsObj);
+		}
+		else if (endpointsObj instanceof String[]) {
+			return ((String[])endpointsObj)[0];
+		}
+		else if (endpointsObj instanceof Collection) {
+			return String.valueOf(((Collection<?>)endpointsObj).iterator().next());
+		}
+
+		return null;
+	}
+
+}
diff --git a/cdi-extension-mp-metrics/src/test/java/org/apache/aries/cdi/extension/mp/metrics/test/MpMetricsTests.java b/cdi-extension-mp-metrics/src/test/java/org/apache/aries/cdi/extension/mp/metrics/test/MpMetricsTests.java
new file mode 100644
index 0000000..8d004fa
--- /dev/null
+++ b/cdi-extension-mp-metrics/src/test/java/org/apache/aries/cdi/extension/mp/metrics/test/MpMetricsTests.java
@@ -0,0 +1,59 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.extension.mp.metrics.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import org.apache.aries.cdi.extension.mp.metrics.test.interfaces.Pojo;
+import org.assertj.core.api.Assertions;
+import org.junit.Test;
+
+public class MpMetricsTests extends JaxrsBaseTestCase {
+
+	@Test
+	public void testMetrics() throws Exception {
+		bcr.installBundle("tb01.jar");
+
+		try (CloseableTracker<Pojo, Pojo> tracker = track("(objectClass=%s)", Pojo.class.getName())) {
+			Pojo pojo = tracker.waitForService(timeout);
+			assertNotNull(pojo);
+
+			WebTarget webTarget = cbr.getService().build().target(getJaxrsEndpoint()).path("/metrics/application");
+
+			Response response = webTarget.request().get();
+
+			assertEquals(response.readEntity(String.class),200, response.getStatus());
+
+			String result = response.readEntity(String.class);
+
+			assertEquals("{\"org.apache.aries.cdi.extension.mp.metrics.test.tb01.A.applicationCount\":0}", result);
+
+			Assertions.assertThat(pojo.foo("Count: ")).isEqualTo("Count: 1");
+
+			response = webTarget.request().get();
+
+			assertEquals(200, response.getStatus());
+
+			result = response.readEntity(String.class);
+
+			assertEquals("{\"org.apache.aries.cdi.extension.mp.metrics.test.tb01.A.applicationCount\":1}", result);
+		}
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java b/cdi-extension-mp-metrics/src/test/java/org/apache/aries/cdi/extension/mp/metrics/test/interfaces/Pojo.java
similarity index 65%
copy from cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java
copy to cdi-extension-mp-metrics/src/test/java/org/apache/aries/cdi/extension/mp/metrics/test/interfaces/Pojo.java
index 315634c..931dfa8 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java
+++ b/cdi-extension-mp-metrics/src/test/java/org/apache/aries/cdi/extension/mp/metrics/test/interfaces/Pojo.java
@@ -12,5 +12,19 @@
  * limitations under the License.
  */
 
-@org.osgi.service.cdi.annotations.Beans
-package org.apache.aries.cdi.test.tb16;
+package org.apache.aries.cdi.extension.mp.metrics.test.interfaces;
+
+import java.util.Collections;
+import java.util.Map;
+
+public interface Pojo {
+
+	public String foo(String fooInput);
+
+	public int getCount();
+
+	default Map<String, Object> getMap() {
+		return Collections.emptyMap();
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb22/A.java b/cdi-extension-mp-metrics/src/test/java/org/apache/aries/cdi/extension/mp/metrics/test/tb01/A.java
similarity index 88%
rename from cdi-itests/src/main/java/org/apache/aries/cdi/test/tb22/A.java
rename to cdi-extension-mp-metrics/src/test/java/org/apache/aries/cdi/extension/mp/metrics/test/tb01/A.java
index 3ab1e2e..e8d8bbd 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb22/A.java
+++ b/cdi-extension-mp-metrics/src/test/java/org/apache/aries/cdi/extension/mp/metrics/test/tb01/A.java
@@ -12,11 +12,11 @@
  * limitations under the License.
  */
 
-package org.apache.aries.cdi.test.tb22;
+package org.apache.aries.cdi.extension.mp.metrics.test.tb01;
 
 import javax.inject.Inject;
 
-import org.apache.aries.cdi.test.interfaces.Pojo;
+import org.apache.aries.cdi.extension.mp.metrics.test.interfaces.Pojo;
 import org.eclipse.microprofile.metrics.Counter;
 import org.eclipse.microprofile.metrics.annotation.Metric;
 import org.osgi.service.cdi.annotations.Bean;
diff --git a/cdi-extension-mp-metrics/weld-itest.bndrun b/cdi-extension-mp-metrics/weld-itest.bndrun
new file mode 100644
index 0000000..db3943a
--- /dev/null
+++ b/cdi-extension-mp-metrics/weld-itest.bndrun
@@ -0,0 +1,68 @@
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+
+-include: base-itest.bndrun
+#-runjdb: 8000
+
+-runrequires: \
+	osgi.identity;filter:='(osgi.identity=javax.ejb-api)',\
+	osgi.identity;filter:='(osgi.identity=javax.transaction-api)',\
+	osgi.identity;filter:='(osgi.identity=org.apache.aries.cdi.weld)'
+
+-runblacklist: \
+	osgi.identity;filter:='(osgi.identity=*owb*)'
+
+-runbundles: \
+	javax.ejb-api;version='[3.2.0,3.2.1)',\
+	javax.transaction-api;version='[1.2.0,1.2.1)',\
+	jboss-classfilewriter;version='[1.2.3,1.2.4)',\
+	org.apache.aries.cdi.extender;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.mp-metrics-tests;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.mp-metrics;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.spi;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extra;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.spi;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.weld;version='[1.1.0,1.1.1)',\
+	org.apache.aries.jax.rs.whiteboard;version='[1.0.7,1.0.8)',\
+	org.apache.aries.spifly.dynamic.framework.extension;version='[1.2.3,1.2.4)',\
+	org.apache.commons.logging;version='[1.2.0,1.2.1)',\
+	org.apache.felix.configadmin;version='[1.9.10,1.9.11)',\
+	org.apache.felix.converter;version='[1.0.12,1.0.13)',\
+	org.apache.felix.gogo.command;version='[1.1.0,1.1.1)',\
+	org.apache.felix.gogo.runtime;version='[1.1.2,1.1.3)',\
+	org.apache.felix.gogo.shell;version='[1.1.2,1.1.3)',\
+	org.apache.felix.http.jetty;version='[4.0.14,4.0.15)',\
+	org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
+	org.apache.geronimo.specs.geronimo-annotation_1.3_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-atinject_1.0_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-el_2.2_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-interceptor_1.2_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-jaxrs_2.1_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-jcdi_2.0_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-json_1.1_spec;version='[1.3.0,1.3.1)',\
+	org.apache.geronimo.specs.geronimo-jsonb_1.0_spec;version='[1.2.0,1.2.1)',\
+	org.apache.httpcomponents.httpclient;version='[4.5.3,4.5.4)',\
+	org.apache.httpcomponents.httpcore;version='[4.4.6,4.4.7)',\
+	org.apache.johnzon.core;version='[1.2.3,1.2.4)',\
+	org.apache.johnzon.jsonb;version='[1.2.3,1.2.4)',\
+	org.apache.johnzon.mapper;version='[1.2.3,1.2.4)',\
+	org.apache.johnzon.osgi;version='[1.2.3,1.2.4)',\
+	org.apache.servicemix.bundles.junit;version='[4.12.0,4.12.1)',\
+	org.assertj.core;version='[3.13.2,3.13.3)',\
+	org.jboss.logging.jboss-logging;version='[3.3.2,3.3.3)',\
+	org.jboss.weld.osgi-bundle;version='[3.0.5,3.0.6)',\
+	org.osgi.service.cdi;version='[1.0.0,1.0.1)',\
+	org.osgi.service.jaxrs;version='[1.0.0,1.0.1)',\
+	org.osgi.test.common;version='[1.0.0,1.0.1)',\
+	org.osgi.test.junit4;version='[1.0.0,1.0.1)',\
+	org.osgi.util.function;version='[1.1.0,1.1.1)',\
+	org.osgi.util.promise;version='[1.1.0,1.1.1)'
diff --git a/cdi-extension-servlet-common/pom.xml b/cdi-extension-servlet-common/pom.xml
index 23fb934..fcda251 100644
--- a/cdi-extension-servlet-common/pom.xml
+++ b/cdi-extension-servlet-common/pom.xml
@@ -59,6 +59,11 @@
 	<dependencies>
 		<dependency>
 			<groupId>org.apache.aries.cdi</groupId>
+			<artifactId>org.apache.aries.cdi.extension.spi</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.aries.cdi</groupId>
 			<artifactId>org.apache.aries.cdi.extra</artifactId>
 			<version>${project.version}</version>
 		</dependency>
diff --git a/cdi-extension-servlet-common/src/main/java/org/apache/aries/cdi/extension/servlet/common/WebFilterProcessor.java b/cdi-extension-servlet-common/src/main/java/org/apache/aries/cdi/extension/servlet/common/WebFilterProcessor.java
index 5318efe..6bb425b 100644
--- a/cdi-extension-servlet-common/src/main/java/org/apache/aries/cdi/extension/servlet/common/WebFilterProcessor.java
+++ b/cdi-extension-servlet-common/src/main/java/org/apache/aries/cdi/extension/servlet/common/WebFilterProcessor.java
@@ -14,17 +14,16 @@
 
 package org.apache.aries.cdi.extension.servlet.common;
 
+import static java.util.Optional.ofNullable;
 import static org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT;
 
-import java.lang.annotation.Annotation;
-import java.util.HashSet;
-import java.util.Set;
-
 import javax.enterprise.inject.spi.AnnotatedType;
 import javax.enterprise.inject.spi.ProcessAnnotatedType;
+import javax.enterprise.inject.spi.configurator.AnnotatedTypeConfigurator;
 import javax.servlet.Filter;
 import javax.servlet.annotation.WebFilter;
 
+import org.apache.aries.cdi.extension.spi.adapt.Adapted;
 import org.apache.aries.cdi.extra.propertytypes.HttpWhiteboardContextSelect;
 import org.apache.aries.cdi.extra.propertytypes.HttpWhiteboardFilterAsyncSupported;
 import org.apache.aries.cdi.extra.propertytypes.HttpWhiteboardFilterDispatcher;
@@ -33,7 +32,6 @@
 import org.apache.aries.cdi.extra.propertytypes.HttpWhiteboardFilterServlet;
 import org.apache.aries.cdi.extra.propertytypes.ServiceDescription;
 import org.apache.aries.cdi.spi.configuration.Configuration;
-import org.osgi.service.cdi.annotations.Service;
 
 public class WebFilterProcessor {
 
@@ -52,45 +50,47 @@
 
 		final AnnotatedType<X> annotatedType = pat.getAnnotatedType();
 
+		AnnotatedTypeConfigurator<X> configurator = pat.configureAnnotatedType();
+
+		if (!Adapted.withServiceTypes(configurator, Filter.class)) {
+			return;
+		}
+
 		WebFilter webFilter = annotatedType.getAnnotation(WebFilter.class);
 
-		final Set<Annotation> annotationsToAdd = new HashSet<>();
-
-		if (!annotatedType.isAnnotationPresent(Service.class)) {
-			annotationsToAdd.add(Service.Literal.of(new Class[] {Filter.class}));
-		}
-
 		if(!annotatedType.isAnnotationPresent(HttpWhiteboardContextSelect.class)) {
-			annotationsToAdd.add(HttpWhiteboardContextSelect.Literal.of((String)configuration.get(HTTP_WHITEBOARD_CONTEXT_SELECT)));
+			ofNullable((String)configuration.get(HTTP_WHITEBOARD_CONTEXT_SELECT)).ifPresent(
+				select -> configurator.add(HttpWhiteboardContextSelect.Literal.of(select))
+			);
 		}
 
-		if (!webFilter.description().isEmpty()) {
-			annotationsToAdd.add(ServiceDescription.Literal.of(webFilter.description()));
+		if (!annotatedType.isAnnotationPresent(ServiceDescription.class) && !webFilter.description().isEmpty()) {
+			configurator.add(ServiceDescription.Literal.of(webFilter.description()));
 		}
 
-		if (!webFilter.filterName().isEmpty()) {
-			annotationsToAdd.add(HttpWhiteboardFilterName.Literal.of(webFilter.filterName()));
+		if (!annotatedType.isAnnotationPresent(HttpWhiteboardFilterName.class) && !webFilter.filterName().isEmpty()) {
+			configurator.add(HttpWhiteboardFilterName.Literal.of(webFilter.filterName()));
 		}
 
-		if (webFilter.servletNames().length > 0) {
-			annotationsToAdd.add(HttpWhiteboardFilterServlet.Literal.of(webFilter.servletNames()));
+		if (!annotatedType.isAnnotationPresent(HttpWhiteboardFilterServlet.class) && webFilter.servletNames().length > 0) {
+			configurator.add(HttpWhiteboardFilterServlet.Literal.of(webFilter.servletNames()));
 		}
 
-		if (webFilter.value().length > 0) {
-			annotationsToAdd.add(HttpWhiteboardFilterPattern.Literal.of(webFilter.value()));
-		}
-		else if (webFilter.urlPatterns().length > 0) {
-			annotationsToAdd.add(HttpWhiteboardFilterPattern.Literal.of(webFilter.urlPatterns()));
+		if (!annotatedType.isAnnotationPresent(HttpWhiteboardFilterPattern.class)) {
+			if (webFilter.value().length > 0) {
+				configurator.add(HttpWhiteboardFilterPattern.Literal.of(webFilter.value()));
+			}
+			else if (webFilter.urlPatterns().length > 0) {
+				configurator.add(HttpWhiteboardFilterPattern.Literal.of(webFilter.urlPatterns()));
+			}
 		}
 
-		if (webFilter.dispatcherTypes().length > 0) {
-			annotationsToAdd.add(HttpWhiteboardFilterDispatcher.Literal.of(webFilter.dispatcherTypes()));
+		if (!annotatedType.isAnnotationPresent(HttpWhiteboardFilterDispatcher.class) && webFilter.dispatcherTypes().length > 0) {
+			configurator.add(HttpWhiteboardFilterDispatcher.Literal.of(webFilter.dispatcherTypes()));
 		}
 
-		annotationsToAdd.add(HttpWhiteboardFilterAsyncSupported.Literal.of(webFilter.asyncSupported()));
-
-		if (!annotationsToAdd.isEmpty()) {
-			annotationsToAdd.forEach(pat.configureAnnotatedType()::add);
+		if(!annotatedType.isAnnotationPresent(HttpWhiteboardFilterAsyncSupported.class)) {
+			configurator.add(HttpWhiteboardFilterAsyncSupported.Literal.of(webFilter.asyncSupported()));
 		}
 	}
 
diff --git a/cdi-extension-servlet-common/src/main/java/org/apache/aries/cdi/extension/servlet/common/WebListenerProcessor.java b/cdi-extension-servlet-common/src/main/java/org/apache/aries/cdi/extension/servlet/common/WebListenerProcessor.java
index c175a39..ac2dfa0 100644
--- a/cdi-extension-servlet-common/src/main/java/org/apache/aries/cdi/extension/servlet/common/WebListenerProcessor.java
+++ b/cdi-extension-servlet-common/src/main/java/org/apache/aries/cdi/extension/servlet/common/WebListenerProcessor.java
@@ -14,23 +14,22 @@
 
 package org.apache.aries.cdi.extension.servlet.common;
 
+import static java.util.Optional.ofNullable;
 import static org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT;
 
-import java.lang.annotation.Annotation;
-import java.util.ArrayList;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Set;
 
 import javax.enterprise.inject.spi.AnnotatedType;
 import javax.enterprise.inject.spi.ProcessAnnotatedType;
+import javax.enterprise.inject.spi.configurator.AnnotatedTypeConfigurator;
 import javax.servlet.annotation.WebListener;
 
+import org.apache.aries.cdi.extension.spi.adapt.Adapted;
 import org.apache.aries.cdi.extra.propertytypes.HttpWhiteboardContextSelect;
 import org.apache.aries.cdi.extra.propertytypes.HttpWhiteboardListener;
 import org.apache.aries.cdi.extra.propertytypes.ServiceDescription;
 import org.apache.aries.cdi.spi.configuration.Configuration;
-import org.osgi.service.cdi.annotations.Service;
 
 public class WebListenerProcessor {
 
@@ -49,52 +48,52 @@
 
 		final AnnotatedType<X> annotatedType = pat.getAnnotatedType();
 
+		AnnotatedTypeConfigurator<X> configurator = pat.configureAnnotatedType();
+
+		Set<Class<?>> serviceTypes = new HashSet<>();
+
+		Class<X> javaClass = annotatedType.getJavaClass();
+
+		if (javax.servlet.ServletContextListener.class.isAssignableFrom(javaClass)) {
+			serviceTypes.add(javax.servlet.ServletContextListener.class);
+		}
+		if (javax.servlet.ServletContextAttributeListener.class.isAssignableFrom(javaClass)) {
+			serviceTypes.add(javax.servlet.ServletContextAttributeListener.class);
+		}
+		if (javax.servlet.ServletRequestListener.class.isAssignableFrom(javaClass)) {
+			serviceTypes.add(javax.servlet.ServletRequestListener.class);
+		}
+		if (javax.servlet.ServletRequestAttributeListener.class.isAssignableFrom(javaClass)) {
+			serviceTypes.add(javax.servlet.ServletRequestAttributeListener.class);
+		}
+		if (javax.servlet.http.HttpSessionListener.class.isAssignableFrom(javaClass)) {
+			serviceTypes.add(javax.servlet.http.HttpSessionListener.class);
+		}
+		if (javax.servlet.http.HttpSessionAttributeListener.class.isAssignableFrom(javaClass)) {
+			serviceTypes.add(javax.servlet.http.HttpSessionAttributeListener.class);
+		}
+		if (javax.servlet.http.HttpSessionIdListener.class.isAssignableFrom(javaClass)) {
+			serviceTypes.add(javax.servlet.http.HttpSessionIdListener.class);
+		}
+
+		if (!Adapted.withServiceTypes(configurator, serviceTypes)) {
+			return;
+		}
+
 		WebListener webListener = annotatedType.getAnnotation(WebListener.class);
 
-		final Set<Annotation> annotationsToAdd = new HashSet<>();
-
-		if (!annotatedType.isAnnotationPresent(Service.class)) {
-			List<Class<?>> listenerTypes = new ArrayList<>();
-
-			Class<X> javaClass = annotatedType.getJavaClass();
-
-			if (javax.servlet.ServletContextListener.class.isAssignableFrom(javaClass)) {
-				listenerTypes.add(javax.servlet.ServletContextListener.class);
-			}
-			if (javax.servlet.ServletContextAttributeListener.class.isAssignableFrom(javaClass)) {
-				listenerTypes.add(javax.servlet.ServletContextAttributeListener.class);
-			}
-			if (javax.servlet.ServletRequestListener.class.isAssignableFrom(javaClass)) {
-				listenerTypes.add(javax.servlet.ServletRequestListener.class);
-			}
-			if (javax.servlet.ServletRequestAttributeListener.class.isAssignableFrom(javaClass)) {
-				listenerTypes.add(javax.servlet.ServletRequestAttributeListener.class);
-			}
-			if (javax.servlet.http.HttpSessionListener.class.isAssignableFrom(javaClass)) {
-				listenerTypes.add(javax.servlet.http.HttpSessionListener.class);
-			}
-			if (javax.servlet.http.HttpSessionAttributeListener.class.isAssignableFrom(javaClass)) {
-				listenerTypes.add(javax.servlet.http.HttpSessionAttributeListener.class);
-			}
-			if (javax.servlet.http.HttpSessionIdListener.class.isAssignableFrom(javaClass)) {
-				listenerTypes.add(javax.servlet.http.HttpSessionIdListener.class);
-			}
-
-			annotationsToAdd.add(Service.Literal.of(listenerTypes.toArray(new Class<?>[0])));
-		}
-
 		if(!annotatedType.isAnnotationPresent(HttpWhiteboardContextSelect.class)) {
-			annotationsToAdd.add(HttpWhiteboardContextSelect.Literal.of((String)configuration.get(HTTP_WHITEBOARD_CONTEXT_SELECT)));
+			ofNullable((String)configuration.get(HTTP_WHITEBOARD_CONTEXT_SELECT)).ifPresent(
+				select -> configurator.add(HttpWhiteboardContextSelect.Literal.of(select))
+			);
 		}
 
-		annotationsToAdd.add(HttpWhiteboardListener.Literal.INSTANCE);
-
-		if (!webListener.value().isEmpty()) {
-			annotationsToAdd.add(ServiceDescription.Literal.of(webListener.value()));
+		if(!annotatedType.isAnnotationPresent(HttpWhiteboardListener.class)) {
+			configurator.add(HttpWhiteboardListener.Literal.INSTANCE);
 		}
 
-		if (!annotationsToAdd.isEmpty()) {
-			annotationsToAdd.forEach(pat.configureAnnotatedType()::add);
+		if (!annotatedType.isAnnotationPresent(ServiceDescription.class) && !webListener.value().isEmpty()) {
+			configurator.add(ServiceDescription.Literal.of(webListener.value()));
 		}
 	}
 
diff --git a/cdi-extension-servlet-common/src/main/java/org/apache/aries/cdi/extension/servlet/common/WebServletProcessor.java b/cdi-extension-servlet-common/src/main/java/org/apache/aries/cdi/extension/servlet/common/WebServletProcessor.java
index 3b6e7e2..590e805 100644
--- a/cdi-extension-servlet-common/src/main/java/org/apache/aries/cdi/extension/servlet/common/WebServletProcessor.java
+++ b/cdi-extension-servlet-common/src/main/java/org/apache/aries/cdi/extension/servlet/common/WebServletProcessor.java
@@ -14,18 +14,17 @@
 
 package org.apache.aries.cdi.extension.servlet.common;
 
+import static java.util.Optional.ofNullable;
 import static org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT;
 
-import java.lang.annotation.Annotation;
-import java.util.HashSet;
-import java.util.Set;
-
 import javax.enterprise.inject.spi.AnnotatedType;
 import javax.enterprise.inject.spi.ProcessAnnotatedType;
+import javax.enterprise.inject.spi.configurator.AnnotatedTypeConfigurator;
 import javax.servlet.Servlet;
 import javax.servlet.annotation.MultipartConfig;
 import javax.servlet.annotation.WebServlet;
 
+import org.apache.aries.cdi.extension.spi.adapt.Adapted;
 import org.apache.aries.cdi.extra.propertytypes.HttpWhiteboardContextSelect;
 import org.apache.aries.cdi.extra.propertytypes.HttpWhiteboardServletAsyncSupported;
 import org.apache.aries.cdi.extra.propertytypes.HttpWhiteboardServletMultipart;
@@ -34,7 +33,6 @@
 import org.apache.aries.cdi.extra.propertytypes.ServiceDescription;
 import org.apache.aries.cdi.extra.propertytypes.ServiceRanking;
 import org.apache.aries.cdi.spi.configuration.Configuration;
-import org.osgi.service.cdi.annotations.Service;
 
 public class WebServletProcessor {
 
@@ -53,50 +51,56 @@
 
 		final AnnotatedType<X> annotatedType = pat.getAnnotatedType();
 
+		AnnotatedTypeConfigurator<X> configurator = pat.configureAnnotatedType();
+
+		if (!Adapted.withServiceTypes(configurator, Servlet.class)) {
+			return;
+		}
+
 		WebServlet webServlet = annotatedType.getAnnotation(WebServlet.class);
 
-		final Set<Annotation> annotationsToAdd = new HashSet<>();
-
-		if (!annotatedType.isAnnotationPresent(Service.class)) {
-			annotationsToAdd.add(Service.Literal.of(new Class[] {Servlet.class}));
-		}
-
 		if(!annotatedType.isAnnotationPresent(HttpWhiteboardContextSelect.class)) {
-			annotationsToAdd.add(HttpWhiteboardContextSelect.Literal.of((String)configuration.get(HTTP_WHITEBOARD_CONTEXT_SELECT)));
+			ofNullable((String)configuration.get(HTTP_WHITEBOARD_CONTEXT_SELECT)).ifPresent(
+				select -> configurator.add(HttpWhiteboardContextSelect.Literal.of(select))
+			);
 		}
 
-		if (!webServlet.name().isEmpty()) {
-			annotationsToAdd.add(HttpWhiteboardServletName.Literal.of(webServlet.name()));
+		if (!annotatedType.isAnnotationPresent(HttpWhiteboardServletName.class) && !webServlet.name().isEmpty()) {
+			configurator.add(HttpWhiteboardServletName.Literal.of(webServlet.name()));
 		}
 
-		if (webServlet.value().length > 0) {
-			annotationsToAdd.add(HttpWhiteboardServletPattern.Literal.of(webServlet.value()));
-		}
-		else if (webServlet.urlPatterns().length > 0) {
-			annotationsToAdd.add(HttpWhiteboardServletPattern.Literal.of(webServlet.urlPatterns()));
+		if(!annotatedType.isAnnotationPresent(HttpWhiteboardServletPattern.class)) {
+			if (webServlet.value().length > 0) {
+				configurator.add(HttpWhiteboardServletPattern.Literal.of(webServlet.value()));
+			}
+			else if (webServlet.urlPatterns().length > 0) {
+				configurator.add(HttpWhiteboardServletPattern.Literal.of(webServlet.urlPatterns()));
+			}
 		}
 
-		annotationsToAdd.add(ServiceRanking.Literal.of(webServlet.loadOnStartup()));
+		if (!annotatedType.isAnnotationPresent(ServiceRanking.class)) {
+			configurator.add(ServiceRanking.Literal.of(webServlet.loadOnStartup()));
+		}
 
 		// TODO Howto: INIT PARAMS ???
 
-		annotationsToAdd.add(HttpWhiteboardServletAsyncSupported.Literal.of(webServlet.asyncSupported()));
-
-		if (!webServlet.description().isEmpty()) {
-			annotationsToAdd.add(ServiceDescription.Literal.of(webServlet.description()));
+		if (!annotatedType.isAnnotationPresent(HttpWhiteboardServletAsyncSupported.class)) {
+			configurator.add(HttpWhiteboardServletAsyncSupported.Literal.of(webServlet.asyncSupported()));
 		}
 
-		MultipartConfig multipartConfig = annotatedType.getAnnotation(MultipartConfig.class);
+		if (!annotatedType.isAnnotationPresent(ServiceDescription.class) && !webServlet.description().isEmpty()) {
+			configurator.add(ServiceDescription.Literal.of(webServlet.description()));
+		}
 
-		if (multipartConfig != null) {
-			annotationsToAdd.add(HttpWhiteboardServletMultipart.Literal.of(true, multipartConfig.fileSizeThreshold(), multipartConfig.location(), multipartConfig.maxFileSize(), multipartConfig.maxRequestSize()));
+		if (!annotatedType.isAnnotationPresent(HttpWhiteboardServletMultipart.class)) {
+			MultipartConfig multipartConfig = annotatedType.getAnnotation(MultipartConfig.class);
+
+			if (multipartConfig != null) {
+				configurator.add(HttpWhiteboardServletMultipart.Literal.of(true, multipartConfig.fileSizeThreshold(), multipartConfig.location(), multipartConfig.maxFileSize(), multipartConfig.maxRequestSize()));
+			}
 		}
 
 		// TODO HowTo: ServletSecurity ???
-
-		if (!annotationsToAdd.isEmpty()) {
-			annotationsToAdd.forEach(pat.configureAnnotatedType()::add);
-		}
 	}
 
 }
diff --git a/cdi-extension-servlet-owb/src/main/java/org/apache/aries/cdi/extension/servlet/owb/ServletActivator.java b/cdi-extension-servlet-owb/src/main/java/org/apache/aries/cdi/extension/servlet/owb/ServletActivator.java
index 0466644..2246bda 100644
--- a/cdi-extension-servlet-owb/src/main/java/org/apache/aries/cdi/extension/servlet/owb/ServletActivator.java
+++ b/cdi-extension-servlet-owb/src/main/java/org/apache/aries/cdi/extension/servlet/owb/ServletActivator.java
@@ -32,10 +32,7 @@
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceRegistration;
 
-@Header(
-	name = BUNDLE_ACTIVATOR,
-	value = "${@class}"
-)
+@Header(name = BUNDLE_ACTIVATOR, value = "${@class}")
 public class ServletActivator implements BundleActivator {
 
 	@Override
diff --git a/cdi-extension-servlet-owb/src/main/java/org/apache/aries/cdi/extension/servlet/owb/package-info.java b/cdi-extension-servlet-owb/src/main/java/org/apache/aries/cdi/extension/servlet/owb/package-info.java
index cfccabf..709ede3 100644
--- a/cdi-extension-servlet-owb/src/main/java/org/apache/aries/cdi/extension/servlet/owb/package-info.java
+++ b/cdi-extension-servlet-owb/src/main/java/org/apache/aries/cdi/extension/servlet/owb/package-info.java
@@ -12,11 +12,11 @@
  * limitations under the License.
  */
 
-@org.osgi.annotation.bundle.Capability(
+@Capability(
 	attribute = "objectClass:List<String>=javax.enterprise.inject.spi.Extension",
 	namespace = SERVICE_NAMESPACE
 )
-@org.osgi.annotation.bundle.Capability(
+@Capability(
 	name = "aries.cdi.http",
 	namespace = CDI_EXTENSION_PROPERTY,
 	uses= {
@@ -30,14 +30,18 @@
 	version = "1.0.0"
 )
 //Deliberately depend on Http Whiteboard version 1.0.0 (the spec annotation starts at 1.1.0)
-@org.osgi.annotation.bundle.Requirement(
+@Requirement(
 	name = "osgi.http",
 	namespace = IMPLEMENTATION_NAMESPACE,
 	version = "1.0.0"
 )
-@org.osgi.service.cdi.annotations.RequireCDIImplementation
+@RequireCDIImplementation
 package org.apache.aries.cdi.extension.servlet.owb;
 
 import static org.osgi.namespace.implementation.ImplementationNamespace.IMPLEMENTATION_NAMESPACE;
 import static org.osgi.namespace.service.ServiceNamespace.SERVICE_NAMESPACE;
 import static org.osgi.service.cdi.CDIConstants.CDI_EXTENSION_PROPERTY;
+
+import org.osgi.annotation.bundle.Capability;
+import org.osgi.annotation.bundle.Requirement;
+import org.osgi.service.cdi.annotations.RequireCDIImplementation;
diff --git a/cdi-extension-servlet-weld/pom.xml b/cdi-extension-servlet-weld/pom.xml
index b3ed01e..4618274 100644
--- a/cdi-extension-servlet-weld/pom.xml
+++ b/cdi-extension-servlet-weld/pom.xml
@@ -151,7 +151,7 @@
 			<groupId>org.osgi</groupId>
 			<artifactId>osgi.annotation</artifactId>
 		</dependency>
-			<dependency>
+		<dependency>
 			<groupId>org.osgi</groupId>
 			<artifactId>osgi.core</artifactId>
 		</dependency>
diff --git a/cdi-extension-servlet-weld/src/main/java/org/apache/aries/cdi/extension/servlet/weld/ServletActivator.java b/cdi-extension-servlet-weld/src/main/java/org/apache/aries/cdi/extension/servlet/weld/ServletActivator.java
index 6d3cb18..88437df 100644
--- a/cdi-extension-servlet-weld/src/main/java/org/apache/aries/cdi/extension/servlet/weld/ServletActivator.java
+++ b/cdi-extension-servlet-weld/src/main/java/org/apache/aries/cdi/extension/servlet/weld/ServletActivator.java
@@ -29,10 +29,7 @@
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceRegistration;
 
-@Header(
-	name = BUNDLE_ACTIVATOR,
-	value = "${@class}"
-)
+@Header(name = BUNDLE_ACTIVATOR, value = "${@class}")
 public class ServletActivator implements BundleActivator {
 
 	@Override
diff --git a/cdi-extension-servlet-weld/src/main/java/org/apache/aries/cdi/extension/servlet/weld/package-info.java b/cdi-extension-servlet-weld/src/main/java/org/apache/aries/cdi/extension/servlet/weld/package-info.java
index 32590be..83d2372 100644
--- a/cdi-extension-servlet-weld/src/main/java/org/apache/aries/cdi/extension/servlet/weld/package-info.java
+++ b/cdi-extension-servlet-weld/src/main/java/org/apache/aries/cdi/extension/servlet/weld/package-info.java
@@ -12,11 +12,11 @@
  * limitations under the License.
  */
 
-@org.osgi.annotation.bundle.Capability(
+@Capability(
 	attribute = "objectClass:List<String>=javax.enterprise.inject.spi.Extension",
 	namespace = SERVICE_NAMESPACE
 )
-@org.osgi.annotation.bundle.Capability(
+@Capability(
 	name = "aries.cdi.http",
 	namespace = CDI_EXTENSION_PROPERTY,
 	uses= {
@@ -30,14 +30,18 @@
 	version = "1.0.0"
 )
 //Deliberately depend on Http Whiteboard version 1.0.0 (the spec annotation starts at 1.1.0)
-@org.osgi.annotation.bundle.Requirement(
+@Requirement(
 	name = "osgi.http",
 	namespace = IMPLEMENTATION_NAMESPACE,
 	version = "1.0.0"
 )
-@org.osgi.service.cdi.annotations.RequireCDIImplementation
+@RequireCDIImplementation
 package org.apache.aries.cdi.extension.servlet.weld;
 
 import static org.osgi.namespace.implementation.ImplementationNamespace.IMPLEMENTATION_NAMESPACE;
 import static org.osgi.namespace.service.ServiceNamespace.SERVICE_NAMESPACE;
 import static org.osgi.service.cdi.CDIConstants.CDI_EXTENSION_PROPERTY;
+
+import org.osgi.annotation.bundle.Capability;
+import org.osgi.annotation.bundle.Requirement;
+import org.osgi.service.cdi.annotations.RequireCDIImplementation;
diff --git a/cdi-extension-spi/pom.xml b/cdi-extension-spi/pom.xml
new file mode 100644
index 0000000..f010ece
--- /dev/null
+++ b/cdi-extension-spi/pom.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<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>
+		<groupId>org.apache.aries.cdi</groupId>
+		<artifactId>org.apache.aries.cdi</artifactId>
+		<version>1.1.0-SNAPSHOT</version>
+		<relativePath>..</relativePath>
+	</parent>
+
+	<artifactId>org.apache.aries.cdi.extension.spi</artifactId>
+	<name>Apache Aries CDI - SPI classes for Portable Extensions</name>
+	<description>Apache Aries CDI - SPI classes for Portable Extensions</description>
+
+	<licenses>
+		<license>
+			<name>ASL 2.0</name>
+			<url>https://www.apache.org/licenses/LICENSE-2.0</url>
+		</license>
+	</licenses>
+
+	<scm>
+		<connection>scm:git:git@github.com:apache/aries-cdi.git</connection>
+		<developerConnection>scm:git:git@github.com:apache/aries-cdi.git</developerConnection>
+		<tag>HEAD</tag>
+		<url>https://github.com/apache/aries-cdi</url>
+	</scm>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>biz.aQute.bnd</groupId>
+				<artifactId>bnd-maven-plugin</artifactId>
+			</plugin>
+		</plugins>
+	</build>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.apache.geronimo.specs</groupId>
+			<artifactId>geronimo-jcdi_2.0_spec</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.service.cdi</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
+			<artifactId>osgi.annotation</artifactId>
+		</dependency>
+	</dependencies>
+</project>
\ No newline at end of file
diff --git a/cdi-extension-spi/src/main/java/org/apache/aries/cdi/extension/spi/adapt/Adapted.java b/cdi-extension-spi/src/main/java/org/apache/aries/cdi/extension/spi/adapt/Adapted.java
new file mode 100644
index 0000000..9f29f05
--- /dev/null
+++ b/cdi-extension-spi/src/main/java/org/apache/aries/cdi/extension/spi/adapt/Adapted.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) OSGi Alliance (2018). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.extension.spi.adapt;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.enterprise.inject.spi.configurator.AnnotatedTypeConfigurator;
+
+import org.apache.aries.cdi.extension.spi.annotation.AdaptedService;
+import org.osgi.service.cdi.annotations.Service;
+
+public class Adapted {
+
+	private Adapted() {
+		//
+	}
+
+	/**
+	 * Adapt the annotated type associated with the configurator with additional
+	 * types in order to publish OSGi services with those types.
+	 * <p>
+	 * The annotated type will not be adapted if it is already annotated with {@link Service @Service}.
+	 *
+	 * @param <X> the type of the annotated type
+	 * @param configurator the configurator
+	 * @param serviceTypes the additional service types
+	 * @return true if the annotated type was adapted
+	 */
+	public static <X> boolean withServiceTypes(AnnotatedTypeConfigurator<X> configurator, Collection<Class<?>> serviceTypes) {
+		return withServiceTypes(configurator, serviceTypes, false);
+	}
+
+	/**
+	 * Adapt the annotated type associated with the configurator with additional
+	 * types in order to publish OSGi services with those types.
+	 * <p>
+	 * The annotated type will not be adapted if it is already annotated with {@link Service @Service}.
+	 *
+	 * @param <X> the type of the annotated type
+	 * @param configurator the configurator
+	 * @param serviceTypes the additional service types
+	 * @return true if the annotated type was adapted
+	 */
+	public static <X> boolean withServiceTypes(AnnotatedTypeConfigurator<X> configurator, Class<?>... serviceTypes) {
+		return withServiceTypes(configurator, Arrays.asList(serviceTypes), false);
+	}
+
+	/**
+	 * Adapt the annotated type associated with the configurator with additional
+	 * types in order to publish OSGi services with those types.
+	 * <p>
+	 * The annotated type will not be adapted if it is already annotated with {@link Service @Service}.
+	 *
+	 * @param <X> the type of the annotated type
+	 * @param configurator the configurator
+	 * @param serviceTypes the additional service types
+	 * @param replace if true do not merge with previous types
+	 * @return true if the annotated type was adapted
+	 */
+	public static <X> boolean withServiceTypes(AnnotatedTypeConfigurator<X> configurator, Collection<Class<?>> serviceTypes, boolean replace) {
+		if (configurator.getAnnotated().isAnnotationPresent(Service.class)) {
+			return false;
+		}
+
+		Set<Class<?>> servicesSet = new HashSet<>(serviceTypes);
+
+		AdaptedService adaptedService = configurator.getAnnotated().getAnnotation(AdaptedService.class);
+
+		if (adaptedService != null) {
+			configurator.remove(adaptedService::equals);
+			if (!replace) {
+				servicesSet.addAll(Arrays.asList(adaptedService.value()));
+			}
+		}
+
+		configurator.add(
+			AdaptedService.Literal.of(servicesSet.toArray(new Class<?>[0])));
+
+		return true;
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java b/cdi-extension-spi/src/main/java/org/apache/aries/cdi/extension/spi/adapt/package-info.java
similarity index 66%
copy from cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java
copy to cdi-extension-spi/src/main/java/org/apache/aries/cdi/extension/spi/adapt/package-info.java
index 315634c..0d09567 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java
+++ b/cdi-extension-spi/src/main/java/org/apache/aries/cdi/extension/spi/adapt/package-info.java
@@ -1,9 +1,11 @@
-/**
+/*
+ * Copyright (c) OSGi Alliance (2018). All Rights Reserved.
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -12,5 +14,6 @@
  * limitations under the License.
  */
 
-@org.osgi.service.cdi.annotations.Beans
-package org.apache.aries.cdi.test.tb16;
+@org.osgi.annotation.bundle.Export
+@org.osgi.annotation.versioning.Version("1.0.0")
+package org.apache.aries.cdi.extension.spi.adapt;
\ No newline at end of file
diff --git a/cdi-extension-spi/src/main/java/org/apache/aries/cdi/extension/spi/annotation/AdaptedService.java b/cdi-extension-spi/src/main/java/org/apache/aries/cdi/extension/spi/annotation/AdaptedService.java
new file mode 100644
index 0000000..b7a22fc
--- /dev/null
+++ b/cdi-extension-spi/src/main/java/org/apache/aries/cdi/extension/spi/annotation/AdaptedService.java
@@ -0,0 +1,72 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.extension.spi.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.enterprise.util.AnnotationLiteral;
+
+/**
+ * Annotation used to hold the service types when an extension is adapting
+ * annotated types for publication as OSGi CDI services.
+ * <p>
+ * The types in question should be those already known to the CDI bundle.
+ * <p>
+ * Services to be published from annotated types provided by the extension
+ * should be created manually.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface AdaptedService {
+
+	/**
+	 * Support inline instantiation of the {@link AdaptedService} annotation.
+	 */
+	public static final class Literal extends AnnotationLiteral<AdaptedService>
+			implements AdaptedService {
+
+		private static final long serialVersionUID = 1L;
+
+		/**
+		 * @param interfaces
+		 * @return instance of {@link AdaptedService}
+		 */
+		public static final Literal of(Class<?>[] interfaces) {
+			return new Literal(interfaces);
+		}
+
+		private Literal(Class<?>[] interfaces) {
+			_interfaces = interfaces;
+		}
+
+		@Override
+		public Class<?>[] value() {
+			return _interfaces;
+		}
+
+		private final Class<?>[] _interfaces;
+	}
+
+	/**
+	 * Override the interfaces under which this service is published.
+	 *
+	 * @return the service types
+	 */
+	Class<?>[] value() default {};
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java b/cdi-extension-spi/src/main/java/org/apache/aries/cdi/extension/spi/annotation/package-info.java
similarity index 66%
copy from cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java
copy to cdi-extension-spi/src/main/java/org/apache/aries/cdi/extension/spi/annotation/package-info.java
index 315634c..6fa3b4d 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java
+++ b/cdi-extension-spi/src/main/java/org/apache/aries/cdi/extension/spi/annotation/package-info.java
@@ -1,9 +1,11 @@
-/**
+/*
+ * Copyright (c) OSGi Alliance (2018). All Rights Reserved.
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -12,5 +14,6 @@
  * limitations under the License.
  */
 
-@org.osgi.service.cdi.annotations.Beans
-package org.apache.aries.cdi.test.tb16;
+@org.osgi.annotation.bundle.Export
+@org.osgi.annotation.versioning.Version("1.0.0")
+package org.apache.aries.cdi.extension.spi.annotation;
\ No newline at end of file
diff --git a/cdi-extra/pom.xml b/cdi-extra/pom.xml
index 21a354f..feaef29 100644
--- a/cdi-extra/pom.xml
+++ b/cdi-extra/pom.xml
@@ -54,6 +54,7 @@
 				<configuration>
 					<bnd><![CDATA[
 						Import-Package: javax.servlet;resolution:=optional, *
+						-conditionalpackage: aQute.bnd.annotation
 						-cdiannotations:
 						-contract: JavaServlet;resolution:=optional
 					]]></bnd>
@@ -66,20 +67,28 @@
 		<dependency>
 			<groupId>org.apache.felix</groupId>
 			<artifactId>org.apache.felix.http.servlet-api</artifactId>
+			<scope>provided</scope>
+			<exclusions>
+				<exclusion>
+					<groupId>org.apache.tomcat</groupId>
+					<artifactId>tomcat-servlet-api</artifactId>
+				</exclusion>
+			</exclusions>
 		</dependency>
 		<dependency>
 			<groupId>org.apache.geronimo.specs</groupId>
 			<artifactId>geronimo-jcdi_2.0_spec</artifactId>
+			<scope>provided</scope>
 		</dependency>
 		<dependency>
 			<groupId>org.apache.geronimo.specs</groupId>
 			<artifactId>geronimo-jaxrs_2.1_spec</artifactId>
-			<version>1.1</version>
 			<scope>provided</scope>
 		</dependency>
 		<dependency>
 			<groupId>org.osgi</groupId>
 			<artifactId>org.osgi.service.cdi</artifactId>
+			<scope>provided</scope>
 		</dependency>
 		<dependency>
 			<groupId>org.osgi</groupId>
@@ -111,5 +120,11 @@
 			<groupId>org.osgi</groupId>
 			<artifactId>osgi.core</artifactId>
 		</dependency>
+		<dependency>
+			<groupId>biz.aQute.bnd</groupId>
+			<artifactId>biz.aQute.bndlib</artifactId>
+			<version>${bnd.version}</version>
+			<scope>provided</scope>
+		</dependency>
 	</dependencies>
 </project>
diff --git a/cdi-extra/src/main/java/org/apache/aries/cdi/extra/RequireCDIExtension.java b/cdi-extra/src/main/java/org/apache/aries/cdi/extra/RequireCDIExtension.java
index 98ed050..20f37c3 100644
--- a/cdi-extra/src/main/java/org/apache/aries/cdi/extra/RequireCDIExtension.java
+++ b/cdi-extra/src/main/java/org/apache/aries/cdi/extra/RequireCDIExtension.java
@@ -31,6 +31,8 @@
 
 import org.osgi.annotation.bundle.Requirement;
 
+import aQute.bnd.annotation.Resolution;
+
 @Retention(CLASS)
 @Target({
 	PACKAGE, TYPE
@@ -45,7 +47,7 @@
 
 	public static final String	EFFECTIVE_MACRO		= "${if;${size;${#effective}};effective:=${#effective}}";
 
-	public static final String	RESOLUTION_MACRO	= "${if;${is;${#resolution};DEFAULT};;resolution:=${#resolution}}";
+	public static final String	RESOLUTION_MACRO	= "${if;${is;${#resolution};default};;resolution:=${#resolution}}";
 
 	public static final String	VERSION_MACRO	= "${if;${size;${#version}};version=${#version}}";
 
@@ -88,35 +90,4 @@
 	 */
 	Resolution resolution() default Resolution.DEFAULT;
 
-	public enum Resolution {
-		/**
-		 * A mandatory requirement forbids the bundle to resolve when the
-		 * requirement is not satisfied.
-		 */
-		MANDATORY("mandatory"), // Namespace.RESOLUTION_MANDATORY
-
-		/**
-		 * An optional requirement allows a bundle to resolve even if the
-		 * requirement is not satisfied.
-		 */
-		OPTIONAL("optional"), // Namespace.RESOLUTION_OPTIONAL
-
-		/**
-		 * Default element value for annotation. This is used to distinguish the
-		 * default value for an element and should not otherwise be used.
-		 */
-		DEFAULT("<<default>>");
-
-		private final String value;
-
-		Resolution(String value) {
-			this.value = value;
-		}
-
-		@Override
-		public String toString() {
-			return value;
-		}
-	}
-
 }
diff --git a/cdi-extra/src/main/java/org/apache/aries/cdi/extra/propertytypes/JSONRequired.java b/cdi-extra/src/main/java/org/apache/aries/cdi/extra/propertytypes/JSONRequired.java
index 1ad336b..0d8bbf0 100644
--- a/cdi-extra/src/main/java/org/apache/aries/cdi/extra/propertytypes/JSONRequired.java
+++ b/cdi-extra/src/main/java/org/apache/aries/cdi/extra/propertytypes/JSONRequired.java
@@ -18,6 +18,7 @@
 
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PACKAGE;
 import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
@@ -52,7 +53,7 @@
 	effective = EFFECTIVE_ACTIVE
 )
 @Retention(RUNTIME)
-@Target({FIELD, METHOD, TYPE})
+@Target({FIELD, METHOD, TYPE, PACKAGE})
 public @interface JSONRequired {
 
 	public static final class Literal extends AnnotationLiteral<JSONRequired> implements JSONRequired {
diff --git a/cdi-itests/base-itest.bndrun b/cdi-itests/base-itest.bndrun
index 5f50527..9a421cb 100644
--- a/cdi-itests/base-itest.bndrun
+++ b/cdi-itests/base-itest.bndrun
@@ -20,7 +20,6 @@
 	logback.configurationFile=file:${.}/logback.xml,\
 	org.osgi.service.http.port=0,\
 	osgi.console=,\
-	tck.config.test.javaconfig.converter.stringvalues=foo,\
 	test.property.a=blah,\
 	test.property.b=,\
 	org.apache.felix.http.host=localhost
@@ -38,6 +37,7 @@
 	org.slf4j.event;version=1.7.25,\
 	org.slf4j.helpers;version=1.7.25,\
 	org.slf4j.spi;version=1.7.25,\
+	sun.invoke,\
 	sun.misc
 
 -runrequires.base: \
@@ -45,3 +45,6 @@
 	osgi.identity;filter:='(osgi.identity=org.apache.aries.cdi.itests)',\
 	osgi.identity;filter:='(osgi.identity=org.apache.aries.jndi.core)',\
 	osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.command)'
+
+-runblacklist.base: \
+	osgi.identity;filter:='(osgi.identity=biz.aQute.bndlib)'
\ No newline at end of file
diff --git a/cdi-itests/bnd.bnd b/cdi-itests/bnd.bnd
index 570c7c3..99952be 100644
--- a/cdi-itests/bnd.bnd
+++ b/cdi-itests/bnd.bnd
@@ -58,13 +58,12 @@
 	tb12.jar,\
 	tb13.jar,\
 	tb14.jar,\
-	tb16.jar,\
 	tb17.jar,\
 	tb18.jar,\
 	tb19.jar,\
 	tb20.jar,\
 	tb21.jar,\
-	tb22.jar
+	tb24.jar
 
 # Don't forget that we had to coax the `maven-jar-plugin` NOT to include the `sub-bundle` packages in
 # the root bundle:
diff --git a/cdi-itests/bnd/tb16.bnd b/cdi-itests/bnd/tb16.bnd
deleted file mode 100644
index c6d1f50..0000000
--- a/cdi-itests/bnd/tb16.bnd
+++ /dev/null
@@ -1,13 +0,0 @@
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-
-Export-Package: ${p}.tb16.*;-split-package:=first
diff --git a/cdi-itests/bnd/tb22.bnd b/cdi-itests/bnd/tb24.bnd
similarity index 91%
rename from cdi-itests/bnd/tb22.bnd
rename to cdi-itests/bnd/tb24.bnd
index 1ec52c6..e6e2413 100644
--- a/cdi-itests/bnd/tb22.bnd
+++ b/cdi-itests/bnd/tb24.bnd
@@ -10,4 +10,4 @@
 #    See the License for the specific language governing permissions and
 #    limitations under the License.
 
-Export-Package: ${p}.tb22.*;-split-package:=first
+Export-Package: ${p}.tb24.*;-split-package:=first
diff --git a/cdi-itests/owb-itest.bndrun b/cdi-itests/owb-itest.bndrun
index 63ebf39..c614a4d 100644
--- a/cdi-itests/owb-itest.bndrun
+++ b/cdi-itests/owb-itest.bndrun
@@ -26,11 +26,11 @@
 	openwebbeans-spi;version='[2.0.13,2.0.14)',\
 	openwebbeans-web;version='[2.0.13,2.0.14)',\
 	org.apache.aries.cdi.extender;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.jaxrs;version='[1.1.0,1.1.1)',\
 	org.apache.aries.cdi.extension.jndi;version='[1.1.0,1.1.1)',\
-	org.apache.aries.cdi.extension.mp-config;version='[1.1.0,1.1.1)',\
-	org.apache.aries.cdi.extension.mp-metrics;version='[1.1.0,1.1.1)',\
 	org.apache.aries.cdi.extension.servlet.common;version='[1.1.0,1.1.1)',\
 	org.apache.aries.cdi.extension.servlet.owb;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.spi;version='[1.1.0,1.1.1)',\
 	org.apache.aries.cdi.extra;version='[1.1.0,1.1.1)',\
 	org.apache.aries.cdi.itests;version='[1.1.0,1.1.1)',\
 	org.apache.aries.cdi.owb;version='[1.1.0,1.1.1)',\
@@ -42,7 +42,6 @@
 	org.apache.aries.util;version='[1.0.0,1.0.1)',\
 	org.apache.commons.logging;version='[1.2.0,1.2.1)',\
 	org.apache.felix.configadmin;version='[1.9.10,1.9.11)',\
-	org.apache.felix.configurator;version='[1.0.10,1.0.11)',\
 	org.apache.felix.converter;version='[1.0.12,1.0.13)',\
 	org.apache.felix.gogo.command;version='[1.1.0,1.1.1)',\
 	org.apache.felix.gogo.runtime;version='[1.1.2,1.1.3)',\
@@ -55,13 +54,8 @@
 	org.apache.geronimo.specs.geronimo-interceptor_1.2_spec;version='[1.1.0,1.1.1)',\
 	org.apache.geronimo.specs.geronimo-jaxrs_2.1_spec;version='[1.1.0,1.1.1)',\
 	org.apache.geronimo.specs.geronimo-jcdi_2.0_spec;version='[1.1.0,1.1.1)',\
-	org.apache.geronimo.specs.geronimo-json_1.1_spec;version='[1.3.0,1.3.1)',\
-	org.apache.geronimo.specs.geronimo-jsonb_1.0_spec;version='[1.2.0,1.2.1)',\
 	org.apache.httpcomponents.httpclient;version='[4.5.3,4.5.4)',\
 	org.apache.httpcomponents.httpcore;version='[4.4.6,4.4.7)',\
-	org.apache.johnzon.core;version='[1.2.2,1.2.3)',\
-	org.apache.johnzon.jsonb;version='[1.2.2,1.2.3)',\
-	org.apache.johnzon.mapper;version='[1.2.2,1.2.3)',\
 	org.apache.xbean.asm7-shaded;version='[4.13.0,4.13.1)',\
 	org.apache.xbean.bundleutils;version='[4.15.0,4.15.1)',\
 	org.apache.xbean.finder-shaded;version='[4.13.0,4.13.1)',\
diff --git a/cdi-itests/pom.xml b/cdi-itests/pom.xml
index d186a37..d478c15 100644
--- a/cdi-itests/pom.xml
+++ b/cdi-itests/pom.xml
@@ -44,36 +44,32 @@
 		<url>https://github.com/apache/aries-cdi</url>
 	</scm>
 
-	<properties>
-		<gpg.skip>true</gpg.skip>
-		<maven.deploy.skip>true</maven.deploy.skip>
-		<maven.install.skip>true</maven.install.skip>
-		<maven.javadoc.skip>true</maven.javadoc.skip>
-		<maven.source.skip>true</maven.source.skip>
-	</properties>
-
 	<dependencies>
 		<!-- COMPILE -->
 		<dependency>
 			<groupId>biz.aQute.bnd</groupId>
 			<artifactId>biz.aQute.junit</artifactId>
 			<version>${bnd.version}</version>
+			<exclusions>
+				<exclusion>
+					<groupId>biz.aQute.bnd</groupId>
+					<artifactId>biz.aQute.bndlib</artifactId>
+				</exclusion>
+			</exclusions>
 		</dependency>
 		<dependency>
 			<groupId>org.apache.aries.cdi</groupId>
 			<artifactId>org.apache.aries.cdi.extra</artifactId>
 		</dependency>
 		<dependency>
-			<groupId>org.apache.aries.cdi</groupId>
-			<artifactId>org.apache.aries.cdi.extension.mp-config</artifactId>
-		</dependency>
-		<dependency>
-			<groupId>org.apache.aries.cdi</groupId>
-			<artifactId>org.apache.aries.cdi.extension.mp-metrics</artifactId>
-		</dependency>
-		<dependency>
 			<groupId>org.apache.felix</groupId>
 			<artifactId>org.apache.felix.http.servlet-api</artifactId>
+			<exclusions>
+				<exclusion>
+					<groupId>org.apache.tomcat</groupId>
+					<artifactId>tomcat-servlet-api</artifactId>
+				</exclusion>
+			</exclusions>
 		</dependency>
 		<dependency>
 			<groupId>org.apache.geronimo.specs</groupId>
@@ -96,6 +92,10 @@
 			<artifactId>geronimo-jcdi_2.0_spec</artifactId>
 		</dependency>
 		<dependency>
+			<groupId>org.apache.geronimo.specs</groupId>
+			<artifactId>geronimo-json_1.1_spec</artifactId>
+		</dependency>
+		<dependency>
 			<groupId>org.apache.httpcomponents</groupId>
 			<artifactId>httpclient-osgi</artifactId>
 			<version>4.5.3</version>
@@ -110,6 +110,10 @@
 		</dependency>
 		<dependency>
 			<groupId>org.osgi</groupId>
+			<artifactId>org.osgi.service.cdi</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.osgi</groupId>
 			<artifactId>org.osgi.service.cm</artifactId>
 		</dependency>
 		<dependency>
@@ -149,16 +153,6 @@
 
 		<!-- RUNTIME dependencies not found in BOMs -->
 		<dependency>
-			<groupId>org.apache.aries.cdi</groupId>
-			<artifactId>org.apache.aries.cdi.owb</artifactId>
-			<scope>runtime</scope>
-		</dependency>
-		<dependency>
-			<groupId>org.apache.aries.cdi</groupId>
-			<artifactId>org.apache.aries.cdi.weld</artifactId>
-			<scope>runtime</scope>
-		</dependency>
-		<dependency>
 			<groupId>org.apache.aries.jndi</groupId>
 			<artifactId>org.apache.aries.jndi.core</artifactId>
 			<version>1.0.2</version>
@@ -174,6 +168,44 @@
 			<groupId>org.apache.felix</groupId>
 			<artifactId>org.apache.felix.http.jetty</artifactId>
 			<scope>runtime</scope>
+			<exclusions>
+				<exclusion>
+					<groupId>org.eclipse.jetty.websocket</groupId>
+					<artifactId>websocket-server</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>org.eclipse.jetty.websocket</groupId>
+					<artifactId>websocket-servlet</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>org.eclipse.jetty</groupId>
+					<artifactId>jetty-security</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>org.eclipse.jetty</groupId>
+					<artifactId>jetty-jmx</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>org.eclipse.jetty</groupId>
+					<artifactId>jetty-webapp</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>org.apache.felix</groupId>
+					<artifactId>org.apache.felix.http.base</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>org.eclipse.jetty</groupId>
+					<artifactId>jetty-util</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>org.eclipse.jetty</groupId>
+					<artifactId>jetty-servlet</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>org.eclipse.jetty</groupId>
+					<artifactId>jetty-server</artifactId>
+				</exclusion>
+			</exclusions>
 		</dependency>
 		<dependency>
 			<groupId>org.apache.httpcomponents</groupId>
@@ -181,12 +213,6 @@
 			<version>4.4.6</version>
 			<scope>runtime</scope>
 		</dependency>
-		<dependency>
-			<groupId>org.apache.openwebbeans</groupId>
-			<artifactId>openwebbeans-web</artifactId>
-			<version>${owb.version}</version>
-			<scope>runtime</scope>
-		</dependency>
 	</dependencies>
 
 	<dependencyManagement>
@@ -221,13 +247,11 @@
 				<groupId>org.apache.maven.plugins</groupId>
 				<artifactId>maven-jar-plugin</artifactId>
 				<configuration>
-					<includes>
-						<include>META-INF/**</include>
-						<include>OSGI-INF/**</include>
-						<include>org/apache/aries/cdi/test/cases/*</include>
-						<include>org/apache/aries/cdi/test/interfaces/*</include>
-						<include>*.jar</include>
-					</includes>
+					<excludes>
+						<exclude>org/apache/aries/cdi/test/beans/**</exclude>
+						<exclude>org/apache/aries/cdi/test/components/**</exclude>
+						<exclude>org/apache/aries/cdi/test/tb*/**</exclude>
+					</excludes>
 				</configuration>
 			</plugin>
 			<plugin>
@@ -294,6 +318,43 @@
 					</execution>
 				</executions>
 			</plugin>
+			<plugin>
+				<groupId>biz.aQute.bnd</groupId>
+				<artifactId>bnd-baseline-maven-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>baseline</id>
+						<phase>none</phase>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<artifactId>maven-install-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>default-install</id>
+						<phase>none</phase>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<artifactId>maven-deploy-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>default-deploy</id>
+						<phase>none</phase>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<artifactId>maven-gpg-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>default-deploy</id>
+						<phase>none</phase>
+					</execution>
+				</executions>
+			</plugin>
 		</plugins>
 	</build>
 
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/AbstractTestCase.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/AbstractTestCase.java
deleted file mode 100644
index 9ca37f6..0000000
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/AbstractTestCase.java
+++ /dev/null
@@ -1,289 +0,0 @@
-/**
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.aries.cdi.test.cases;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import java.io.InputStream;
-import java.util.Dictionary;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.function.Supplier;
-
-import javax.enterprise.context.spi.CreationalContext;
-import javax.enterprise.inject.Any;
-import javax.enterprise.inject.spi.Bean;
-import javax.enterprise.inject.spi.BeanManager;
-
-import org.apache.aries.cdi.extra.RequireCDIExtension;
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Rule;
-import org.junit.rules.TestWatcher;
-import org.junit.runner.Description;
-import org.osgi.annotation.bundle.Requirement;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Filter;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
-import org.osgi.framework.wiring.BundleWire;
-import org.osgi.framework.wiring.BundleWiring;
-import org.osgi.namespace.extender.ExtenderNamespace;
-import org.osgi.namespace.service.ServiceNamespace;
-import org.osgi.service.cdi.CDIConstants;
-import org.osgi.service.cdi.runtime.CDIComponentRuntime;
-import org.osgi.service.cdi.runtime.dto.ContainerDTO;
-import org.osgi.service.configurator.annotations.RequireConfigurator;
-import org.osgi.util.promise.PromiseFactory;
-import org.osgi.util.tracker.ServiceTracker;
-import org.osgi.util.tracker.ServiceTrackerCustomizer;
-
-@Requirement(
-	effective = "active",
-	filter = "(objectClass=org.osgi.service.cm.ConfigurationAdmin)",
-	namespace = ServiceNamespace.SERVICE_NAMESPACE
-)
-@RequireCDIExtension("aries.cdi.http")
-@RequireCDIExtension("aries.cdi.jndi")
-@RequireCDIExtension("eclipse.microprofile.config")
-@RequireCDIExtension("eclipse.microprofile.metrics")
-@RequireConfigurator
-public abstract class AbstractTestCase {
-
-	@Rule
-	public TestWatcher watchman= new TestWatcher() {
-		@Override
-		protected void failed(Throwable e, Description description) {
-			System.out.printf("--------- TEST: %s#%s [%s]%n", description.getTestClass(), description.getMethodName(), "FAILED");
-		}
-
-		@Override
-		protected void succeeded(Description description) {
-			System.out.printf("--------- TEST: %s#%s [%s]%n", description.getTestClass(), description.getMethodName(), "PASSED");
-		}
-	};
-
-	@BeforeClass
-	public static void beforeClass() throws Exception {
-		runtimeTracker = new ServiceTracker<>(
-				bundleContext, CDIComponentRuntime.class, null);
-		runtimeTracker.open();
-		servicesBundle = installBundle("services-one.jar", false);
-		servicesBundle.start();
-	}
-
-	@AfterClass
-	public static void afterClass() throws Exception {
-		runtimeTracker.close();
-		servicesBundle.uninstall();
-	}
-
-	@Before
-	public void setUp() throws Exception {
-		cdiRuntime = runtimeTracker.waitForService(timeout);
-		cdiBundle = installBundle("basic-beans.jar", false);
-		cdiBundle.start();
-	}
-
-	@After
-	public void tearDown() throws Exception {
-		cdiBundle.uninstall();
-	}
-
-	void assertBeanExists(Class<?> clazz, BeanManager beanManager) {
-		Set<Bean<?>> beans = beanManager.getBeans(clazz, Any.Literal.INSTANCE);
-
-		assertFalse(beans.isEmpty());
-		Iterator<Bean<?>> iterator = beans.iterator();
-		Bean<?> bean = iterator.next();
-		assertTrue(clazz.isAssignableFrom(bean.getBeanClass()));
-		assertFalse(iterator.hasNext());
-
-		bean = beanManager.resolve(beans);
-		CreationalContext<?> ctx = beanManager.createCreationalContext(bean);
-		Object pojo = clazz.cast(beanManager.getReference(bean, clazz, ctx));
-		assertNotNull(pojo);
-	}
-
-	static InputStream getBundle(String name) {
-		ClassLoader classLoader = AbstractTestCase.class.getClassLoader();
-
-		return classLoader.getResourceAsStream(name);
-	}
-
-	Bundle getCdiExtenderBundle() {
-		BundleWiring bundleWiring = cdiBundle.adapt(BundleWiring.class);
-
-		List<BundleWire> requiredWires = bundleWiring.getRequiredWires(ExtenderNamespace.EXTENDER_NAMESPACE);
-
-		for (BundleWire wire : requiredWires) {
-			Map<String, Object> attributes = wire.getCapability().getAttributes();
-			String extender = (String)attributes.get(ExtenderNamespace.EXTENDER_NAMESPACE);
-
-			if (CDIConstants.CDI_CAPABILITY_NAME.equals(extender)) {
-				return wire.getProvider().getBundle();
-			}
-		}
-
-		return null;
-	}
-
-	public ContainerDTO getContainerDTO(CDIComponentRuntime runtime, Bundle bundle) {
-		Iterator<ContainerDTO> iterator;
-		ContainerDTO containerDTO = null;
-		int attempts = 50;
-		while (--attempts > 0) {
-			iterator = cdiRuntime.getContainerDTOs(bundle).iterator();
-			if (iterator.hasNext()) {
-				containerDTO = iterator.next();
-				if (containerDTO != null) {
-					break;
-				}
-			}
-			try {
-				Thread.sleep(100);
-			} catch (InterruptedException e) {
-				e.printStackTrace();
-			}
-		}
-		assertNotNull(containerDTO);
-		return containerDTO;
-	}
-
-	public static Bundle installBundle(String url) throws Exception {
-		return installBundle(url, true);
-	}
-
-	public static Bundle installBundle(String bundleName, boolean start) throws Exception {
-		Bundle b = bundleContext.installBundle(bundleName, getBundle(bundleName));
-
-		if (start) {
-			b.start();
-		}
-
-		return b;
-	}
-
-	Filter filter(String pattern, Object... objects) {
-		try {
-			return FrameworkUtil.createFilter(String.format(pattern, objects));
-		}
-		catch (InvalidSyntaxException e) {
-			throw new RuntimeException(e.getMessage());
-		}
-	}
-
-	public Dictionary<String, Object> getProperties(ServiceReference<Integer> reference) {
-		Dictionary<String, Object> properties = new Hashtable<>();
-		for (String key : reference.getPropertyKeys()) {
-			properties.put(key, reference.getProperty(key));
-		}
-		return properties;
-	}
-
-	public <S,T> CloseableTracker<S, T> track(Filter filter) {
-		CloseableTracker<S, T> tracker = new CloseableTracker<>(bundleContext, filter);
-		tracker.open();
-		return tracker;
-	}
-
-	public <S,T> CloseableTracker<S, T> track(String pattern, Object... objects) {
-		return track(filter(pattern, objects));
-	}
-
-	public <S> CloseableTracker<S, ServiceReference<S>> trackSR(String pattern, Object... objects) {
-		return trackSR(filter(pattern, objects));
-	}
-
-	public <S> CloseableTracker<S, ServiceReference<S>> trackSR(Filter filter) {
-		CloseableTracker<S, ServiceReference<S>> tracker = new CloseableTracker<>(bundleContext, filter, new ServiceTrackerCustomizer<S, ServiceReference<S>>() {
-
-			@Override
-			public ServiceReference<S> addingService(ServiceReference<S> reference) {
-				return reference;
-			}
-
-			@Override
-			public void modifiedService(ServiceReference<S> reference, ServiceReference<S> service) {
-			}
-
-			@Override
-			public void removedService(ServiceReference<S> reference, ServiceReference<S> service) {
-			}
-
-		});
-		tracker.open();
-		return tracker;
-	}
-
-	BeanManager getBeanManager(Bundle bundle) throws Exception {
-		return trackBM(bundle).waitForService(timeout);
-	}
-
-	CloseableTracker<BeanManager, BeanManager> trackBM(Bundle bundle) throws Exception {
-		CloseableTracker<BeanManager, BeanManager> serviceTracker = new CloseableTracker<>(
-			bundle.getBundleContext(),
-			filter(
-				"(&(objectClass=%s)(service.bundleid=%d))",
-				BeanManager.class.getName(),
-				bundle.getBundleId()),
-			null);
-		serviceTracker.open();
-		return serviceTracker;
-	}
-
-	long getChangeCount(ServiceReference<?> reference) {
-		return Optional.ofNullable(
-			reference.getProperty("service.changecount")
-		).map(
-			v -> (Long)v
-		).orElse(
-			new Long(-1l)
-		).longValue();
-	}
-
-	<T> T with(ClassLoader classLoader, Supplier<T> supplier) {
-		Thread currentThread = Thread.currentThread();
-		ClassLoader original = currentThread.getContextClassLoader();
-		try {
-			currentThread.setContextClassLoader(classLoader);
-			return supplier.get();
-		}
-		finally {
-			currentThread.setContextClassLoader(original);
-		}
-	}
-
-	static final Bundle bundle = FrameworkUtil.getBundle(CdiBeanTests.class);
-	static final BundleContext bundleContext = bundle.getBundleContext();
-	static final long timeout = 500;
-	static Bundle servicesBundle;
-	static ServiceTracker<CDIComponentRuntime, CDIComponentRuntime> runtimeTracker;
-
-	Bundle cdiBundle;
-	CDIComponentRuntime cdiRuntime;
-	final PromiseFactory promiseFactory = new PromiseFactory(null);
-
-}
\ No newline at end of file
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/BeanPropertyTypeTest.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/BeanPropertyTypeTest.java
index f729adc..abdb7cf 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/BeanPropertyTypeTest.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/BeanPropertyTypeTest.java
@@ -18,46 +18,32 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import org.apache.aries.cdi.test.cases.base.SlimBaseTestCase;
 import org.junit.After;
-import org.junit.AfterClass;
 import org.junit.Before;
-import org.junit.BeforeClass;
 import org.junit.Test;
 import org.osgi.framework.Bundle;
 import org.osgi.service.http.runtime.HttpServiceRuntime;
 import org.osgi.service.http.runtime.dto.ServletContextDTO;
 import org.osgi.util.tracker.ServiceTracker;
 
-public class BeanPropertyTypeTest extends AbstractTestCase {
-
-	@BeforeClass
-	public static void beforeClass() throws Exception {
-	}
-
-	@AfterClass
-	public static void afterClass() throws Exception {
-	}
+public class BeanPropertyTypeTest extends SlimBaseTestCase {
 
 	@Test
 	public void beanPropertyAnnotationsWereUsed() throws Exception {
-		Bundle tbBundle = installBundle("tb13.jar");
+		Bundle tbBundle = bcr.installBundle("tb13.jar");
 
-		try {
-			getBeanManager(tbBundle);
+		getBeanManager(tbBundle);
 
-			ServletContextDTO contextDTO = waitFor("customContext");
+		ServletContextDTO contextDTO = waitFor("customContext");
 
-			assertThat(contextDTO).isNotNull();
-		}
-		finally {
-			tbBundle.uninstall();
-		}
+		assertThat(contextDTO).isNotNull();
 	}
 
 	@Before
 	@Override
 	public void setUp() throws Exception {
-		hsrTracker = new ServiceTracker<>(bundleContext, HttpServiceRuntime.class, null);
+		hsrTracker = new ServiceTracker<>(bcr.getBundleContext(), HttpServiceRuntime.class, null);
 
 		hsrTracker.open();
 
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/CdiBeanTests.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/CdiBeanTests.java
index 29e613a..4cebbda 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/CdiBeanTests.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/CdiBeanTests.java
@@ -27,6 +27,8 @@
 import javax.enterprise.inject.spi.BeanManager;
 import javax.enterprise.util.AnnotationLiteral;
 
+import org.apache.aries.cdi.test.cases.base.BaseTestCase;
+import org.apache.aries.cdi.test.cases.base.CloseableTracker;
 import org.apache.aries.cdi.test.interfaces.BeanService;
 import org.apache.aries.cdi.test.interfaces.BundleContextBeanQualifier;
 import org.apache.aries.cdi.test.interfaces.FieldInjectedReference;
@@ -39,7 +41,7 @@
 import org.osgi.service.cdi.runtime.dto.ContainerDTO;
 
 @SuppressWarnings("rawtypes")
-public class CdiBeanTests extends AbstractTestCase {
+public class CdiBeanTests extends BaseTestCase {
 
 	@Test
 	public void testConstructorInjectedService() throws Exception {
@@ -121,7 +123,7 @@
 			assertNotNull(beanService);
 			assertEquals("PREFIXMETHOD", beanService.doSomething());
 
-			ContainerDTO containerDTO = getContainerDTO(cdiRuntime, cdiBundle);
+			ContainerDTO containerDTO = getContainerDTO(ccrr.getService(), cdiBundle);
 			assertNotNull(containerDTO);
 
 			ComponentDTO containerComponentDTO = containerDTO.components.stream().filter(
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/CdiContainerTests.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/CdiContainerTests.java
index b543a62..c417df1 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/CdiContainerTests.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/CdiContainerTests.java
@@ -14,11 +14,13 @@
 
 package org.apache.aries.cdi.test.cases;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 
 import javax.enterprise.inject.spi.BeanManager;
 import javax.enterprise.inject.spi.CDI;
 
+import org.apache.aries.cdi.test.cases.base.BaseTestCase;
 import org.apache.aries.cdi.test.interfaces.Pojo;
 import org.junit.Ignore;
 import org.junit.Test;
@@ -28,7 +30,7 @@
 import org.osgi.service.cdi.runtime.dto.ComponentInstanceDTO;
 import org.osgi.service.cdi.runtime.dto.ContainerDTO;
 
-public class CdiContainerTests extends AbstractTestCase {
+public class CdiContainerTests extends BaseTestCase {
 
 	@Test
 	public void testGetBeanFromCdiContainerService() throws Exception {
@@ -41,7 +43,7 @@
 	@Test
 	@Ignore("Due to a Service Loader Mediator incompatibility in the official CDI 2.0 API")
 	public void testGetBeanManagerFromCDI() throws Exception {
-		BeanManager beanManager = with(
+		BeanManager beanManager = tccl(
 			cdiBundle.adapt(BundleWiring.class).getClassLoader(),
 			() ->
 				CDI.current().getBeanManager()
@@ -53,11 +55,11 @@
 
 	@Test
 	public void testContainerComponentSingleton() throws Exception {
-		while (getContainerDTO(cdiRuntime, cdiBundle).components.isEmpty()) {
+		while (getContainerDTO(ccrr.getService(), cdiBundle).components.isEmpty()) {
 			Thread.sleep(10);
 		}
 
-		ContainerDTO containerDTO = getContainerDTO(cdiRuntime, cdiBundle);
+		ContainerDTO containerDTO = getContainerDTO(ccrr.getService(), cdiBundle);
 		assertNotNull(containerDTO);
 
 		ComponentDTO containerComponentDTO = containerDTO.components.stream()
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/CdiExtenderTests.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/CdiExtenderTests.java
index e0df465..0a83e39 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/CdiExtenderTests.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/CdiExtenderTests.java
@@ -19,10 +19,12 @@
 
 import javax.enterprise.inject.spi.BeanManager;
 
+import org.apache.aries.cdi.test.cases.base.BaseTestCase;
+import org.apache.aries.cdi.test.cases.base.CloseableTracker;
 import org.junit.Test;
 import org.osgi.framework.Bundle;
 
-public class CdiExtenderTests extends AbstractTestCase {
+public class CdiExtenderTests extends BaseTestCase {
 
 	@Test
 	public void testStopExtender() throws Exception {
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/ConfigurationTests.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/ConfigurationTests.java
index 065b0f4..2a0e621 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/ConfigurationTests.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/ConfigurationTests.java
@@ -27,56 +27,32 @@
 import java.util.concurrent.Callable;
 import java.util.stream.Collectors;
 
+import org.apache.aries.cdi.test.cases.base.CloseableTracker;
+import org.apache.aries.cdi.test.cases.base.SlimBaseTestCase;
 import org.apache.aries.cdi.test.interfaces.BeanService;
-import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
 import org.osgi.framework.Bundle;
 import org.osgi.service.cdi.ConfigurationPolicy;
-import org.osgi.service.cdi.runtime.CDIComponentRuntime;
 import org.osgi.service.cdi.runtime.dto.ComponentDTO;
 import org.osgi.service.cdi.runtime.dto.ContainerDTO;
 import org.osgi.service.cdi.runtime.dto.template.ConfigurationTemplateDTO;
 import org.osgi.service.cm.Configuration;
-import org.osgi.service.cm.ConfigurationAdmin;
-import org.osgi.util.tracker.ServiceTracker;
 
-public class ConfigurationTests extends AbstractTestCase {
-
-	@Before
-	@Override
-	public void setUp() throws Exception {
-		runtimeTracker = new ServiceTracker<>(
-			bundleContext, CDIComponentRuntime.class, null);
-		runtimeTracker.open();
-
-		cdiRuntime = runtimeTracker.waitForService(timeout);
-
-		adminTracker = new ServiceTracker<>(bundleContext, ConfigurationAdmin.class, null);
-		adminTracker.open();
-		configurationAdmin = adminTracker.getService();
-	}
-
-	@After
-	@Override
-	public void tearDown() throws Exception {
-		runtimeTracker.close();
-		adminTracker.close();
-	}
+public class ConfigurationTests extends SlimBaseTestCase {
 
 	@Test
 	@SuppressWarnings({ "rawtypes", "unchecked" })
 	public void testConfiguration() throws Exception {
-		Bundle tb3Bundle = installBundle("tb3.jar");
+		Bundle tb3Bundle = bcr.installBundle("tb3.jar");
 
 		Configuration configurationA = null, configurationB = null;
-		ServiceTracker<BeanService, BeanService> stA = null, stB = null;
+
 		try {
 			int attempts = 50;
 			ComponentDTO configurationBeanA = null;
 
 			while (--attempts > 0) {
-				ContainerDTO containerDTO = getContainerDTO(cdiRuntime, tb3Bundle);
+				ContainerDTO containerDTO = getContainerDTO(ccrr.getService(), tb3Bundle);
 
 				configurationBeanA = containerDTO.components.stream().filter(
 					c -> c.template.name.equals("configurationBeanA")
@@ -98,7 +74,7 @@
 				)
 			);
 
-			configurationA = configurationAdmin.getConfiguration("configurationBeanA", "?");
+			configurationA = car.getService().getConfiguration("configurationBeanA", "?");
 
 			Dictionary<String, Object> p1 = new Hashtable<>();
 			p1.put("ports", new int[] {12, 4567});
@@ -110,47 +86,41 @@
 				)
 			);
 
-			configurationB = configurationAdmin.getConfiguration("configurationBeanB", "?");
+			configurationB = car.getService().getConfiguration("configurationBeanB", "?");
 
 			Dictionary<String, Object> p2 = new Hashtable<>();
 			p2.put("color", "green");
 			p2.put("ports", new int[] {80});
 			configurationB.update(p2);
 
-			stA = new ServiceTracker<>(
-					bundleContext, bundleContext.createFilter(
-					"(&(objectClass=org.apache.aries.cdi.test.interfaces.BeanService)(bean=A))"), null);
-			stA.open(true);
+			try (CloseableTracker<BeanService, BeanService> stA = track("(&(objectClass=%s)(bean=A))", BeanService.class.getName())) {
+				BeanService<Callable<int[]>> beanService = stA.waitForService(timeout);
 
-			BeanService<Callable<int[]>> beanService = stA.waitForService(timeout);
+				assertNotNull(beanService);
 
-			assertNotNull(beanService);
+				assertWithRetries(() -> {
+					assertEquals("blue", beanService.doSomething());
+					try {
+						assertArrayEquals(new int[]{12, 4567}, beanService.get().call());
+					} catch (final Exception e) {
+						fail(e.getMessage());
+					}
+				});
+			}
 
-			assertWithRetries(() -> {
-				assertEquals("blue", beanService.doSomething());
-				try {
-					assertArrayEquals(new int[]{12, 4567}, beanService.get().call());
-				} catch (final Exception e) {
-					fail(e.getMessage());
-				}
-			});
+			try (CloseableTracker<BeanService, BeanService> stB = track("(&(objectClass=%s)(bean=B))", BeanService.class.getName())) {
+				final BeanService<Callable<int[]>> beanServiceB = stB.waitForService(timeout);
+				assertNotNull(beanServiceB);
 
-			stB = new ServiceTracker<>(
-					bundleContext, bundleContext.createFilter(
-					"(&(objectClass=org.apache.aries.cdi.test.interfaces.BeanService)(bean=B))"), null);
-			stB.open(true);
-
-			final BeanService<Callable<int[]>> beanServiceB = stB.waitForService(timeout);
-			assertNotNull(beanServiceB);
-
-			assertWithRetries(() -> {
-				assertEquals("green", beanServiceB.doSomething());
-				try {
-					assertArrayEquals(new int[]{80}, beanServiceB.get().call());
-				} catch (final Exception e) {
-					fail(e.getMessage());
-				}
-			});
+				assertWithRetries(() -> {
+					assertEquals("green", beanServiceB.doSomething());
+					try {
+						assertArrayEquals(new int[]{80}, beanServiceB.get().call());
+					} catch (final Exception e) {
+						fail(e.getMessage());
+					}
+				});
+			}
 		}
 		finally {
 			if (configurationA != null) {
@@ -169,13 +139,6 @@
 					// ignore
 				}
 			}
-			if (stA != null) {
-				stA.close();
-			}
-			if (stB != null) {
-				stB.close();
-			}
-			tb3Bundle.uninstall();
 		}
 	}
 
@@ -198,36 +161,25 @@
 	@Test
 	@SuppressWarnings({ "rawtypes", "unchecked" })
 	public void testOptionalConfiguration() throws Exception {
-		Bundle tb5Bundle = installBundle("tb5.jar");
+		bcr.installBundle("tb5.jar");
 
 		Configuration configurationC = null;
-		ServiceTracker<BeanService, BeanService> stC = new ServiceTracker<BeanService, BeanService>(
-			bundleContext, bundleContext.createFilter(
-				"(&(objectClass=org.apache.aries.cdi.test.interfaces.BeanService)(bean=C))"), null);
 
-		try {
-			Thread.sleep(1000); // <---- TODO fix this
-
-			stC.open(true);
-
+		try (CloseableTracker<BeanService, BeanService> stC = track("(&(objectClass=%s)(bean=C))", BeanService.class.getName())) {
 			BeanService<Callable<int[]>> beanService = stC.waitForService(timeout);
 
 			assertNotNull(beanService);
 			assertEquals("blue", beanService.doSomething());
 			assertArrayEquals(new int[] {35777}, beanService.get().call());
+		}
 
-			configurationC = configurationAdmin.getConfiguration("foo.bar", "?");
+		configurationC = car.getService().getConfiguration("foo.bar", "?");
 
-			Dictionary<String, Object> properties = new Hashtable<>();
-			properties.put("ports", new int[] {12, 4567});
-			configurationC.update(properties);
+		Dictionary<String, Object> properties = new Hashtable<>();
+		properties.put("ports", new int[] {12, 4567});
+		configurationC.update(properties);
 
-			stC.close();
-			stC = new ServiceTracker<>(
-					bundleContext, bundleContext.createFilter(
-					"(&(objectClass=org.apache.aries.cdi.test.interfaces.BeanService)(bean=C)(ports=12))"), null);
-			stC.open(true);
-
+		try (CloseableTracker<BeanService, BeanService> stC = track("(&(objectClass=%s)(bean=C)(ports=12))", BeanService.class.getName())) {
 			final BeanService<Callable<int[]>> beanServiceC = stC.waitForService(timeout);
 
 			assertNotNull(beanServiceC);
@@ -239,21 +191,18 @@
 					fail(e.getMessage());
 				}
 			});
+		}
 
-			configurationC.delete();
+		configurationC.delete();
 
-			stC.close();
-			stC = new ServiceTracker<>(
-					bundleContext, bundleContext.createFilter(
-					"(&(objectClass=org.apache.aries.cdi.test.interfaces.BeanService)(bean=C)(!(ports=*)))"), null);
-			stC.open(true);
-			final BeanService<Callable<int[]>> beanServiceC2 = stC.waitForService(timeout);
+		try (CloseableTracker<BeanService, BeanService> stC = track("(&(objectClass=%s)(bean=C)(!(ports=*)))", BeanService.class.getName())) {
+			final BeanService<Callable<int[]>> beanService = stC.waitForService(timeout);
 
 			assertNotNull(beanService);
 			assertWithRetries(() -> {
-				assertEquals("blue", beanServiceC2.doSomething());
+				assertEquals("blue", beanService.doSomething());
 				try {
-					assertArrayEquals(new int[] {35777}, beanServiceC2.get().call());
+					assertArrayEquals(new int[] {35777}, beanService.get().call());
 				} catch (final Exception e) {
 					fail(e.getMessage());
 				}
@@ -268,14 +217,7 @@
 					// ignore
 				}
 			}
-			if (stC != null) {
-				stC.close();
-			}
-			tb5Bundle.uninstall();
 		}
 	}
 
-	private ServiceTracker<ConfigurationAdmin, ConfigurationAdmin> adminTracker;
-	private ConfigurationAdmin configurationAdmin;
-
 }
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/DisableComponentTests.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/DisableComponentTests.java
index 0f019c2..2790c29 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/DisableComponentTests.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/DisableComponentTests.java
@@ -20,34 +20,18 @@
 import java.util.Dictionary;
 import java.util.Hashtable;
 
+import org.apache.aries.cdi.test.cases.base.CloseableTracker;
+import org.apache.aries.cdi.test.cases.base.SlimBaseTestCase;
 import org.apache.aries.cdi.test.interfaces.Pojo;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.osgi.framework.Bundle;
 import org.osgi.service.cm.Configuration;
-import org.osgi.service.cm.ConfigurationAdmin;
-import org.osgi.test.junit4.service.ServiceUseRule;
 
-public class DisableComponentTests	 extends AbstractTestCase {
-
-	@Rule
-	public ServiceUseRule<ConfigurationAdmin> car = new ServiceUseRule.Builder<ConfigurationAdmin>(ConfigurationAdmin.class).build();
-
-	@Before
-	@Override
-	public void setUp() throws Exception {
-	}
-
-	@After
-	@Override
-	public void tearDown() throws Exception {
-	}
+public class DisableComponentTests extends SlimBaseTestCase {
 
 	@Test
 	public void testDisableContainerComponent() throws Exception {
-		Bundle tb8Bundle = installBundle("tb8.jar");
+		Bundle tb8Bundle = bcr.installBundle("tb8.jar");
 
 		try (CloseableTracker<Pojo, Pojo> tracker = track(
 				"(&(objectClass=%s)(objectClass=*.%s)(service.bundleid=%s))",
@@ -99,14 +83,13 @@
 						// ignore
 					}
 				}
-				tb8Bundle.uninstall();
 			}
 		}
 	}
 
 	@Test
 	public void testDisableSingleComponent() throws Exception {
-		Bundle tb8Bundle = installBundle("tb8.jar");
+		Bundle tb8Bundle = bcr.installBundle("tb8.jar");
 
 		try (CloseableTracker<Pojo, Pojo> tracker = track(
 			"(&(objectClass=%s)(objectClass=*.%s)(service.bundleid=%s))",
@@ -158,7 +141,6 @@
 						// ignore
 					}
 				}
-				tb8Bundle.uninstall();
 			}
 		}
 	}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/EventsTests.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/EventsTests.java
index 52d273a..e555536 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/EventsTests.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/EventsTests.java
@@ -14,34 +14,23 @@
 
 package org.apache.aries.cdi.test.cases;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
 
 import java.util.Dictionary;
 
+import org.apache.aries.cdi.test.cases.base.CloseableTracker;
+import org.apache.aries.cdi.test.cases.base.SlimBaseTestCase;
 import org.apache.aries.cdi.test.interfaces.Pojo;
-import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.cdi.runtime.dto.ContainerDTO;
 
-public class EventsTests extends AbstractTestCase {
-
-	@Override
-	@Before
-	public void setUp() throws Exception {
-		cdiRuntime = runtimeTracker.waitForService(timeout);
-	}
-
-	@Override
-	@After
-	public void tearDown() throws Exception {
-	}
+public class EventsTests extends SlimBaseTestCase {
 
 	@Test
 	public void testContainerComponentReferenceEventHandler() throws Exception {
-		Bundle tb = installBundle("tb9.jar");
+		Bundle tb = bcr.installBundle("tb9.jar");
 
 		try (CloseableTracker<Pojo, Pojo> tracker = track("(objectClass=%s)", Pojo.class.getName())) {
 			Pojo pojo = tracker.waitForService(timeout);
@@ -49,14 +38,14 @@
 			assertEquals(0, pojo.getCount());
 			assertEquals("[]", pojo.foo(null));
 
-			ContainerDTO containerDTO = getContainerDTO(cdiRuntime, tb);
+			ContainerDTO containerDTO = getContainerDTO(ccrr.getService(), tb);
 
 			long changeCount = containerDTO.changeCount;
 
-			ServiceRegistration<Integer> int1 = bundleContext.registerService(Integer.class, new Integer(12), null);
+			ServiceRegistration<Integer> int1 = bcr.getBundleContext().registerService(Integer.class, new Integer(12), null);
 
 			try {
-				for (long i = 10; i > 0 && (getContainerDTO(cdiRuntime, tb).changeCount == changeCount); i--) {
+				for (long i = 10; i > 0 && (getContainerDTO(ccrr.getService(), tb).changeCount == changeCount); i--) {
 					Thread.sleep(20);
 				}
 
@@ -65,11 +54,11 @@
 
 				changeCount = containerDTO.changeCount;
 
-				Dictionary<String, Object> properties = getProperties(int1.getReference());
+				Dictionary<String, Object> properties = int1.getReference().getProperties();
 				properties.put("foo", "bar");
 				int1.setProperties(properties);
 
-				for (long i = 10; i > 0 && (getContainerDTO(cdiRuntime, tb).changeCount == changeCount); i--) {
+				for (long i = 10; i > 0 && (getContainerDTO(ccrr.getService(), tb).changeCount == changeCount); i--) {
 					Thread.sleep(20);
 				}
 
@@ -80,7 +69,7 @@
 
 				int1.unregister();
 
-				for (long i = 10; i > 0 && (getContainerDTO(cdiRuntime, tb).changeCount == changeCount); i--) {
+				for (long i = 10; i > 0 && (getContainerDTO(ccrr.getService(), tb).changeCount == changeCount); i--) {
 					Thread.sleep(20);
 				}
 
@@ -88,14 +77,11 @@
 				assertEquals("[]", pojo.foo(null));
 			}
 		}
-		finally {
-			tb.uninstall();
-		}
 	}
 
 	@Test
 	public void testSingleComponentReferenceEventHandler() throws Exception {
-		Bundle tb = installBundle("tb10.jar");
+		Bundle tb = bcr.installBundle("tb10.jar");
 
 		try (CloseableTracker<Pojo, Pojo> tracker = track("(objectClass=%s)", Pojo.class.getName())) {
 			Pojo pojo = tracker.waitForService(timeout);
@@ -103,14 +89,14 @@
 			assertEquals(0, pojo.getCount());
 			assertEquals("[]", pojo.foo(null));
 
-			ContainerDTO containerDTO = getContainerDTO(cdiRuntime, tb);
+			ContainerDTO containerDTO = getContainerDTO(ccrr.getService(), tb);
 
 			long changeCount = containerDTO.changeCount;
 
-			ServiceRegistration<Integer> int1 = bundleContext.registerService(Integer.class, new Integer(12), null);
+			ServiceRegistration<Integer> int1 = bcr.getBundleContext().registerService(Integer.class, new Integer(12), null);
 
 			try {
-				for (long i = 10; i > 0 && (getContainerDTO(cdiRuntime, tb).changeCount == changeCount); i--) {
+				for (long i = 10; i > 0 && (getContainerDTO(ccrr.getService(), tb).changeCount == changeCount); i--) {
 					Thread.sleep(20);
 				}
 
@@ -119,11 +105,11 @@
 
 				changeCount = containerDTO.changeCount;
 
-				Dictionary<String, Object> properties = getProperties(int1.getReference());
+				Dictionary<String, Object> properties = int1.getReference().getProperties();
 				properties.put("foo", "bar");
 				int1.setProperties(properties);
 
-				for (long i = 10; i > 0 && (getContainerDTO(cdiRuntime, tb).changeCount == changeCount); i--) {
+				for (long i = 10; i > 0 && (getContainerDTO(ccrr.getService(), tb).changeCount == changeCount); i--) {
 					Thread.sleep(20);
 				}
 
@@ -134,7 +120,7 @@
 
 				int1.unregister();
 
-				for (long i = 10; i > 0 && (getContainerDTO(cdiRuntime, tb).changeCount == changeCount); i--) {
+				for (long i = 10; i > 0 && (getContainerDTO(ccrr.getService(), tb).changeCount == changeCount); i--) {
 					Thread.sleep(20);
 				}
 
@@ -142,9 +128,6 @@
 				assertEquals("[]", pojo.foo(null));
 			}
 		}
-		finally {
-			tb.uninstall();
-		}
 	}
 
 }
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/ExcludeTests.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/ExcludeTests.java
index 6e73fa1..1aade58 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/ExcludeTests.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/ExcludeTests.java
@@ -17,96 +17,47 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
+import org.apache.aries.cdi.test.cases.base.BaseTestCase;
 import org.junit.Test;
 import org.osgi.framework.Bundle;
-import org.osgi.service.cdi.runtime.CDIComponentRuntime;
 import org.osgi.service.cdi.runtime.dto.ContainerDTO;
-import org.osgi.util.tracker.ServiceTracker;
 
-public class ExcludeTests extends AbstractTestCase {
-
-	@BeforeClass
-	public static void beforeClass() throws Exception {
-		runtimeTracker = new ServiceTracker<>(
-				bundleContext, CDIComponentRuntime.class, null);
-		runtimeTracker.open();
-	}
-
-	@AfterClass
-	public static void afterClass() throws Exception {
-		runtimeTracker.close();
-	}
-
-	@Override
-	@Before
-	public void setUp() throws Exception {
-		cdiRuntime = runtimeTracker.waitForService(timeout);
-	}
-
-	@Override
-	@After
-	public void tearDown() throws Exception {
-	}
+public class ExcludeTests extends BaseTestCase {
 
 	@Test
 	public void testExclude_ByName() throws Exception {
-		Bundle tb2Bundle = installBundle("tb18.jar", false);
+		Bundle tb2Bundle = bcr.installBundle("tb18.jar");
 
-		tb2Bundle.start();
+		ContainerDTO containerDTO = getContainerDTO(ccrr.getService(), tb2Bundle);
+		assertNotNull(containerDTO);
 
-		try {
-			ContainerDTO containerDTO = getContainerDTO(cdiRuntime, tb2Bundle);
-			assertNotNull(containerDTO);
+		assertEquals(1, containerDTO.template.components.size());
 
-			assertEquals(1, containerDTO.template.components.size());
-
-			assertEquals(3, containerDTO.template.components.get(0).beans.size());
-		}
-		finally {
-			tb2Bundle.uninstall();
-		}
+		assertEquals(3, containerDTO.template.components.get(0).beans.size());
 	}
 
 	@Test
 	public void testExclude_IfClassAvailable() throws Exception {
-		Bundle tb2Bundle = installBundle("tb19.jar", false);
+		Bundle tb2Bundle = bcr.installBundle("tb19.jar");
 
-		tb2Bundle.start();
+		ContainerDTO containerDTO = getContainerDTO(ccrr.getService(), tb2Bundle);
+		assertNotNull(containerDTO);
 
-		try {
-			ContainerDTO containerDTO = getContainerDTO(cdiRuntime, tb2Bundle);
-			assertNotNull(containerDTO);
+		assertEquals(1, containerDTO.template.components.size());
 
-			assertEquals(1, containerDTO.template.components.size());
-
-			assertEquals(1, containerDTO.template.components.get(0).beans.size());
-		}
-		finally {
-			tb2Bundle.uninstall();
-		}
+		assertEquals(1, containerDTO.template.components.get(0).beans.size());
 	}
 
 	@Test
 	public void testExclude_IfSystemProperty() throws Exception {
-		Bundle tb2Bundle = installBundle("tb20.jar", false);
+		Bundle tb2Bundle = bcr.installBundle("tb20.jar");
 
-		tb2Bundle.start();
+		ContainerDTO containerDTO = getContainerDTO(ccrr.getService(), tb2Bundle);
+		assertNotNull(containerDTO);
 
-		try {
-			ContainerDTO containerDTO = getContainerDTO(cdiRuntime, tb2Bundle);
-			assertNotNull(containerDTO);
+		assertEquals(1, containerDTO.template.components.size());
 
-			assertEquals(1, containerDTO.template.components.size());
-
-			assertEquals(2, containerDTO.template.components.get(0).beans.size());
-		}
-		finally {
-			tb2Bundle.uninstall();
-		}
+		assertEquals(2, containerDTO.template.components.get(0).beans.size());
 	}
 
 }
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/FactoryComponentTests.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/FactoryComponentTests.java
index e22bfea..3cec8f6 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/FactoryComponentTests.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/FactoryComponentTests.java
@@ -14,40 +14,25 @@
 
 package org.apache.aries.cdi.test.cases;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 
 import java.util.Dictionary;
 import java.util.Hashtable;
 
+import org.apache.aries.cdi.test.cases.base.CloseableTracker;
+import org.apache.aries.cdi.test.cases.base.SlimBaseTestCase;
 import org.apache.aries.cdi.test.interfaces.BeanService;
-import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
-import org.osgi.framework.Bundle;
 import org.osgi.service.cm.Configuration;
-import org.osgi.service.cm.ConfigurationAdmin;
-import org.osgi.util.tracker.ServiceTracker;
 
-public class FactoryComponentTests extends AbstractTestCase {
-
-	@Before
-	@Override
-	public void setUp() throws Exception {
-		adminTracker = new ServiceTracker<>(bundleContext, ConfigurationAdmin.class, null);
-		adminTracker.open();
-		configurationAdmin = adminTracker.getService();
-	}
-
-	@After
-	@Override
-	public void tearDown() throws Exception {
-		adminTracker.close();
-	}
+public class FactoryComponentTests extends SlimBaseTestCase {
 
 	@SuppressWarnings("rawtypes")
 	@Test
 	public void testFactoryComponent() throws Exception {
-		Bundle tb7Bundle = installBundle("tb7.jar");
+		bcr.installBundle("tb7.jar");
 
 		try (CloseableTracker<BeanService, BeanService> tracker = track(
 			"(&(objectClass=%s)(objectClass=*.%s))",
@@ -59,37 +44,34 @@
 			assertNull(beanService);
 
 			Configuration configurationA = null, configurationB = null;
-			ServiceTracker<BeanService, BeanService> trackerA = null, trackerB = null;
 
-			try {
-				configurationA = configurationAdmin.getFactoryConfiguration("configurationBeanF", "one");
+			try (CloseableTracker<BeanService, BeanService> trackerA = track(
+					"(&(objectClass=%s)(objectClass=*.%s)(instance=A))",
+					BeanService.class.getName(),
+					"ConfigurationBeanF");
+				CloseableTracker<BeanService, BeanService> trackerB = track(
+					"(&(objectClass=%s)(objectClass=*.%s)(instance=B))",
+					BeanService.class.getName(),
+					"ConfigurationBeanF")) {
+
+				configurationA = car.getService().getFactoryConfiguration("configurationBeanF", "one");
 
 				Dictionary<String, Object> p1 = new Hashtable<>();
 				p1.put("ports", new int[] {12, 4567});
 				p1.put("instance", "A");
 				configurationA.update(p1);
 
-				trackerA = track(
-					"(&(objectClass=%s)(objectClass=*.%s)(instance=A))",
-					BeanService.class.getName(),
-					"ConfigurationBeanF");
-
 				BeanService beanServiceA = trackerA.waitForService(timeout);
 
 				assertNotNull(beanServiceA);
 
-				configurationB = configurationAdmin.getFactoryConfiguration("configurationBeanF", "two");
+				configurationB = car.getService().getFactoryConfiguration("configurationBeanF", "two");
 
 				p1 = new Hashtable<>();
 				p1.put("ports", new int[] {45689, 1065});
 				p1.put("instance", "B");
 				configurationB.update(p1);
 
-				trackerB = track(
-					"(&(objectClass=%s)(objectClass=*.%s)(instance=B))",
-					BeanService.class.getName(),
-					"ConfigurationBeanF");
-
 				BeanService beanServiceB = trackerB.waitForService(timeout);
 
 				assertNotNull(beanServiceB);
@@ -137,18 +119,8 @@
 						// ignore
 					}
 				}
-				if (trackerA != null) {
-					trackerA.close();
-				}
-				if (trackerB != null) {
-					trackerB.close();
-				}
-				tb7Bundle.uninstall();
 			}
 		}
 	}
 
-	private ServiceTracker<ConfigurationAdmin, ConfigurationAdmin> adminTracker;
-	private ConfigurationAdmin configurationAdmin;
-
 }
\ No newline at end of file
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/HttpTestCase.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/HttpTestCase.java
index 45bbd82..f424fcd 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/HttpTestCase.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/HttpTestCase.java
@@ -15,12 +15,10 @@
 package org.apache.aries.cdi.test.cases;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
 
-import java.io.InputStream;
 import java.net.URI;
 
+import org.apache.aries.cdi.test.cases.base.HttpBaseTestCase;
 import org.apache.http.HttpEntity;
 import org.apache.http.client.CookieStore;
 import org.apache.http.client.methods.CloseableHttpResponse;
@@ -30,230 +28,127 @@
 import org.apache.http.impl.client.BasicCookieStore;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClientBuilder;
-import org.apache.http.osgi.services.HttpClientBuilderFactory;
 import org.apache.http.protocol.BasicHttpContext;
 import org.apache.http.protocol.HttpContext;
-import org.assertj.core.util.Arrays;
-import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.http.runtime.HttpServiceRuntime;
-import org.osgi.service.http.runtime.dto.ServletContextDTO;
 import org.osgi.service.http.runtime.dto.ServletDTO;
-import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
-import org.osgi.util.tracker.ServiceTracker;
 
-public class HttpTestCase extends AbstractTestCase {
+public class HttpTestCase extends HttpBaseTestCase {
 
 	@Test
 	public void testSessionScoped() throws Exception {
-		Bundle tb6Bundle = installBundle("tb6.jar");
-		Bundle tb2Bundle = installBundle("tb2.jar");
+		bcr.installBundle("tb6.jar");
+		bcr.installBundle("tb2.jar");
 
-		try {
-			String path = "/foo";
+		String path = "/foo";
 
-			ServletDTO servletDTO = waitFor(path);
+		ServletDTO servletDTO = waitFor(path);
 
-			assertEquals("foo", servletDTO.name);
+		assertEquals("foo", servletDTO.name);
 
-			HttpClientBuilder clientBuilder = hcbf.newBuilder();
-			CloseableHttpClient httpclient = clientBuilder.build();
+		HttpClientBuilder clientBuilder = hcbfr.getService().newBuilder();
+		CloseableHttpClient httpclient = clientBuilder.build();
 
-			CookieStore cookieStore = new BasicCookieStore();
-			HttpContext httpContext = new BasicHttpContext();
-			httpContext.setAttribute(HttpClientContext.COOKIE_STORE, cookieStore);
+		CookieStore cookieStore = new BasicCookieStore();
+		HttpContext httpContext = new BasicHttpContext();
+		httpContext.setAttribute(HttpClientContext.COOKIE_STORE, cookieStore);
 
-			URI uri = new URIBuilder(getEndpoint()).
+		URI uri = new URIBuilder(getHttpEndpoint()).
 				setPath(path).
 				setParameter("name", "test").
 				build();
 
-			HttpGet httpget = new HttpGet(uri);
+		HttpGet httpget = new HttpGet(uri);
+
+		try (CloseableHttpResponse response = httpclient.execute(httpget, httpContext)) {
+			HttpEntity entity = response.getEntity();
+
+			assertEquals("test", read(entity));
+		}
+
+		for (int i = 0; i < 10; i++) {
+			uri = new URIBuilder(getHttpEndpoint()).
+					setPath(path).
+					build();
+
+			httpget = new HttpGet(uri);
 
 			try (CloseableHttpResponse response = httpclient.execute(httpget, httpContext)) {
 				HttpEntity entity = response.getEntity();
 
 				assertEquals("test", read(entity));
 			}
+		}
 
-			for (int i = 0; i < 10; i++) {
-				uri = new URIBuilder(getEndpoint()).
-					setPath(path).
-					build();
-
-				httpget = new HttpGet(uri);
-
-				try (CloseableHttpResponse response = httpclient.execute(httpget, httpContext)) {
-					HttpEntity entity = response.getEntity();
-
-					assertEquals("test", read(entity));
-				}
-			}
-
-			uri = new URIBuilder(getEndpoint()).
+		uri = new URIBuilder(getHttpEndpoint()).
 				setPath(path).
 				build();
 
-			httpget = new HttpGet(uri);
+		httpget = new HttpGet(uri);
 
-			try (CloseableHttpResponse response = httpclient.execute(httpget)) {
-				HttpEntity entity = response.getEntity();
+		try (CloseableHttpResponse response = httpclient.execute(httpget)) {
+			HttpEntity entity = response.getEntity();
 
-				assertEquals("", read(entity));
-			}
-		}
-		finally {
-			tb6Bundle.uninstall();
-			tb2Bundle.uninstall();
+			assertEquals("", read(entity));
 		}
 	}
 
 	@Test
 	public void testRequestScopedWithReference() throws Exception {
-		Bundle tb6Bundle = installBundle("tb6.jar");
-		Bundle tb2Bundle = installBundle("tb2.jar");
+		bcr.installBundle("tb6.jar");
+		bcr.installBundle("tb2.jar");
 
-		try {
-			String path = "/bar";
+		String path = "/bar";
 
-			ServletDTO servletDTO = waitFor(path);
+		ServletDTO servletDTO = waitFor(path);
 
-			assertEquals("bar", servletDTO.name);
+		assertEquals("bar", servletDTO.name);
 
-			HttpClientBuilder clientBuilder = hcbf.newBuilder();
-			CloseableHttpClient httpclient = clientBuilder.build();
+		HttpClientBuilder clientBuilder = hcbfr.getService().newBuilder();
+		CloseableHttpClient httpclient = clientBuilder.build();
 
-			CookieStore cookieStore = new BasicCookieStore();
-			HttpContext httpContext = new BasicHttpContext();
-			httpContext.setAttribute(HttpClientContext.COOKIE_STORE, cookieStore);
+		CookieStore cookieStore = new BasicCookieStore();
+		HttpContext httpContext = new BasicHttpContext();
+		httpContext.setAttribute(HttpClientContext.COOKIE_STORE, cookieStore);
 
-			URI uri = new URIBuilder(getEndpoint()).
+		URI uri = new URIBuilder(getHttpEndpoint()).
 				setPath(path).
 				setParameter("name", "test").
 				build();
 
-			HttpGet httpget = new HttpGet(uri);
+		HttpGet httpget = new HttpGet(uri);
 
-			try (CloseableHttpResponse response = httpclient.execute(httpget, httpContext)) {
-				HttpEntity entity = response.getEntity();
+		try (CloseableHttpResponse response = httpclient.execute(httpget, httpContext)) {
+			HttpEntity entity = response.getEntity();
 
-				assertEquals("POJO-IMPLtest", read(entity));
-			}
+			assertEquals("POJO-IMPLtest", read(entity));
+		}
 
-			for (int i = 0; i < 10; i++) {
-				uri = new URIBuilder(getEndpoint()).
+		for (int i = 0; i < 10; i++) {
+			uri = new URIBuilder(getHttpEndpoint()).
 					setPath(path).
 					build();
 
-				httpget = new HttpGet(uri);
-
-				try (CloseableHttpResponse response = httpclient.execute(httpget, httpContext)) {
-					HttpEntity entity = response.getEntity();
-
-					assertEquals("", read(entity));
-				}
-			}
-
-			uri = new URIBuilder(getEndpoint()).
-				setPath(path).
-				build();
-
 			httpget = new HttpGet(uri);
 
-			try (CloseableHttpResponse response = httpclient.execute(httpget)) {
+			try (CloseableHttpResponse response = httpclient.execute(httpget, httpContext)) {
 				HttpEntity entity = response.getEntity();
 
 				assertEquals("", read(entity));
 			}
 		}
-		finally {
-			tb6Bundle.uninstall();
-			tb2Bundle.uninstall();
+
+		uri = new URIBuilder(getHttpEndpoint()).
+				setPath(path).
+				build();
+
+		httpget = new HttpGet(uri);
+
+		try (CloseableHttpResponse response = httpclient.execute(httpget)) {
+			HttpEntity entity = response.getEntity();
+
+			assertEquals("", read(entity));
 		}
 	}
 
-	private String getEndpoint() {
-		String[] endpoints = (String[])hsrReference.getProperty("osgi.http.endpoint");
-
-		if (endpoints == null || endpoints.length == 0) {
-			String port = (String)hsrReference.getProperty("org.osgi.service.http.port");
-			return "http://localhost:" + port;
-		}
-
-		return endpoints[0];
-	}
-
-	@Before
-	@Override
-	public void setUp() throws Exception {
-		hsrTracker = new ServiceTracker<>(bundleContext, HttpServiceRuntime.class, null);
-
-		hsrTracker.open();
-
-		hsr = hsrTracker.waitForService(timeout);
-
-		hsrReference = hsrTracker.getServiceReference();
-
-		hcbfTracker = new ServiceTracker<>(bundleContext, HttpClientBuilderFactory.class, null);
-
-		hcbfTracker.open();
-
-		hcbf = hcbfTracker.waitForService(timeout);
-
-		assertNotNull(hsr);
-	}
-
-	@After
-	@Override
-	public void tearDown() throws Exception {
-		hsrTracker.close();
-		hcbfTracker.close();
-	}
-
-	private static String read(HttpEntity entity) throws Exception {
-		if (entity == null) {
-			return null;
-		}
-
-		try (InputStream in = entity.getContent();
-			java.util.Scanner s = new java.util.Scanner(in)) {
-
-			s.useDelimiter("\\A");
-			return s.hasNext() ? s.next() : "";
-		}
-	}
-
-	private ServletDTO waitFor(String path) throws InterruptedException {
-		return waitFor(path, 20);
-	}
-
-	private ServletDTO waitFor(String path, int intervals) throws InterruptedException {
-		for (int j = intervals; j > 0; j--) {
-			for (ServletContextDTO scDTO : hsr.getRuntimeDTO().servletContextDTOs) {
-				if (scDTO.name.equals(HttpWhiteboardConstants.HTTP_WHITEBOARD_DEFAULT_CONTEXT_NAME)) {
-					for (ServletDTO sDTO : scDTO.servletDTOs) {
-						if (Arrays.asList(sDTO.patterns).contains(path)) {
-							return sDTO;
-						}
-					}
-				}
-			}
-
-			Thread.sleep(50);
-		}
-
-		assertTrue(String.format("%s not found in time", path), false);
-
-		return null;
-	}
-
-	private HttpClientBuilderFactory hcbf;
-	private HttpServiceRuntime hsr;
-	private ServiceReference<HttpServiceRuntime> hsrReference;
-	private ServiceTracker<HttpClientBuilderFactory, HttpClientBuilderFactory> hcbfTracker;
-	private ServiceTracker<HttpServiceRuntime, HttpServiceRuntime> hsrTracker;
-
 }
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/JndiExtensionTests.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/JndiExtensionTests.java
index 0dbc60c..e4680fc 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/JndiExtensionTests.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/JndiExtensionTests.java
@@ -21,19 +21,20 @@
 import javax.enterprise.inject.spi.BeanManager;
 import javax.naming.InitialContext;
 
+import org.apache.aries.cdi.test.cases.base.CloseableTracker;
+import org.apache.aries.cdi.test.cases.base.SlimBaseTestCase;
 import org.apache.aries.cdi.test.interfaces.Pojo;
 import org.junit.Test;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleEvent;
 import org.osgi.framework.wiring.BundleWiring;
 import org.osgi.util.tracker.BundleTracker;
-import org.osgi.util.tracker.ServiceTracker;
 
-public class JndiExtensionTests extends SlimTestCase {
+public class JndiExtensionTests extends SlimBaseTestCase {
 
 	@Test
 	public void testGetBeanManagerThroughJNDI() throws Exception {
-		Bundle testBundle = installBundle("tb21.jar", false);
+		Bundle testBundle = bcr.installBundle("tb21.jar", false);
 
 		testBundle.start();
 
@@ -57,7 +58,7 @@
 
 	@Test
 	public void testDisableExtensionAndCDIContainerWaits() throws Exception {
-		BundleTracker<Bundle> bundleTracker = new BundleTracker<Bundle>(bundleContext, Bundle.ACTIVE, null) {
+		BundleTracker<Bundle> bundleTracker = new BundleTracker<Bundle>(bcr.getBundleContext(), Bundle.ACTIVE, null) {
 			@Override
 			public Bundle addingBundle(Bundle bundle, BundleEvent event) {
 				if (bundle.getSymbolicName().equals("org.apache.aries.cdi.extension.jndi")) {
@@ -68,15 +69,11 @@
 		};
 		bundleTracker.open();
 
-		Bundle testBundle = installBundle("tb21.jar", false);
+		Bundle testBundle = bcr.installBundle("tb21.jar", false);
 
 		testBundle.start();
 
-		ServiceTracker<Pojo, Pojo> st = new ServiceTracker<Pojo, Pojo>(
-			bundleContext, Pojo.class, null);
-		st.open(true);
-
-		try {
+		try (CloseableTracker<Pojo, Pojo> tracker = track("(objectClass=%s)", Pojo.class.getName())) {
 			assertFalse(bundleTracker.isEmpty());
 
 			Bundle extensionBundle = bundleTracker.getBundles()[0];
@@ -102,9 +99,7 @@
 			}
 		}
 		finally {
-			testBundle.uninstall();
 			bundleTracker.close();
-			st.close();
 		}
 	}
 
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/MpConfigTests.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/MpConfigTests.java
deleted file mode 100644
index 7b083e5..0000000
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/MpConfigTests.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.aries.cdi.test.cases;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-import org.apache.aries.cdi.test.interfaces.Pojo;
-import org.junit.Test;
-import org.osgi.framework.Bundle;
-import org.osgi.util.tracker.ServiceTracker;
-
-public class MpConfigTests extends SlimTestCase {
-
-	@Test
-	public void testConfigIsSet() throws Exception {
-		Bundle tb2Bundle = installBundle("tb16.jar", false);
-
-		tb2Bundle.start();
-
-		ServiceTracker<Pojo, Pojo> st = new ServiceTracker<Pojo, Pojo>(
-			bundleContext, Pojo.class, null);
-		st.open(true);
-
-		try {
-			Pojo pojo = st.waitForService(timeout);
-			assertNotNull(pojo);
-			assertEquals("[foo]", pojo.foo(null));
-		}
-		finally {
-			st.close();
-			tb2Bundle.uninstall();
-		}
-	}
-
-}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/MpMetricsTests.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/MpMetricsTests.java
deleted file mode 100644
index 09a8c55..0000000
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/MpMetricsTests.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/**
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.aries.cdi.test.cases;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-import java.util.Collection;
-
-import javax.ws.rs.client.ClientBuilder;
-import javax.ws.rs.client.WebTarget;
-import javax.ws.rs.core.Response;
-
-import org.apache.aries.cdi.test.interfaces.Pojo;
-import org.assertj.core.api.Assertions;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.http.runtime.HttpServiceRuntime;
-import org.osgi.service.jaxrs.runtime.JaxrsServiceRuntime;
-import org.osgi.service.jaxrs.runtime.JaxrsServiceRuntimeConstants;
-import org.osgi.util.tracker.ServiceTracker;
-
-public class MpMetricsTests extends SlimTestCase {
-
-	@Test
-	public void testMetrics() throws Exception {
-		Bundle tb2Bundle = installBundle("tb22.jar", false);
-
-		tb2Bundle.start();
-
-		ServiceTracker<Pojo, Pojo> st = new ServiceTracker<Pojo, Pojo>(
-			bundleContext, Pojo.class, null);
-		st.open(true);
-
-		try {
-			Pojo pojo = st.waitForService(timeout);
-			assertNotNull(pojo);
-
-			WebTarget webTarget = cb.build().target(getEndpoint()).path("/metrics/application");
-
-			Response response = webTarget.request().get();
-
-			assertEquals(response.readEntity(String.class),200, response.getStatus());
-
-			String result = response.readEntity(String.class);
-
-			assertEquals("{\"org.apache.aries.cdi.test.tb22.A.applicationCount\":0}", result);
-
-			Assertions.assertThat(pojo.foo("Count: ")).isEqualTo("Count: 1");
-
-			response = webTarget.request().get();
-
-			assertEquals(200, response.getStatus());
-
-			result = response.readEntity(String.class);
-
-			assertEquals("{\"org.apache.aries.cdi.test.tb22.A.applicationCount\":1}", result);
-		}
-		finally {
-			st.close();
-			tb2Bundle.uninstall();
-		}
-	}
-
-	private String getEndpoint() {
-		Object endpointsObj = jsrReference.getProperty(
-			JaxrsServiceRuntimeConstants.JAX_RS_SERVICE_ENDPOINT);
-
-		if (endpointsObj instanceof String) {
-			return String.valueOf(endpointsObj);
-		}
-		else if (endpointsObj instanceof String[]) {
-			return ((String[])endpointsObj)[0];
-		}
-		else if (endpointsObj instanceof Collection) {
-			return String.valueOf(((Collection<?>)endpointsObj).iterator().next());
-		}
-
-		return null;
-	}
-
-	@Before
-	@Override
-	public void setUp() throws Exception {
-		hsrTracker = new ServiceTracker<>(bundleContext, HttpServiceRuntime.class, null);
-
-		hsrTracker.open();
-
-		hsr = hsrTracker.waitForService(timeout);
-
-		jsrTracker = new ServiceTracker<>(bundleContext, JaxrsServiceRuntime.class, null);
-
-		jsrTracker.open();
-
-		jsr = jsrTracker.waitForService(timeout);
-
-		jsrReference = jsrTracker.getServiceReference();
-
-		cbTracker = new ServiceTracker<>(bundleContext, ClientBuilder.class, null);
-
-		cbTracker.open();
-
-		cb = cbTracker.waitForService(timeout);
-
-		assertNotNull(cb);
-	}
-
-	@After
-	@Override
-	public void tearDown() throws Exception {
-		hsrTracker.close();
-		jsrTracker.close();
-		cbTracker.close();
-	}
-
-	ClientBuilder cb;
-	ServiceTracker<ClientBuilder, ClientBuilder> cbTracker;
-	HttpServiceRuntime hsr;
-	ServiceTracker<HttpServiceRuntime, HttpServiceRuntime> hsrTracker = new ServiceTracker<>(bundleContext, HttpServiceRuntime.class, null);
-	JaxrsServiceRuntime jsr;
-	ServiceReference<JaxrsServiceRuntime> jsrReference;
-	ServiceTracker<JaxrsServiceRuntime, JaxrsServiceRuntime> jsrTracker;
-
-}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/OSGiBeanDescriptorTests.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/OSGiBeanDescriptorTests.java
index c8d8233..3f0984c 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/OSGiBeanDescriptorTests.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/OSGiBeanDescriptorTests.java
@@ -24,53 +24,40 @@
 import javax.enterprise.inject.spi.BeanManager;
 import javax.enterprise.util.TypeLiteral;
 
+import org.apache.aries.cdi.test.cases.base.BaseTestCase;
+import org.apache.aries.cdi.test.cases.base.CloseableTracker;
 import org.apache.aries.cdi.test.interfaces.BeanService;
 import org.apache.aries.cdi.test.interfaces.Pojo;
 import org.junit.Test;
 import org.osgi.framework.Bundle;
-import org.osgi.util.tracker.ServiceTracker;
 
-public class OSGiBeanDescriptorTests extends AbstractTestCase {
+public class OSGiBeanDescriptorTests extends BaseTestCase {
 
 	@Test
 	public void testServices() throws Exception {
-		Bundle tb2Bundle = installBundle("tb2.jar");
+		bcr.installBundle("tb2.jar");
 
-		ServiceTracker<Pojo, Pojo> st = new ServiceTracker<Pojo, Pojo>(
-			bundleContext, Pojo.class, null);
-		st.open(true);
-
-		try {
-			Pojo pojo = st.waitForService(timeout);
+		try (CloseableTracker<Pojo, Pojo> tracker = track("(objectClass=%s)", Pojo.class.getName())) {
+			Pojo pojo = tracker.waitForService(timeout);
 			assertNotNull(pojo);
 		}
-		finally {
-			st.close();
-			tb2Bundle.uninstall();
-		}
 	}
 
 	@SuppressWarnings("serial")
 	@Test
 	public void testReferences() throws Exception {
-		Bundle tb1Bundle = installBundle("tb1.jar");
-		Bundle tb2Bundle = installBundle("tb2.jar");
+		Bundle tb1Bundle = bcr.installBundle("tb1.jar");
+		bcr.installBundle("tb2.jar");
 
-		try {
-			BeanManager beanManager = getBeanManager(tb1Bundle);
-			Set<Bean<?>> beans = beanManager.getBeans("beanimpl");
-			Bean<?> bean = beanManager.resolve(beans);
-			CreationalContext<?> ctx = beanManager.createCreationalContext(bean);
-			BeanService<?> beanService = (BeanService<?>)beanManager.getReference(
-				bean, new TypeLiteral<BeanService<?>>() {}.getType(), ctx);
+		BeanManager beanManager = getBeanManager(tb1Bundle);
+		Set<Bean<?>> beans = beanManager.getBeans("beanimpl");
+		Bean<?> bean = beanManager.resolve(beans);
+		CreationalContext<?> ctx = beanManager.createCreationalContext(bean);
+		BeanService<?> beanService = (BeanService<?>)beanManager.getReference(
+			bean, new TypeLiteral<BeanService<?>>() {}.getType(), ctx);
 
-			assertNotNull(beanService);
-			assertEquals("POJO-IMPLBEAN-IMPL", beanService.doSomething());
-		}
-		finally {
-			tb2Bundle.uninstall();
-			tb1Bundle.uninstall();
-		}
+		assertNotNull(beanService);
+		assertEquals("POJO-IMPLBEAN-IMPL", beanService.doSomething());
 	}
 
 }
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/OptionalReluctantReferenceTests.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/OptionalReluctantReferenceTests.java
index 871d0eb..64c2eb3 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/OptionalReluctantReferenceTests.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/OptionalReluctantReferenceTests.java
@@ -14,38 +14,26 @@
 
 package org.apache.aries.cdi.test.cases;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 
 import java.util.Collections;
 import java.util.Hashtable;
 
+import org.apache.aries.cdi.test.cases.base.BaseTestCase;
+import org.apache.aries.cdi.test.cases.base.CloseableTracker;
 import org.apache.aries.cdi.test.interfaces.Pojo;
-import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.cdi.runtime.dto.ContainerDTO;
 import org.osgi.service.cm.Configuration;
-import org.osgi.service.cm.ConfigurationAdmin;
-import org.osgi.util.tracker.ServiceTracker;
 
-public class OptionalReluctantReferenceTests extends AbstractTestCase {
-
-	@Override
-	@Before
-	public void setUp() throws Exception {
-		cdiRuntime = runtimeTracker.waitForService(timeout);
-	}
-
-	@Override
-	@After
-	public void tearDown() throws Exception {
-	}
+public class OptionalReluctantReferenceTests extends BaseTestCase {
 
 	@Test
 	public void applicationScoped() throws Exception {
-		Bundle tb = installBundle("tb11.jar");
+		Bundle tb = bcr.installBundle("tb11.jar");
 
 		try (CloseableTracker<Pojo, Pojo> tracker = track("(&(objectClass=%s)(bean.id=as))", Pojo.class.getName());) {
 			Pojo pojo = tracker.waitForService(timeout);
@@ -53,16 +41,16 @@
 			assertEquals(-1, pojo.getCount());
 			assertEquals("-1", pojo.foo(""));
 
-			ContainerDTO containerDTO = getContainerDTO(cdiRuntime, tb);
+			ContainerDTO containerDTO = getContainerDTO(ccrr.getService(), tb);
 
 			long changeCount = containerDTO.changeCount;
 
-			ServiceRegistration<Integer> int1 = bundleContext.registerService(
+			ServiceRegistration<Integer> int1 = bcr.getBundleContext().registerService(
 				Integer.class, new Integer(12),
 				new Hashtable<>(Collections.singletonMap("bean.id", "as")));
 
 			try {
-				for (long i = 10; i > 0 && (getContainerDTO(cdiRuntime, tb).changeCount == changeCount); i--) {
+				for (long i = 10; i > 0 && (getContainerDTO(ccrr.getService(), tb).changeCount == changeCount); i--) {
 					Thread.sleep(20);
 				}
 
@@ -78,11 +66,11 @@
 				assertEquals("12", pojo.foo(""));
 			}
 			finally {
-				changeCount = getContainerDTO(cdiRuntime, tb).changeCount;
+				changeCount = getContainerDTO(ccrr.getService(), tb).changeCount;
 
 				int1.unregister();
 
-				for (long i = 10; i > 0 && (getContainerDTO(cdiRuntime, tb).changeCount == changeCount); i--) {
+				for (long i = 10; i > 0 && (getContainerDTO(ccrr.getService(), tb).changeCount == changeCount); i--) {
 					Thread.sleep(20);
 				}
 
@@ -92,14 +80,11 @@
 				assertEquals("-1", pojo.foo(""));
 			}
 		}
-		finally {
-			tb.uninstall();
-		}
 	}
 
 	@Test
 	public void singleComponent() throws Exception {
-		Bundle tb = installBundle("tb11.jar");
+		Bundle tb = bcr.installBundle("tb11.jar");
 
 		try (CloseableTracker<Pojo, Pojo> tracker = track("(&(objectClass=%s)(bean.id=sc))", Pojo.class.getName());) {
 			Pojo pojo = tracker.waitForService(timeout);
@@ -107,16 +92,16 @@
 			assertEquals(-1, pojo.getCount());
 			assertEquals("-1", pojo.foo(""));
 
-			ContainerDTO containerDTO = getContainerDTO(cdiRuntime, tb);
+			ContainerDTO containerDTO = getContainerDTO(ccrr.getService(), tb);
 
 			long changeCount = containerDTO.changeCount;
 
-			ServiceRegistration<Integer> int1 = bundleContext.registerService(
+			ServiceRegistration<Integer> int1 = bcr.getBundleContext().registerService(
 				Integer.class, new Integer(12),
 				new Hashtable<>(Collections.singletonMap("bean.id", "sc")));
 
 			try {
-				for (long i = 10; i > 0 && (getContainerDTO(cdiRuntime, tb).changeCount == changeCount); i--) {
+				for (long i = 10; i > 0 && (getContainerDTO(ccrr.getService(), tb).changeCount == changeCount); i--) {
 					Thread.sleep(20);
 				}
 
@@ -132,11 +117,11 @@
 				assertEquals("12", pojo.foo(""));
 			}
 			finally {
-				changeCount = getContainerDTO(cdiRuntime, tb).changeCount;
+				changeCount = getContainerDTO(ccrr.getService(), tb).changeCount;
 
 				int1.unregister();
 
-				for (long i = 10; i > 0 && (getContainerDTO(cdiRuntime, tb).changeCount == changeCount); i--) {
+				for (long i = 10; i > 0 && (getContainerDTO(ccrr.getService(), tb).changeCount == changeCount); i--) {
 					Thread.sleep(20);
 				}
 
@@ -146,18 +131,11 @@
 				assertEquals("-1", pojo.foo(""));
 			}
 		}
-		finally {
-			tb.uninstall();
-		}
 	}
 
 	@Test
 	public void factoryComponent() throws Exception {
-		adminTracker = new ServiceTracker<>(bundleContext, ConfigurationAdmin.class, null);
-		adminTracker.open();
-		configurationAdmin = adminTracker.getService();
-
-		Bundle tb = installBundle("tb11.jar");
+		Bundle tb = bcr.installBundle("tb11.jar");
 
 		try (CloseableTracker<Pojo, Pojo> tracker = track("(&(objectClass=%s)(bean.id=fc))", Pojo.class.getName());) {
 			Pojo pojo = tracker.waitForService(timeout);
@@ -166,7 +144,7 @@
 
 			int trackingCount = tracker.getTrackingCount();
 
-			Configuration configuration = configurationAdmin.createFactoryConfiguration("optionalReference_FC");
+			Configuration configuration = car.getService().createFactoryConfiguration("optionalReference_FC");
 			configuration.update(new Hashtable<>(Collections.singletonMap("foo", "bar")));
 
 			for (long i = 10; i > 0 && (tracker.getTrackingCount() == trackingCount); i--) {
@@ -178,16 +156,16 @@
 			assertEquals(-1, pojo.getCount());
 			assertEquals("-1", pojo.foo(""));
 
-			ContainerDTO containerDTO = getContainerDTO(cdiRuntime, tb);
+			ContainerDTO containerDTO = getContainerDTO(ccrr.getService(), tb);
 
 			long changeCount = containerDTO.changeCount;
 
-			ServiceRegistration<Integer> int1 = bundleContext.registerService(
+			ServiceRegistration<Integer> int1 = bcr.getBundleContext().registerService(
 				Integer.class, new Integer(12),
 				new Hashtable<>(Collections.singletonMap("bean.id", "fc")));
 
 			try {
-				for (long i = 10; i > 0 && (getContainerDTO(cdiRuntime, tb).changeCount == changeCount); i--) {
+				for (long i = 10; i > 0 && (getContainerDTO(ccrr.getService(), tb).changeCount == changeCount); i--) {
 					Thread.sleep(20);
 				}
 
@@ -203,11 +181,11 @@
 				assertEquals("12", pojo.foo(""));
 			}
 			finally {
-				changeCount = getContainerDTO(cdiRuntime, tb).changeCount;
+				changeCount = getContainerDTO(ccrr.getService(), tb).changeCount;
 
 				int1.unregister();
 
-				for (long i = 10; i > 0 && (getContainerDTO(cdiRuntime, tb).changeCount == changeCount); i--) {
+				for (long i = 10; i > 0 && (getContainerDTO(ccrr.getService(), tb).changeCount == changeCount); i--) {
 					Thread.sleep(20);
 				}
 
@@ -219,13 +197,6 @@
 				configuration.delete();
 			}
 		}
-		finally {
-			tb.uninstall();
-			adminTracker.close();
-		}
 	}
 
-	private ServiceTracker<ConfigurationAdmin, ConfigurationAdmin> adminTracker;
-	private ConfigurationAdmin configurationAdmin;
-
 }
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/ProducerTest.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/ProducerTest.java
index 15bab0c..37a112a 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/ProducerTest.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/ProducerTest.java
@@ -18,23 +18,17 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import org.apache.aries.cdi.test.cases.base.CloseableTracker;
+import org.apache.aries.cdi.test.cases.base.SlimBaseTestCase;
 import org.apache.aries.cdi.test.interfaces.Pojo;
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
 import org.junit.Test;
-import org.osgi.framework.Bundle;
 import org.osgi.framework.Constants;
-import org.osgi.service.cdi.runtime.CDIComponentRuntime;
-import org.osgi.util.tracker.ServiceTracker;
 
-public class ProducerTest extends AbstractTestCase {
+public class ProducerTest extends SlimBaseTestCase {
 
 	@Test
 	public void checkProducersAreProperlyHandled() throws Exception {
-		Bundle bundle = installBundle("tb12.jar");
-		bundle.start();
+		bcr.installBundle("tb12.jar");
 
 		try (CloseableTracker<Pojo, Pojo> track = track("(&(objectClass=%s)(component.name=integerManager))", Pojo.class.getName())) {
 			Pojo pojo = track.waitForService(5000);
@@ -45,32 +39,6 @@
 			assertTrue(pojo.getMap().containsKey(Constants.SERVICE_RANKING));
 			assertEquals(100000, pojo.getMap().get(Constants.SERVICE_RANKING));
 		}
-		finally {
-			bundle.uninstall();
-		}
-	}
-
-	@BeforeClass
-	public static void beforeClass() throws Exception {
-		runtimeTracker = new ServiceTracker<>(
-			bundleContext, CDIComponentRuntime.class, null);
-		runtimeTracker.open();
-	}
-
-	@AfterClass
-	public static void afterClass() throws Exception {
-		runtimeTracker.close();
-	}
-
-	@Override
-	@Before
-	public void setUp() throws Exception {
-		cdiRuntime = runtimeTracker.waitForService(timeout);
-	}
-
-	@Override
-	@After
-	public void tearDown() throws Exception {
 	}
 
 }
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/Test152_2.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/Test152_2.java
index f92f56a..bb14cfe 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/Test152_2.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/Test152_2.java
@@ -14,44 +14,35 @@
 
 package org.apache.aries.cdi.test.cases;
 
-import static org.assertj.core.api.Assertions.*;
+import static org.assertj.core.api.Assertions.assertThat;
 
+import org.apache.aries.cdi.test.cases.base.BaseTestCase;
 import org.junit.Test;
 import org.osgi.framework.Bundle;
 import org.osgi.service.cdi.runtime.dto.ContainerDTO;
 
-public class Test152_2 extends AbstractTestCase {
+public class Test152_2 extends BaseTestCase {
 
 	@Test
 	public void checkUniqueComponentNames() throws Exception {
-		Bundle tb152_2Bundle = installBundle("tb152_2.jar");
+		Bundle tb = bcr.installBundle("tb152_2.jar");
 
-		try {
-			getBeanManager(tb152_2Bundle);
+		getBeanManager(tb);
 
-			ContainerDTO containerDTO = getContainerDTO(cdiRuntime, tb152_2Bundle);
-			assertThat(containerDTO).isNotNull();
-			assertThat(containerDTO.errors).isNotNull().asList().isNotEmpty();
-		}
-		finally {
-			tb152_2Bundle.uninstall();
-		}
+		ContainerDTO containerDTO = getContainerDTO(ccrr.getService(), tb);
+		assertThat(containerDTO).isNotNull();
+		assertThat(containerDTO.errors).isNotNull().asList().isNotEmpty();
 	}
 
 	@Test
 	public void checkUniqueComponentNames_b() throws Exception {
-		Bundle tb152_2bBundle = installBundle("tb152_2b.jar");
+		Bundle tb = bcr.installBundle("tb152_2b.jar");
 
-		try {
-			getBeanManager(tb152_2bBundle);
+		getBeanManager(tb);
 
-			ContainerDTO containerDTO = getContainerDTO(cdiRuntime, tb152_2bBundle);
-			assertThat(containerDTO).isNotNull();
-			assertThat(containerDTO.errors).isNotNull().asList().isNotEmpty();
-		}
-		finally {
-			tb152_2bBundle.uninstall();
-		}
+		ContainerDTO containerDTO = getContainerDTO(ccrr.getService(), tb);
+		assertThat(containerDTO).isNotNull();
+		assertThat(containerDTO.errors).isNotNull().asList().isNotEmpty();
 	}
 
 }
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/Test152_3.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/Test152_3.java
index e0aec15..509becb 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/Test152_3.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/Test152_3.java
@@ -18,27 +18,25 @@
 
 import javax.enterprise.context.spi.Context;
 
+import org.apache.aries.cdi.test.cases.base.CloseableTracker;
+import org.apache.aries.cdi.test.cases.base.SlimBaseTestCase;
 import org.apache.aries.cdi.test.interfaces.BeanService;
 import org.junit.Test;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.Constants;
-import org.osgi.util.tracker.ServiceTracker;
 
-public class Test152_3 extends SlimTestCase {
+public class Test152_3 extends SlimBaseTestCase {
 
 	@Test
 	public void componentScopeContext() throws Exception {
-		Bundle tbBundle = installBundle("tb152_3.jar");
+		Bundle tbBundle = bcr.installBundle("tb152_3.jar");
 
-		ServiceTracker<Object, Object> oneTracker = track("(&(objectClass=%s)(%s=%s))", BeanService.class.getName(), Constants.SERVICE_DESCRIPTION, "one");
-		ServiceTracker<Object, Object> twoTracker = track("(&(objectClass=%s)(%s=%s))", BeanService.class.getName(), Constants.SERVICE_DESCRIPTION, "two");
-		try {
+		try (CloseableTracker<Object, Object> oneTracker = track("(&(objectClass=%s)(%s=%s))", BeanService.class.getName(), Constants.SERVICE_DESCRIPTION, "one");
+				CloseableTracker<Object, Object> twoTracker = track("(&(objectClass=%s)(%s=%s))", BeanService.class.getName(), Constants.SERVICE_DESCRIPTION, "two")) {
+
 			getBeanManager(tbBundle);
 
-			oneTracker.open();
 			Object service = oneTracker.waitForService(timeout);
-
-			twoTracker.open();
 			twoTracker.waitForService(timeout);
 
 			assertThat(service).isNotNull();
@@ -47,11 +45,6 @@
 			Context context = bs.get();
 			assertThat(context).isNotNull();
 		}
-		finally {
-			oneTracker.close();
-			twoTracker.close();
-			tbBundle.uninstall();
-		}
 	}
 
 }
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/Test152_3_1.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/Test152_3_1.java
index 44ec642..88b53cc 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/Test152_3_1.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/Test152_3_1.java
@@ -23,8 +23,9 @@
 import javax.enterprise.inject.spi.BeanManager;
 import javax.enterprise.inject.spi.EventMetadata;
 
+import org.apache.aries.cdi.test.cases.base.CloseableTracker;
+import org.apache.aries.cdi.test.cases.base.SlimBaseTestCase;
 import org.apache.aries.cdi.test.interfaces.BeanService;
-import org.junit.After;
 import org.junit.Test;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.Constants;
@@ -32,28 +33,13 @@
 import org.osgi.service.cdi.annotations.ComponentScoped;
 import org.osgi.service.cdi.annotations.Service;
 import org.osgi.service.cm.Configuration;
-import org.osgi.service.cm.ConfigurationAdmin;
-import org.osgi.util.tracker.ServiceTracker;
 
-public class Test152_3_1 extends SlimTestCase {
-
-	@Override
-	public void setUp() throws Exception {
-		adminTracker = new ServiceTracker<>(bundleContext, ConfigurationAdmin.class, null);
-		adminTracker.open();
-		configurationAdmin = adminTracker.getService();
-	}
-
-	@After
-	@Override
-	public void tearDown() throws Exception {
-		adminTracker.close();
-	}
+public class Test152_3_1 extends SlimBaseTestCase {
 
 	@SuppressWarnings({ "rawtypes", "serial", "unchecked" })
 	@Test
 	public void checkSingleComponentContextEvents() throws Exception {
-		Bundle tb152_3_1Bundle = installBundle("tb152_3_1.jar");
+		Bundle tb152_3_1Bundle = bcr.installBundle("tb152_3_1.jar");
 
 		AtomicReference<Object[]> a = new AtomicReference<>();
 		AtomicReference<Object[]> b = new AtomicReference<>();
@@ -78,21 +64,19 @@
 			return null;
 		};
 
-		ServiceRegistration<Function> onInitializedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Function.class, onInitialized,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onInitialized");}});
 
-		ServiceRegistration<Function> onBeforeDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Function.class, onBeforeDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onBeforeDestroyed");}});
 
-		ServiceRegistration<Function> onDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Function.class, onDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onDestroyed");}});
 
-		ServiceTracker<Object, Object> twoTracker = track("(&(objectClass=%s)(%s=%s))", BeanService.class.getName(), Constants.SERVICE_DESCRIPTION, "two");
-
-		try {
+		try (CloseableTracker<Object, Object> twoTracker = track("(&(objectClass=%s)(%s=%s))", BeanService.class.getName(), Constants.SERVICE_DESCRIPTION, "two")) {
 			getBeanManager(tb152_3_1Bundle);
 
 			assertThat(a.get()).isNull();
@@ -102,7 +86,7 @@
 			twoTracker.open();
 			int trackingCount = twoTracker.getTrackingCount();
 
-			ServiceRegistration<Integer> integerReg = bundleContext.registerService(
+			ServiceRegistration<Integer> integerReg = bcr.getBundleContext().registerService(
 				Integer.class, new Integer(45),
 				new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "two");}});
 
@@ -146,19 +130,12 @@
 			eventMetadata = (EventMetadata)objects[2];
 			assertThat(eventMetadata.getQualifiers()).contains(Service.Literal.of(new Class<?>[0]));
 		}
-		finally {
-			twoTracker.close();
-			onInitializedReg.unregister();
-			onBeforeDestroyedReg.unregister();
-			onDestroyedReg.unregister();
-			tb152_3_1Bundle.uninstall();
-		}
 	}
 
 	@SuppressWarnings({ "rawtypes", "serial", "unchecked" })
 	@Test
 	public void checkFactoryComponentContextEvents() throws Exception {
-		Bundle tb152_3_1Bundle = installBundle("tb152_3_1.jar");
+		Bundle tb152_3_1Bundle = bcr.installBundle("tb152_3_1.jar");
 
 		AtomicReference<Object[]> a = new AtomicReference<>();
 		AtomicReference<Object[]> b = new AtomicReference<>();
@@ -183,22 +160,21 @@
 			return null;
 		};
 
-		ServiceRegistration<Function> onInitializedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Function.class, onInitialized,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onInitialized");}});
 
-		ServiceRegistration<Function> onBeforeDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Function.class, onBeforeDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onBeforeDestroyed");}});
 
-		ServiceRegistration<Function> onDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Function.class, onDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onDestroyed");}});
 
 		Configuration configuration = null;
-		ServiceTracker<Object, Object> threeTracker = track("(&(objectClass=%s)(%s=%s))", BeanService.class.getName(), Constants.SERVICE_DESCRIPTION, "three");
 
-		try {
+		try (CloseableTracker<Object, Object> threeTracker = track("(&(objectClass=%s)(%s=%s))", BeanService.class.getName(), Constants.SERVICE_DESCRIPTION, "three")) {
 			getBeanManager(tb152_3_1Bundle);
 
 			assertThat(a.get()).isNull();
@@ -208,7 +184,7 @@
 			threeTracker.open();
 			int trackingCount = threeTracker.getTrackingCount();
 
-			ServiceRegistration<Integer> integerReg = bundleContext.registerService(
+			ServiceRegistration<Integer> integerReg = bcr.getBundleContext().registerService(
 				Integer.class, new Integer(45),
 				new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "three");}});
 
@@ -220,7 +196,7 @@
 			assertThat(b.get()).isNull();
 			assertThat(c.get()).isNull();
 
-			configuration = configurationAdmin.createFactoryConfiguration("three");
+			configuration = car.getService().createFactoryConfiguration("three");
 			configuration.update(new Hashtable() {{put("foo", "bar");}});
 
 			count = 10;
@@ -265,16 +241,8 @@
 			assertThat(eventMetadata.getQualifiers()).contains(Service.Literal.of(new Class<?>[0]));
 		}
 		finally {
-			threeTracker.close();
 			configuration.delete();
-			onInitializedReg.unregister();
-			onBeforeDestroyedReg.unregister();
-			onDestroyedReg.unregister();
-			tb152_3_1Bundle.uninstall();
 		}
 	}
 
-	private ServiceTracker<ConfigurationAdmin, ConfigurationAdmin> adminTracker;
-	private ConfigurationAdmin configurationAdmin;
-
 }
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/Test152_3_1_1.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/Test152_3_1_1.java
index 3e4db7c..db1fd14 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/Test152_3_1_1.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/Test152_3_1_1.java
@@ -24,35 +24,19 @@
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Consumer;
 
+import org.apache.aries.cdi.test.cases.base.CloseableTracker;
+import org.apache.aries.cdi.test.cases.base.SlimBaseTestCase;
 import org.apache.aries.cdi.test.interfaces.Pojo;
-import org.junit.After;
 import org.junit.Test;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceObjects;
 import org.osgi.framework.ServiceReference;
-import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.cm.Configuration;
-import org.osgi.service.cm.ConfigurationAdmin;
 import org.osgi.util.promise.Deferred;
 import org.osgi.util.promise.Success;
-import org.osgi.util.tracker.ServiceTracker;
 
-public class Test152_3_1_1 extends SlimTestCase {
-
-	@Override
-	public void setUp() throws Exception {
-		adminTracker = new ServiceTracker<>(bundleContext, ConfigurationAdmin.class, null);
-		adminTracker.open();
-		configurationAdmin = adminTracker.getService();
-		assertThat(bundleContext.getBundle().getRegisteredServices()).isNull();
-	}
-
-	@After
-	@Override
-	public void tearDown() throws Exception {
-		adminTracker.close();
-	}
+public class Test152_3_1_1 extends SlimBaseTestCase {
 
 	@SuppressWarnings({ "rawtypes", "serial", "unchecked" })
 	@Test
@@ -65,38 +49,33 @@
 		Consumer<Object[]> onBeforeDestroyed = (o) -> {try {b.get().resolve(o);} catch (Exception e) {}};
 		Consumer<Object[]> onDestroyed = (o) -> {try {c.get().resolve(o);} catch (Exception e) {}};
 
-		ServiceRegistration<Consumer> onInitializedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onInitialized,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onInitialized");}});
 
-		ServiceRegistration<Consumer> onBeforeDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onBeforeDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onBeforeDestroyed");}});
 
-		ServiceRegistration<Consumer> onDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onDestroyed");}});
 
-		Bundle tbBundle = installBundle("tb152_3_1_1l.jar");
+		Bundle tbBundle = bcr.installBundle("tb152_3_1_1l.jar");
 		Configuration configuration = null;
 
 		try {
 			getBeanManager(tbBundle);
 
-			Success<Object[], Object[]> assertFailed = s -> {
-				fail("shouldn't have have succeeded");
-				return s;
-			};
-
-			a.get().getPromise().timeout(timeout).then(assertFailed).getFailure();
+			assertPromiseIsNotResolved(a);
 
 			try (CloseableTracker<Object, ServiceReference<Object>> tracker = trackSR("(objectClass=%s)", Pojo.class.getName())) {
 				assertThat(tracker.waitForService(50)).isNull();
 
 				// we didn't do a "get" yet so this should fail
-				a.get().getPromise().timeout(timeout).then(assertFailed).getFailure();
+				assertPromiseIsNotResolved(a);
 
-				configuration = configurationAdmin.getConfiguration("prototypeFactory", "?");
+				configuration = car.getService().getConfiguration("prototypeFactory", "?");
 
 				// this will trigger the onInitialized because of the tracker
 				configuration.update(new Hashtable() {{put("foo", "bar");}});
@@ -106,12 +85,12 @@
 
 				configuration.delete();
 
-				configuration = configurationAdmin.getFactoryConfiguration("prototypeFactory", "one", "?");
+				configuration = car.getService().getFactoryConfiguration("prototypeFactory", "one", "?");
 				configuration.update(new Hashtable() {{put("foo", "bar");}});
 
 				assertThat(tracker.waitForService(50)).isNotNull();
 
-				ServiceObjects<Object> serviceObjects = bundleContext.getServiceObjects(tracker.getService());
+				ServiceObjects<Object> serviceObjects = bcr.getBundleContext().getServiceObjects(tracker.getService());
 
 				a.set(new Deferred<>());
 				b.set(new Deferred<>());
@@ -139,8 +118,8 @@
 					},
 					f -> fail(f.toString())
 				).getValue();
-				b.get().getPromise().timeout(timeout).then(assertFailed).getFailure();
-				c.get().getPromise().timeout(timeout).then(assertFailed).getFailure();
+				assertPromiseIsNotResolved(b);
+				assertPromiseIsNotResolved(c);
 
 				a.set(new Deferred<>());
 
@@ -166,8 +145,8 @@
 					},
 					f -> fail(f.toString())
 				).getValue();
-				b.get().getPromise().timeout(timeout).then(assertFailed).getFailure();
-				c.get().getPromise().timeout(timeout).then(assertFailed).getFailure();
+				assertPromiseIsNotResolved(b);
+				assertPromiseIsNotResolved(c);
 
 				assertThat(service).isNotEqualTo(other);
 
@@ -260,14 +239,6 @@
 					// ignore
 				}
 			}
-			try {
-				tbBundle.uninstall();
-			}
-			finally {
-				onInitializedReg.unregister();
-				onBeforeDestroyedReg.unregister();
-				onDestroyedReg.unregister();
-			}
 		}
 	}
 
@@ -282,19 +253,19 @@
 		Consumer<Object[]> onBeforeDestroyed = (o) -> b.get().resolve(o);
 		Consumer<Object[]> onDestroyed = (o) -> c.get().resolve(o);
 
-		ServiceRegistration<Consumer> onInitializedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onInitialized,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onInitialized");}});
 
-		ServiceRegistration<Consumer> onBeforeDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onBeforeDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onBeforeDestroyed");}});
 
-		ServiceRegistration<Consumer> onDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onDestroyed");}});
 
-		Bundle tbBundle = installBundle("tb152_3_1_1k.jar");
+		Bundle tbBundle = bcr.installBundle("tb152_3_1_1k.jar");
 		Configuration configuration = null;
 
 		try {
@@ -313,7 +284,7 @@
 				// we didn't do a "get" yet so this should fail
 				a.get().getPromise().timeout(timeout).then(assertFailed).getFailure();
 
-				configuration = configurationAdmin.getConfiguration("prototypeSingle_C", "?");
+				configuration = car.getService().getConfiguration("prototypeSingle_C", "?");
 
 				configuration.update(new Hashtable() {{put("foo", "bar");}});
 
@@ -321,7 +292,7 @@
 				b.get().getPromise().timeout(timeout).then(assertFailed).getFailure();
 				c.get().getPromise().timeout(timeout).then(assertFailed).getFailure();
 
-				ServiceObjects<Object> serviceObjects = bundleContext.getServiceObjects(tracker.getService());
+				ServiceObjects<Object> serviceObjects = bcr.getBundleContext().getServiceObjects(tracker.getService());
 
 				Object instance1 = serviceObjects.getService();
 				assertThat(instance1).isNotNull();
@@ -441,14 +412,6 @@
 					// ignore
 				}
 			}
-			try {
-				tbBundle.uninstall();
-			}
-			finally {
-				onInitializedReg.unregister();
-				onBeforeDestroyedReg.unregister();
-				onDestroyedReg.unregister();
-			}
 		}
 	}
 
@@ -463,152 +426,140 @@
 		Consumer<Object[]> onBeforeDestroyed = (o) -> {try {b.get().resolve(o);} catch (Exception e) {}};
 		Consumer<Object[]> onDestroyed = (o) -> {try {c.get().resolve(o);} catch (Exception e) {}};
 
-		ServiceRegistration<Consumer> onInitializedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onInitialized,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onInitialized");}});
 
-		ServiceRegistration<Consumer> onBeforeDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onBeforeDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onBeforeDestroyed");}});
 
-		ServiceRegistration<Consumer> onDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onDestroyed");}});
 
-		Bundle tbBundle = installBundle("tb152_3_1_1j.jar");
+		Bundle tbBundle = bcr.installBundle("tb152_3_1_1j.jar");
 
-		try {
-			getBeanManager(tbBundle);
+		getBeanManager(tbBundle);
 
-			Success<Object[], Object[]> assertFailed = s -> {
-				fail("shouldn't have have succeeded");
-				return s;
-			};
+		Success<Object[], Object[]> assertFailed = s -> {
+			fail("shouldn't have have succeeded");
+			return s;
+		};
 
-			a.get().getPromise().timeout(timeout).then(assertFailed).getFailure();
+		a.get().getPromise().timeout(timeout).then(assertFailed).getFailure();
 
-			try (CloseableTracker<Object, ServiceReference<Object>> tracker = trackSR("(objectClass=%s)", Pojo.class.getName())) {
-				assertThat(tracker.waitForService(50)).isNotNull();
+		try (CloseableTracker<Object, ServiceReference<Object>> tracker = trackSR("(objectClass=%s)", Pojo.class.getName())) {
+			assertThat(tracker.waitForService(50)).isNotNull();
 
-				ServiceObjects<Object> serviceObjects = bundleContext.getServiceObjects(tracker.getService());
+			ServiceObjects<Object> serviceObjects = bcr.getBundleContext().getServiceObjects(tracker.getService());
 
-				a.set(new Deferred<>());
+			a.set(new Deferred<>());
 
-				Object service = serviceObjects.getService();
-				assertThat(service).isNotNull();
+			Object service = serviceObjects.getService();
+			assertThat(service).isNotNull();
 
-				a.get().getPromise().timeout(timeout).then(
-					s -> {
-						Object[] values = s.getValue();
+			a.get().getPromise().timeout(timeout).then(
+				s -> {
+					Object[] values = s.getValue();
 
-						assertThat(service).isEqualTo(values[0]);
-						assertThat((Map<String, Object>)values[1]).contains(
-							entry("component.name", "prototypeSingle")
-						);
+					assertThat(service).isEqualTo(values[0]);
+					assertThat((Map<String, Object>)values[1]).contains(
+						entry("component.name", "prototypeSingle")
+					);
 
-						return s;
-					},
-					f -> fail(f.toString())
-				).getValue();
-				b.get().getPromise().timeout(timeout).then(assertFailed).getFailure();
-				c.get().getPromise().timeout(timeout).then(assertFailed).getFailure();
+					return s;
+				},
+				f -> fail(f.toString())
+			).getValue();
+			b.get().getPromise().timeout(timeout).then(assertFailed).getFailure();
+			c.get().getPromise().timeout(timeout).then(assertFailed).getFailure();
 
-				a.set(new Deferred<>());
+			a.set(new Deferred<>());
 
-				Object other = serviceObjects.getService();
-				assertThat(other).isNotNull();
+			Object other = serviceObjects.getService();
+			assertThat(other).isNotNull();
 
-				a.get().getPromise().timeout(timeout).then(
-					s -> {
-						Object[] values = s.getValue();
+			a.get().getPromise().timeout(timeout).then(
+				s -> {
+					Object[] values = s.getValue();
 
-						assertThat(other).isEqualTo(values[0]);
-						assertThat((Map<String, Object>)values[1]).contains(
-							entry("component.name", "prototypeSingle")
-						);
+					assertThat(other).isEqualTo(values[0]);
+					assertThat((Map<String, Object>)values[1]).contains(
+						entry("component.name", "prototypeSingle")
+					);
 
-						return s;
-					},
-					f -> fail(f.toString())
-				).getValue();
-				b.get().getPromise().timeout(timeout).then(assertFailed).getFailure();
-				c.get().getPromise().timeout(timeout).then(assertFailed).getFailure();
+					return s;
+				},
+				f -> fail(f.toString())
+			).getValue();
+			b.get().getPromise().timeout(timeout).then(assertFailed).getFailure();
+			c.get().getPromise().timeout(timeout).then(assertFailed).getFailure();
 
-				assertThat(service).isNotEqualTo(other);
+			assertThat(service).isNotEqualTo(other);
 
-				serviceObjects.ungetService(service);
+			serviceObjects.ungetService(service);
 
-				b.get().getPromise().timeout(timeout).then(
-					s -> {
-						Object[] values = s.getValue();
+			b.get().getPromise().timeout(timeout).then(
+				s -> {
+					Object[] values = s.getValue();
 
-						assertThat(service).isEqualTo(values[0]);
-						assertThat((Map<String, Object>)values[1]).contains(
-							entry("component.name", "prototypeSingle")
-						);
+					assertThat(service).isEqualTo(values[0]);
+					assertThat((Map<String, Object>)values[1]).contains(
+						entry("component.name", "prototypeSingle")
+					);
 
-						return s;
-					},
-					f -> fail(f.toString())
-				).getValue();
+					return s;
+				},
+				f -> fail(f.toString())
+			).getValue();
 
-				c.get().getPromise().timeout(timeout).then(
-					s -> {
-						Object[] values = s.getValue();
+			c.get().getPromise().timeout(timeout).then(
+				s -> {
+					Object[] values = s.getValue();
 
-						assertThat(service).isEqualTo(values[0]);
-						assertThat((Map<String, Object>)values[1]).contains(
-							entry("component.name", "prototypeSingle")
-						);
+					assertThat(service).isEqualTo(values[0]);
+					assertThat((Map<String, Object>)values[1]).contains(
+						entry("component.name", "prototypeSingle")
+					);
 
-						return s;
-					},
-					f -> fail(f.toString())
-				).getValue();
+					return s;
+				},
+				f -> fail(f.toString())
+			).getValue();
 
-				b.set(new Deferred<>());
-				c.set(new Deferred<>());
+			b.set(new Deferred<>());
+			c.set(new Deferred<>());
 
-				serviceObjects.ungetService(other);
+			serviceObjects.ungetService(other);
 
-				b.get().getPromise().timeout(timeout).then(
-					s -> {
-						Object[] values = s.getValue();
+			b.get().getPromise().timeout(timeout).then(
+				s -> {
+					Object[] values = s.getValue();
 
-						assertThat(other).isEqualTo(values[0]);
-						assertThat((Map<String, Object>)values[1]).contains(
-							entry("component.name", "prototypeSingle")
-						);
+					assertThat(other).isEqualTo(values[0]);
+					assertThat((Map<String, Object>)values[1]).contains(
+						entry("component.name", "prototypeSingle")
+					);
 
-						return s;
-					},
-					f -> fail(f.toString())
-				).getValue();
+					return s;
+				},
+				f -> fail(f.toString())
+			).getValue();
 
-				c.get().getPromise().timeout(timeout).then(
-					s -> {
-						Object[] values = s.getValue();
+			c.get().getPromise().timeout(timeout).then(
+				s -> {
+					Object[] values = s.getValue();
 
-						assertThat(other).isEqualTo(values[0]);
-						assertThat((Map<String, Object>)values[1]).contains(
-							entry("component.name", "prototypeSingle")
-						);
+					assertThat(other).isEqualTo(values[0]);
+					assertThat((Map<String, Object>)values[1]).contains(
+						entry("component.name", "prototypeSingle")
+					);
 
-						return s;
-					},
-					f -> fail(f.toString())
-				).getValue();
-			}
-		}
-		finally {
-			try {
-				tbBundle.uninstall();
-			}
-			finally {
-				onInitializedReg.unregister();
-				onBeforeDestroyedReg.unregister();
-				onDestroyedReg.unregister();
-			}
+					return s;
+				},
+				f -> fail(f.toString())
+			).getValue();
 		}
 	}
 
@@ -623,19 +574,19 @@
 		Consumer<Object[]> onBeforeDestroyed = (o) -> b.get().resolve(o);
 		Consumer<Object[]> onDestroyed = (o) -> c.get().resolve(o);
 
-		ServiceRegistration<Consumer> onInitializedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onInitialized,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onInitialized");}});
 
-		ServiceRegistration<Consumer> onBeforeDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onBeforeDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onBeforeDestroyed");}});
 
-		ServiceRegistration<Consumer> onDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onDestroyed");}});
 
-		Bundle tbBundle = installBundle("tb152_3_1_1i.jar");
+		Bundle tbBundle = bcr.installBundle("tb152_3_1_1i.jar");
 		Configuration configuration = null;
 
 		try {
@@ -643,7 +594,7 @@
 
 			assertPromiseIsNotResolved(a);
 
-			configuration = configurationAdmin.getConfiguration("bundleFactory", "?");
+			configuration = car.getService().getConfiguration("bundleFactory", "?");
 			configuration.update(new Hashtable() {{put("foo", "bar");}});
 
 			// only accept factory configuration instances
@@ -657,7 +608,7 @@
 
 			configuration.delete();
 
-			configuration = configurationAdmin.getFactoryConfiguration("bundleFactory", "one", "?");
+			configuration = car.getService().getFactoryConfiguration("bundleFactory", "one", "?");
 			configuration.update(new Hashtable() {{put("foo", "bar");}});
 
 			// Even with configuration, there's still no instance until a "get" is performed
@@ -749,14 +700,6 @@
 					// ignore
 				}
 			}
-			try {
-				tbBundle.uninstall();
-			}
-			finally {
-				onInitializedReg.unregister();
-				onBeforeDestroyedReg.unregister();
-				onDestroyedReg.unregister();
-			}
 		}
 	}
 
@@ -773,19 +716,19 @@
 		Consumer<Object[]> onBeforeDestroyed = (o) -> b.get().resolve(o);
 		Consumer<Object[]> onDestroyed = (o) -> c.get().resolve(o);
 
-		ServiceRegistration<Consumer> onInitializedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onInitialized,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onInitialized");}});
 
-		ServiceRegistration<Consumer> onBeforeDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onBeforeDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onBeforeDestroyed");}});
 
-		ServiceRegistration<Consumer> onDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onDestroyed");}});
 
-		Bundle tbBundle = installBundle("tb152_3_1_1h.jar");
+		Bundle tbBundle = bcr.installBundle("tb152_3_1_1h.jar");
 		Configuration configuration = null;
 
 		try {
@@ -793,7 +736,7 @@
 
 			assertPromiseIsNotResolved(a);
 
-			configuration = configurationAdmin.getConfiguration("bundleSingle_C", "?");
+			configuration = car.getService().getConfiguration("bundleSingle_C", "?");
 			configuration.update(new Hashtable() {{put("foo", "bar");}});
 
 			assertPromiseIsNotResolved(a);
@@ -868,23 +811,6 @@
 					// ignore
 				}
 			}
-			try {
-				tbBundle.uninstall();
-			}
-			finally {
-				onInitializedReg.unregister();
-				onBeforeDestroyedReg.unregister();
-				onDestroyedReg.unregister();
-			}
-		}
-	}
-
-	private void assertPromiseIsResolved(AtomicReference<Deferred<Object[]>> a) throws Exception {
-		Throwable throwable = a.get().getPromise().timeout(timeout).getFailure();
-
-		if (throwable != null) {
-			Object[] value = a.get().getPromise().getValue();
-			throw new AssertionError("Is Not Resolved! " + value[1]);
 		}
 	}
 
@@ -908,19 +834,19 @@
 		Consumer<Object[]> onBeforeDestroyed = (o) -> {try {b.resolve(o);} catch (Exception e) {}};
 		Consumer<Object[]> onDestroyed = (o) -> {try {c.resolve(o);} catch (Exception e) {}};
 
-		ServiceRegistration<Consumer> onInitializedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onInitialized,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onInitialized");}});
 
-		ServiceRegistration<Consumer> onBeforeDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onBeforeDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onBeforeDestroyed");}});
 
-		ServiceRegistration<Consumer> onDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onDestroyed");}});
 
-		Bundle tbBundle = installBundle("tb152_3_1_1g.jar");
+		Bundle tbBundle = bcr.installBundle("tb152_3_1_1g.jar");
 
 		try {
 			getBeanManager(tbBundle);
@@ -950,44 +876,37 @@
 			).getValue();
 		}
 		finally {
-			try {
-				tbBundle.uninstall();
+			tbBundle.uninstall();
 
-				try (CloseableTracker<Object, Object> tracker = track("(objectClass=%s)", Pojo.class.getName())) {
-					assertThat(tracker.waitForService(50)).isNull();
-				}
-
-				b.getPromise().timeout(timeout).then(
-					s -> {
-						Object[] values = s.getValue();
-
-						assertThat((Map<String, Object>)values[1]).contains(
-							entry("component.name", "bundleSingle")
-						);
-
-						return s;
-					},
-					f -> fail(f.toString())
-				).getValue();
-
-				c.getPromise().timeout(timeout).then(
-					s -> {
-						Object[] values = s.getValue();
-
-						assertThat((Map<String, Object>)values[1]).contains(
-							entry("component.name", "bundleSingle")
-						);
-
-						return s;
-					},
-					f -> fail(f.toString())
-				).getValue();
+			try (CloseableTracker<Object, Object> tracker = track("(objectClass=%s)", Pojo.class.getName())) {
+				assertThat(tracker.waitForService(50)).isNull();
 			}
-			finally {
-				onInitializedReg.unregister();
-				onBeforeDestroyedReg.unregister();
-				onDestroyedReg.unregister();
-			}
+
+			b.getPromise().timeout(timeout).then(
+				s -> {
+					Object[] values = s.getValue();
+
+					assertThat((Map<String, Object>)values[1]).contains(
+						entry("component.name", "bundleSingle")
+					);
+
+					return s;
+				},
+				f -> fail(f.toString())
+			).getValue();
+
+			c.getPromise().timeout(timeout).then(
+				s -> {
+					Object[] values = s.getValue();
+
+					assertThat((Map<String, Object>)values[1]).contains(
+						entry("component.name", "bundleSingle")
+					);
+
+					return s;
+				},
+				f -> fail(f.toString())
+			).getValue();
 		}
 	}
 
@@ -1002,19 +921,19 @@
 		Consumer<Object[]> onBeforeDestroyed = (o) -> {try {b.resolve(o);} catch (Exception e) {}};
 		Consumer<Object[]> onDestroyed = (o) -> {try {c.resolve(o);} catch (Exception e) {}};
 
-		ServiceRegistration<Consumer> onInitializedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onInitialized,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onInitialized");}});
 
-		ServiceRegistration<Consumer> onBeforeDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onBeforeDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onBeforeDestroyed");}});
 
-		ServiceRegistration<Consumer> onDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onDestroyed");}});
 
-		Bundle tbBundle = installBundle("tb152_3_1_1f.jar");
+		Bundle tbBundle = bcr.installBundle("tb152_3_1_1f.jar");
 		Configuration configuration = null;
 
 		try {
@@ -1027,7 +946,7 @@
 
 			a.getPromise().timeout(timeout).then(assertFailed).getFailure();
 
-			configuration = configurationAdmin.getConfiguration("singletonFactory", "?");
+			configuration = car.getService().getConfiguration("singletonFactory", "?");
 			configuration.update(new Hashtable() {{put("foo", "bar");}});
 
 			// only accept factory configuration instances
@@ -1035,7 +954,7 @@
 
 			configuration.delete();
 
-			configuration = configurationAdmin.getFactoryConfiguration("singletonFactory", "one", "?");
+			configuration = car.getService().getFactoryConfiguration("singletonFactory", "one", "?");
 			configuration.update(new Hashtable() {{put("foo", "bar");}});
 
 			Success<Object[], Object[]> assertSucceeded = s -> {
@@ -1118,15 +1037,6 @@
 					// ignore
 				}
 			}
-			try {
-				tbBundle.uninstall();
-
-			}
-			finally {
-				onInitializedReg.unregister();
-				onBeforeDestroyedReg.unregister();
-				onDestroyedReg.unregister();
-			}
 		}
 	}
 
@@ -1141,19 +1051,19 @@
 		Consumer<Object[]> onBeforeDestroyed = (o) -> {try {b.resolve(o);} catch (Exception e) {}};
 		Consumer<Object[]> onDestroyed = (o) -> {try {c.resolve(o);} catch (Exception e) {}};
 
-		ServiceRegistration<Consumer> onInitializedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onInitialized,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onInitialized");}});
 
-		ServiceRegistration<Consumer> onBeforeDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onBeforeDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onBeforeDestroyed");}});
 
-		ServiceRegistration<Consumer> onDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onDestroyed");}});
 
-		Bundle tbBundle = installBundle("tb152_3_1_1e.jar");
+		Bundle tbBundle = bcr.installBundle("tb152_3_1_1e.jar");
 		Configuration configuration = null;
 
 		try {
@@ -1166,7 +1076,7 @@
 
 			a.getPromise().timeout(timeout).then(assertFailed).getFailure();
 
-			configuration = configurationAdmin.getConfiguration("singletonSingle_C", "?");
+			configuration = car.getService().getConfiguration("singletonSingle_C", "?");
 			configuration.update(new Hashtable() {{put("foo", "bar");}});
 
 			Success<Object[], Object[]> assertSucceeded = s -> {
@@ -1243,14 +1153,6 @@
 					// ignore
 				}
 			}
-			try {
-				tbBundle.uninstall();
-			}
-			finally {
-				onInitializedReg.unregister();
-				onBeforeDestroyedReg.unregister();
-				onDestroyedReg.unregister();
-			}
 		}
 	}
 
@@ -1265,19 +1167,19 @@
 		Consumer<Object[]> onBeforeDestroyed = (o) -> {try {b.resolve(o);} catch (Exception e) {}};
 		Consumer<Object[]> onDestroyed = (o) -> {try {c.resolve(o);} catch (Exception e) {}};
 
-		ServiceRegistration<Consumer> onInitializedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onInitialized,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onInitialized");}});
 
-		ServiceRegistration<Consumer> onBeforeDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onBeforeDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onBeforeDestroyed");}});
 
-		ServiceRegistration<Consumer> onDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onDestroyed");}});
 
-		Bundle tbBundle = installBundle("tb152_3_1_1d.jar");
+		Bundle tbBundle = bcr.installBundle("tb152_3_1_1d.jar");
 
 		try {
 			getBeanManager(tbBundle);
@@ -1300,44 +1202,37 @@
 			).getValue();
 		}
 		finally {
-			try {
-				tbBundle.uninstall();
+			tbBundle.uninstall();
 
-				b.getPromise().timeout(timeout).then(
-					s -> {
-						Object[] values = s.getValue();
+			b.getPromise().timeout(timeout).then(
+				s -> {
+					Object[] values = s.getValue();
 
-						assertThat((Map<String, Object>)values[1]).contains(
-							entry("component.name", "singletonSingle")
-						);
+					assertThat((Map<String, Object>)values[1]).contains(
+						entry("component.name", "singletonSingle")
+					);
 
-						try (CloseableTracker<Object, Object> tracker = track("(objectClass=%s)", Pojo.class.getName())) {
-							assertThat(tracker.waitForService(50)).isNull();
-						}
+					try (CloseableTracker<Object, Object> tracker = track("(objectClass=%s)", Pojo.class.getName())) {
+						assertThat(tracker.waitForService(50)).isNull();
+					}
 
-						return s;
-					},
-					f -> fail(f.toString())
-				).getValue();
+					return s;
+				},
+				f -> fail(f.toString())
+			).getValue();
 
-				c.getPromise().timeout(timeout).then(
-					s -> {
-						Object[] values = s.getValue();
+			c.getPromise().timeout(timeout).then(
+				s -> {
+					Object[] values = s.getValue();
 
-						assertThat((Map<String, Object>)values[1]).contains(
-							entry("component.name", "singletonSingle")
-						);
+					assertThat((Map<String, Object>)values[1]).contains(
+						entry("component.name", "singletonSingle")
+					);
 
-						return s;
-					},
-					f -> fail(f.toString())
-				).getValue();
-			}
-			finally {
-				onInitializedReg.unregister();
-				onBeforeDestroyedReg.unregister();
-				onDestroyedReg.unregister();
-			}
+					return s;
+				},
+				f -> fail(f.toString())
+			).getValue();
 		}
 	}
 
@@ -1352,19 +1247,19 @@
 		Consumer<Object[]> onBeforeDestroyed = (o) -> {try {b.resolve(o);} catch (Exception e) {}};
 		Consumer<Object[]> onDestroyed = (o) -> {try {c.resolve(o);} catch (Exception e) {}};
 
-		ServiceRegistration<Consumer> onInitializedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onInitialized,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onInitialized");}});
 
-		ServiceRegistration<Consumer> onBeforeDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onBeforeDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onBeforeDestroyed");}});
 
-		ServiceRegistration<Consumer> onDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onDestroyed");}});
 
-		Bundle tbBundle = installBundle("tb152_3_1_1c.jar");
+		Bundle tbBundle = bcr.installBundle("tb152_3_1_1c.jar");
 		Configuration configuration = null;
 
 		try {
@@ -1377,7 +1272,7 @@
 
 			a.getPromise().timeout(timeout).then(assertFailed).getFailure();
 
-			configuration = configurationAdmin.getConfiguration("immediateFactory", "?");
+			configuration = car.getService().getConfiguration("immediateFactory", "?");
 			configuration.update(new Hashtable() {{put("foo", "bar");}});
 
 			// only accept factory configuration instances
@@ -1385,7 +1280,7 @@
 
 			configuration.delete();
 
-			configuration = configurationAdmin.getFactoryConfiguration("immediateFactory", "one", "?");
+			configuration = car.getService().getFactoryConfiguration("immediateFactory", "one", "?");
 			configuration.update(new Hashtable() {{put("foo", "bar");}});
 
 			Success<Object[], Object[]> assertSucceeded = s -> {
@@ -1436,15 +1331,6 @@
 					// ignore
 				}
 			}
-			try {
-				tbBundle.uninstall();
-
-			}
-			finally {
-				onInitializedReg.unregister();
-				onBeforeDestroyedReg.unregister();
-				onDestroyedReg.unregister();
-			}
 		}
 	}
 
@@ -1459,19 +1345,19 @@
 		Consumer<Object[]> onBeforeDestroyed = (o) -> {try {b.resolve(o);} catch (Exception e) {}};
 		Consumer<Object[]> onDestroyed = (o) -> {try {c.resolve(o);} catch (Exception e) {}};
 
-		ServiceRegistration<Consumer> onInitializedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onInitialized,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onInitialized");}});
 
-		ServiceRegistration<Consumer> onBeforeDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onBeforeDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onBeforeDestroyed");}});
 
-		ServiceRegistration<Consumer> onDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onDestroyed");}});
 
-		Bundle tbBundle = installBundle("tb152_3_1_1b.jar");
+		Bundle tbBundle = bcr.installBundle("tb152_3_1_1b.jar");
 		Configuration configuration = null;
 
 		try {
@@ -1484,7 +1370,7 @@
 
 			a.getPromise().timeout(timeout).then(assertFailed).getFailure();
 
-			configuration = configurationAdmin.getConfiguration("immediateSingle_C", "?");
+			configuration = car.getService().getConfiguration("immediateSingle_C", "?");
 			configuration.update(new Hashtable() {{put("foo", "bar");}});
 
 			Success<Object[], Object[]> assertSucceeded = s -> {
@@ -1533,10 +1419,6 @@
 					// ignore
 				}
 			}
-			onInitializedReg.unregister();
-			onBeforeDestroyedReg.unregister();
-			onDestroyedReg.unregister();
-			tbBundle.uninstall();
 		}
 	}
 
@@ -1551,19 +1433,19 @@
 		Consumer<Object[]> onBeforeDestroyed = (o) -> {try {b.resolve(o);} catch (Exception e) {}};
 		Consumer<Object[]> onDestroyed = (o) -> {try {c.resolve(o);} catch (Exception e) {}};
 
-		ServiceRegistration<Consumer> onInitializedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onInitialized,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onInitialized");}});
 
-		ServiceRegistration<Consumer> onBeforeDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onBeforeDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onBeforeDestroyed");}});
 
-		ServiceRegistration<Consumer> onDestroyedReg = bundleContext.registerService(
+		bcr.getBundleContext().registerService(
 			Consumer.class, onDestroyed,
 			new Hashtable() {{put(Constants.SERVICE_DESCRIPTION, "onDestroyed");}});
 
-		Bundle tbBundle = installBundle("tb152_3_1_1a.jar");
+		Bundle tbBundle = bcr.installBundle("tb152_3_1_1a.jar");
 
 		try {
 			getBeanManager(tbBundle);
@@ -1586,40 +1468,33 @@
 			).getValue();
 		}
 		finally {
-			try {
-				tbBundle.uninstall();
+			tbBundle.uninstall();
 
-				b.getPromise().timeout(timeout).then(
-					s -> {
-						Object[] values = s.getValue();
+			b.getPromise().timeout(timeout).then(
+				s -> {
+					Object[] values = s.getValue();
 
-						assertThat((Map<String, Object>)values[1]).contains(
-							entry("component.name", "immediateSingle")
-						);
+					assertThat((Map<String, Object>)values[1]).contains(
+						entry("component.name", "immediateSingle")
+					);
 
-						return s;
-					},
-					f -> fail(f.toString())
-				).getValue();
+					return s;
+				},
+				f -> fail(f.toString())
+			).getValue();
 
-				c.getPromise().timeout(timeout).then(
-					s -> {
-						Object[] values = s.getValue();
+			c.getPromise().timeout(timeout).then(
+				s -> {
+					Object[] values = s.getValue();
 
-						assertThat((Map<String, Object>)values[1]).contains(
-							entry("component.name", "immediateSingle")
-						);
+					assertThat((Map<String, Object>)values[1]).contains(
+						entry("component.name", "immediateSingle")
+					);
 
-						return s;
-					},
-					f -> fail(f.toString())
-				).getValue();
-			}
-			finally {
-				onInitializedReg.unregister();
-				onBeforeDestroyedReg.unregister();
-				onDestroyedReg.unregister();
-			}
+					return s;
+				},
+				f -> fail(f.toString())
+			).getValue();
 		}
 	}
 
@@ -1629,7 +1504,4 @@
 		}
 	}
 
-	private ServiceTracker<ConfigurationAdmin, ConfigurationAdmin> adminTracker;
-	private ConfigurationAdmin configurationAdmin;
-
 }
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/Test_discoverByBeansXml.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/Test_discoverByBeansXml.java
index 1296396..8610726 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/Test_discoverByBeansXml.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/Test_discoverByBeansXml.java
@@ -19,35 +19,26 @@
 import java.util.List;
 import java.util.stream.Collectors;
 
+import org.apache.aries.cdi.test.cases.base.SlimBaseTestCase;
 import org.junit.Test;
 import org.osgi.framework.Bundle;
-import org.osgi.service.cdi.runtime.CDIComponentRuntime;
 import org.osgi.service.cdi.runtime.dto.template.ContainerTemplateDTO;
 
-public class Test_discoverByBeansXml extends SlimTestCase {
+public class Test_discoverByBeansXml extends SlimBaseTestCase {
 
 	@Test
 	public void componentScopeContext() throws Exception {
-		Bundle tbBundle = installBundle("tb14.jar");
+		Bundle tbBundle = bcr.installBundle("tb14.jar");
 
 		getBeanManager(tbBundle);
 
-		try (CloseableTracker<CDIComponentRuntime, CDIComponentRuntime> ccrTracker = track(
-				"(objectClass=%s)", CDIComponentRuntime.class.getName())) {
+		ContainerTemplateDTO containerTemplateDTO = ccrr.getService().getContainerTemplateDTO(tbBundle);
 
-			CDIComponentRuntime ccr = ccrTracker.waitForService(timeout);
+		assertThat(containerTemplateDTO).isNotNull();
 
-			ContainerTemplateDTO containerTemplateDTO = ccr.getContainerTemplateDTO(tbBundle);
+		List<String> beans = containerTemplateDTO.components.stream().flatMap(c -> c.beans.stream()).collect(Collectors.toList());
 
-			assertThat(containerTemplateDTO).isNotNull();
-
-			List<String> beans = containerTemplateDTO.components.stream().flatMap(c -> c.beans.stream()).collect(Collectors.toList());
-
-			assertThat(beans).isNotEmpty().contains("org.apache.aries.cdi.test.tb14.ABean");
-		}
-		finally {
-			tbBundle.uninstall();
-		}
+		assertThat(beans).isNotEmpty().contains("org.apache.aries.cdi.test.tb14.ABean");
 	}
 
 }
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/TrimTests.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/TrimTests.java
index 0a054b4..160de96 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/TrimTests.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/TrimTests.java
@@ -17,60 +17,25 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
+import org.apache.aries.cdi.test.cases.base.SlimBaseTestCase;
 import org.junit.Test;
 import org.osgi.framework.Bundle;
-import org.osgi.service.cdi.runtime.CDIComponentRuntime;
 import org.osgi.service.cdi.runtime.dto.ContainerDTO;
-import org.osgi.util.tracker.ServiceTracker;
 
-public class TrimTests extends AbstractTestCase {
-
-	@BeforeClass
-	public static void beforeClass() throws Exception {
-		runtimeTracker = new ServiceTracker<>(
-				bundleContext, CDIComponentRuntime.class, null);
-		runtimeTracker.open();
-	}
-
-	@AfterClass
-	public static void afterClass() throws Exception {
-		runtimeTracker.close();
-	}
-
-	@Override
-	@Before
-	public void setUp() throws Exception {
-		cdiRuntime = runtimeTracker.waitForService(timeout);
-	}
-
-	@Override
-	@After
-	public void tearDown() throws Exception {
-	}
+public class TrimTests extends SlimBaseTestCase {
 
 	@Test
 	public void testTrimmed() throws Exception {
-		Bundle tb2Bundle = installBundle("tb17.jar", false);
+		Bundle tb2Bundle = bcr.installBundle("tb17.jar");
 
-		tb2Bundle.start();
+		ContainerDTO containerDTO = getContainerDTO(ccrr.getService(), tb2Bundle);
+		assertNotNull(containerDTO);
 
-		try {
-			ContainerDTO containerDTO = getContainerDTO(cdiRuntime, tb2Bundle);
-			assertNotNull(containerDTO);
+		assertEquals(5, containerDTO.template.components.get(0).beans.size());
 
-			assertEquals(5, containerDTO.template.components.get(0).beans.size());
+		assertEquals(2, containerDTO.template.components.size());
 
-			assertEquals(2, containerDTO.template.components.size());
-
-			assertEquals(2, containerDTO.template.components.get(1).beans.size());
-		}
-		finally {
-			tb2Bundle.uninstall();
-		}
+		assertEquals(2, containerDTO.template.components.get(1).beans.size());
 	}
 
 }
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/base/BaseTestCase.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/base/BaseTestCase.java
new file mode 100644
index 0000000..62de0f3
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/base/BaseTestCase.java
@@ -0,0 +1,222 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.test.cases.base;
+
+import static java.util.Optional.ofNullable;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.osgi.test.common.filter.Filters.format;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Supplier;
+
+import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.Any;
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.BeanManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.namespace.extender.ExtenderNamespace;
+import org.osgi.service.cdi.CDIConstants;
+import org.osgi.service.cdi.runtime.CDIComponentRuntime;
+import org.osgi.service.cdi.runtime.dto.ContainerDTO;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.test.junit4.context.BundleContextRule;
+import org.osgi.test.junit4.service.ServiceUseRule;
+import org.osgi.util.promise.PromiseFactory;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+public abstract class BaseTestCase {
+
+	public static final long timeout = 500;
+	public Bundle cdiBundle;
+	public Bundle servicesBundle;
+	public static final PromiseFactory promiseFactory = new PromiseFactory(null);
+
+	@Rule
+	public BundleContextRule bcr = new BundleContextRule();
+	@Rule
+	public ServiceUseRule<CDIComponentRuntime> ccrr = new ServiceUseRule.Builder<CDIComponentRuntime>(CDIComponentRuntime.class, bcr).build();
+	@Rule
+	public ServiceUseRule<ConfigurationAdmin> car = new ServiceUseRule.Builder<ConfigurationAdmin>(ConfigurationAdmin.class, bcr).build();
+
+	@Rule
+	public TestWatcher watchman= new TestWatcher() {
+		@Override
+		protected void failed(Throwable e, Description description) {
+			System.out.printf("--------- TEST: %s#%s [%s]%n", description.getTestClass(), description.getMethodName(), "FAILED");
+		}
+
+		@Override
+		protected void succeeded(Description description) {
+			System.out.printf("--------- TEST: %s#%s [%s]%n", description.getTestClass(), description.getMethodName(), "PASSED");
+		}
+	};
+
+	@Before
+	public void setUp() throws Exception {
+		servicesBundle = bcr.installBundle("services-one.jar", false);
+		servicesBundle.start();
+		cdiBundle = bcr.installBundle("basic-beans.jar", false);
+		cdiBundle.start();
+	}
+
+	@After
+	public void tearDown() throws Exception {
+		cdiBundle.uninstall();
+		servicesBundle.uninstall();
+	}
+
+	public void assertBeanExists(Class<?> clazz, BeanManager beanManager) {
+		Set<Bean<?>> beans = beanManager.getBeans(clazz, Any.Literal.INSTANCE);
+
+		assertFalse(beans.isEmpty());
+		Iterator<Bean<?>> iterator = beans.iterator();
+		Bean<?> bean = iterator.next();
+		assertTrue(clazz.isAssignableFrom(bean.getBeanClass()));
+		assertFalse(iterator.hasNext());
+
+		bean = beanManager.resolve(beans);
+		CreationalContext<?> ctx = beanManager.createCreationalContext(bean);
+		Object pojo = clazz.cast(beanManager.getReference(bean, clazz, ctx));
+		assertNotNull(pojo);
+	}
+
+	public Bundle getCdiExtenderBundle() {
+		BundleWiring bundleWiring = cdiBundle.adapt(BundleWiring.class);
+
+		List<BundleWire> requiredWires = bundleWiring.getRequiredWires(ExtenderNamespace.EXTENDER_NAMESPACE);
+
+		for (BundleWire wire : requiredWires) {
+			Map<String, Object> attributes = wire.getCapability().getAttributes();
+			String extender = (String)attributes.get(ExtenderNamespace.EXTENDER_NAMESPACE);
+
+			if (CDIConstants.CDI_CAPABILITY_NAME.equals(extender)) {
+				return wire.getProvider().getBundle();
+			}
+		}
+
+		return null;
+	}
+
+	public ContainerDTO getContainerDTO(CDIComponentRuntime runtime, Bundle bundle) {
+		Iterator<ContainerDTO> iterator;
+		ContainerDTO containerDTO = null;
+		int attempts = 50;
+		while (--attempts > 0) {
+			iterator = ccrr.getService().getContainerDTOs(bundle).iterator();
+			if (iterator.hasNext()) {
+				containerDTO = iterator.next();
+				if (containerDTO != null) {
+					break;
+				}
+			}
+			try {
+				Thread.sleep(100);
+			} catch (InterruptedException e) {
+				e.printStackTrace();
+			}
+		}
+		assertNotNull(containerDTO);
+		return containerDTO;
+	}
+
+	public <S,T> CloseableTracker<S, T> track(Filter filter) {
+		CloseableTracker<S, T> tracker = new CloseableTracker<>(bcr.getBundleContext(), filter);
+		tracker.open();
+		return tracker;
+	}
+
+	public <S,T> CloseableTracker<S, T> track(String pattern, Object... objects) {
+		return track(format(pattern, objects));
+	}
+
+	public <S> CloseableTracker<S, ServiceReference<S>> trackSR(String pattern, Object... objects) {
+		return trackSR(format(pattern, objects));
+	}
+
+	public <S> CloseableTracker<S, ServiceReference<S>> trackSR(Filter filter) {
+		CloseableTracker<S, ServiceReference<S>> tracker = new CloseableTracker<>(bcr.getBundleContext(), filter, new ServiceTrackerCustomizer<S, ServiceReference<S>>() {
+
+			@Override
+			public ServiceReference<S> addingService(ServiceReference<S> reference) {
+				return reference;
+			}
+
+			@Override
+			public void modifiedService(ServiceReference<S> reference, ServiceReference<S> service) {
+			}
+
+			@Override
+			public void removedService(ServiceReference<S> reference, ServiceReference<S> service) {
+			}
+
+		});
+		tracker.open();
+		return tracker;
+	}
+
+	public BeanManager getBeanManager(Bundle bundle) throws Exception {
+		return trackBM(bundle).waitForService(timeout);
+	}
+
+	public CloseableTracker<BeanManager, BeanManager> trackBM(Bundle bundle) throws Exception {
+		CloseableTracker<BeanManager, BeanManager> serviceTracker = new CloseableTracker<>(
+			bundle.getBundleContext(),
+			format(
+				"(&(objectClass=%s)(service.bundleid=%d))",
+				BeanManager.class.getName(),
+				bundle.getBundleId()),
+			null);
+		serviceTracker.open();
+		return serviceTracker;
+	}
+
+	public long getChangeCount(ServiceReference<?> reference) {
+		return ofNullable(
+			reference.getProperty("service.changecount")
+		).map(
+			Long.class::cast
+		).orElseGet(
+			() -> new Long(-1l)
+		).longValue();
+	}
+
+	public static <T> T tccl(ClassLoader classLoader, Supplier<T> supplier) {
+		Thread currentThread = Thread.currentThread();
+		ClassLoader original = currentThread.getContextClassLoader();
+		try {
+			currentThread.setContextClassLoader(classLoader);
+			return supplier.get();
+		}
+		finally {
+			currentThread.setContextClassLoader(original);
+		}
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/CloseableTracker.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/base/CloseableTracker.java
similarity index 95%
rename from cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/CloseableTracker.java
rename to cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/base/CloseableTracker.java
index 7ec6eea..746b1f7 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/CloseableTracker.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/base/CloseableTracker.java
@@ -12,7 +12,7 @@
  * limitations under the License.
  */
 
-package org.apache.aries.cdi.test.cases;
+package org.apache.aries.cdi.test.cases.base;
 
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Filter;
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/base/HttpBaseTestCase.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/base/HttpBaseTestCase.java
new file mode 100644
index 0000000..d3d03ef
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/base/HttpBaseTestCase.java
@@ -0,0 +1,86 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.test.cases.base;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.InputStream;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.osgi.services.HttpClientBuilderFactory;
+import org.assertj.core.util.Arrays;
+import org.junit.Rule;
+import org.osgi.service.http.runtime.HttpServiceRuntime;
+import org.osgi.service.http.runtime.dto.ServletContextDTO;
+import org.osgi.service.http.runtime.dto.ServletDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+import org.osgi.test.junit4.service.ServiceUseRule;
+
+public abstract class HttpBaseTestCase extends SlimBaseTestCase {
+
+	@Rule
+	public ServiceUseRule<HttpServiceRuntime> hsrr = new ServiceUseRule.Builder<HttpServiceRuntime>(HttpServiceRuntime.class, bcr).build();
+	@Rule
+	public ServiceUseRule<HttpClientBuilderFactory> hcbfr = new ServiceUseRule.Builder<HttpClientBuilderFactory>(HttpClientBuilderFactory.class, bcr).build();
+
+	public String getHttpEndpoint() {
+		String[] endpoints = (String[])hsrr.getServiceReference().getProperty("osgi.http.endpoint");
+
+		if (endpoints == null || endpoints.length == 0) {
+			String port = (String)hsrr.getServiceReference().getProperty("org.osgi.service.http.port");
+			return "http://localhost:" + port;
+		}
+
+		return endpoints[0];
+	}
+
+	public String read(HttpEntity entity) throws Exception {
+		if (entity == null) {
+			return null;
+		}
+
+		try (InputStream in = entity.getContent();
+			java.util.Scanner s = new java.util.Scanner(in)) {
+
+			s.useDelimiter("\\A");
+			return s.hasNext() ? s.next() : "";
+		}
+	}
+
+	public ServletDTO waitFor(String path) throws InterruptedException {
+		return waitFor(path, 20);
+	}
+
+	public ServletDTO waitFor(String path, int intervals) throws InterruptedException {
+		for (int j = intervals; j > 0; j--) {
+			for (ServletContextDTO scDTO : hsrr.getService().getRuntimeDTO().servletContextDTOs) {
+				if (scDTO.name.equals(HttpWhiteboardConstants.HTTP_WHITEBOARD_DEFAULT_CONTEXT_NAME)) {
+					for (ServletDTO sDTO : scDTO.servletDTOs) {
+						if (Arrays.asList(sDTO.patterns).contains(path)) {
+							return sDTO;
+						}
+					}
+				}
+			}
+
+			Thread.sleep(50);
+		}
+
+		assertTrue(String.format("%s not found in time", path), false);
+
+		return null;
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/base/JaxrsBaseTestCase.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/base/JaxrsBaseTestCase.java
new file mode 100644
index 0000000..209eb4e
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/base/JaxrsBaseTestCase.java
@@ -0,0 +1,50 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.test.cases.base;
+
+import java.util.Collection;
+
+import javax.ws.rs.client.ClientBuilder;
+
+import org.junit.Rule;
+import org.osgi.service.jaxrs.runtime.JaxrsServiceRuntime;
+import org.osgi.service.jaxrs.runtime.JaxrsServiceRuntimeConstants;
+import org.osgi.test.junit4.service.ServiceUseRule;
+
+public abstract class JaxrsBaseTestCase extends HttpBaseTestCase {
+
+	@Rule
+	public ServiceUseRule<JaxrsServiceRuntime> jsrr = new ServiceUseRule.Builder<JaxrsServiceRuntime>(JaxrsServiceRuntime.class, bcr).build();
+	@Rule
+	public ServiceUseRule<ClientBuilder> cbr = new ServiceUseRule.Builder<ClientBuilder>(ClientBuilder.class, bcr).build();
+
+	public String getJaxrsEndpoint() {
+		Object endpointsObj = jsrr.getServiceReference().getProperty(
+			JaxrsServiceRuntimeConstants.JAX_RS_SERVICE_ENDPOINT);
+
+		if (endpointsObj instanceof String) {
+			return String.valueOf(endpointsObj);
+		}
+		else if (endpointsObj instanceof String[]) {
+			return ((String[])endpointsObj)[0];
+		}
+		else if (endpointsObj instanceof Collection) {
+			return String.valueOf(((Collection<?>)endpointsObj).iterator().next());
+		}
+
+		return null;
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/SlimTestCase.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/base/SlimBaseTestCase.java
similarity index 70%
rename from cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/SlimTestCase.java
rename to cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/base/SlimBaseTestCase.java
index 1c929a6..18d32ad 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/SlimTestCase.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/base/SlimBaseTestCase.java
@@ -12,21 +12,11 @@
  * limitations under the License.
  */
 
-package org.apache.aries.cdi.test.cases;
+package org.apache.aries.cdi.test.cases.base;
 
 import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
 
-public abstract class SlimTestCase extends AbstractTestCase {
-
-	@BeforeClass
-	public static void beforeClass() throws Exception {
-	}
-
-	@AfterClass
-	public static void afterClass() throws Exception {
-	}
+public abstract class SlimBaseTestCase extends BaseTestCase {
 
 	@Override
 	public void setUp() throws Exception {
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/base/package-info.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/base/package-info.java
new file mode 100644
index 0000000..f237016
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/base/package-info.java
@@ -0,0 +1,27 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@Requirement(
+	effective = "active",
+	filter = "(objectClass=org.osgi.service.cm.ConfigurationAdmin)",
+	namespace = ServiceNamespace.SERVICE_NAMESPACE
+)
+@RequireCDIExtension("aries.cdi.http")
+@RequireCDIExtension("aries.cdi.jaxrs")
+@RequireCDIExtension("aries.cdi.jndi")
+package org.apache.aries.cdi.test.cases.base;
+
+import org.apache.aries.cdi.extra.RequireCDIExtension;
+import org.osgi.annotation.bundle.Requirement;
+import org.osgi.namespace.service.ServiceNamespace;
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/jaxrs/ResourceTests.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/jaxrs/ResourceTests.java
new file mode 100644
index 0000000..8d72c90
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/jaxrs/ResourceTests.java
@@ -0,0 +1,76 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.aries.cdi.test.cases.jaxrs;
+
+import static javax.ws.rs.core.MediaType.TEXT_PLAIN_TYPE;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+
+import org.apache.aries.cdi.test.cases.base.JaxrsBaseTestCase;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.service.jaxrs.runtime.dto.RuntimeDTO;
+
+public class ResourceTests extends JaxrsBaseTestCase {
+
+	@Before
+	@Override
+	public void setUp() throws Exception {
+		super.setUp();
+		cdiBundle = bcr.installBundle("tb24.jar");
+
+		int count = 100;
+		RuntimeDTO runtimeDTO;
+		while ((runtimeDTO = jsrr.getService().getRuntimeDTO()).defaultApplication.resourceDTOs.length < 1 && (count > 0)) {
+			count--;
+			Thread.sleep(100);
+		}
+
+		assertThat(runtimeDTO.defaultApplication.resourceDTOs).extracting("name").contains(
+			"A");
+	}
+
+	@After
+	@Override
+	public void tearDown() throws Exception {
+		cdiBundle.uninstall();
+	}
+
+	@Test
+	public void test() throws Exception {
+		final ClientBuilder cb = cbr.getService();
+		cb.connectTimeout(1000, TimeUnit.SECONDS);
+		cb.readTimeout(1000, TimeUnit.SECONDS);
+
+		final Client client = cb.build();
+
+		try {
+			final String serverToken = client.target(getJaxrsEndpoint())
+					.path("a")
+					.request(TEXT_PLAIN_TYPE)
+					.get(String.class);
+			assertEquals("a", serverToken);
+		} finally {
+			client.close();
+		}
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb24/A.java
similarity index 66%
copy from cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java
copy to cdi-itests/src/main/java/org/apache/aries/cdi/test/tb24/A.java
index 315634c..b6c6cdb 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb24/A.java
@@ -12,5 +12,21 @@
  * limitations under the License.
  */
 
-@org.osgi.service.cdi.annotations.Beans
-package org.apache.aries.cdi.test.tb16;
+package org.apache.aries.cdi.test.tb24;
+
+import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+@Path("a")
+public class A {
+
+	@GET
+	@Produces(TEXT_PLAIN)
+	public String get() {
+		return "a";
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb24/package-info.java
similarity index 74%
copy from cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java
copy to cdi-itests/src/main/java/org/apache/aries/cdi/test/tb24/package-info.java
index 315634c..737d907 100644
--- a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb16/package-info.java
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb24/package-info.java
@@ -12,5 +12,9 @@
  * limitations under the License.
  */
 
-@org.osgi.service.cdi.annotations.Beans
-package org.apache.aries.cdi.test.tb16;
+@Beans
+@RequireCDIExtension("aries.cdi.jaxrs")
+package org.apache.aries.cdi.test.tb24;
+
+import org.apache.aries.cdi.extra.RequireCDIExtension;
+import org.osgi.service.cdi.annotations.Beans;
diff --git a/cdi-itests/src/main/resources/OSGI-INF/configurator/config.json b/cdi-itests/src/main/resources/OSGI-INF/configurator/config.json
index 13ba63b..2675836 100644
--- a/cdi-itests/src/main/resources/OSGI-INF/configurator/config.json
+++ b/cdi-itests/src/main/resources/OSGI-INF/configurator/config.json
@@ -15,7 +15,6 @@
 
 	"org.apache.aries.jax.rs.whiteboard.default": {
 		"osgi.http.whiteboard.context.select": "(osgi.http.whiteboard.context.name=default)",
-		"osgi.jaxrs.extension.select": "(osgi.jaxrs.name=metrics.json.provider)",
 		"default.web": false
 	}
 }
\ No newline at end of file
diff --git a/cdi-itests/weld-itest.bndrun b/cdi-itests/weld-itest.bndrun
index bf3f0c8..858d5d9 100644
--- a/cdi-itests/weld-itest.bndrun
+++ b/cdi-itests/weld-itest.bndrun
@@ -27,11 +27,11 @@
 	javax.transaction-api;version='[1.2.0,1.2.1)',\
 	jboss-classfilewriter;version='[1.2.3,1.2.4)',\
 	org.apache.aries.cdi.extender;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.jaxrs;version='[1.1.0,1.1.1)',\
 	org.apache.aries.cdi.extension.jndi;version='[1.1.0,1.1.1)',\
-	org.apache.aries.cdi.extension.mp-config;version='[1.1.0,1.1.1)',\
-	org.apache.aries.cdi.extension.mp-metrics;version='[1.1.0,1.1.1)',\
 	org.apache.aries.cdi.extension.servlet.common;version='[1.1.0,1.1.1)',\
 	org.apache.aries.cdi.extension.servlet.weld;version='[1.1.0,1.1.1)',\
+	org.apache.aries.cdi.extension.spi;version='[1.1.0,1.1.1)',\
 	org.apache.aries.cdi.extra;version='[1.1.0,1.1.1)',\
 	org.apache.aries.cdi.itests;version='[1.1.0,1.1.1)',\
 	org.apache.aries.cdi.spi;version='[1.1.0,1.1.1)',\
@@ -43,7 +43,6 @@
 	org.apache.aries.util;version='[1.0.0,1.0.1)',\
 	org.apache.commons.logging;version='[1.2.0,1.2.1)',\
 	org.apache.felix.configadmin;version='[1.9.10,1.9.11)',\
-	org.apache.felix.configurator;version='[1.0.10,1.0.11)',\
 	org.apache.felix.converter;version='[1.0.12,1.0.13)',\
 	org.apache.felix.gogo.command;version='[1.1.0,1.1.1)',\
 	org.apache.felix.gogo.runtime;version='[1.1.2,1.1.3)',\
@@ -56,13 +55,8 @@
 	org.apache.geronimo.specs.geronimo-interceptor_1.2_spec;version='[1.1.0,1.1.1)',\
 	org.apache.geronimo.specs.geronimo-jaxrs_2.1_spec;version='[1.1.0,1.1.1)',\
 	org.apache.geronimo.specs.geronimo-jcdi_2.0_spec;version='[1.1.0,1.1.1)',\
-	org.apache.geronimo.specs.geronimo-json_1.1_spec;version='[1.3.0,1.3.1)',\
-	org.apache.geronimo.specs.geronimo-jsonb_1.0_spec;version='[1.2.0,1.2.1)',\
 	org.apache.httpcomponents.httpclient;version='[4.5.3,4.5.4)',\
 	org.apache.httpcomponents.httpcore;version='[4.4.6,4.4.7)',\
-	org.apache.johnzon.core;version='[1.2.2,1.2.3)',\
-	org.apache.johnzon.jsonb;version='[1.2.2,1.2.3)',\
-	org.apache.johnzon.mapper;version='[1.2.2,1.2.3)',\
 	org.jboss.logging.jboss-logging;version='[3.3.2,3.3.3)',\
 	org.jboss.weld.osgi-bundle;version='[3.0.5,3.0.6)',\
 	org.osgi.service.cdi;version='[1.0.0,1.0.1)',\
diff --git a/pom.xml b/pom.xml
index cd09b52..9730e3a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,13 +38,16 @@
 		<maven.compiler.source>${java.version}</maven.compiler.source>
 		<maven.compiler.target>${java.version}</maven.compiler.target>
 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<maven.site.skip>true</maven.site.skip>
+		<maven.site.deploy.skip>true</maven.site.deploy.skip>
 
 		<!-- Versions -->
 		<bnd.version>4.3.1</bnd.version>
-		<byte.buddy.version>1.10.3</byte.buddy.version>
-		<johnzon.version>1.2.3-SNAPSHOT</johnzon.version>
+		<byte.buddy.version>1.10.6</byte.buddy.version>
+		<jax.rs.whiteboard.version>1.0.6</jax.rs.whiteboard.version>
 		<jsp.version>2.0</jsp.version>
 		<mp.config.version>1.3</mp.config.version>
+		<mp.jwt.auth.version>1.1.1</mp.jwt.auth.version>
 		<mp.metrics.version>1.1.1</mp.metrics.version>
 		<owb.version>2.0.13</owb.version>
 		<slf4j.version>1.7.28</slf4j.version>
@@ -70,18 +73,17 @@
 		<module>cdi-build-tools</module>
 		<module>cdi-extra</module>
 		<module>cdi-spi</module>
+		<module>cdi-extension-spi</module>
 		<module>cdi-extender</module>
 		<module>cdi-extension-el-jsp</module>
+		<module>cdi-extension-jaxrs</module>
 		<module>cdi-extension-jndi</module>
 		<module>cdi-extension-servlet-common</module>
 		<module>cdi-extension-servlet-owb</module>
 		<module>cdi-extension-servlet-weld</module>
-		<module>cdi-extension-mp-config</module>
-		<module>cdi-extension-mp-metrics</module>
 		<module>cdi-owb</module>
 		<module>cdi-weld</module>
 		<module>cdi-bom</module>
-		<module>cdi-itests</module>
 		<module>cdi-executable</module>
 	</modules>
 
@@ -180,7 +182,7 @@
 			<dependency>
 				<groupId>org.osgi</groupId>
 				<artifactId>org.osgi.service.http.whiteboard</artifactId>
-				<version>1.0.0</version>
+				<version>1.1.0</version>
 				<scope>provided</scope>
 			</dependency>
 			<dependency>
@@ -216,6 +218,49 @@
 
 	<profiles>
 		<profile>
+			<id>itests</id>
+			<modules>
+				<module>cdi-itests</module>
+			</modules>
+		</profile>
+
+		<profile>
+			<id>experimental</id>
+			<properties>
+				<experimental>true</experimental>
+				<bnd.version>5.1.0-SNAPSHOT</bnd.version>
+				<johnzon.version>1.2.3-SNAPSHOT</johnzon.version>
+			</properties>
+			<modules>
+				<module>cdi-extension-mp-config</module>
+				<module>cdi-extension-mp-jwt-auth</module>
+				<module>cdi-extension-mp-metrics</module>
+			</modules>
+
+			<repositories>
+				<repository>
+					<id>Apache Snapshots</id>
+					<layout>default</layout>
+					<url>https://repository.apache.org/content/repositories/snapshots/</url>
+					<releases>
+						<enabled>false</enabled>
+					</releases>
+				</repository>
+			</repositories>
+
+			<pluginRepositories>
+				<pluginRepository>
+					<id>bnd-snapshots</id>
+					<url>https://bndtools.jfrog.io/bndtools/libs-snapshot/</url>
+					<layout>default</layout>
+					<releases>
+						<enabled>false</enabled>
+					</releases>
+				</pluginRepository>
+			</pluginRepositories>
+		</profile>
+
+		<profile>
 			<id>apache-release</id>
 			<build>
 				<plugins>
@@ -436,6 +481,42 @@
 				<groupId>biz.aQute.bnd</groupId>
 				<artifactId>bnd-run-maven-plugin</artifactId>
 			</plugin>
+			<plugin>
+				<artifactId>maven-resources-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>default-testResources</id>
+						<phase>none</phase>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>default-testCompile</id>
+						<phase>none</phase>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<artifactId>maven-surefire-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>default-test</id>
+						<phase>none</phase>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<artifactId>maven-site-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>attach-descriptor</id>
+						<phase>none</phase>
+					</execution>
+				</executions>
+			</plugin>
 		</plugins>
 	</build>