You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by jb...@apache.org on 2020/09/18 07:17:12 UTC

[karaf-winegrower] branch master updated: First skeleton for requirements support + OSGi-CDI cepage (#4)

This is an automated email from the ASF dual-hosted git repository.

jbonofre pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/karaf-winegrower.git


The following commit(s) were added to refs/heads/master by this push:
     new b49ed9d  First skeleton for requirements support + OSGi-CDI cepage (#4)
b49ed9d is described below

commit b49ed9d4eec6d4005abc8e02265a282f6a8e3270
Author: Romain Manni-Bucau <rm...@gmail.com>
AuthorDate: Fri Sep 18 09:14:44 2020 +0200

    First skeleton for requirements support + OSGi-CDI cepage (#4)
---
 winegrower-cepages/pom.xml                         |   1 +
 winegrower-cepages/winegrower-cepage-http/pom.xml  |   1 -
 .../winegrower-cepage-osgi-cdi/pom.xml             | 107 ++++++
 .../manifest/OSGiCDIManifestContributor.java       |  91 ++++++
 ...winegrower.scanner.manifest.ManifestContributor |   1 +
 .../winegrower/cepage/osgicdi/OSGiCDITest.java     |  59 ++++
 .../src/test/resources/META-INF/beans.xml          |  25 ++
 .../winegrower/deployer/BundleCapabilityImpl.java  |  59 ++++
 .../org/apache/winegrower/deployer/BundleImpl.java | 358 +++++++++++++++++++--
 .../winegrower/deployer/BundleRequirementImpl.java |  73 +++++
 .../winegrower/deployer/BundleRevisionImpl.java    |   6 +-
 .../apache/winegrower/deployer/BundleWireImpl.java |  70 ++++
 .../winegrower/deployer/BundleWiringImpl.java      |  49 ++-
 .../winegrower/deployer/OSGiBundleLifecycle.java   |   2 +-
 .../apache/winegrower/scanner/KnownJarsFilter.java |   1 +
 .../java/org/apache/winegrower/RipenerTest.java    |   3 +-
 .../apache/winegrower/deployer/BundleImplTest.java |   4 +-
 .../winegrower/service/ServiceListenerTest.java    |   2 -
 .../test/listener/filter/Registrator.java          |   2 -
 .../osgi-cdi}/pom.xml                              |  51 +--
 .../winegrower/examples/osgicdi/HelloOsgiCdi.java  |  25 ++
 winegrower-examples/pom.xml                        |   5 +-
 22 files changed, 930 insertions(+), 65 deletions(-)

diff --git a/winegrower-cepages/pom.xml b/winegrower-cepages/pom.xml
index a6916f0..a10d6a1 100644
--- a/winegrower-cepages/pom.xml
+++ b/winegrower-cepages/pom.xml
@@ -45,6 +45,7 @@
     <module>winegrower-cepage-http</module>
     <module>winegrower-cepage-cxf-rs</module>
     <module>winegrower-cepage-jaxrs</module>
+    <module>winegrower-cepage-osgi-cdi</module>
   </modules>
 
   <build>
diff --git a/winegrower-cepages/winegrower-cepage-http/pom.xml b/winegrower-cepages/winegrower-cepage-http/pom.xml
index 97471ca..75755db 100644
--- a/winegrower-cepages/winegrower-cepage-http/pom.xml
+++ b/winegrower-cepages/winegrower-cepage-http/pom.xml
@@ -91,5 +91,4 @@
       <version>${karaf.version}</version>
     </dependency>
   </dependencies>
-
 </project>
diff --git a/winegrower-cepages/winegrower-cepage-osgi-cdi/pom.xml b/winegrower-cepages/winegrower-cepage-osgi-cdi/pom.xml
new file mode 100644
index 0000000..4f948a3
--- /dev/null
+++ b/winegrower-cepages/winegrower-cepage-osgi-cdi/pom.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<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/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.winegrower.cepages</groupId>
+    <artifactId>winegrower-cepages</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>winegrower-cepage-osgi-cdi</artifactId>
+  <name>Apache Winegrower :: Cepages :: OSGi CDI</name>
+
+  <properties>
+    <aries-cdi.version>1.1.0</aries-cdi.version>
+    <owb.version>2.0.14</owb.version>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.winegrower</groupId>
+      <artifactId>winegrower-core</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.aries.cdi</groupId>
+      <artifactId>org.apache.aries.cdi.extender</artifactId>
+      <version>${aries-cdi.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.aries.cdi</groupId>
+      <artifactId>org.apache.aries.cdi.extension.servlet.owb</artifactId>
+      <version>${aries-cdi.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.aries.cdi</groupId>
+      <artifactId>org.apache.aries.cdi.owb</artifactId>
+      <version>${aries-cdi.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.openwebbeans</groupId>
+      <artifactId>openwebbeans-spi</artifactId>
+      <version>${owb.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.openwebbeans</groupId>
+      <artifactId>openwebbeans-impl</artifactId>
+      <version>${owb.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.openwebbeans</groupId>
+      <artifactId>openwebbeans-web</artifactId>
+      <version>${owb.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.openwebbeans</groupId>
+      <artifactId>openwebbeans-el22</artifactId>
+      <version>${owb.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.aries.spifly</groupId>
+      <artifactId>org.apache.aries.spifly.dynamic.bundle</artifactId>
+      <version>1.2.3</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-el_2.2_spec</artifactId>
+      <version>1.1</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.winegrower</groupId>
+      <artifactId>winegrower-testing-junit5</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-simple</artifactId>
+      <version>${slf4j.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/winegrower-cepages/winegrower-cepage-osgi-cdi/src/main/java/org/apache/winegrower/scanner/manifest/OSGiCDIManifestContributor.java b/winegrower-cepages/winegrower-cepage-osgi-cdi/src/main/java/org/apache/winegrower/scanner/manifest/OSGiCDIManifestContributor.java
new file mode 100644
index 0000000..0e5501a
--- /dev/null
+++ b/winegrower-cepages/winegrower-cepage-osgi-cdi/src/main/java/org/apache/winegrower/scanner/manifest/OSGiCDIManifestContributor.java
@@ -0,0 +1,91 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.winegrower.scanner.manifest;
+
+import static java.util.Optional.ofNullable;
+import static java.util.stream.Collectors.joining;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.file.Files;
+import java.util.Objects;
+import java.util.function.Supplier;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import java.util.stream.Stream;
+
+import org.apache.xbean.finder.AnnotationFinder;
+import org.apache.xbean.finder.archive.Archive;
+import org.apache.xbean.finder.archive.FileArchive;
+import org.osgi.framework.Constants;
+
+public class OSGiCDIManifestContributor implements ManifestContributor {
+    @Override
+    public void contribute(final AnnotationFinder finder, final Supplier<Manifest> manifest) {
+        final Archive archive = finder.getArchive();
+        if (!FileArchive.class.isInstance(archive) || !hasBeansXml(FileArchive.class.cast(archive))) {
+            return; // already a jar, its manifest is likely already good
+        }
+
+        final Attributes attributes = manifest.get().getMainAttributes();
+        final String newRequireCapability = toCapability(finder);
+        final String existing = attributes.getValue(Constants.REQUIRE_CAPABILITY);
+        final String ariesCdiExtensions = findCdiExtensions(finder);
+        attributes.putValue(Constants.REQUIRE_CAPABILITY, Stream.of(existing, newRequireCapability, ariesCdiExtensions)
+            .filter(Objects::nonNull)
+            .filter(it -> !it.isEmpty())
+            .collect(joining(",")));
+    }
+
+    // todo: drop and handle @Requirement transitively in core
+    private String findCdiExtensions(final AnnotationFinder finder) {
+        try {
+            final ClassLoader loader = ofNullable(Thread.currentThread().getContextClassLoader())
+                    .orElseGet(ClassLoader::getSystemClassLoader);
+            final Class<? extends Annotation> singular = (Class<? extends Annotation>)
+                    loader.loadClass("org.apache.aries.cdi.extra.RequireCDIExtension");
+            final Class<? extends Annotation> plural = (Class<? extends Annotation>)
+                    loader.loadClass("org.apache.aries.cdi.extra.RequireCDIExtensions");
+            final Method value = singular.getMethod("value");
+            return Stream.concat(
+                    finder.findAnnotatedClasses(plural).stream(),
+                    finder.findAnnotatedClasses(singular).stream())
+                    .distinct()
+                    .flatMap(c -> Stream.of(c.getAnnotationsByType(singular)))
+                    .map(rce -> {
+                        try {
+                            final Object extension = value.invoke(rce);
+                            return "osgi.cdi.extension;filter:=\"(osgi.cdi.extension=" + extension + ")\"";
+                        } catch (final IllegalAccessException e) {
+                            throw new IllegalStateException(e);
+                        } catch (final InvocationTargetException e) {
+                            throw new IllegalStateException(e.getTargetException());
+                        }
+                    })
+                    .collect(joining(","));
+        } catch (final Exception cnfe) {
+            return null;
+        }
+    }
+
+    private String toCapability(final AnnotationFinder finder) {
+        return "osgi.extender;filter:=\"(osgi.extender=osgi.cdi)\";beans:List<String>=\"" +
+                String.join(",", finder.getAnnotatedClassNames()) + "\"";
+    }
+
+    private boolean hasBeansXml(final FileArchive archive) {
+        return Files.exists(archive.getDir().toPath().resolve("META-INF/beans.xml"));
+    }
+}
diff --git a/winegrower-cepages/winegrower-cepage-osgi-cdi/src/main/resources/META-INF/services/org.apache.winegrower.scanner.manifest.ManifestContributor b/winegrower-cepages/winegrower-cepage-osgi-cdi/src/main/resources/META-INF/services/org.apache.winegrower.scanner.manifest.ManifestContributor
new file mode 100644
index 0000000..434c653
--- /dev/null
+++ b/winegrower-cepages/winegrower-cepage-osgi-cdi/src/main/resources/META-INF/services/org.apache.winegrower.scanner.manifest.ManifestContributor
@@ -0,0 +1 @@
+org.apache.winegrower.scanner.manifest.OSGiCDIManifestContributor
diff --git a/winegrower-cepages/winegrower-cepage-osgi-cdi/src/test/java/org/apache/winegrower/cepage/osgicdi/OSGiCDITest.java b/winegrower-cepages/winegrower-cepage-osgi-cdi/src/test/java/org/apache/winegrower/cepage/osgicdi/OSGiCDITest.java
new file mode 100644
index 0000000..017efa5
--- /dev/null
+++ b/winegrower-cepages/winegrower-cepage-osgi-cdi/src/test/java/org/apache/winegrower/cepage/osgicdi/OSGiCDITest.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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.winegrower.cepage.osgicdi;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.winegrower.Ripener;
+import org.apache.winegrower.api.InjectedService;
+import org.apache.winegrower.deployer.OSGiBundleLifecycle;
+import org.apache.winegrower.extension.testing.junit5.Winegrower;
+import org.junit.jupiter.api.Test;
+import org.osgi.service.cdi.runtime.CDIComponentRuntime;
+import org.osgi.service.cdi.runtime.dto.ContainerDTO;
+
+@Winegrower
+class OSGiCDITest {
+    @InjectedService
+    private Ripener ripener;
+
+    @InjectedService
+    private CDIComponentRuntime ccr;
+
+    @Test
+    void test() {
+        assertNotNull(ccr);
+
+        final Map<Long, OSGiBundleLifecycle> bundles = ripener.getRegistry().getBundles();
+        final long id = bundles.entrySet().stream()
+                .filter(e -> e.getValue().getBundle().getLocation().endsWith("test-classes"))
+                .findFirst()
+                .map(Map.Entry::getKey)
+                .orElseGet(() -> fail("no test-classes bundle"));
+
+        final Collection<ContainerDTO> containerDTOs = ccr.getContainerDTOs(bundles.get(id).getBundle());
+        assertNotNull(containerDTOs);
+        assertEquals(1, containerDTOs.size());
+
+        final ContainerDTO dto = containerDTOs.iterator().next();
+        assertNotNull(dto);
+        assertTrue(dto.errors.isEmpty(), () -> dto.errors.toString());
+    }
+}
diff --git a/winegrower-cepages/winegrower-cepage-osgi-cdi/src/test/resources/META-INF/beans.xml b/winegrower-cepages/winegrower-cepage-osgi-cdi/src/test/resources/META-INF/beans.xml
new file mode 100644
index 0000000..1d9dae2
--- /dev/null
+++ b/winegrower-cepages/winegrower-cepage-osgi-cdi/src/test/resources/META-INF/beans.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<beans bean-discovery-mode="all" version="2.0"
+       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">
+  <trim/>
+</beans>
diff --git a/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleCapabilityImpl.java b/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleCapabilityImpl.java
new file mode 100644
index 0000000..a00b1d5
--- /dev/null
+++ b/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleCapabilityImpl.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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.winegrower.deployer;
+
+import java.util.Map;
+
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRevision;
+
+public class BundleCapabilityImpl implements BundleCapability {
+    private final BundleRevision revision;
+    private final String path;
+    private final Map<String, String> directives;
+    private final Map<String, Object> attributes;
+
+    public BundleCapabilityImpl(final BundleRevision revision, final String path,
+                                final Map<String, String> directives, final Map<String, Object> attributes) {
+        this.revision = revision;
+        this.path = path;
+        this.directives = directives;
+        this.attributes = attributes;
+    }
+
+    @Override
+    public BundleRevision getRevision() {
+        return revision;
+    }
+
+    @Override
+    public String getNamespace() {
+        return path;
+    }
+
+    @Override
+    public Map<String, String> getDirectives() {
+        return directives;
+    }
+
+    @Override
+    public Map<String, Object> getAttributes() {
+        return attributes;
+    }
+
+    @Override
+    public BundleRevision getResource() {
+        return revision;
+    }
+}
diff --git a/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleImpl.java b/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleImpl.java
index e1090cf..2e16b4f 100644
--- a/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleImpl.java
+++ b/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleImpl.java
@@ -33,6 +33,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Dictionary;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
@@ -44,15 +45,19 @@ import java.util.stream.Stream;
 import java.util.zip.ZipEntry;
 
 import org.apache.winegrower.Ripener;
+import org.apache.winegrower.service.BundleRegistry;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleEvent;
 import org.osgi.framework.BundleListener;
 import org.osgi.framework.Constants;
 import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.framework.Version;
+import org.osgi.framework.wiring.BundleRevision;
 import org.osgi.framework.wiring.BundleWiring;
 
 public class BundleImpl implements Bundle {
@@ -65,46 +70,86 @@ public class BundleImpl implements Bundle {
     private final Dictionary<String, String> headers;
     private final File dataFileBase;
     private final Collection<String> includedResources;
+    private final List<BundleRequirementImpl> requirements;
+    private final List<BundleCapabilityImpl> capabilities;
+    private final BundleRegistry registry;
+
+    private volatile BundleRevision bundleRevision;
+    private volatile BundleWiring bundleWiring;
+
     private int state = Bundle.UNINSTALLED;
 
     BundleImpl(final Manifest manifest, final File file, final BundleContextImpl context,
                final Ripener.Configuration configuration, final long id,
-               final Collection<String> includedResources) {
+               final Collection<String> includedResources, final BundleRegistry registry) {
         this.file = file;
         this.dataFileBase = new File(configuration.getWorkDir(),
                 file == null ? Long.toString(System.identityHashCode(manifest)) : file.getName());
         this.context = context;
         this.id = id;
+        this.registry = registry;
         this.loader = Thread.currentThread().getContextClassLoader();
         this.includedResources = includedResources;
         this.version = ofNullable(manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION))
-            .map(Version::new)
-            .orElse(Version.emptyVersion);
+                .map(Version::new)
+                .orElse(Version.emptyVersion);
         this.symbolicName = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
         this.headers = manifest.getMainAttributes().entrySet().stream()
-            .collect(Collector.of(
-                    Hashtable::new,
-                    (t, e) -> t.put(Attributes.Name.class.cast(e.getKey()).toString(), e.getValue().toString()),
-                    (t1, t2) -> {
-                        t1.putAll(t2);
-                        return t1;
-                    }));
+                .collect(Collector.of(
+                        Hashtable::new,
+                        (t, e) -> t.put(Attributes.Name.class.cast(e.getKey()).toString(), e.getValue().toString()),
+                        (t1, t2) -> {
+                            t1.putAll(t2);
+                            return t1;
+                        }));
+
+        final List<HeaderClause> requireClauses = Headers.parse(headers.get(Constants.REQUIRE_CAPABILITY));
+        Headers.coerceCapabilityClauses(requireClauses);
+        requirements = requireClauses.stream()
+                .flatMap(clause -> {
+                    final String filterStr = clause.directives.get(Constants.FILTER_DIRECTIVE);
+                    try {
+                        final Filter filter = filterStr == null ? null : FrameworkUtil.createFilter(filterStr);
+                        return clause.paths.stream()
+                                .map(path -> new BundleRequirementImpl(
+                                        adapt(BundleRevision.class), path, clause.directives, clause.attributes, filter));
+                    } catch (final InvalidSyntaxException e) {
+                        throw new IllegalArgumentException(e);
+                    }
+                })
+                .collect(toList());
+
+        final List<HeaderClause> provideClauses = Headers.parse(headers.get(Constants.PROVIDE_CAPABILITY));
+        Headers.coerceCapabilityClauses(provideClauses);
+        capabilities = provideClauses.stream()
+                .flatMap(clause -> clause.paths.stream()
+                        .map(path -> new BundleCapabilityImpl(
+                                adapt(BundleRevision.class), path, clause.directives, clause.attributes)))
+                .collect(toList());
     }
 
     ClassLoader getLoader() {
         return loader;
     }
 
+    List<BundleRequirementImpl> getRequirements() {
+        return requirements;
+    }
+
+    List<BundleCapabilityImpl> getCapabilities() {
+        return capabilities;
+    }
+
     private Stream<BundleListener> allBundleListeners() {
         return context.getRegistry().getBundles().values().stream()
-                      .flatMap(it -> BundleContextImpl.class.cast(it.getBundle().getBundleContext()).getBundleListeners().stream());
+                .flatMap(it -> BundleContextImpl.class.cast(it.getBundle().getBundleContext()).getBundleListeners().stream());
     }
 
     void onStart() {
         start();
         final BundleEvent event = new BundleEvent(BundleEvent.STARTED, this);
         allBundleListeners()
-               .forEach(listener -> listener.bundleChanged(event));
+                .forEach(listener -> listener.bundleChanged(event));
     }
 
     void onStop() {
@@ -266,9 +311,9 @@ public class BundleImpl implements Bundle {
         if (includedResources != null) {
             if (!recurse) {
                 return enumeration(includedResources.stream()
-                    .filter(it -> doFilterEntry(filter, prefix, it))
-                    .map(loader::getResource)
-                    .collect(toList()));
+                        .filter(it -> doFilterEntry(filter, prefix, it))
+                        .map(loader::getResource)
+                        .collect(toList()));
             }
         }
 
@@ -309,8 +354,8 @@ public class BundleImpl implements Bundle {
         } else {
             try (final JarFile jar = new JarFile(file)) {
                 return enumeration(list(jar.entries()).stream().filter(it -> it.getName().startsWith(prefix))
-                                                      .map(ZipEntry::getName).filter(name -> !name.endsWith("/")) // folders
-                                                      .filter(name -> doFilterEntry(filter, prefix, name)).map(name -> {
+                        .map(ZipEntry::getName).filter(name -> !name.endsWith("/")) // folders
+                        .filter(name -> doFilterEntry(filter, prefix, name)).map(name -> {
                             try {
                                 return new URL("jar", null, file.toURI().toURL().toExternalForm() + "!/" + name);
                             } catch (final MalformedURLException e) {
@@ -354,7 +399,24 @@ public class BundleImpl implements Bundle {
     @Override
     public <A> A adapt(final Class<A> type) {
         if (BundleWiring.class == type) {
-            return type.cast(new BundleWiringImpl(this));
+            if (bundleWiring == null) {
+                synchronized (this) {
+                    if (bundleWiring == null) {
+                        bundleWiring = new BundleWiringImpl(this, registry);
+                    }
+                }
+            }
+            return type.cast(bundleWiring);
+        }
+        if (BundleRevision.class == type) {
+            if (bundleRevision == null) {
+                synchronized (this) {
+                    if (bundleRevision == null) {
+                        bundleRevision = new BundleRevisionImpl(this);
+                    }
+                }
+            }
+            return type.cast(bundleRevision);
         }
         return null;
     }
@@ -375,4 +437,264 @@ public class BundleImpl implements Bundle {
     public String toString() {
         return "BundleImpl{file=" + file + ", id=" + id + '}';
     }
+
+    public static class HeaderClause {
+        public final List<String> paths;
+        public final Map<String, String> directives;
+        public final Map<String, Object> attributes;
+        public final Map<String, String> types;
+
+        public HeaderClause(
+                final List<String> paths, final Map<String, String> dirs, final Map<String, Object> attrs,
+                final Map<String, String> types) {
+            this.paths = paths;
+            this.directives = dirs;
+            this.attributes = attrs;
+            this.types = types;
+        }
+    }
+
+    // taken from felix
+    private static class Headers {
+        private static final char EOF = (char) -1;
+        private static final int CLAUSE_START = 0;
+        private static final int PARAMETER_START = 1;
+        private static final int KEY = 2;
+        private static final int DIRECTIVE_OR_TYPEDATTRIBUTE = 4;
+        private static final int ARGUMENT = 8;
+        private static final int VALUE = 16;
+
+        private static List<HeaderClause> parse(final String header) {
+            final List<HeaderClause> clauses = new ArrayList<>();
+            if (header == null) {
+                return clauses;
+            }
+            HeaderClause clause = null;
+            String key = null;
+            Map targetMap = null;
+            int state = CLAUSE_START;
+            int currentPosition = 0;
+            int startPosition = 0;
+            int length = header.length();
+            boolean quoted = false;
+            boolean escaped = false;
+
+            char currentChar;
+            do {
+                currentChar = currentPosition >= length ? EOF : header.charAt(currentPosition);
+                switch (state) {
+                    case CLAUSE_START:
+                        clause = new HeaderClause(new ArrayList<>(), new HashMap<>(), new HashMap<>(), new HashMap<>());
+                        clauses.add(clause);
+                        // not needed to be called but "logically" needed: state = PARAMETER_START;
+                    case PARAMETER_START:
+                        startPosition = currentPosition;
+                        state = KEY;
+                    case KEY:
+                        switch (currentChar) {
+                            case ':':
+                            case '=':
+                                key = header.substring(startPosition, currentPosition).trim();
+                                startPosition = currentPosition + 1;
+                                targetMap = clause.attributes;
+                                state = currentChar == ':' ? DIRECTIVE_OR_TYPEDATTRIBUTE : ARGUMENT;
+                                break;
+                            case EOF:
+                            case ',':
+                            case ';':
+                                clause.paths.add(header.substring(startPosition, currentPosition).trim());
+                                state = currentChar == ',' ? CLAUSE_START : PARAMETER_START;
+                                break;
+                            default:
+                                break;
+                        }
+                        currentPosition++;
+                        break;
+                    case DIRECTIVE_OR_TYPEDATTRIBUTE:
+                        if (currentChar == '=') {
+                            if (startPosition != currentPosition) {
+                                clause.types.put(key, header.substring(startPosition, currentPosition).trim());
+                            } else {
+                                targetMap = clause.directives;
+                            }
+                            state = ARGUMENT;
+                            startPosition = currentPosition + 1;
+                        }
+                        currentPosition++;
+                        break;
+                    case ARGUMENT:
+                        if (currentChar == '\"') {
+                            quoted = true;
+                            currentPosition++;
+                        } else {
+                            quoted = false;
+                        }
+                        if (!Character.isWhitespace(currentChar)) {
+                            state = VALUE;
+                        } else {
+                            currentPosition++;
+                        }
+                        break;
+                    case VALUE:
+                        if (escaped) {
+                            escaped = false;
+                        } else {
+                            if (currentChar == '\\') {
+                                escaped = true;
+                            } else if (quoted && currentChar == '\"') {
+                                quoted = false;
+                            } else if (!quoted) {
+                                String value;
+                                switch (currentChar) {
+                                    case EOF:
+                                    case ';':
+                                    case ',':
+                                        value = header.substring(startPosition, currentPosition).trim();
+                                        if (value.startsWith("\"") && value.endsWith("\"")) {
+                                            value = value.substring(1, value.length() - 1);
+                                        }
+                                        if (targetMap.put(key, value) != null) {
+                                            throw new IllegalArgumentException(
+                                                    "Duplicate '" + key + "' in: " + header);
+                                        }
+                                        state = currentChar == ';' ? PARAMETER_START : CLAUSE_START;
+                                        break;
+                                    default:
+                                        break;
+                                }
+                            }
+                        }
+                        currentPosition++;
+                        break;
+                    default:
+                        break;
+                }
+            } while (currentChar != EOF);
+
+            if (state > PARAMETER_START) {
+                throw new IllegalArgumentException("Unable to parse header: " + header);
+            }
+            return clauses;
+        }
+
+        public static List<String> parseDelimitedString(String value, String delim, boolean trim) {
+            if (value == null) {
+                value = "";
+            }
+
+            List<String> list = new ArrayList<String>();
+
+            int CHAR = 1;
+            int DELIMITER = 2;
+            int STARTQUOTE = 4;
+            int ENDQUOTE = 8;
+
+            StringBuilder sb = new StringBuilder();
+
+            int expecting = (CHAR | DELIMITER | STARTQUOTE);
+
+            boolean isEscaped = false;
+            for (int i = 0; i < value.length(); i++) {
+                char c = value.charAt(i);
+
+                boolean isDelimiter = (delim.indexOf(c) >= 0);
+
+                if (!isEscaped && (c == '\\')) {
+                    isEscaped = true;
+                    continue;
+                }
+
+                if (isEscaped) {
+                    sb.append(c);
+                } else if (isDelimiter && ((expecting & DELIMITER) > 0)) {
+                    if (trim) {
+                        list.add(sb.toString().trim());
+                    } else {
+                        list.add(sb.toString());
+                    }
+                    sb.delete(0, sb.length());
+                    expecting = (CHAR | DELIMITER | STARTQUOTE);
+                } else if ((c == '"') && ((expecting & STARTQUOTE) > 0)) {
+                    sb.append(c);
+                    expecting = CHAR | ENDQUOTE;
+                } else if ((c == '"') && ((expecting & ENDQUOTE) > 0)) {
+                    sb.append(c);
+                    expecting = (CHAR | STARTQUOTE | DELIMITER);
+                } else if ((expecting & CHAR) > 0) {
+                    sb.append(c);
+                } else {
+                    throw new IllegalArgumentException("Invalid delimited string: " + value);
+                }
+
+                isEscaped = false;
+            }
+
+            if (sb.length() > 0) {
+                if (trim) {
+                    list.add(sb.toString().trim());
+                } else {
+                    list.add(sb.toString());
+                }
+            }
+
+            return list;
+        }
+
+        private static void coerceCapabilityClauses(final List<HeaderClause> clauses) {
+            clauses.forEach(clause -> clause.types.forEach((key, type) -> {
+                if (!type.equals("String")) {
+                    if (type.equals("Double")) {
+                        clause.attributes.put(
+                                key,
+                                new Double(clause.attributes.get(key).toString().trim()));
+                    } else if (type.equals("Version")) {
+                        clause.attributes.put(
+                                key,
+                                new Version(clause.attributes.get(key).toString().trim()));
+                    } else if (type.equals("Long")) {
+                        clause.attributes.put(
+                                key,
+                                Long.valueOf(clause.attributes.get(key).toString().trim()));
+                    } else if (type.startsWith("List")) {
+                        final int startIdx = type.indexOf('<');
+                        final int endIdx = type.indexOf('>');
+                        if ((startIdx > 0 && endIdx <= startIdx) || (startIdx < 0 && endIdx > 0)) {
+                            throw new IllegalArgumentException("Invalid Provide-Capability attribute list type for '"
+                                    + key + "' : " + type);
+                        }
+
+                        final String listType;
+                        if (endIdx > startIdx) {
+                            listType = type.substring(startIdx + 1, endIdx).trim();
+                        } else {
+                            listType = "String";
+                        }
+
+                        final List<String> tokens = parseDelimitedString(
+                                clause.attributes.get(key).toString(), ",", false);
+                        clause.attributes.put(key, tokens.stream()
+                                .map(token -> {
+                                    switch (listType) {
+                                        case "String":
+                                            return token;
+                                        case "Double":
+                                            return Double.valueOf(token.trim());
+                                        case "Version":
+                                            return new Version(token.trim());
+                                        case "Long":
+                                            return Long.valueOf(token.trim());
+                                        default:
+                                            throw new IllegalArgumentException(
+                                                    "Unknown Provide-Capability attribute list type for '"
+                                                            + key + "' : " + type);
+                                    }
+                                }).collect(toList()));
+                    } else {
+                        throw new IllegalArgumentException("Unknown Provide-Capability attribute type for '" +
+                                key + "' : " + type);
+                    }
+                }
+            }));
+        }
+    }
 }
diff --git a/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleRequirementImpl.java b/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleRequirementImpl.java
new file mode 100644
index 0000000..1339b24
--- /dev/null
+++ b/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleRequirementImpl.java
@@ -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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.winegrower.deployer;
+
+import java.util.Map;
+
+import org.osgi.framework.Filter;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+
+public class BundleRequirementImpl implements BundleRequirement {
+    private final BundleRevision revision;
+    private final String path;
+    private final Map<String, String> directives;
+    private final Map<String, Object> attributes;
+    private final Filter filter;
+
+    public BundleRequirementImpl(final BundleRevision revision, final String path,
+                                 final Map<String, String> directives, final Map<String, Object> attributes,
+                                 final Filter filter) {
+        this.revision = revision;
+        this.path = path;
+        this.directives = directives;
+        this.attributes = attributes;
+        this.filter = filter;
+    }
+
+    @Override
+    public boolean matches(final BundleCapability capability) {
+        return filter == null || filter.matches(capability.getAttributes());
+    }
+
+    @Override
+    public String getNamespace() {
+        return path;
+    }
+
+    @Override
+    public Map<String, String> getDirectives() {
+        return directives;
+    }
+
+    @Override
+    public Map<String, Object> getAttributes() {
+        return attributes;
+    }
+
+    @Override
+    public BundleRevision getRevision() {
+        return revision;
+    }
+
+    @Override
+    public BundleRevision getResource() {
+        return revision;
+    }
+
+    Filter getFilter() {
+        return filter;
+    }
+}
diff --git a/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleRevisionImpl.java b/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleRevisionImpl.java
index 2e31e66..62d0322 100644
--- a/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleRevisionImpl.java
+++ b/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleRevisionImpl.java
@@ -28,11 +28,9 @@ import org.osgi.resource.Requirement;
 
 public class BundleRevisionImpl implements BundleRevision {
     private final BundleImpl bundle;
-    private final BundleWiringImpl wiring;
 
-    BundleRevisionImpl(final BundleImpl bundle, final BundleWiringImpl bundleWiring) {
+    BundleRevisionImpl(final BundleImpl bundle) {
         this.bundle = bundle;
-        this.wiring = bundleWiring;
     }
 
     @Override
@@ -62,7 +60,7 @@ public class BundleRevisionImpl implements BundleRevision {
 
     @Override
     public BundleWiring getWiring() {
-        return wiring;
+        return bundle.adapt(BundleWiring.class);
     }
 
     @Override
diff --git a/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleWireImpl.java b/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleWireImpl.java
new file mode 100644
index 0000000..5ba9791
--- /dev/null
+++ b/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleWireImpl.java
@@ -0,0 +1,70 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.winegrower.deployer;
+
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
+
+public class BundleWireImpl implements BundleWire {
+    private final BundleRevision requirer;
+    private final BundleRevision provider;
+    private final BundleWiring requirerWiring;
+    private final BundleWiring providerWiring;
+    private final BundleRequirement requirement;
+    private final BundleCapability capability;
+
+    public BundleWireImpl(final BundleRevision requirer, final BundleRevision provider,
+                          final BundleWiring requirerWiring, final BundleWiring providerWiring,
+                          final BundleRequirement requirement, final BundleCapability capability) {
+        this.requirer = requirer;
+        this.provider = provider;
+        this.requirerWiring = requirerWiring;
+        this.providerWiring = providerWiring;
+        this.requirement = requirement;
+        this.capability = capability;
+    }
+
+    @Override
+    public BundleCapability getCapability() {
+        return capability;
+    }
+
+    @Override
+    public BundleRequirement getRequirement() {
+        return requirement;
+    }
+
+    @Override
+    public BundleWiring getProviderWiring() {
+        return providerWiring;
+    }
+
+    @Override
+    public BundleWiring getRequirerWiring() {
+        return requirerWiring;
+    }
+
+    @Override
+    public BundleRevision getProvider() {
+        return provider;
+    }
+
+    @Override
+    public BundleRevision getRequirer() {
+        return requirer;
+    }
+}
diff --git a/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleWiringImpl.java b/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleWiringImpl.java
index 0d6d010..598efa8 100644
--- a/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleWiringImpl.java
+++ b/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleWiringImpl.java
@@ -21,7 +21,10 @@ import java.net.URL;
 import java.nio.file.Paths;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
 
+import org.apache.winegrower.service.BundleRegistry;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.wiring.BundleCapability;
 import org.osgi.framework.wiring.BundleRequirement;
@@ -34,11 +37,11 @@ import org.osgi.resource.Wire;
 
 public class BundleWiringImpl implements BundleWiring {
     private final BundleImpl bundle;
-    private final BundleRevision revision;
+    private final BundleRegistry registry;
 
-    BundleWiringImpl(final BundleImpl bundle) {
+    BundleWiringImpl(final BundleImpl bundle, final BundleRegistry registry) {
         this.bundle = bundle;
-        this.revision = new BundleRevisionImpl(bundle, this);
+        this.registry = registry;
     }
 
     @Override
@@ -53,12 +56,22 @@ public class BundleWiringImpl implements BundleWiring {
 
     @Override
     public List<BundleCapability> getCapabilities(final String namespace) {
-        return emptyList();
+        return bundle.getCapabilities().stream()
+                .filter(c -> Objects.equals(c.getNamespace(), namespace))
+                .map(capability -> new BundleCapabilityImpl(
+                        bundle.adapt(BundleRevision.class), capability.getNamespace(),
+                        capability.getDirectives(), capability.getAttributes()))
+                .collect(toList());
     }
 
     @Override
     public List<BundleRequirement> getRequirements(final String namespace) {
-        return emptyList();
+        return bundle.getRequirements().stream()
+                .filter(r -> Objects.equals(r.getNamespace(), namespace))
+                .map(requirement -> new BundleRequirementImpl(
+                        bundle.adapt(BundleRevision.class), requirement.getNamespace(),
+                        requirement.getDirectives(), requirement.getAttributes(), requirement.getFilter()))
+                .collect(toList());
     }
 
     @Override
@@ -68,12 +81,15 @@ public class BundleWiringImpl implements BundleWiring {
 
     @Override
     public List<BundleWire> getRequiredWires(final String namespace) {
-        return emptyList();
+        return bundle.getRequirements().stream()
+                .map(requirement -> toWire(requirement, registry.getBundles()))
+                .filter(Objects::nonNull)
+                .collect(toList());
     }
 
     @Override
     public BundleRevision getRevision() {
-        return revision;
+        return bundle.adapt(BundleRevision.class);
     }
 
     @Override
@@ -125,11 +141,28 @@ public class BundleWiringImpl implements BundleWiring {
 
     @Override
     public BundleRevision getResource() {
-        return revision;
+        return bundle.adapt(BundleRevision.class);
     }
 
     @Override
     public Bundle getBundle() {
         return bundle;
     }
+
+    private BundleWire toWire(final BundleRequirementImpl requirement, final Map<Long, OSGiBundleLifecycle> bundles) {
+        return bundles.entrySet().stream()
+                .sorted(Map.Entry.comparingByKey())
+                .filter(e -> e.getValue().getBundle().getCapabilities().stream()
+                        .anyMatch(requirement::matches))
+                .findFirst()
+                .map(e -> e.getValue().getBundle())
+                .map(bundle -> new BundleWireImpl(
+                        this.bundle.adapt(BundleRevision.class), bundle.adapt(BundleRevision.class),
+                        this.bundle.adapt(BundleWiring.class), bundle.adapt(BundleWiring.class), requirement,
+                        bundle.getCapabilities().stream()
+                                .filter(requirement::matches).findFirst()
+                                .orElseThrow(() -> new IllegalArgumentException(
+                                        "Missing capability " + requirement + " in " + bundle))))
+                .orElse(null);
+    }
 }
diff --git a/winegrower-core/src/main/java/org/apache/winegrower/deployer/OSGiBundleLifecycle.java b/winegrower-core/src/main/java/org/apache/winegrower/deployer/OSGiBundleLifecycle.java
index 82efc3c..910cd3e 100644
--- a/winegrower-core/src/main/java/org/apache/winegrower/deployer/OSGiBundleLifecycle.java
+++ b/winegrower-core/src/main/java/org/apache/winegrower/deployer/OSGiBundleLifecycle.java
@@ -37,7 +37,7 @@ public class OSGiBundleLifecycle {
                                final BundleRegistry registry, final Ripener.Configuration configuration,
                                final long id, final Collection<String> includedResources) {
         this.context = new BundleContextImpl(manifest, services, this::getBundle, registry);
-        this.bundle = new BundleImpl(manifest, file, context, configuration, id, includedResources);
+        this.bundle = new BundleImpl(manifest, file, context, configuration, id, includedResources, registry);
     }
 
     public BundleActivatorHandler getActivator() {
diff --git a/winegrower-core/src/main/java/org/apache/winegrower/scanner/KnownJarsFilter.java b/winegrower-core/src/main/java/org/apache/winegrower/scanner/KnownJarsFilter.java
index 2a92ff0..13bbb61 100644
--- a/winegrower-core/src/main/java/org/apache/winegrower/scanner/KnownJarsFilter.java
+++ b/winegrower-core/src/main/java/org/apache/winegrower/scanner/KnownJarsFilter.java
@@ -25,6 +25,7 @@ import org.apache.winegrower.Ripener;
 public class KnownJarsFilter implements Predicate<String> {
     private final Collection<String> forceIncludes = new HashSet<String>() {{
         add("winegrower-cdi");
+        add("org.apache.aries.cdi");
     }};
     private final Collection<String> excludes = new HashSet<String>() {{
         add("activation-");
diff --git a/winegrower-core/src/test/java/org/apache/winegrower/RipenerTest.java b/winegrower-core/src/test/java/org/apache/winegrower/RipenerTest.java
index 6e09724..623b537 100644
--- a/winegrower-core/src/test/java/org/apache/winegrower/RipenerTest.java
+++ b/winegrower-core/src/test/java/org/apache/winegrower/RipenerTest.java
@@ -110,7 +110,8 @@ class RipenerTest {
     @Test
     @WithRipener(includeResources = @Entry(path = "org.apache.winegrower.test.simpleactivator"))
     void simpleActivator(@Service final Ripener ripener) {
-        assertEquals(11, ripener.getRegistry().getBundles().size());
+        final Map<Long, OSGiBundleLifecycle> bundles = ripener.getRegistry().getBundles();
+        assertEquals(11 /*includes junit5 with v >= 5.6*/, bundles.size());
 
         final BundleActivatorHandler activatorHandler = ripener.getRegistry().getBundles().values().stream()
                 .filter(it -> it.getActivator() != null)
diff --git a/winegrower-core/src/test/java/org/apache/winegrower/deployer/BundleImplTest.java b/winegrower-core/src/test/java/org/apache/winegrower/deployer/BundleImplTest.java
index c14736b..c1e50e2 100644
--- a/winegrower-core/src/test/java/org/apache/winegrower/deployer/BundleImplTest.java
+++ b/winegrower-core/src/test/java/org/apache/winegrower/deployer/BundleImplTest.java
@@ -53,13 +53,13 @@ class BundleImplTest {
         registry = new BundleRegistry(services, configuration);
         context = new BundleContextImpl(manifest, services, () -> bundle, registry);
         final File file = new File(registry.getFramework().getParentFile(), "test-classes");
-        bundle = new BundleImpl(manifest, file, context, configuration, 1, null);
+        bundle = new BundleImpl(manifest, file, context, configuration, 1, null, null);
         registry.getBundles().put(bundle.getBundleId(), new OSGiBundleLifecycle(manifest, file, services, registry, configuration, 1, null));
     }
 
     @Test
     void noEmptyLocation() {
-        assertNotNull(new BundleImpl(manifest, null, context, configuration, 1, null).getLocation());
+        assertNotNull(new BundleImpl(manifest, null, context, configuration, 1, null, null).getLocation());
     }
 
     @Test
diff --git a/winegrower-core/src/test/java/org/apache/winegrower/service/ServiceListenerTest.java b/winegrower-core/src/test/java/org/apache/winegrower/service/ServiceListenerTest.java
index 4cf1688..09045a3 100644
--- a/winegrower-core/src/test/java/org/apache/winegrower/service/ServiceListenerTest.java
+++ b/winegrower-core/src/test/java/org/apache/winegrower/service/ServiceListenerTest.java
@@ -1,6 +1,4 @@
 /**
- * Copyright (C) 2006-2019 Talend Inc. - www.talend.com
- * <p>
  * 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
diff --git a/winegrower-core/src/test/java/org/apache/winegrower/test/listener/filter/Registrator.java b/winegrower-core/src/test/java/org/apache/winegrower/test/listener/filter/Registrator.java
index 346a04d..a78f304 100644
--- a/winegrower-core/src/test/java/org/apache/winegrower/test/listener/filter/Registrator.java
+++ b/winegrower-core/src/test/java/org/apache/winegrower/test/listener/filter/Registrator.java
@@ -1,6 +1,4 @@
 /**
- * Copyright (C) 2006-2019 Talend Inc. - www.talend.com
- * <p>
  * 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
diff --git a/winegrower-cepages/pom.xml b/winegrower-examples/osgi-cdi/pom.xml
similarity index 56%
copy from winegrower-cepages/pom.xml
copy to winegrower-examples/osgi-cdi/pom.xml
index a6916f0..0711bd2 100644
--- a/winegrower-cepages/pom.xml
+++ b/winegrower-examples/osgi-cdi/pom.xml
@@ -24,39 +24,42 @@
   <modelVersion>4.0.0</modelVersion>
 
   <parent>
-    <groupId>org.apache.winegrower</groupId>
-    <artifactId>winegrower</artifactId>
+    <groupId>org.apache.winegrower.examples</groupId>
+    <artifactId>winegrower-examples</artifactId>
     <version>1.0-SNAPSHOT</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
 
-  <groupId>org.apache.winegrower.cepages</groupId>
-  <artifactId>winegrower-cepages</artifactId>
-  <name>Apache Winegrower :: Cepages</name>
-  <packaging>pom</packaging>
+  <artifactId>osgi-cdi</artifactId>
+  <name>Apache Winegrower :: Examples :: OSGi CDI</name>
 
-  <properties>
-    <karaf.version>4.2.8</karaf.version>
-    <cxf.version>3.4.0</cxf.version>
-  </properties>
-
-  <modules>
-    <module>winegrower-cepage-shell</module>
-    <module>winegrower-cepage-http</module>
-    <module>winegrower-cepage-cxf-rs</module>
-    <module>winegrower-cepage-jaxrs</module>
-  </modules>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.winegrower</groupId>
+      <artifactId>winegrower-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.winegrower.cepages</groupId>
+      <artifactId>winegrower-cepage-osgi-cdi</artifactId>
+      <version>${project.version}</version>
+      <type>pom</type>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.winegrower.cepages</groupId>
+      <artifactId>winegrower-cepage-http</artifactId>
+      <version>${project.version}</version>
+      <type>pom</type>
+    </dependency>
+  </dependencies>
 
   <build>
     <plugins>
       <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <version>3.1.0</version>
-        <configuration>
-          <skipIfEmpty>true</skipIfEmpty>
-        </configuration>
+        <groupId>org.apache.winegrower</groupId>
+        <artifactId>winegrower-maven-plugin</artifactId>
+        <version>${project.version}</version>
       </plugin>
     </plugins>
   </build>
-</project>
\ No newline at end of file
+</project>
diff --git a/winegrower-examples/osgi-cdi/src/main/java/org/apache/winegrower/examples/osgicdi/HelloOsgiCdi.java b/winegrower-examples/osgi-cdi/src/main/java/org/apache/winegrower/examples/osgicdi/HelloOsgiCdi.java
new file mode 100644
index 0000000..217c9fe
--- /dev/null
+++ b/winegrower-examples/osgi-cdi/src/main/java/org/apache/winegrower/examples/osgicdi/HelloOsgiCdi.java
@@ -0,0 +1,25 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.winegrower.examples.osgicdi;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.context.Initialized;
+import javax.enterprise.event.Observes;
+
+@ApplicationScoped
+public class HelloOsgiCdi {
+    public void onStart(@Observes @Initialized(ApplicationScoped.class) final Object start) {
+        System.out.println("Hi");
+    }
+}
diff --git a/winegrower-examples/pom.xml b/winegrower-examples/pom.xml
index 0b94ddc..04cac3a 100644
--- a/winegrower-examples/pom.xml
+++ b/winegrower-examples/pom.xml
@@ -38,13 +38,14 @@
   <modules>
     <module>simple</module>
     <module>bundle</module>
-    <!-- <module>services</module> -->
-    <!-- <module>scr</module> -->
     <module>config</module>
     <module>shell</module>
     <module>http</module>
     <module>jaxrs</module>
     <module>jaxrs-whiteboard</module>
+    <module>osgi-cdi</module>
+    <!-- <module>services</module> -->
+    <!-- <module>scr</module> -->
     <!-- <module>monitoring</module> -->
     <!-- <module>cdi</module> -->
     <!-- <module>spring</module> -->