excludes from the bean archive

Signed-off-by: Raymond Augé <rotty3000@apache.org>
diff --git a/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/annotated/AnnotatedImpl.java b/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/annotated/AnnotatedImpl.java
index 62d54d5..463fc05 100644
--- a/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/annotated/AnnotatedImpl.java
+++ b/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/annotated/AnnotatedImpl.java
@@ -14,7 +14,7 @@
 
 package org.apache.aries.cdi.container.internal.annotated;
 
-import static java.util.stream.Collectors.*;
+import static java.util.stream.Collectors.toSet;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.AnnotatedElement;
@@ -68,4 +68,9 @@
 		return _annotatedElement.isAnnotationPresent(annotationType);
 	}
 
+	@Override
+	public String toString() {
+		return _baseType.getTypeName();
+	}
+
 }
diff --git a/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/container/Discovery.java b/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/container/Discovery.java
index 9e1e3d7..96e68ce 100644
--- a/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/container/Discovery.java
+++ b/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/container/Discovery.java
@@ -77,6 +77,7 @@
 import org.osgi.service.cdi.reference.BindService;
 import org.osgi.service.cdi.reference.BindServiceReference;
 import org.osgi.service.cdi.runtime.dto.template.ComponentTemplateDTO;
+import org.w3c.dom.Attr;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.NodeList;
@@ -90,6 +91,7 @@
 	static final DocumentBuilderFactory	dbf	= DocumentBuilderFactory.newInstance();
 	static final XPathFactory			xpf	= XPathFactory.newInstance();
 	static final XPathExpression		trimExpression;
+	static final XPathExpression		excludeExpression;
 
 	static {
 		try {
@@ -98,6 +100,7 @@
 			dbf.setExpandEntityReferences(false);
 			XPath xPath = xpf.newXPath();
 			trimExpression = xPath.compile("boolean(/beans/trim)");
+			excludeExpression = xPath.compile("/beans/scan/exclude");
 		} catch (Throwable t) {
 			throw Exceptions.duck(t);
 		}
@@ -114,6 +117,7 @@
 
 		_beansModel.getBeansXml().stream().map(this::readXMLResource).forEach(doc -> {
 			if (!trim.get()) trim.set(checkTrim(doc));
+			_excludes.addAll(getExcludes(doc));
 		});
 
 		_trim = trim.get();
@@ -125,7 +129,7 @@
 
 			AnnotatedType<?> annotatedType = new AnnotatedTypeImpl<>(osgiBean.getBeanClass());
 
-			if (trimIt(annotatedType)) {
+			if (trimIt(annotatedType) || exclude(annotatedType)) {
 				return;
 			}
 
@@ -202,6 +206,11 @@
 		postProcessComponentScopedBeans();
 	}
 
+	boolean exclude(AnnotatedType<?> annotatedType) {
+		// See https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#exclude_filters
+		return _excludes.stream().anyMatch(ex -> ex.exclude(annotatedType));
+	}
+
 	boolean trimIt(AnnotatedType<?> annotatedType) {
 		// See https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#trimmed_bean_archive
 		if (!_trim) return false;
@@ -457,6 +466,25 @@
 		}
 	}
 
+	List<Exclude> getExcludes(Document document) {
+		try {
+			List<Exclude> excludes = new ArrayList<>();
+
+			NodeList excludeNodes = NodeList.class.cast(excludeExpression.evaluate(document, XPathConstants.NODESET));
+
+			for (int i = 0; i < excludeNodes.getLength(); i++) {
+				Element excludeElement = (Element)excludeNodes.item(i);
+
+				excludes.add(new Exclude(excludeElement));
+			}
+
+			return excludes;
+		}
+		catch (Exception e) {
+			throw Exceptions.duck(e);
+		}
+	}
+
 	Document readXMLResource(URL resource) {
 		try {
 			DocumentBuilder db = dbf.newDocumentBuilder();
@@ -471,12 +499,136 @@
 		}
 	}
 
