killbill-uncached

Details

diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
index 08813a4..71b62fa 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
@@ -130,5 +130,9 @@ public interface JaxrsResource {
     public static final String EXPORT = "export";
     public static final String EXPORT_PATH = PREFIX + "/" + EXPORT;
 
+    public static final String PLUGINS = "plugins";
+    // No PREFIX here!
+    public static final String PLUGINS_PATH = "/" + PLUGINS;
+
     public static final String CBA_REBALANCING = "cbaRebalancing";
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PluginResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PluginResource.java
new file mode 100644
index 0000000..9fee49d
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PluginResource.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.jaxrs.resources;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HEAD;
+import javax.ws.rs.OPTIONS;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Response;
+
+import com.ning.billing.jaxrs.util.Context;
+import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
+import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.api.CustomFieldUserApi;
+import com.ning.billing.util.api.TagUserApi;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.google.inject.name.Named;
+
+@Singleton
+@Path(JaxrsResource.PLUGINS_PATH)
+public class PluginResource extends JaxRsResourceBase {
+
+    private final HttpServlet osgiServlet;
+
+    @Inject
+    public PluginResource(@Named("osgi") final HttpServlet osgiServlet,
+                          final JaxrsUriBuilder uriBuilder,
+                          final TagUserApi tagUserApi,
+                          final CustomFieldUserApi customFieldUserApi,
+                          final AuditUserApi auditUserApi,
+                          final Context context) {
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, context);
+        this.osgiServlet = osgiServlet;
+    }
+
+    @DELETE
+    @Path("/{pluginName:" + STRING_PATTERN + "}/{rest:.+}")
+    public Response doDELETE(@PathParam("pluginName") final String pluginName,
+                             @javax.ws.rs.core.Context final HttpServletRequest request,
+                             @javax.ws.rs.core.Context final HttpServletResponse response) throws ServletException, IOException {
+        return serviceViaOSGIPlugin(pluginName, request, response);
+    }
+
+    @GET
+    @Path("/{pluginName:" + STRING_PATTERN + "}/{rest:.+}")
+    public Response doGET(@PathParam("pluginName") final String pluginName,
+                          @javax.ws.rs.core.Context final HttpServletRequest request,
+                          @javax.ws.rs.core.Context final HttpServletResponse response) throws ServletException, IOException {
+        return serviceViaOSGIPlugin(pluginName, request, response);
+    }
+
+    @OPTIONS
+    @Path("/{pluginName:" + STRING_PATTERN + "}/{rest:.+}")
+    public Response doOPTIONS(@PathParam("pluginName") final String pluginName,
+                              @javax.ws.rs.core.Context final HttpServletRequest request,
+                              @javax.ws.rs.core.Context final HttpServletResponse response) throws ServletException, IOException {
+        return serviceViaOSGIPlugin(pluginName, request, response);
+    }
+
+    @POST
+    @Path("/{pluginName:" + STRING_PATTERN + "}/{rest:.+}")
+    public Response doPOST(@PathParam("pluginName") final String pluginName,
+                           @javax.ws.rs.core.Context final HttpServletRequest request,
+                           @javax.ws.rs.core.Context final HttpServletResponse response) throws ServletException, IOException {
+        return serviceViaOSGIPlugin(pluginName, request, response);
+    }
+
+    @PUT
+    @Path("/{pluginName:" + STRING_PATTERN + "}/{rest:.+}")
+    public Response doPUT(@PathParam("pluginName") final String pluginName,
+                          @javax.ws.rs.core.Context final HttpServletRequest request,
+                          @javax.ws.rs.core.Context final HttpServletResponse response) throws ServletException, IOException {
+        return serviceViaOSGIPlugin(pluginName, request, response);
+    }
+
+    @HEAD
+    @Path("/{pluginName:" + STRING_PATTERN + "}/{rest:.+}")
+    public Response doHEAD(@PathParam("pluginName") final String pluginName,
+                           @javax.ws.rs.core.Context final HttpServletRequest request,
+                           @javax.ws.rs.core.Context final HttpServletResponse response) throws ServletException, IOException {
+        prepareOSGIRequest(pluginName, request);
+        osgiServlet.service(request, response);
+
+        // Make sure to return 204
+        return Response.noContent().build();
+    }
+
+    private Response serviceViaOSGIPlugin(final String pluginName, final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
+        prepareOSGIRequest(pluginName, request);
+        osgiServlet.service(request, response);
+
+        return Response.status(response.getStatus()).build();
+    }
+
+    private void prepareOSGIRequest(final String pluginName, final HttpServletRequest request) {
+        request.setAttribute("killbill.osgi.pluginName", pluginName);
+    }
+}

