killbill-aplcache

Merge branch 'new-paymentpluginapi' of github.com:killbill/killbill

2/11/2013 8:17:03 PM

Changes

api/pom.xml 20(+4 -16)

api/src/main/java/com/ning/billing/payment/plugin/api/PaymentProviderAccount.java 138(+0 -138)

bin/start-server 14(+7 -7)

pom.xml 7(+7 -0)

Details

api/pom.xml 20(+4 -16)

diff --git a/api/pom.xml b/api/pom.xml
index 17ffe14..fb75ef5 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -28,18 +28,12 @@
     <packaging>jar</packaging>
     <dependencies>
         <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.jdbi</groupId>
-            <artifactId>jdbi</artifactId>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
         </dependency>
         <dependency>
-            <groupId>com.google.inject</groupId>
-            <artifactId>guice</artifactId>
-            <scope>provided</scope>
+            <groupId>com.google.code.findbugs</groupId>
+            <artifactId>jsr305</artifactId>
         </dependency>
         <dependency>
             <groupId>javax.servlet</groupId>
@@ -57,11 +51,5 @@
             <groupId>org.skife.config</groupId>
             <artifactId>config-magic</artifactId>
         </dependency>
-        <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-annotations</artifactId>
-        </dependency>
     </dependencies>
-    <build>
-    </build>
 </project>
diff --git a/api/src/main/java/com/ning/billing/lifecycle/LifecycleHandlerType.java b/api/src/main/java/com/ning/billing/lifecycle/LifecycleHandlerType.java
index 6948773..d0b9453 100644
--- a/api/src/main/java/com/ning/billing/lifecycle/LifecycleHandlerType.java
+++ b/api/src/main/java/com/ning/billing/lifecycle/LifecycleHandlerType.java
@@ -20,10 +20,9 @@ import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.util.ArrayList;
 import java.util.List;
 
