You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/10/18 23:20:37 UTC

[sling-org-apache-sling-resource-presence] 01/08: SLING-6582 Announce presence of resources as OSGi services

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

rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-resource-presence.git

commit dc8baf4b09b7e510eea1af0d89f77261429e07f0
Author: Oliver Lietz <ol...@apache.org>
AuthorDate: Wed Mar 1 12:55:45 2017 +0000

    SLING-6582 Announce presence of resources as OSGi services
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1784924 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml                                            | 207 +++++++++++++++++++++
 .../sling/resource/presence/ResourcePresence.java  |  28 +++
 .../presence/internal/ResourcePresenter.java       | 148 +++++++++++++++
 .../internal/ResourcePresenterConfiguration.java   |  36 ++++
 .../sling/resource/presence/package-info.java      |  22 +++
 .../presence/ResourcePresenterTestSupport.java     |  69 +++++++
 .../apache/sling/resource/presence/SimpleIT.java   |  71 +++++++
 src/test/resources/exam.properties                 |  19 ++
 src/test/resources/logback.xml                     |  30 +++
 9 files changed, 630 insertions(+)

diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..2451d8a
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,207 @@
+<?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.sling</groupId>
+    <artifactId>sling</artifactId>
+    <version>30-SNAPSHOT</version>
+    <relativePath />
+  </parent>
+
+  <artifactId>org.apache.sling.resource.presence</artifactId>
+  <version>0.0.1-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+
+  <name>Apache Sling Resource Presence</name>
+  <description>Apache Sling Resource Presence</description>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    <sling.java.version>8</sling.java.version>
+    <org.ops4j.pax.exam.version>4.10.0</org.ops4j.pax.exam.version>
+  </properties>
+
+  <scm>
+    <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/org.apache.sling.resource.presence</connection>
+    <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/org.apache.sling.resource.presence</developerConnection>
+    <url>http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/org.apache.sling.resource.presence</url>
+  </scm>
+
+  <dependencies>
+    <!-- javax -->
+    <dependency>
+      <groupId>javax.inject</groupId>
+      <artifactId>javax.inject</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <!-- OSGi -->
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>osgi.annotation</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>osgi.cmpn</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.service.component.annotations</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.service.metatype.annotations</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <!-- Apache Felix -->
+    <dependency>
+      <groupId>org.apache.felix</groupId>
+      <artifactId>org.apache.felix.framework</artifactId>
+      <version>5.6.2</version>
+      <scope>test</scope>
+    </dependency>
+    <!-- Apache Sling -->
+    <dependency>
+      <groupId>org.apache.sling</groupId>
+      <artifactId>org.apache.sling.api</artifactId>
+      <version>2.11.0</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.sling</groupId>
+      <artifactId>org.apache.sling.serviceusermapper</artifactId>
+      <version>1.2.4</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.sling</groupId>
+      <artifactId>org.apache.sling.testing.paxexam</artifactId>
+      <version>0.0.3-SNAPSHOT</version>
+      <scope>provided</scope>
+    </dependency>
+    <!-- jsr305 -->
+    <dependency>
+      <groupId>com.google.code.findbugs</groupId>
+      <artifactId>jsr305</artifactId>
+    </dependency>
+    <!-- logging -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <!-- testing -->
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.servicemix.bundles</groupId>
+      <artifactId>org.apache.servicemix.bundles.hamcrest</artifactId>
+      <version>1.3_1</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.ops4j.pax.exam</groupId>
+      <artifactId>pax-exam</artifactId>
+      <version>${org.ops4j.pax.exam.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.ops4j.pax.exam</groupId>
+      <artifactId>pax-exam-cm</artifactId>
+      <version>${org.ops4j.pax.exam.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.ops4j.pax.exam</groupId>
+      <artifactId>pax-exam-container-forked</artifactId>
+      <version>${org.ops4j.pax.exam.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.ops4j.pax.exam</groupId>
+      <artifactId>pax-exam-junit4</artifactId>
+      <version>${org.ops4j.pax.exam.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.ops4j.pax.exam</groupId>
+      <artifactId>pax-exam-link-mvn</artifactId>
+      <version>${org.ops4j.pax.exam.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>integration-test</goal>
+              <goal>verify</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <redirectTestOutputToFile>true</redirectTestOutputToFile>
+          <systemProperties>
+            <property>
+              <name>bundle.filename</name>
+              <value>${basedir}/target/${project.build.finalName}.jar</value>
+            </property>
+          </systemProperties>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.servicemix.tooling</groupId>
+        <artifactId>depends-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>generate-depends-file</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/src/main/java/org/apache/sling/resource/presence/ResourcePresence.java b/src/main/java/org/apache/sling/resource/presence/ResourcePresence.java
new file mode 100644
index 0000000..71b6a9d
--- /dev/null
+++ b/src/main/java/org/apache/sling/resource/presence/ResourcePresence.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.resource.presence;
+
+import org.osgi.annotation.versioning.ProviderType;
+
+@ProviderType
+public interface ResourcePresence {
+
+    String getPath();
+
+}
diff --git a/src/main/java/org/apache/sling/resource/presence/internal/ResourcePresenter.java b/src/main/java/org/apache/sling/resource/presence/internal/ResourcePresenter.java
new file mode 100644
index 0000000..aa16cd1
--- /dev/null
+++ b/src/main/java/org/apache/sling/resource/presence/internal/ResourcePresenter.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.resource.presence.internal;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.api.resource.observation.ResourceChange;
+import org.apache.sling.api.resource.observation.ResourceChange.ChangeType;
+import org.apache.sling.api.resource.observation.ResourceChangeListener;
+import org.apache.sling.resource.presence.ResourcePresence;
+import org.apache.sling.serviceusermapping.ServiceUserMapped;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.ConfigurationPolicy;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.metatype.annotations.Designate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component(
+    configurationPolicy = ConfigurationPolicy.REQUIRE
+)
+@Designate(
+    ocd = ResourcePresenterConfiguration.class,
+    factory = true
+)
+public class ResourcePresenter {
+
+    private String path;
+
+    private BundleContext bundleContext;
+
+    private ServiceRegistration<ResourcePresence> presenceRegistration;
+
+    private ServiceRegistration<ResourceChangeListener> listenerRegistration;
+
+    @Reference
+    private ResourceResolverFactory resourceResolverFactory;
+
+    @Reference
+    private ServiceUserMapped serviceUserMapped;
+
+    private final Logger logger = LoggerFactory.getLogger(ResourcePresenter.class);
+
+    @Activate
+    public void activate(final ResourcePresenterConfiguration configuration, final BundleContext bundleContext) {
+        logger.info("activating resource presenter for {}", configuration.path());
+        path = configuration.path();
+        this.bundleContext = bundleContext;
+        try (final ResourceResolver resourceResolver = getServiceResourceResolver()) {
+            final Resource resource = resourceResolver.getResource(path);
+            if (resource != null) {
+                registerResourcePresence();
+            }
+            registerResourceChangeListener();
+        } catch (LoginException e) {
+            logger.error(e.getMessage(), e);
+        }
+    }
+
+    @Deactivate
+    public void deactivate() {
+        logger.info("deactivating resource presenter for {}", path);
+        unregisterResourceChangeListener();
+        unregisterResourcePresence();
+        bundleContext = null;
+    }
+
+    private ResourcePresence resourcePresence() {
+        return () -> path;
+    }
+
+    private ResourceChangeListener resourceChangeListener() {
+        return resourceChanges -> {
+            for (final ResourceChange resourceChange : resourceChanges) {
+                if (path.equals(resourceChange.getPath())) {
+                    final ChangeType type = resourceChange.getType();
+                    logger.info("change for {} observed: {}", path, type);
+                    if (type == ChangeType.ADDED) {
+                        unregisterResourcePresence();
+                        registerResourcePresence();
+                    } else if (type == ChangeType.REMOVED) {
+                        unregisterResourcePresence();
+                    }
+                }
+            }
+        };
+    }
+
+    private void registerResourcePresence() {
+        final Dictionary<String, Object> properties = new Hashtable<>();
+        properties.put("path", path);
+        presenceRegistration = bundleContext.registerService(ResourcePresence.class, resourcePresence(), properties);
+        logger.info("resource presence for {} registered", path);
+    }
+
+    private void unregisterResourcePresence() {
+        if (presenceRegistration != null) {
+            presenceRegistration.unregister();
+            presenceRegistration = null;
+            logger.info("resource presence for {} unregistered", path);
+        }
+    }
+
+    private void registerResourceChangeListener() {
+        final Dictionary<String, Object> properties = new Hashtable<>();
+        properties.put(ResourceChangeListener.PATHS, path);
+        listenerRegistration = bundleContext.registerService(ResourceChangeListener.class, resourceChangeListener(), properties);
+        logger.info("resource change listener for {} registered", path);
+    }
+
+    private void unregisterResourceChangeListener() {
+        if (listenerRegistration != null) {
+            listenerRegistration.unregister();
+            listenerRegistration = null;
+            logger.info("resource change listener for {} unregistered", path);
+        }
+    }
+
+    private ResourceResolver getServiceResourceResolver() throws LoginException {
+        return resourceResolverFactory.getServiceResourceResolver(null);
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/resource/presence/internal/ResourcePresenterConfiguration.java b/src/main/java/org/apache/sling/resource/presence/internal/ResourcePresenterConfiguration.java
new file mode 100644
index 0000000..5d7dc60
--- /dev/null
+++ b/src/main/java/org/apache/sling/resource/presence/internal/ResourcePresenterConfiguration.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.resource.presence.internal;
+
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+
+@ObjectClassDefinition(
+    name = "Apache Sling Resource Presenter",
+    description = "Registers a resource presence (OSGi service) per resource."
+)
+@interface ResourcePresenterConfiguration {
+
+    @AttributeDefinition(
+        name = "resource path",
+        description = "Path of the presented resource."
+    )
+    String path();
+
+}
diff --git a/src/main/java/org/apache/sling/resource/presence/package-info.java b/src/main/java/org/apache/sling/resource/presence/package-info.java
new file mode 100644
index 0000000..0eec5e0
--- /dev/null
+++ b/src/main/java/org/apache/sling/resource/presence/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+@Version("1.0.0")
+package org.apache.sling.resource.presence;
+
+import org.osgi.annotation.versioning.Version;
diff --git a/src/test/java/org/apache/sling/resource/presence/ResourcePresenterTestSupport.java b/src/test/java/org/apache/sling/resource/presence/ResourcePresenterTestSupport.java
new file mode 100644
index 0000000..52fca3c
--- /dev/null
+++ b/src/test/java/org/apache/sling/resource/presence/ResourcePresenterTestSupport.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.resource.presence;
+
+import org.apache.sling.testing.paxexam.TestSupport;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.util.PathUtils;
+
+import static org.apache.sling.testing.paxexam.SlingOptions.slingLaunchpadOakTar;
+import static org.ops4j.pax.exam.CoreOptions.composite;
+import static org.ops4j.pax.exam.CoreOptions.junitBundles;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.factoryConfiguration;
+
+public abstract class ResourcePresenterTestSupport extends TestSupport {
+
+    protected static final String FACTORY_PID = "org.apache.sling.resource.presence.internal.ResourcePresenter";
+
+    protected Option baseConfiguration() {
+        return composite(
+            super.baseConfiguration(),
+            launchpad(),
+            // Sling Resource Presenter
+            testBundle("bundle.filename"),
+            factoryConfiguration("org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended")
+                .put("user.mapping", new String[]{"org.apache.sling.resource.presence=sling-readall"})
+                .asOption(),
+            // testing
+            mavenBundle().groupId("org.apache.servicemix.bundles").artifactId("org.apache.servicemix.bundles.hamcrest").versionAsInProject(),
+            junitBundles(),
+            logging()
+        );
+    }
+
+    protected Option launchpad() {
+        final int httpPort = findFreePort();
+        final String workingDirectory = workingDirectory();
+        return slingLaunchpadOakTar(workingDirectory, httpPort);
+    }
+
+    protected Option logging() {
+        final String filename = String.format("file:%s/src/test/resources/logback.xml", PathUtils.getBaseDir());
+        return composite(
+            systemProperty("logback.configurationFile").value(filename),
+            mavenBundle().groupId("org.slf4j").artifactId("slf4j-api").version("1.7.21"),
+            mavenBundle().groupId("org.slf4j").artifactId("jcl-over-slf4j").version("1.7.21"),
+            mavenBundle().groupId("ch.qos.logback").artifactId("logback-core").version("1.1.7"),
+            mavenBundle().groupId("ch.qos.logback").artifactId("logback-classic").version("1.1.7")
+        );
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/resource/presence/SimpleIT.java b/src/test/java/org/apache/sling/resource/presence/SimpleIT.java
new file mode 100644
index 0000000..46d3ec3
--- /dev/null
+++ b/src/test/java/org/apache/sling/resource/presence/SimpleIT.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.resource.presence;
+
+import javax.inject.Inject;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+import org.ops4j.pax.exam.util.Filter;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.factoryConfiguration;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class SimpleIT extends ResourcePresenterTestSupport {
+
+    @Inject
+    @Filter(value = "(path=/apps)")
+    private ResourcePresence apps;
+
+    @Inject
+    @Filter(value = "(path=/libs)")
+    private ResourcePresence libs;
+
+    @Configuration
+    public Option[] configuration() {
+        return new Option[]{
+            baseConfiguration(),
+            factoryConfiguration(FACTORY_PID)
+                .put("path", "/apps")
+                .asOption(),
+            factoryConfiguration(FACTORY_PID)
+                .put("path", "/libs")
+                .asOption()
+        };
+    }
+
+    @Test
+    public void testApps() {
+        assertThat(apps.getPath(), is("/apps"));
+    }
+
+    @Test
+    public void testLibs() {
+        assertThat(libs.getPath(), is("/libs"));
+    }
+
+}
diff --git a/src/test/resources/exam.properties b/src/test/resources/exam.properties
new file mode 100644
index 0000000..c98a668
--- /dev/null
+++ b/src/test/resources/exam.properties
@@ -0,0 +1,19 @@
+#
+#  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.
+#
+pax.exam.logging=none
diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml
new file mode 100644
index 0000000..d46a4ae
--- /dev/null
+++ b/src/test/resources/logback.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+<configuration>
+  <appender name="file" class="ch.qos.logback.core.FileAppender">
+    <file>target/testing.log</file>
+    <encoder>
+      <pattern>%date %level [%thread] %logger{10} [%file : %line] %msg%n</pattern>
+    </encoder>
+  </appender>
+  <root level="debug">
+    <appender-ref ref="file"/>
+  </root>
+</configuration>

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.