osgi/pom.xml 4(+4 -0)

diff --git a/osgi/pom.xml b/osgi/pom.xml
index b21699c..f3303b1 100644
--- a/osgi/pom.xml
+++ b/osgi/pom.xml
@@ -49,6 +49,10 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.skife.config</groupId>
             <artifactId>config-magic</artifactId>
         </dependency>
diff --git a/osgi/src/main/java/com/ning/billing/osgi/glue/DefaultOSGIModule.java b/osgi/src/main/java/com/ning/billing/osgi/glue/DefaultOSGIModule.java
index b505635..c496bda 100644
--- a/osgi/src/main/java/com/ning/billing/osgi/glue/DefaultOSGIModule.java
+++ b/osgi/src/main/java/com/ning/billing/osgi/glue/DefaultOSGIModule.java
@@ -16,15 +16,19 @@
 
 package com.ning.billing.osgi.glue;
 
+import javax.servlet.http.HttpServlet;
+
 import org.skife.config.ConfigurationObjectFactory;
 
 import com.ning.billing.osgi.KillbillActivator;
+import com.ning.billing.osgi.OSGIServlet;
 import com.ning.billing.osgi.api.config.PluginConfigServiceApi;
 import com.ning.billing.osgi.pluginconf.DefaultPluginConfigServiceApi;
 import com.ning.billing.osgi.pluginconf.PluginFinder;
 import com.ning.billing.util.config.OSGIConfig;
 
 import com.google.inject.AbstractModule;
+import com.google.inject.name.Names;
 
 public class DefaultOSGIModule extends AbstractModule {
 
@@ -33,11 +37,17 @@ public class DefaultOSGIModule extends AbstractModule {
         bind(OSGIConfig.class).toInstance(config);
     }
 
+    protected void installOSGIServlet() {
+        bind(HttpServlet.class).annotatedWith(Names.named("osgi")).to(OSGIServlet.class).asEagerSingleton();
+    }
+
     @Override
     protected void configure() {
         installConfig();
         bind(KillbillActivator.class).asEagerSingleton();
         bind(PluginFinder.class).asEagerSingleton();
         bind(PluginConfigServiceApi.class).to(DefaultPluginConfigServiceApi.class).asEagerSingleton();
+
+        installOSGIServlet();
     }
 }