-import com.google.common.collect.Lists;
-
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.METHOD)
 public @interface LifecycleHandlerType {
@@ -97,7 +96,7 @@ public @interface LifecycleHandlerType {
         // Returns an ordered list of level for a particular sequence
         //
         public static List<LifecycleLevel> getLevelsForSequence(final Sequence seq) {
-            final List<LifecycleLevel> result = Lists.newLinkedList();
+            final List<LifecycleLevel> result = new ArrayList<LifecycleLevel>();
             for (final LifecycleLevel level : LifecycleLevel.values()) {
                 if (level.getSequence() == seq) {
                     result.add(level);
diff --git a/api/src/main/java/com/ning/billing/osgi/api/http/ServletRouter.java b/api/src/main/java/com/ning/billing/osgi/api/http/ServletRouter.java
index ef4d3bb..7a77de6 100644
--- a/api/src/main/java/com/ning/billing/osgi/api/http/ServletRouter.java
+++ b/api/src/main/java/com/ning/billing/osgi/api/http/ServletRouter.java
@@ -22,5 +22,7 @@ public interface ServletRouter {
 
     void registerServlet(String pluginName, HttpServlet httpServlet);
 
+    void unregisterServlet(String pluginName);
+
     HttpServlet getServletForPlugin(String pluginName);
 }
diff --git a/api/src/main/java/com/ning/billing/osgi/api/OSGIKillbill.java b/api/src/main/java/com/ning/billing/osgi/api/OSGIKillbill.java
index 4212b9b..b9a7adc 100644
--- a/api/src/main/java/com/ning/billing/osgi/api/OSGIKillbill.java
+++ b/api/src/main/java/com/ning/billing/osgi/api/OSGIKillbill.java
@@ -16,7 +16,6 @@
 
 package com.ning.billing.osgi.api;
 
-import javax.servlet.http.HttpServlet;
 import javax.sql.DataSource;
 
 import com.ning.billing.account.api.AccountUserApi;
@@ -92,12 +91,4 @@ public interface OSGIKillbill {
      * @return the dataSource for the OSGI bundles
      */
     public DataSource getDataSource();
-
-    /**
-     * Register a servlet
-     *
-     * @param pluginName plugin name
-     * @param pluginServlet servlet from the bundle
-     */
-    public void registerServlet(String pluginName, HttpServlet pluginServlet);
 }
diff --git a/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java b/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java
index 64fe06b..3101622 100644
--- a/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java
+++ b/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java
@@ -17,20 +17,19 @@
 package com.ning.billing.util.tag;
 
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.ObjectType;
 
-import com.google.common.collect.ImmutableList;
-
 public enum ControlTagType {
 
-    AUTO_PAY_OFF(new UUID(0, 1), "Suspends payments until removed.", true, false, ImmutableList.<ObjectType>of(ObjectType.ACCOUNT)),
-    AUTO_INVOICING_OFF(new UUID(0, 2), "Suspends invoicing until removed.", false, true, ImmutableList.<ObjectType>of(ObjectType.ACCOUNT)),
-    OVERDUE_ENFORCEMENT_OFF(new UUID(0, 3), "Suspends overdue enforcement behaviour until removed.", false, false, ImmutableList.<ObjectType>of(ObjectType.ACCOUNT)),
-    WRITTEN_OFF(new UUID(0, 4), "Indicates that an invoice is written off. No billing or payment effect.", false, false, ImmutableList.<ObjectType>of(ObjectType.INVOICE)),
-    MANUAL_PAY(new UUID(0, 5), "Indicates that Killbill doesn't process payments for that account (external payments only)", true, false, ImmutableList.<ObjectType>of(ObjectType.ACCOUNT));
+    AUTO_PAY_OFF(new UUID(0, 1), "Suspends payments until removed.", true, false, Collections.<ObjectType>singletonList(ObjectType.ACCOUNT)),
+    AUTO_INVOICING_OFF(new UUID(0, 2), "Suspends invoicing until removed.", false, true, Collections.<ObjectType>singletonList(ObjectType.ACCOUNT)),
+    OVERDUE_ENFORCEMENT_OFF(new UUID(0, 3), "Suspends overdue enforcement behaviour until removed.", false, false, Collections.<ObjectType>singletonList(ObjectType.ACCOUNT)),
+    WRITTEN_OFF(new UUID(0, 4), "Indicates that an invoice is written off. No billing or payment effect.", false, false, Collections.<ObjectType>singletonList(ObjectType.INVOICE)),
+    MANUAL_PAY(new UUID(0, 5), "Indicates that Killbill doesn't process payments for that account (external payments only)", true, false, Collections.<ObjectType>singletonList(ObjectType.ACCOUNT));
 
     private final UUID id;
     private final String description;
diff --git a/api/src/main/java/com/ning/billing/util/tag/TagDefinition.java b/api/src/main/java/com/ning/billing/util/tag/TagDefinition.java
index 7f24b4a..4f673ed 100644
--- a/api/src/main/java/com/ning/billing/util/tag/TagDefinition.java
+++ b/api/src/main/java/com/ning/billing/util/tag/TagDefinition.java
@@ -24,7 +24,6 @@ import com.ning.billing.util.entity.Entity;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 
 // TODO: needs to surface created date, created by
-
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
 public interface TagDefinition extends Entity {
 

bin/start-server 14(+7 -7)

diff --git a/bin/start-server b/bin/start-server
index ce254da..ff9b090 100755
--- a/bin/start-server
+++ b/bin/start-server
@@ -30,7 +30,7 @@ DEBUG_OPTS_ECLIPSE_WAIT=" -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend
 
 OPTS_ECLIPSE=" -Xmx2048m  -XX:+UseConcMarkSweepGC -XX:MaxPermSize=128m  "
 
-LOG="$SERVER/src/main/resources/log4j.xml"
+LOG="$SERVER/src/main/resources/logback.xml"
 
 # From Argument Options
 PORT=8080
@@ -42,16 +42,16 @@ WAIT_DEBUGGER=
 function usage() {
     echo -n "./start-server "
     echo -n " -s (start server)"
-    echo -n " -d (debugger turned on)"    
+    echo -n " -d (debugger turned on)"
     echo -n " -w (along with -d, wait for debugger before starting)"
     echo -n " -p <port_number> default 8080"
-    echo -n "-h this message"        
+    echo -n "-h this message"
     exit 1
 }
 
 function build_properties() {
     local opts=
-    local prop= 
+    local prop=
     for prop in `cat  $PROPERTIES | grep =`; do
         local k=`echo $prop | awk '  BEGIN {FS="="} { print $1 }'`
         local v=`echo $prop | awk 'BEGIN {FS="="} { print $2 }'`
@@ -62,9 +62,9 @@ function build_properties() {
 
 function start() {
     local opts=`build_properties`
-    local start_cmd="mvn $opts -Dlog4j.configuration=file://$LOG -Dning.jmx.http.port=$PORT -Dxn.host.external.port=$PORT -DjettyPort=$PORT -Dxn.server.port=$PORT jetty:run"    
+    local start_cmd="mvn $opts -Dlogback.configurationFile=$LOG -Dning.jmx.http.port=$PORT -Dxn.host.external.port=$PORT -DjettyPort=$PORT -Dxn.server.port=$PORT jetty:run"
 
-    local debug_opts_eclipse= 
+    local debug_opts_eclipse=
     if [ ! -z $DEBUG ]; then
         if  [ ! -z $WAIT_DEBUGGER ]; then
             debug_opts_eclipse=$DEBUG_OPTS_ECLIPSE_WAIT
@@ -73,7 +73,7 @@ function start() {
         fi
     fi
     export MAVEN_OPTS=" -Duser.timezone=UTC $OPTS_ECLIPSE $debug_opts_eclipse"
-    
+
     echo "Starting IRS MAVEN_OPTS = $MAVEN_OPTS"
     echo "$start_cmd"
     cd $SERVER
diff --git a/osgi/src/main/java/com/ning/billing/osgi/DefaultOSGIKillbill.java b/osgi/src/main/java/com/ning/billing/osgi/DefaultOSGIKillbill.java
index dbcb4bc..08e2d04 100644
--- a/osgi/src/main/java/com/ning/billing/osgi/DefaultOSGIKillbill.java
+++ b/osgi/src/main/java/com/ning/billing/osgi/DefaultOSGIKillbill.java
@@ -18,7 +18,6 @@ package com.ning.billing.osgi;
 
 import javax.inject.Inject;
 import javax.inject.Named;
-import javax.servlet.http.HttpServlet;
 import javax.sql.DataSource;
 
 import com.ning.billing.account.api.AccountUserApi;
@@ -232,9 +231,4 @@ public class DefaultOSGIKillbill implements OSGIKillbill {
     public DataSource getDataSource() {
         return dataSource;
     }
-
-    @Override
-    public void registerServlet(final String pluginName, final HttpServlet pluginServlet) {
-        servletRouter.registerServlet(pluginName, pluginServlet);
-    }
 }
diff --git a/osgi/src/main/java/com/ning/billing/osgi/DefaultOSGIService.java b/osgi/src/main/java/com/ning/billing/osgi/DefaultOSGIService.java
index bdf7ff6..d37acd0 100644
--- a/osgi/src/main/java/com/ning/billing/osgi/DefaultOSGIService.java
+++ b/osgi/src/main/java/com/ning/billing/osgi/DefaultOSGIService.java
@@ -136,7 +136,7 @@ public class DefaultOSGIService implements OSGIService {
         }
     }
 
-    private void installAndStartBundles(final Framework framework) throws BundleException {
+    private void installAndStartBundles(final Framework framework) {
         try {
             final BundleContext context = framework.getBundleContext();
 
@@ -148,10 +148,16 @@ public class DefaultOSGIService implements OSGIService {
             // Start all the bundles
             for (final Bundle bundle : installedBundles) {
                 logger.info("Starting bundle {}", bundle.getLocation());
-                bundle.start();
+                try {
+                    bundle.start();
+                } catch (BundleException e) {
+                    logger.warn("Unable to start bundle", e);
+                }
             }
         } catch (PluginConfigException e) {
             logger.error("Error while parsing plugin configurations", e);
+        } catch (BundleException e) {
+            logger.error("Error while parsing plugin configurations", e);
         }
     }
 
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 8b57895..4b10b8b 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
@@ -22,8 +22,10 @@ import javax.sql.DataSource;
 import org.skife.config.ConfigurationObjectFactory;
 
 import com.ning.billing.osgi.DefaultOSGIKillbill;
+import com.ning.billing.osgi.DefaultOSGIService;
 import com.ning.billing.osgi.KillbillActivator;
 import com.ning.billing.osgi.api.OSGIKillbill;
+import com.ning.billing.osgi.api.OSGIService;
 import com.ning.billing.osgi.api.config.PluginConfigServiceApi;
 import com.ning.billing.osgi.api.http.ServletRouter;
 import com.ning.billing.osgi.http.DefaultServletRouter;
@@ -57,6 +59,8 @@ public class DefaultOSGIModule extends AbstractModule {
         installConfig();
         installOSGIServlet();
 
+        bind(OSGIService.class).to(DefaultOSGIService.class).asEagerSingleton();
+
         bind(KillbillActivator.class).asEagerSingleton();
         bind(PluginFinder.class).asEagerSingleton();
         bind(PluginConfigServiceApi.class).to(DefaultPluginConfigServiceApi.class).asEagerSingleton();
diff --git a/osgi/src/main/java/com/ning/billing/osgi/http/DefaultServletRouter.java b/osgi/src/main/java/com/ning/billing/osgi/http/DefaultServletRouter.java
index dea87a9..a4795d5 100644
--- a/osgi/src/main/java/com/ning/billing/osgi/http/DefaultServletRouter.java
+++ b/osgi/src/main/java/com/ning/billing/osgi/http/DefaultServletRouter.java
@@ -16,8 +16,8 @@
 
 package com.ning.billing.osgi.http;
 
-import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 import javax.inject.Singleton;
 import javax.servlet.http.HttpServlet;
@@ -27,7 +27,7 @@ import com.ning.billing.osgi.api.http.ServletRouter;
 @Singleton
 public class DefaultServletRouter implements ServletRouter {
 
-    private final Map<String, HttpServlet> pluginServlets = new HashMap<String, HttpServlet>();
+    private final Map<String, HttpServlet> pluginServlets = new ConcurrentHashMap<String, HttpServlet>();
 
     @Override
     public void registerServlet(final String pluginName, final HttpServlet httpServlet) {
@@ -35,6 +35,11 @@ public class DefaultServletRouter implements ServletRouter {
     }
 
     @Override
+    public void unregisterServlet(final String pluginName) {
+        pluginServlets.remove(pluginName);
+    }
+
+    @Override
     public HttpServlet getServletForPlugin(final String pluginName) {
         return pluginServlets.get(pluginName);
     }
diff --git a/osgi/src/main/java/com/ning/billing/osgi/KillbillActivator.java b/osgi/src/main/java/com/ning/billing/osgi/KillbillActivator.java
index 5b83eeb..8025980 100644
--- a/osgi/src/main/java/com/ning/billing/osgi/KillbillActivator.java
+++ b/osgi/src/main/java/com/ning/billing/osgi/KillbillActivator.java
@@ -16,47 +16,96 @@
 
 package com.ning.billing.osgi;
 
-import java.util.List;
-
 import javax.inject.Inject;
+import javax.servlet.http.HttpServlet;
 
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
 
 import com.ning.billing.osgi.api.OSGIKillbill;
-import com.ning.billing.payment.plugin.api.PaymentPluginApi;
-
-import com.google.common.base.Function;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.ImmutableList;
+import com.ning.billing.osgi.api.http.ServletRouter;
 
-public class KillbillActivator implements BundleActivator {
+public class KillbillActivator implements BundleActivator, ServiceListener {
 
     private final OSGIKillbill osgiKillbill;
+    private final ServletRouter servletRouter;
 
     private volatile ServiceRegistration osgiKillbillRegistration;
+
+    private BundleContext context = null;
+
     @Inject
-    public KillbillActivator(final OSGIKillbill osgiKillbill) {
+    public KillbillActivator(final OSGIKillbill osgiKillbill,
+                             final ServletRouter servletRouter) {
         this.osgiKillbill = osgiKillbill;
+        this.servletRouter = servletRouter;
     }
 
     @Override
     public void start(final BundleContext context) throws Exception {
+        this.context = context;
+
+        context.addServiceListener(this);
         registerServices(context);
     }
 
+    @Override
+    public void stop(final BundleContext context) throws Exception {
+        this.context = null;
+
+        context.removeServiceListener(this);
+        unregisterServices();
+    }
+
+    @Override
+    public void serviceChanged(final ServiceEvent event) {
+        listenForServlets(event);
+    }
+
+    private void listenForServlets(final ServiceEvent event) {
+        if (event.getType() != ServiceEvent.REGISTERED && event.getType() != ServiceEvent.UNREGISTERING) {
+            // Servlets can only be added or removed, not modified
+            return;
+        }
+        final ServiceReference serviceReference = event.getServiceReference();
+
+        // Make sure we can retrieve the plugin name
+        final String pluginName = (String) serviceReference.getProperty("killbill.pluginName");
+        if (pluginName == null) {
+            return;
+        }
+
+        // Make sure this event is for a servlet
+        HttpServlet httpServlet = null;
+        final String[] objectClass = (String[]) event.getServiceReference().getProperty("objectClass");
+        if (context != null && objectClass != null && objectClass.length > 0 && HttpServlet.class.getName().equals(objectClass[0])) {
+            final Object service = context.getService(serviceReference);
+            httpServlet = (HttpServlet) service;
+        }
+
+        if (httpServlet == null) {
+            return;
+        }
+
+        if (event.getType() == ServiceEvent.REGISTERED) {
+            servletRouter.registerServlet(pluginName, httpServlet);
+        } else if (event.getType() == ServiceEvent.UNREGISTERING) {
+            servletRouter.unregisterServlet(pluginName);
+        }
+    }
+
     private void registerServices(final BundleContext context) {
         osgiKillbillRegistration = context.registerService(OSGIKillbill.class.getName(), osgiKillbill, null);
     }
 
-    @Override
-    public void stop(final BundleContext context) throws Exception {
+    private void unregisterServices() {
         if (osgiKillbillRegistration != null) {
             osgiKillbillRegistration.unregister();
             osgiKillbillRegistration = null;
         }
     }
-
 }
diff --git a/osgi-bundles/jruby/pom.xml b/osgi-bundles/jruby/pom.xml
index 18b9486..7166849 100644
--- a/osgi-bundles/jruby/pom.xml
+++ b/osgi-bundles/jruby/pom.xml
@@ -73,9 +73,10 @@
                 <configuration>
                     <instructions>
                         <Bundle-Activator>com.ning.billing.osgi.bundles.jruby.Activator</Bundle-Activator>
-                        <Import-Package>
-                            *;resolution:=optional,org.osgi.service.log;resolution:=optional,org.jruby;resolution:=optional;version="[1.7,2)",javax.management,javax.crypto,javax.net.ssl,javax.security.auth.x500;resolution:=optional
-                        </Import-Package>
+                        <Export-Package></Export-Package>
+                        <Private-Package>com.ning.billing.osgi.bundles.jruby.*</Private-Package>
+                        <!-- Optional resolution because exported by the Felix system bundle -->
+                        <Import-Package>*;resolution:=optional,javax.management;javax.management.*;javax.crypto;javax.crypto.*;javax.net;javax.net.*;javax.security;javax.security.*;resolution:=optional</Import-Package>
                     </instructions>
                 </configuration>
                 <executions>
diff --git a/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/Activator.java b/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/Activator.java
index 1d3ac3f..ab65cb6 100644
--- a/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/Activator.java
+++ b/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/Activator.java
@@ -54,9 +54,9 @@ public class Activator implements BundleActivator {
         // Setup JRuby
         final ScriptingContainer scriptingContainer = setupScriptingContainer(rubyConfig);
         if (PluginType.NOTIFICATION.equals(rubyConfig.getPluginType())) {
-            plugin = new JRubyNotificationPlugin(rubyConfig, scriptingContainer, osgiKillbill, logger);
+            plugin = new JRubyNotificationPlugin(rubyConfig, scriptingContainer, context, logger);
         } else if (PluginType.PAYMENT.equals(rubyConfig.getPluginType())) {
-            plugin = new JRubyPaymentPlugin(rubyConfig, scriptingContainer, osgiKillbill, logger);
+            plugin = new JRubyPaymentPlugin(rubyConfig, scriptingContainer, context, logger);
         }
 
         // Validate and instantiate the plugin
diff --git a/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyHttpServlet.java b/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyHttpServlet.java
new file mode 100644
index 0000000..7e576cc
--- /dev/null
+++ b/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyHttpServlet.java
@@ -0,0 +1,40 @@
+/*
+ * 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.bundles.jruby;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.jruby.runtime.builtin.IRubyObject;
+
+public class JRubyHttpServlet extends HttpServlet {
+
+    private final HttpServlet delegate;
+
+    public JRubyHttpServlet(final IRubyObject rubyObject) {
+        delegate = (HttpServlet) rubyObject.toJava(HttpServlet.class);
+    }
+
+    @Override
+    protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+        delegate.service(req, resp);
+    }
+}
diff --git a/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyNotificationPlugin.java b/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyNotificationPlugin.java
index 9ac561a..3887a80 100644
--- a/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyNotificationPlugin.java
+++ b/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyNotificationPlugin.java
@@ -26,7 +26,6 @@ import org.osgi.service.log.LogService;
 
 import com.ning.billing.beatrix.bus.api.ExtBusEvent;
 import com.ning.billing.beatrix.bus.api.ExternalBus;
-import com.ning.billing.osgi.api.OSGIKillbill;
 import com.ning.billing.osgi.api.config.PluginRubyConfig;
 
 import com.google.common.eventbus.Subscribe;
@@ -34,8 +33,8 @@ import com.google.common.eventbus.Subscribe;
 public class JRubyNotificationPlugin extends JRubyPlugin {
 
     public JRubyNotificationPlugin(final PluginRubyConfig config, final ScriptingContainer container,
-                                   final OSGIKillbill osgiKillbill, @Nullable final LogService logger) {
-        super(config, container, osgiKillbill, logger);
+                                   final BundleContext bundleContext, @Nullable final LogService logger) {
+        super(config, container, bundleContext, logger);
     }
 
     @Override
diff --git a/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyPaymentPlugin.java b/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyPaymentPlugin.java
index 2f061f0..3002635 100644
--- a/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyPaymentPlugin.java
+++ b/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyPaymentPlugin.java
@@ -31,7 +31,6 @@ import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.log.LogService;
 
-import com.ning.billing.osgi.api.OSGIKillbill;
 import com.ning.billing.osgi.api.config.PluginRubyConfig;
 import com.ning.billing.payment.api.PaymentMethodPlugin;
 import com.ning.billing.payment.plugin.api.PaymentInfoPlugin;
@@ -47,8 +46,8 @@ public class JRubyPaymentPlugin extends JRubyPlugin implements PaymentPluginApi 
     private volatile ServiceRegistration<PaymentPluginApi> paymentInfoPluginRegistration;
 
     public JRubyPaymentPlugin(final PluginRubyConfig config, final ScriptingContainer container,
-                              final OSGIKillbill osgiKillbill, @Nullable final LogService logger) {
-        super(config, container, osgiKillbill, logger);
+                              final BundleContext bundleContext, @Nullable final LogService logger) {
+        super(config, container, bundleContext, logger);
     }
 
     @Override
diff --git a/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyPlugin.java b/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyPlugin.java
index ea0fb90..84f75a9 100644
--- a/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyPlugin.java
+++ b/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyPlugin.java
@@ -17,6 +17,7 @@
 package com.ning.billing.osgi.bundles.jruby;
 
 import java.util.Arrays;
+import java.util.Hashtable;
 import java.util.Map;
 
 import javax.annotation.Nullable;
@@ -28,9 +29,9 @@ import org.jruby.embed.EvalFailedException;
 import org.jruby.embed.ScriptingContainer;
 import org.jruby.runtime.builtin.IRubyObject;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.log.LogService;
 
-import com.ning.billing.osgi.api.OSGIKillbill;
 import com.ning.billing.osgi.api.config.PluginRubyConfig;
 
 // Bridge between the OSGI bundle and the ruby plugin
@@ -46,7 +47,7 @@ public abstract class JRubyPlugin {
     private static final String ACTIVE = "@active";
 
     protected final LogService logger;
-    protected final OSGIKillbill osgiKillbill;
+    protected final BundleContext bundleContext;
     protected final String pluginGemName;
     protected final String rubyRequire;
     protected final String pluginMainClass;
@@ -55,12 +56,13 @@ public abstract class JRubyPlugin {
 
     protected RubyObject pluginInstance;
 
+    private ServiceRegistration httpServletServiceRegistration = null;
     private String cachedRequireLine = null;
 
     public JRubyPlugin(final PluginRubyConfig config, final ScriptingContainer container,
-                       final OSGIKillbill osgiKillbill, @Nullable final LogService logger) {
+                       final BundleContext bundleContext, @Nullable final LogService logger) {
         this.logger = logger;
-        this.osgiKillbill = osgiKillbill;
+        this.bundleContext = bundleContext;
         this.pluginGemName = config.getPluginName();
         this.rubyRequire = config.getRubyRequire();
         this.pluginMainClass = config.getRubyMainClass();
@@ -92,26 +94,41 @@ public abstract class JRubyPlugin {
 
         // Start the plugin
         pluginInstance = (RubyObject) container.runScriptlet(pluginMainClass + ".new(" + JAVA_APIS + ")");
-
-        // Register the rack handler
-        final IRubyObject rackHandler = pluginInstance.callMethod("rack_handler");
-        if (!rackHandler.isNil()) {
-            osgiKillbill.registerServlet(pluginGemName, (HttpServlet) rackHandler);
-        }
     }
 
     public void startPlugin(final BundleContext context) {
         checkPluginIsStopped();
         pluginInstance.callMethod("start_plugin");
         checkPluginIsRunning();
+        registerHttpServlet();
     }
 
     public void stopPlugin(final BundleContext context) {
         checkPluginIsRunning();
+        unregisterHttpServlet();
         pluginInstance.callMethod("stop_plugin");
         checkPluginIsStopped();
     }
 
+    private void registerHttpServlet() {
+        // Register the rack handler
+        final IRubyObject rackHandler = pluginInstance.callMethod("rack_handler");
+        if (!rackHandler.isNil()) {
+            log(LogService.LOG_INFO, String.format("Using %s as rack handler", rackHandler.getMetaClass()));
+
+            final JRubyHttpServlet jRubyHttpServlet = new JRubyHttpServlet(rackHandler);
+            final Hashtable<String, String> properties = new Hashtable<>();
+            properties.put("killbill.pluginName", pluginGemName);
+            httpServletServiceRegistration = bundleContext.registerService(HttpServlet.class.getName(), jRubyHttpServlet, properties);
+        }
+    }
+
+    private void unregisterHttpServlet() {
+        if (httpServletServiceRegistration != null) {
+            httpServletServiceRegistration.unregister();
+        }
+    }
+
     protected void checkPluginIsRunning() {
         if (pluginInstance == null || !pluginInstance.getInstanceVariable(ACTIVE).isTrue()) {
             throw new IllegalStateException(String.format("Plugin %s didn't start properly", pluginMainClass));

pom.xml 7(+7 -0)

diff --git a/pom.xml b/pom.xml
index d9b4619..dc27391 100644
--- a/pom.xml
+++ b/pom.xml
@@ -92,6 +92,7 @@
                 <groupId>javax.servlet</groupId>
                 <artifactId>javax.servlet-api</artifactId>
                 <version>3.0.1</version>
+                <scope>provided</scope>
             </dependency>
             <dependency>
                 <groupId>com.ning.billing</groupId>
@@ -316,6 +317,12 @@
                 <scope>provided</scope>
             </dependency>
             <dependency>
+                <groupId>com.google.code.findbugs</groupId>
+                <artifactId>jsr305</artifactId>
+                <version>1.3.9</version>
+                <scope>provided</scope>
+            </dependency>
+            <dependency>
                 <groupId>com.google.inject</groupId>
                 <artifactId>guice</artifactId>
                 <version>3.0</version>
diff --git a/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java b/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
index 1640670..7ac6d45 100644
--- a/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
+++ b/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
@@ -31,18 +31,21 @@ import com.ning.billing.jaxrs.resources.CatalogResource;
 import com.ning.billing.jaxrs.resources.InvoiceResource;
 import com.ning.billing.jaxrs.resources.PaymentMethodResource;
 import com.ning.billing.jaxrs.resources.PaymentResource;
+import com.ning.billing.jaxrs.resources.PluginResource;
 import com.ning.billing.jaxrs.resources.RefundResource;
 import com.ning.billing.jaxrs.resources.SubscriptionResource;
 import com.ning.billing.jaxrs.resources.TagResource;
 import com.ning.billing.jaxrs.resources.TenantResource;
 import com.ning.billing.jaxrs.util.KillbillEventHandler;
 import com.ning.billing.junction.glue.DefaultJunctionModule;
+import com.ning.billing.osgi.glue.DefaultOSGIModule;
 import com.ning.billing.overdue.glue.DefaultOverdueModule;
 import com.ning.billing.payment.glue.PaymentModule;
 import com.ning.billing.server.DefaultServerService;
 import com.ning.billing.server.ServerService;
 import com.ning.billing.server.notifications.PushNotificationListener;
 import com.ning.billing.tenant.glue.TenantModule;
+import com.ning.billing.usage.glue.UsageModule;
 import com.ning.billing.util.email.EmailModule;
 import com.ning.billing.util.email.templates.TemplateModule;
 import com.ning.billing.util.glue.AuditModule;
@@ -93,6 +96,7 @@ public class KillbillServerModule extends AbstractModule {
         bind(CatalogResource.class).asEagerSingleton();
         bind(PaymentMethodResource.class).asEagerSingleton();
         bind(PaymentResource.class).asEagerSingleton();
+        bind(PluginResource.class).asEagerSingleton();
         bind(RefundResource.class).asEagerSingleton();
         bind(TenantResource.class).asEagerSingleton();
         bind(KillbillEventHandler.class).asEagerSingleton();
@@ -125,6 +129,9 @@ public class KillbillServerModule extends AbstractModule {
         install(new ExportModule());
         install(new TagStoreModule());
         install(new NonEntityDaoModule());
+        install(new DefaultOSGIModule());
+        install(new UsageModule());
+
         installClock();
     }
 }
diff --git a/util/src/main/java/com/ning/billing/util/config/OSGIConfig.java b/util/src/main/java/com/ning/billing/util/config/OSGIConfig.java
index cae9b53..8299caf 100644
--- a/util/src/main/java/com/ning/billing/util/config/OSGIConfig.java
+++ b/util/src/main/java/com/ning/billing/util/config/OSGIConfig.java
@@ -53,6 +53,11 @@ public interface OSGIConfig extends KillbillConfig {
              "com.ning.billing.util.api," +
              "com.ning.billing.util.callcontext," +
              "com.google.common.eventbus," +
+             // javax.servlet and javax.servlet.http are not exported by default - we
+             // need the bundles to see them for them to be able to register their servlets.
+             // Note: bundles should mark javax.servlet:servlet-api as provided
+             "javax.servlet;version=3.0," +
+             "javax.servlet.http;version=3.0," +
              "org.osgi.service.log")
     public String getSystemBundleExportPackages();