-	private static final String NS = "http://xmlns.jcp.org/xml/ns/javaee";
-
 	private final BeansModel _beansModel;
 	private final Set<OSGiBean> _componentScoped = new HashSet<>();
 	private final ComponentTemplateDTO _containerTemplate;
 	private final ContainerState _containerState;
 	private final boolean _trim;
+	private final List<Exclude> _excludes;
+
+	enum Match {
+		CLASSNAME, PACKAGE_NAME, PACKAGE_PREFIX
+	}
+
+	class Exclude {
+
+		private final String name;
+		private final Match match;
+		private final List<String> ifClassAvailableS = new ArrayList<>();
+		private final List<String> ifClassesNotAvailableS = new ArrayList<>();
+		private final Map<String, String> ifSystemPropertyS = new HashMap<>();
+
+		public Exclude(Element excludeElement) {
+			String glob = excludeElement.getAttribute("name");
+
+			if (glob.endsWith(".**")) {
+				match = Match.PACKAGE_PREFIX;
+				name = glob.substring(0, glob.length() - 3);
+			}
+			else if (glob.endsWith(".*")) {
+				match = Match.PACKAGE_NAME;
+				name = glob.substring(0, glob.length() - 2);
+			}
+			else {
+				match = Match.CLASSNAME;
+				name = glob;
+			}
+
+			NodeList ifClassAvailableNodes = excludeElement.getElementsByTagName("if-class-available");
+
+			for (int iCAIdx = 0; iCAIdx < ifClassAvailableNodes.getLength(); iCAIdx++) {
+				Element ifClassAvailableElement = (Element)ifClassAvailableNodes.item(iCAIdx);
+
+				Attr nameAttribute = ifClassAvailableElement.getAttributeNode("name");
+
+				ifClassAvailableS.add(nameAttribute.getValue());
+			}
+
+			NodeList ifClassNotAvailableNodes = excludeElement.getElementsByTagName("if-class-not-available");
+
+			for (int iCNAIdx = 0; iCNAIdx < ifClassNotAvailableNodes.getLength(); iCNAIdx++) {
+				Element ifClassNotAvailableElement = (Element)ifClassNotAvailableNodes.item(iCNAIdx);
+
+				Attr nameAttribute = ifClassNotAvailableElement.getAttributeNode("name");
+
+				ifClassesNotAvailableS.add(nameAttribute.getValue());
+			}
+
+			NodeList ifSystemPropertyNodes = excludeElement.getElementsByTagName("if-system-property");
+
+			for (int iCNAIdx = 0; iCNAIdx < ifSystemPropertyNodes.getLength(); iCNAIdx++) {
+				Element ifSystemPropertyElement = (Element)ifSystemPropertyNodes.item(iCNAIdx);
+
+				String value = "";
+
+				if (ifSystemPropertyElement.hasAttribute("value")) {
+					value = ifSystemPropertyElement.getAttributeNode("value").getValue();
+				}
+
+				Attr nameAttribute = ifSystemPropertyElement.getAttributeNode("name");
+
+				ifSystemPropertyS.put(nameAttribute.getValue(), value);
+			}
+		}
+
+		public boolean exclude(AnnotatedType<?> annotatedType) {
+			String className = annotatedType.getJavaClass().getName();
+			String packageName = annotatedType.getJavaClass().getPackage().getName();
+
+			boolean matches = false;
+			switch (match) {
+				case CLASSNAME: {
+					matches = className.equals(name);
+					break;
+				}
+				case PACKAGE_NAME: {
+					matches = packageName.equals(name);
+					break;
+				}
+				case PACKAGE_PREFIX: {
+					matches = packageName.startsWith(name);
+				}
+			}
+
+			if (matches &&
+				ifClassAvailableS.stream().allMatch(this::classIsAvailable) &&
+				ifClassesNotAvailableS.stream().allMatch(this::classIsNotAvailable) &&
+				ifSystemPropertyS.entrySet().stream().allMatch(this::isPropertySet)) {
+
+				return true;
+			}
+
+			return false;
+		}
+
+		boolean classIsNotAvailable(String className) {
+			return !classIsAvailable(className);
+		}
+
+		boolean classIsAvailable(String className) {
+			try {
+				Class.forName(className, false, _containerState.classLoader());
+				return true;
+			}
+			catch (ClassNotFoundException cnfe) {
+				return false;
+			}
+		}
+
+		boolean isPropertySet(Entry<String, String> entry) {
+			if (entry.getValue().isEmpty()) {
+				return _containerState.bundleContext().getProperty(entry.getKey()) != null;
+			}
+			else {
+				return entry.getValue().equals(_containerState.bundleContext().getProperty(entry.getKey()));
+			}
+		}
+
+		@Override
+		public String toString() {
+			return name + ":" + match;
+		}
+
+	}
 
 }