diff --git a/osgi/src/main/java/com/ning/billing/osgi/OSGIServlet.java b/osgi/src/main/java/com/ning/billing/osgi/OSGIServlet.java
new file mode 100644
index 0000000..d8420a9
--- /dev/null
+++ b/osgi/src/main/java/com/ning/billing/osgi/OSGIServlet.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.osgi;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class OSGIServlet extends HttpServlet {
+
+    private final Map<String, HttpServlet> pluginServlets = new HashMap<String, HttpServlet>();
+
+    public void registerResource(final String pluginName, final HttpServlet httpServlet) {
+        pluginServlets.put(pluginName, httpServlet);
+    }
+
+    @Override
+    protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+        serviceViaPlugin(req, resp);
+    }
+
+    @Override
+    protected void doHead(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+        serviceViaPlugin(req, resp);
+    }
+
+    @Override
+    protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+        serviceViaPlugin(req, resp);
+    }
+
+    @Override
+    protected void doPut(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+        serviceViaPlugin(req, resp);
+    }
+
+    @Override
+    protected void doDelete(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+        serviceViaPlugin(req, resp);
+    }
+
+    @Override
+    protected void doOptions(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+        serviceViaPlugin(req, resp);
+    }
+
+    private void serviceViaPlugin(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+        final HttpServlet pluginServlet = getPluginServlet(req);
+        if (pluginServlet != null) {
+            pluginServlet.service(req, resp);
+        } else {
+            resp.sendError(404);
+        }
+    }
+
+    private HttpServlet getPluginServlet(final HttpServletRequest req) {
+        final String pluginName = (String) req.getAttribute("killbill.osgi.pluginName");
+        if (pluginName != null) {
+            return pluginServlets.get(pluginName);
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java b/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
index e4ee470..94cb566 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
@@ -812,6 +812,58 @@ public abstract class KillbillClient extends ServerTestSuiteWithEmbeddedDB {
     }
 
     //
+    // PLUGINS
+    //
+
+    protected Response pluginGET(final String uri) throws Exception {
+        return pluginGET(uri, DEFAULT_EMPTY_QUERY);
+    }
+
+    protected Response pluginGET(final String uri, final Map<String, String> queryParams) throws Exception {
+        return doGet(JaxrsResource.PLUGINS_PATH + "/" + uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+    }
+
+    protected Response pluginHEAD(final String uri) throws Exception {
+        return pluginHEAD(uri, DEFAULT_EMPTY_QUERY);
+    }
+
+    protected Response pluginHEAD(final String uri, final Map<String, String> queryParams) throws Exception {
+        return doHead(JaxrsResource.PLUGINS_PATH + "/" + uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+    }
+
+    protected Response pluginPOST(final String uri, @Nullable final String body) throws Exception {
+        return pluginPOST(uri, body, DEFAULT_EMPTY_QUERY);
+    }
+
+    protected Response pluginPOST(final String uri, @Nullable final String body, final Map<String, String> queryParams) throws Exception {
+        return doPost(JaxrsResource.PLUGINS_PATH + "/" + uri, body, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+    }
+
+    protected Response pluginPUT(final String uri, @Nullable final String body) throws Exception {
+        return pluginPUT(uri, body, DEFAULT_EMPTY_QUERY);
+    }
+
+    protected Response pluginPUT(final String uri, @Nullable final String body, final Map<String, String> queryParams) throws Exception {
+        return doPut(JaxrsResource.PLUGINS_PATH + "/" + uri, body, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+    }
+
+    protected Response pluginDELETE(final String uri) throws Exception {
+        return pluginDELETE(uri, DEFAULT_EMPTY_QUERY);
+    }
+
+    protected Response pluginDELETE(final String uri, final Map<String, String> queryParams) throws Exception {
+        return doDelete(JaxrsResource.PLUGINS_PATH + "/" + uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+    }
+
+    protected Response pluginOPTIONS(final String uri) throws Exception {
+        return pluginOPTIONS(uri, DEFAULT_EMPTY_QUERY);
+    }
+
+    protected Response pluginOPTIONS(final String uri, final Map<String, String> queryParams) throws Exception {
+        return doOptions(JaxrsResource.PLUGINS_PATH + "/" + uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+    }
+
+    //
     // HTTP CLIENT HELPERS
     //
     protected Response doPost(final String uri, @Nullable final String body, final Map<String, String> queryParams, final int timeoutSec) {
@@ -846,11 +898,31 @@ public abstract class KillbillClient extends ServerTestSuiteWithEmbeddedDB {
         return doGetWithUrl(url, queryParams, timeoutSec);
     }
 
+    protected Response doHead(final String uri, final Map<String, String> queryParams, final int timeoutSec) {
+        final String url = String.format("http://%s:%d%s", config.getServerHost(), config.getServerPort(), uri);
+        return doHeadWithUrl(url, queryParams, timeoutSec);
+    }
+
+    protected Response doOptions(final String uri, final Map<String, String> queryParams, final int timeoutSec) {
+        final String url = String.format("http://%s:%d%s", config.getServerHost(), config.getServerPort(), uri);
+        return doOptionsWithUrl(url, queryParams, timeoutSec);
+    }
+
     protected Response doGetWithUrl(final String url, final Map<String, String> queryParams, final int timeoutSec) {
         final BoundRequestBuilder builder = getBuilderWithHeaderAndQuery("GET", url, queryParams);
         return executeAndWait(builder, timeoutSec, false);
     }
 
+    protected Response doHeadWithUrl(final String url, final Map<String, String> queryParams, final int timeoutSec) {
+        final BoundRequestBuilder builder = getBuilderWithHeaderAndQuery("HEAD", url, queryParams);
+        return executeAndWait(builder, timeoutSec, false);
+    }
+
+    protected Response doOptionsWithUrl(final String url, final Map<String, String> queryParams, final int timeoutSec) {
+        final BoundRequestBuilder builder = getBuilderWithHeaderAndQuery("OPTIONS", url, queryParams);
+        return executeAndWait(builder, timeoutSec, false);
+    }
+
     private Response executeAndWait(final BoundRequestBuilder builder, final int timeoutSec, final boolean addContextHeader) {
 
         if (addContextHeader) {
@@ -890,6 +962,10 @@ public abstract class KillbillClient extends ServerTestSuiteWithEmbeddedDB {
             builder = httpClient.preparePut(url);
         } else if (verb.equals("DELETE")) {
             builder = httpClient.prepareDelete(url);
+        } else if (verb.equals("HEAD")) {
+            builder = httpClient.prepareHead(url);
+        } else if (verb.equals("OPTIONS")) {
+            builder = httpClient.prepareOptions(url);
         } else {
             Assert.fail("Unknown verb " + verb);
         }
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
index 6e21000..15df041 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
@@ -23,6 +23,10 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.servlet.http.HttpServlet;
+
 import org.eclipse.jetty.servlet.FilterHolder;
 import org.joda.time.LocalDate;
 import org.skife.config.ConfigurationObjectFactory;
@@ -89,6 +93,10 @@ public class TestJaxrsBase extends KillbillClient {
 
     protected static final int DEFAULT_HTTP_TIMEOUT_SEC = 6000; // 5;
 
+    @Inject
+    @Named("osgi")
+    protected HttpServlet osgiServlet;
+
     protected static TestKillbillGuiceListener listener;
 
     private final DBTestingHelper helper = KillbillTestSuiteWithEmbeddedDB.getDBTestingHelper();
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestPlugin.java b/server/src/test/java/com/ning/billing/jaxrs/TestPlugin.java
new file mode 100644
index 0000000..f8da0a7
--- /dev/null
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestPlugin.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.jaxrs;
+
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.annotation.Nullable;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.ning.billing.jaxrs.resources.JaxrsResource;
+import com.ning.billing.osgi.OSGIServlet;
+import com.ning.http.client.Response;
+
+public class TestPlugin extends TestJaxrsBase {
+
+    private static final String TEST_PLUGIN_NAME = "test-osgi";
+
+    private static final byte[] TEST_PLUGIN_RESPONSE_BYTES = new byte[]{0xC, 0x0, 0xF, 0xF, 0xE, 0xE};
+
+    private static final String TEST_PLUGIN_VALID_GET_PATH = "setGETMarkerToTrue";
+    private static final String TEST_PLUGIN_VALID_HEAD_PATH = "setHEADMarkerToTrue";
+    private static final String TEST_PLUGIN_VALID_POST_PATH = "setPOSTMarkerToTrue";
+    private static final String TEST_PLUGIN_VALID_PUT_PATH = "setPUTMarkerToTrue";
+    private static final String TEST_PLUGIN_VALID_DELETE_PATH = "setDELETEMarkerToTrue";
+    private static final String TEST_PLUGIN_VALID_OPTIONS_PATH = "setOPTIONSMarkerToTrue";
+
+    private final AtomicBoolean requestGETMarker = new AtomicBoolean(false);
+    private final AtomicBoolean requestHEADMarker = new AtomicBoolean(false);
+    private final AtomicBoolean requestPOSTMarker = new AtomicBoolean(false);
+    private final AtomicBoolean requestPUTMarker = new AtomicBoolean(false);
+    private final AtomicBoolean requestDELETEMarker = new AtomicBoolean(false);
+    private final AtomicBoolean requestOPTIONSMarker = new AtomicBoolean(false);
+
+    @BeforeMethod(groups = "slow")
+    public void setUp() throws Exception {
+        setupOSGIPlugin();
+        resetAllMarkers();
+    }
+
+    @Test(groups = "slow")
+    public void testPassRequestsToUnknownPlugin() throws Exception {
+        final String uri = "pluginDoesNotExist/something";
+        Response response;
+
+        // We don't test the output here as it is some Jetty specific HTML blurb
+
+        response = pluginGET(uri);
+        testAndResetAllMarkers(response, 404, null, false, false, false, false, false, false);
+
+        response = pluginHEAD(uri);
+        testAndResetAllMarkers(response, 404, null, false, false, false, false, false, false);
+
+        response = pluginPOST(uri, null);
+        testAndResetAllMarkers(response, 404, null, false, false, false, false, false, false);
+
+        response = pluginPUT(uri, null);
+        testAndResetAllMarkers(response, 404, null, false, false, false, false, false, false);
+
+        response = pluginDELETE(uri);
+        testAndResetAllMarkers(response, 404, null, false, false, false, false, false, false);
+
+        response = pluginOPTIONS(uri);
+        testAndResetAllMarkers(response, 404, null, false, false, false, false, false, false);
+    }
+
+    @Test(groups = "slow")
+    public void testPassRequestsToKnownPluginButWrongPath() throws Exception {
+        final String uri = TEST_PLUGIN_NAME + "/somethingSomething";
+        Response response;
+
+        response = pluginGET(uri);
+        testAndResetAllMarkers(response, 200, new byte[]{}, false, false, false, false, false, false);
+
+        response = pluginHEAD(uri);
+        testAndResetAllMarkers(response, 204, new byte[]{}, false, false, false, false, false, false);
+
+        response = pluginPOST(uri, null);
+        testAndResetAllMarkers(response, 200, new byte[]{}, false, false, false, false, false, false);
+
+        response = pluginPUT(uri, null);
+        testAndResetAllMarkers(response, 200, new byte[]{}, false, false, false, false, false, false);
+
+        response = pluginDELETE(uri);
+        testAndResetAllMarkers(response, 200, new byte[]{}, false, false, false, false, false, false);
+
+        response = pluginOPTIONS(uri);
+        testAndResetAllMarkers(response, 200, new byte[]{}, false, false, false, false, false, false);
+    }
+
+    @Test(groups = "slow")
+    public void testPassRequestsToKnownPluginAndKnownPath() throws Exception {
+        Response response;
+
+        response = pluginGET(TEST_PLUGIN_NAME + "/" + TEST_PLUGIN_VALID_GET_PATH);
+        testAndResetAllMarkers(response, 230, TEST_PLUGIN_RESPONSE_BYTES, true, false, false, false, false, false);
+
+        response = pluginHEAD(TEST_PLUGIN_NAME + "/" + TEST_PLUGIN_VALID_HEAD_PATH);
+        testAndResetAllMarkers(response, 204, new byte[]{}, false, true, false, false, false, false);
+
+        response = pluginPOST(TEST_PLUGIN_NAME + "/" + TEST_PLUGIN_VALID_POST_PATH, null);
+        testAndResetAllMarkers(response, 230, TEST_PLUGIN_RESPONSE_BYTES, false, false, true, false, false, false);
+
+        response = pluginPUT(TEST_PLUGIN_NAME + "/" + TEST_PLUGIN_VALID_PUT_PATH, null);
+        testAndResetAllMarkers(response, 230, TEST_PLUGIN_RESPONSE_BYTES, false, false, false, true, false, false);
+
+        response = pluginDELETE(TEST_PLUGIN_NAME + "/" + TEST_PLUGIN_VALID_DELETE_PATH);
+        testAndResetAllMarkers(response, 230, TEST_PLUGIN_RESPONSE_BYTES, false, false, false, false, true, false);
+
+        response = pluginOPTIONS(TEST_PLUGIN_NAME + "/" + TEST_PLUGIN_VALID_OPTIONS_PATH);
+        testAndResetAllMarkers(response, 230, TEST_PLUGIN_RESPONSE_BYTES, false, false, false, false, false, true);
+    }
+
+    private void testAndResetAllMarkers(final Response response, final int responseCode, @Nullable final byte[] responseBytes, final boolean get, final boolean head,
+                                        final boolean post, final boolean put, final boolean delete, final boolean options) throws IOException {
+        Assert.assertEquals(response.getStatusCode(), responseCode);
+        if (responseBytes != null) {
+            Assert.assertEquals(response.getResponseBodyAsBytes(), responseBytes);
+        }
+
+        Assert.assertEquals(requestGETMarker.get(), get);
+        Assert.assertEquals(requestHEADMarker.get(), head);
+        Assert.assertEquals(requestPOSTMarker.get(), post);
+        Assert.assertEquals(requestPUTMarker.get(), put);
+        Assert.assertEquals(requestDELETEMarker.get(), delete);
+        Assert.assertEquals(requestOPTIONSMarker.get(), options);
+
+        resetAllMarkers();
+    }
+
+    private void resetAllMarkers() {
+        requestGETMarker.set(false);
+        requestHEADMarker.set(false);
+        requestPOSTMarker.set(false);
+        requestPUTMarker.set(false);
+        requestDELETEMarker.set(false);
+        requestOPTIONSMarker.set(false);
+    }
+
+    private void setupOSGIPlugin() {
+        ((OSGIServlet) osgiServlet).registerResource(TEST_PLUGIN_NAME, new HttpServlet() {
+            @Override
+            protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+                if ((JaxrsResource.PLUGINS_PATH + "/" + TEST_PLUGIN_NAME + "/" + TEST_PLUGIN_VALID_GET_PATH).equals(req.getPathInfo())) {
+                    requestGETMarker.set(true);
+                    resp.getOutputStream().write(TEST_PLUGIN_RESPONSE_BYTES);
+                    resp.setStatus(230);
+                }
+            }
+
+            @Override
+            protected void doHead(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+                if ((JaxrsResource.PLUGINS_PATH + "/" + TEST_PLUGIN_NAME + "/" + TEST_PLUGIN_VALID_HEAD_PATH).equals(req.getPathInfo())) {
+                    requestHEADMarker.set(true);
+                }
+            }
+
+            @Override
+            protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+                if ((JaxrsResource.PLUGINS_PATH + "/" + TEST_PLUGIN_NAME + "/" + TEST_PLUGIN_VALID_POST_PATH).equals(req.getPathInfo())) {
+                    requestPOSTMarker.set(true);
+                    resp.getOutputStream().write(TEST_PLUGIN_RESPONSE_BYTES);
+                    resp.setStatus(230);
+                }
+            }
+
+            @Override
+            protected void doPut(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+                if ((JaxrsResource.PLUGINS_PATH + "/" + TEST_PLUGIN_NAME + "/" + TEST_PLUGIN_VALID_PUT_PATH).equals(req.getPathInfo())) {
+                    requestPUTMarker.set(true);
+                    resp.getOutputStream().write(TEST_PLUGIN_RESPONSE_BYTES);
+                    resp.setStatus(230);
+                }
+            }
+
+            @Override
+            protected void doDelete(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+                if ((JaxrsResource.PLUGINS_PATH + "/" + TEST_PLUGIN_NAME + "/" + TEST_PLUGIN_VALID_DELETE_PATH).equals(req.getPathInfo())) {
+                    requestDELETEMarker.set(true);
+                    resp.getOutputStream().write(TEST_PLUGIN_RESPONSE_BYTES);
+                    resp.setStatus(230);
+                }
+            }
+
+            @Override
+            protected void doOptions(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+                if ((JaxrsResource.PLUGINS_PATH + "/" + TEST_PLUGIN_NAME + "/" + TEST_PLUGIN_VALID_OPTIONS_PATH).equals(req.getPathInfo())) {
+                    requestOPTIONSMarker.set(true);
+                    resp.getOutputStream().write(TEST_PLUGIN_RESPONSE_BYTES);
+                    resp.setStatus(230);
+                }
+            }
+        });
+    }
+}