diff --git a/cdi-itests/base-itest.bndrun b/cdi-itests/base-itest.bndrun
index cbf4ddb..a82ea48 100644
--- a/cdi-itests/base-itest.bndrun
+++ b/cdi-itests/base-itest.bndrun
@@ -20,7 +20,9 @@
 	logback.configurationFile=file:${.}/logback.xml,\
 	org.osgi.service.http.port=0,\
 	osgi.console=,\
-	tck.config.test.javaconfig.converter.stringvalues=foo
+	tck.config.test.javaconfig.converter.stringvalues=foo,\
+	test.property.a=blah,\
+	test.property.b=
 
 -resolve.effective: resolve, active
 
diff --git a/cdi-itests/bnd.bnd b/cdi-itests/bnd.bnd
index ef86c47..1c801b6 100644
--- a/cdi-itests/bnd.bnd
+++ b/cdi-itests/bnd.bnd
@@ -59,7 +59,10 @@
 	tb13.jar,\
 	tb14.jar,\
 	tb16.jar,\
-	tb17.jar
+	tb17.jar,\
+	tb18.jar,\
+	tb19.jar,\
+	tb20.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/tb18.bnd b/cdi-itests/bnd/tb18.bnd
new file mode 100644
index 0000000..1c2e6c2
--- /dev/null
+++ b/cdi-itests/bnd/tb18.bnd
@@ -0,0 +1,15 @@
+#    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}.tb18.*;-split-package:=first
+-includeresource: META-INF/beans.xml=bnd/tb18.xml
+-cdiannotations: *;discover=all
\ No newline at end of file
diff --git a/cdi-itests/bnd/tb18.xml b/cdi-itests/bnd/tb18.xml
new file mode 100644
index 0000000..d9f0a3f
--- /dev/null
+++ b/cdi-itests/bnd/tb18.xml
@@ -0,0 +1,34 @@
+<?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.
+ */
+-->
+
+<beans
+	xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="
+		http://xmlns.jcp.org/xml/ns/javaee
+		http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
+	bean-discovery-mode="all" version="2.0">
+
+	<scan>
+		<exclude name="org.apache.aries.cdi.test.tb18.B" />
+
+		<exclude name="org.apache.aries.cdi.test.tb18.pA.**" />
+
+		<exclude name="org.apache.aries.cdi.test.tb18.pB.*" />
+	</scan>
+</beans>
\ No newline at end of file
diff --git a/cdi-itests/bnd/tb19.bnd b/cdi-itests/bnd/tb19.bnd
new file mode 100644
index 0000000..aa9f63a
--- /dev/null
+++ b/cdi-itests/bnd/tb19.bnd
@@ -0,0 +1,15 @@
+#    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}.tb19.*;-split-package:=first
+-includeresource: META-INF/beans.xml=bnd/tb19.xml
+-cdiannotations: *;discover=all
\ No newline at end of file
diff --git a/cdi-itests/bnd/tb19.xml b/cdi-itests/bnd/tb19.xml
new file mode 100644
index 0000000..ab2a051
--- /dev/null
+++ b/cdi-itests/bnd/tb19.xml
@@ -0,0 +1,38 @@
+<?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.
+ */
+-->
+
+<beans
+	xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="
+		http://xmlns.jcp.org/xml/ns/javaee
+		http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
+	bean-discovery-mode="all" version="2.0">
+
+	<scan>
+		<exclude name="org.apache.aries.cdi.test.tb19.A">
+			<if-class-not-available name="com.foo.Bar"/>
+		</exclude>
+		<exclude name="org.apache.aries.cdi.test.tb19.B">
+			<if-class-not-available name="com.foo.Bar"/>
+		</exclude>
+		<exclude name="org.apache.aries.cdi.test.tb19.C">
+			<if-class-available name="org.osgi.service.cdi.annotations.Service"/>
+		</exclude>
+	</scan>
+</beans>
\ No newline at end of file
diff --git a/cdi-itests/bnd/tb20.bnd b/cdi-itests/bnd/tb20.bnd
new file mode 100644
index 0000000..67ec8e3
--- /dev/null
+++ b/cdi-itests/bnd/tb20.bnd
@@ -0,0 +1,15 @@
+#    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}.tb20.*;-split-package:=first
+-includeresource: META-INF/beans.xml=bnd/tb20.xml
+-cdiannotations: *;discover=all
\ No newline at end of file
diff --git a/cdi-itests/bnd/tb20.xml b/cdi-itests/bnd/tb20.xml
new file mode 100644
index 0000000..4653be4
--- /dev/null
+++ b/cdi-itests/bnd/tb20.xml
@@ -0,0 +1,40 @@
+<?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.
+ */
+-->
+
+<beans
+	xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="
+		http://xmlns.jcp.org/xml/ns/javaee
+		http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
+	bean-discovery-mode="all" version="2.0">
+
+	<scan>
+		<exclude name="org.apache.aries.cdi.test.tb20.A">
+			<if-system-property name="test.property.a" value="blah" />
+		</exclude>
+
+		<exclude name="org.apache.aries.cdi.test.tb20.B">
+			<if-system-property name="test.property.b" />
+		</exclude>
+
+		<exclude name="org.apache.aries.cdi.test.tb20.C">
+			<if-system-property name="test.property.c" value="bar" />
+		</exclude>
+	</scan>
+</beans>
\ No newline at end of file
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
new file mode 100644
index 0000000..6e73fa1
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/cases/ExcludeTests.java
@@ -0,0 +1,112 @@
+/**
+ * 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.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.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 {
+	}
+
+	@Test
+	public void testExclude_ByName() throws Exception {
+		Bundle tb2Bundle = installBundle("tb18.jar", false);
+
+		tb2Bundle.start();
+
+		try {
+			ContainerDTO containerDTO = getContainerDTO(cdiRuntime, tb2Bundle);
+			assertNotNull(containerDTO);
+
+			assertEquals(1, containerDTO.template.components.size());
+
+			assertEquals(3, containerDTO.template.components.get(0).beans.size());
+		}
+		finally {
+			tb2Bundle.uninstall();
+		}
+	}
+
+	@Test
+	public void testExclude_IfClassAvailable() throws Exception {
+		Bundle tb2Bundle = installBundle("tb19.jar", false);
+
+		tb2Bundle.start();
+
+		try {
+			ContainerDTO containerDTO = getContainerDTO(cdiRuntime, tb2Bundle);
+			assertNotNull(containerDTO);
+
+			assertEquals(1, containerDTO.template.components.size());
+
+			assertEquals(1, containerDTO.template.components.get(0).beans.size());
+		}
+		finally {
+			tb2Bundle.uninstall();
+		}
+	}
+
+	@Test
+	public void testExclude_IfSystemProperty() throws Exception {
+		Bundle tb2Bundle = installBundle("tb20.jar", false);
+
+		tb2Bundle.start();
+
+		try {
+			ContainerDTO containerDTO = getContainerDTO(cdiRuntime, tb2Bundle);
+			assertNotNull(containerDTO);
+
+			assertEquals(1, containerDTO.template.components.size());
+
+			assertEquals(2, containerDTO.template.components.get(0).beans.size());
+		}
+		finally {
+			tb2Bundle.uninstall();
+		}
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/A.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/A.java
new file mode 100644
index 0000000..89dad56
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/A.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.test.tb18;
+
+import org.apache.aries.cdi.test.interfaces.Pojo;
+import org.osgi.service.cdi.annotations.Service;
+
+@Service
+public class A implements Pojo {
+
+	@Override
+	public String foo(String input) {
+		return "A" + input;
+	}
+
+	@Override
+	public int getCount() {
+		return 0;
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/B.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/B.java
new file mode 100644
index 0000000..33e8aec
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/B.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.test.tb18;
+
+import org.apache.aries.cdi.test.interfaces.Pojo;
+import org.osgi.service.cdi.annotations.Service;
+
+@Service
+public class B implements Pojo {
+
+	@Override
+	public String foo(String input) {
+		return "B" + input;
+	}
+
+	@Override
+	public int getCount() {
+		return 0;
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pA/C.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pA/C.java
new file mode 100644
index 0000000..5aeb4f9
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pA/C.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.test.tb18.pA;
+
+import org.apache.aries.cdi.test.interfaces.Pojo;
+import org.osgi.service.cdi.annotations.Service;
+
+@Service
+public class C implements Pojo {
+
+	@Override
+	public String foo(String input) {
+		return "A" + input;
+	}
+
+	@Override
+	public int getCount() {
+		return 0;
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pA/D.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pA/D.java
new file mode 100644
index 0000000..5496a17
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pA/D.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.test.tb18.pA;
+
+import org.apache.aries.cdi.test.interfaces.Pojo;
+import org.osgi.service.cdi.annotations.Service;
+
+@Service
+public class D implements Pojo {
+
+	@Override
+	public String foo(String input) {
+		return "A" + input;
+	}
+
+	@Override
+	public int getCount() {
+		return 0;
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pA/pAA/E.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pA/pAA/E.java
new file mode 100644
index 0000000..25c1777
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pA/pAA/E.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.test.tb18.pA.pAA;
+
+import org.apache.aries.cdi.test.interfaces.Pojo;
+import org.osgi.service.cdi.annotations.Service;
+
+@Service
+public class E implements Pojo {
+
+	@Override
+	public String foo(String input) {
+		return "A" + input;
+	}
+
+	@Override
+	public int getCount() {
+		return 0;
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pA/pAA/F.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pA/pAA/F.java
new file mode 100644
index 0000000..f8d4d27
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pA/pAA/F.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.test.tb18.pA.pAA;
+
+import org.apache.aries.cdi.test.interfaces.Pojo;
+import org.osgi.service.cdi.annotations.Service;
+
+@Service
+public class F implements Pojo {
+
+	@Override
+	public String foo(String input) {
+		return "A" + input;
+	}
+
+	@Override
+	public int getCount() {
+		return 0;
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pB/C.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pB/C.java
new file mode 100644
index 0000000..598a0d8
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pB/C.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.test.tb18.pB;
+
+import org.apache.aries.cdi.test.interfaces.Pojo;
+import org.osgi.service.cdi.annotations.Service;
+
+@Service
+public class C implements Pojo {
+
+	@Override
+	public String foo(String input) {
+		return "A" + input;
+	}
+
+	@Override
+	public int getCount() {
+		return 0;
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pB/D.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pB/D.java
new file mode 100644
index 0000000..ca071a9
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pB/D.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.test.tb18.pB;
+
+import org.apache.aries.cdi.test.interfaces.Pojo;
+import org.osgi.service.cdi.annotations.Service;
+
+@Service
+public class D implements Pojo {
+
+	@Override
+	public String foo(String input) {
+		return "A" + input;
+	}
+
+	@Override
+	public int getCount() {
+		return 0;
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pB/pBB/E.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pB/pBB/E.java
new file mode 100644
index 0000000..d6b82cd
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pB/pBB/E.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.test.tb18.pB.pBB;
+
+import org.apache.aries.cdi.test.interfaces.Pojo;
+import org.osgi.service.cdi.annotations.Service;
+
+@Service
+public class E implements Pojo {
+
+	@Override
+	public String foo(String input) {
+		return "A" + input;
+	}
+
+	@Override
+	public int getCount() {
+		return 0;
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pB/pBB/F.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pB/pBB/F.java
new file mode 100644
index 0000000..1622925
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb18/pB/pBB/F.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.test.tb18.pB.pBB;
+
+import org.apache.aries.cdi.test.interfaces.Pojo;
+import org.osgi.service.cdi.annotations.Service;
+
+@Service
+public class F implements Pojo {
+
+	@Override
+	public String foo(String input) {
+		return "A" + input;
+	}
+
+	@Override
+	public int getCount() {
+		return 0;
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb19/A.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb19/A.java
new file mode 100644
index 0000000..fa75bbe
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb19/A.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.test.tb19;
+
+import org.apache.aries.cdi.test.interfaces.Pojo;
+import org.osgi.service.cdi.annotations.Service;
+
+@Service
+public class A implements Pojo {
+
+	@Override
+	public String foo(String input) {
+		return "A" + input;
+	}
+
+	@Override
+	public int getCount() {
+		return 0;
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb19/B.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb19/B.java
new file mode 100644
index 0000000..71af86b
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb19/B.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.test.tb19;
+
+import org.apache.aries.cdi.test.interfaces.Pojo;
+import org.osgi.service.cdi.annotations.Service;
+
+@Service
+public class B implements Pojo {
+
+	@Override
+	public String foo(String input) {
+		return "B" + input;
+	}
+
+	@Override
+	public int getCount() {
+		return 0;
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb19/C.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb19/C.java
new file mode 100644
index 0000000..91236f6
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb19/C.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.test.tb19;
+
+import org.apache.aries.cdi.test.interfaces.Pojo;
+import org.osgi.service.cdi.annotations.Service;
+
+@Service
+public class C implements Pojo {
+
+	@Override
+	public String foo(String input) {
+		return "B" + input;
+	}
+
+	@Override
+	public int getCount() {
+		return 0;
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb19/D.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb19/D.java
new file mode 100644
index 0000000..11106ee
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb19/D.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.test.tb19;
+
+import org.apache.aries.cdi.test.interfaces.Pojo;
+import org.osgi.service.cdi.annotations.Service;
+
+@Service
+public class D implements Pojo {
+
+	@Override
+	public String foo(String input) {
+		return "B" + input;
+	}
+
+	@Override
+	public int getCount() {
+		return 0;
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb20/A.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb20/A.java
new file mode 100644
index 0000000..2dd932e
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb20/A.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.test.tb20;
+
+import org.apache.aries.cdi.test.interfaces.Pojo;
+import org.osgi.service.cdi.annotations.Service;
+
+@Service
+public class A implements Pojo {
+
+	@Override
+	public String foo(String input) {
+		return "A" + input;
+	}
+
+	@Override
+	public int getCount() {
+		return 0;
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb20/B.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb20/B.java
new file mode 100644
index 0000000..af260a7
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb20/B.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.test.tb20;
+
+import org.apache.aries.cdi.test.interfaces.Pojo;
+import org.osgi.service.cdi.annotations.Service;
+
+@Service
+public class B implements Pojo {
+
+	@Override
+	public String foo(String input) {
+		return "B" + input;
+	}
+
+	@Override
+	public int getCount() {
+		return 0;
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb20/C.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb20/C.java
new file mode 100644
index 0000000..8cf6bfd
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb20/C.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.test.tb20;
+
+import org.apache.aries.cdi.test.interfaces.Pojo;
+import org.osgi.service.cdi.annotations.Service;
+
+@Service
+public class C implements Pojo {
+
+	@Override
+	public String foo(String input) {
+		return "B" + input;
+	}
+
+	@Override
+	public int getCount() {
+		return 0;
+	}
+
+}
diff --git a/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb20/D.java b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb20/D.java
new file mode 100644
index 0000000..f9bdd20
--- /dev/null
+++ b/cdi-itests/src/main/java/org/apache/aries/cdi/test/tb20/D.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.test.tb20;
+
+import org.apache.aries.cdi.test.interfaces.Pojo;
+import org.osgi.service.cdi.annotations.Service;
+
+@Service
+public class D implements Pojo {
+
+	@Override
+	public String foo(String input) {
+		return "B" + input;
+	}
+
+	@Override
+	public int getCount() {
+		return 0;
+	}
+
+}