killbill-aplcache

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

2/21/2013 12:28:06 AM

Changes

.gitignore 1(+1 -0)

.travis.yml 2(+1 -1)

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

bin/start-server 5(+3 -2)

jaxrs/pom.xml 1(+0 -1)

osgi/pom.xml 20(+14 -6)

pom.xml 511(+334 -177)

server/pom.xml 92(+0 -92)

Details

.gitignore 1(+1 -0)

diff --git a/.gitignore b/.gitignore
index ba3098e..ed2d54b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,4 @@ logs/
 catalog/src/test/resources/CatalogSchema.xsd
 */test-output/
 beatrix/src/test/resources/killbill-osgi-bundles-test-*-jar-with-dependencies.jar
+server/load

.travis.yml 2(+1 -1)

diff --git a/.travis.yml b/.travis.yml
index 6a9f366..add54e4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,7 +5,7 @@ install: mvn install -DskipTests=true
 
 notifications:
   email:
-    - ri-dev@ning.com
+    - killbilling-dev@googlegroups.com
 
 jdk:
   - openjdk6
diff --git a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
index 12638d2..7166f74 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
@@ -176,9 +176,8 @@ public class TestAnalyticsService extends AnalyticsTestSuiteWithEmbeddedDB {
         invoiceCreationNotification = new DefaultInvoiceCreationEvent(invoice.getId(), account.getId(),
                                                                       INVOICE_AMOUNT, ACCOUNT_CURRENCY, null, 1L, 1L);
 
-
-        paymentInfoNotification = new DefaultPaymentInfoEvent(UUID.randomUUID(), account.getId(), invoice.getId(), null, INVOICE_AMOUNT, -1,
-                                                              PaymentStatus.UNKNOWN, null, null, null, clock.getUTCNow(), 1L, 1L);
+        paymentInfoNotification = new DefaultPaymentInfoEvent(account.getId(), invoice.getId(), null, INVOICE_AMOUNT, -1,
+                                                              PaymentStatus.UNKNOWN, null, clock.getUTCNow(), 1L, 1L);
     }
 
     @Test(groups = "slow")

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

diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index e047bf5..9026ef1 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -81,6 +81,10 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.skife.config</groupId>
             <artifactId>config-magic</artifactId>
         </dependency>
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/osgi/TestBasicOSGIWithTestBundle.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/osgi/TestBasicOSGIWithTestBundle.java
index df39b19..bac5205 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/osgi/TestBasicOSGIWithTestBundle.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/osgi/TestBasicOSGIWithTestBundle.java
@@ -78,7 +78,7 @@ public class TestBasicOSGIWithTestBundle extends TestOSGIBase {
         // OSGIDataSourceConfig
         super.setup();
 
-        // This is extracted from surefire system configuration-- needs to be added explicitely in IntelliJ for correct running
+        // This is extracted from surefire system configuration-- needs to be added explicitly in IntelliJ for correct running
         final String killbillVersion = System.getProperty("killbill.version");
         SetupBundleWithAssertion setupTest = new SetupBundleWithAssertion(BUNDLE_TEST_RESOURCE, osgiConfig, killbillVersion);
         setupTest.setupBundle();

bin/start-server 5(+3 -2)

diff --git a/bin/start-server b/bin/start-server
index ff9b090..e5b6582 100755
--- a/bin/start-server
+++ b/bin/start-server
@@ -28,7 +28,8 @@ PROPERTIES="$SERVER/src/main/resources/killbill-server.properties"
 DEBUG_OPTS_ECLIPSE=" -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=12345 "
 DEBUG_OPTS_ECLIPSE_WAIT=" -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=12345 "
 
-OPTS_ECLIPSE=" -Xmx2048m  -XX:+UseConcMarkSweepGC -XX:MaxPermSize=128m  "
+# Default JVM settings if unset
+MAVEN_OPTS=${MAVEN_OPTS-"-Xms512m -Xmx1024m -XX:MaxPermSize=512m -XX:MaxDirectMemorySize=512m -XX:+UseConcMarkSweepGC"}
 
 LOG="$SERVER/src/main/resources/logback.xml"
 
@@ -72,7 +73,7 @@ function start() {
             debug_opts_eclipse=$DEBUG_OPTS_ECLIPSE
         fi
     fi
-    export MAVEN_OPTS=" -Duser.timezone=UTC $OPTS_ECLIPSE $debug_opts_eclipse"
+    export MAVEN_OPTS="$MAVEN_OPTS -Duser.timezone=UTC $debug_opts_eclipse"
 
     echo "Starting IRS MAVEN_OPTS = $MAVEN_OPTS"
     echo "$start_cmd"

jaxrs/pom.xml 1(+0 -1)

diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index 3902d2d..1e260a8 100644
--- a/jaxrs/pom.xml
+++ b/jaxrs/pom.xml
@@ -35,7 +35,6 @@
         <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>
-            <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>javax.ws.rs</groupId>
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
index 9fee49d..fae67ff 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PluginResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PluginResource.java
@@ -18,10 +18,14 @@ package com.ning.billing.jaxrs.resources;
 
 import java.io.IOException;
 
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
 import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
 import javax.ws.rs.HEAD;
@@ -29,7 +33,6 @@ 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;
@@ -43,7 +46,7 @@ import com.google.inject.Singleton;
 import com.google.inject.name.Named;
 
 @Singleton
-@Path(JaxrsResource.PLUGINS_PATH)
+@Path(JaxrsResource.PLUGINS_PATH + "{subResources:.*}")
 public class PluginResource extends JaxRsResourceBase {
 
     private final HttpServlet osgiServlet;
@@ -60,65 +63,96 @@ public class PluginResource extends JaxRsResourceBase {
     }
 
     @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);
+    public Response doDELETE(@javax.ws.rs.core.Context final HttpServletRequest request,
+                             @javax.ws.rs.core.Context final HttpServletResponse response,
+                             @javax.ws.rs.core.Context final ServletContext servletContext,
+                             @javax.ws.rs.core.Context final ServletConfig servletConfig) throws ServletException, IOException {
+        return serviceViaOSGIPlugin(request, response, servletContext, servletConfig);
     }
 
     @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);
+    public Response doGET(@javax.ws.rs.core.Context final HttpServletRequest request,
+                          @javax.ws.rs.core.Context final HttpServletResponse response,
+                          @javax.ws.rs.core.Context final ServletContext servletContext,
+                          @javax.ws.rs.core.Context final ServletConfig servletConfig) throws ServletException, IOException {
+        return serviceViaOSGIPlugin(request, response, servletContext, servletConfig);
     }
 
     @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);
+    public Response doOPTIONS(@javax.ws.rs.core.Context final HttpServletRequest request,
+                              @javax.ws.rs.core.Context final HttpServletResponse response,
+                              @javax.ws.rs.core.Context final ServletContext servletContext,
+                              @javax.ws.rs.core.Context final ServletConfig servletConfig) throws ServletException, IOException {
+        return serviceViaOSGIPlugin(request, response, servletContext, servletConfig);
     }
 
     @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);
+    public Response doPOST(@javax.ws.rs.core.Context final HttpServletRequest request,
+                           @javax.ws.rs.core.Context final HttpServletResponse response,
+                           @javax.ws.rs.core.Context final ServletContext servletContext,
+                           @javax.ws.rs.core.Context final ServletConfig servletConfig) throws ServletException, IOException {
+        return serviceViaOSGIPlugin(request, response, servletContext, servletConfig);
     }
 
     @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);
+    public Response doPUT(@javax.ws.rs.core.Context final HttpServletRequest request,
+                          @javax.ws.rs.core.Context final HttpServletResponse response,
+                          @javax.ws.rs.core.Context final ServletContext servletContext,
+                          @javax.ws.rs.core.Context final ServletConfig servletConfig) throws ServletException, IOException {
+        return serviceViaOSGIPlugin(request, response, servletContext, servletConfig);
     }
 
     @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);
+    public Response doHEAD(@javax.ws.rs.core.Context final HttpServletRequest request,
+                           @javax.ws.rs.core.Context final HttpServletResponse response,
+                           @javax.ws.rs.core.Context final ServletContext servletContext,
+                           @javax.ws.rs.core.Context final ServletConfig servletConfig) throws ServletException, IOException {
+        serviceViaOSGIPlugin(request, response, servletContext, servletConfig);
 
         // 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);
+    private Response serviceViaOSGIPlugin(final HttpServletRequest request, final HttpServletResponse response,
+                                          final ServletContext servletContext, final ServletConfig servletConfig) throws ServletException, IOException {
+        prepareOSGIRequest(request, servletContext, servletConfig);
+        osgiServlet.service(new OSGIServletRequestWrapper(request), new OSGIServletResponseWrapper(response));
 
         return Response.status(response.getStatus()).build();
     }
 
-    private void prepareOSGIRequest(final String pluginName, final HttpServletRequest request) {
-        request.setAttribute("killbill.osgi.pluginName", pluginName);
+    private void prepareOSGIRequest(final HttpServletRequest request, final ServletContext servletContext, final ServletConfig servletConfig) {
+        request.setAttribute("killbill.osgi.servletContext", servletContext);
+        request.setAttribute("killbill.osgi.servletConfig", servletConfig);
+    }
+
+    // Request wrapper to hide the /plugins prefix to OSGI bundles
+    private static final class OSGIServletRequestWrapper extends HttpServletRequestWrapper {
+
+        public OSGIServletRequestWrapper(final HttpServletRequest request) {
+            super(request);
+        }
+
+        @Override
+        public String getPathInfo() {
+            return super.getPathInfo().replace(JaxrsResource.PLUGINS_PATH, "");
+        }
+
+        @Override
+        public String getContextPath() {
+            return JaxrsResource.PLUGINS_PATH;
+        }
+
+        @Override
+        public String getServletPath() {
+            return super.getServletPath().replace(JaxrsResource.PLUGINS_PATH, "");
+        }
+    }
+
+    private static final class OSGIServletResponseWrapper extends HttpServletResponseWrapper {
+
+        public OSGIServletResponseWrapper(final HttpServletResponse response) {
+            super(response);
+        }
     }
 }

osgi/pom.xml 20(+14 -6)

diff --git a/osgi/pom.xml b/osgi/pom.xml
index aa7d674..242dd7e 100644
--- a/osgi/pom.xml
+++ b/osgi/pom.xml
@@ -15,7 +15,8 @@
   ~ 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">
+<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>com.ning.billing</groupId>
@@ -32,6 +33,18 @@
             <artifactId>org.apache.felix.framework</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.fileinstall</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+        <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         </dependency>
@@ -64,11 +77,6 @@
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
-        <!-- Default Bundles -->
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>osgi-over-slf4j</artifactId>
-        </dependency>
         <!-- TEST SCOPE -->
         <dependency>
             <groupId>org.slf4j</groupId>
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 c6f503b..2f2fc65 100644
--- a/osgi/src/main/java/com/ning/billing/osgi/DefaultOSGIService.java
+++ b/osgi/src/main/java/com/ning/billing/osgi/DefaultOSGIService.java
@@ -24,35 +24,29 @@ import java.util.Map;
 
 import javax.inject.Inject;
 
+import org.apache.felix.fileinstall.internal.FileInstall;
 import org.apache.felix.framework.Felix;
 import org.apache.felix.framework.util.FelixConstants;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleException;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
 import org.osgi.framework.launch.Framework;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.slf4j.osgi.logservice.impl.Activator;
 
 import com.ning.billing.lifecycle.LifecycleHandlerType;
 import com.ning.billing.lifecycle.LifecycleHandlerType.LifecycleLevel;
-import com.ning.billing.osgi.api.OSGIPluginProperties;
 import com.ning.billing.osgi.api.OSGIService;
-import com.ning.billing.osgi.api.OSGIServiceRegistration;
 import com.ning.billing.osgi.api.config.PluginConfigServiceApi;
 import com.ning.billing.osgi.api.config.PluginJavaConfig;
 import com.ning.billing.osgi.api.config.PluginRubyConfig;
 import com.ning.billing.osgi.pluginconf.DefaultPluginConfigServiceApi;
 import com.ning.billing.osgi.pluginconf.PluginConfigException;
 import com.ning.billing.osgi.pluginconf.PluginFinder;
-import com.ning.billing.payment.plugin.api.PaymentPluginApi;
 import com.ning.billing.util.config.OSGIConfig;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 
 public class DefaultOSGIService implements OSGIService {
 
@@ -60,15 +54,12 @@ public class DefaultOSGIService implements OSGIService {
 
     private static final Logger logger = LoggerFactory.getLogger(DefaultOSGIService.class);
 
-
     private final OSGIConfig osgiConfig;
     private final PluginFinder pluginFinder;
     private final PluginConfigServiceApi pluginConfigServiceApi;
     private final KillbillActivator killbillActivator;
 
     private Framework framework;
-    private volatile ServiceReference[] paymentApiReferences;
-    private Map<String, PaymentPluginApi> paymentPluginApis;
 
     @Inject
     public DefaultOSGIService(final OSGIConfig osgiConfig, final PluginFinder pluginFinder,
@@ -115,7 +106,6 @@ public class DefaultOSGIService implements OSGIService {
     public void startFramework() {
     }
 
-
     @LifecycleHandlerType(LifecycleLevel.STOP_SERVICE)
     public void stop() {
         try {
@@ -133,6 +123,8 @@ public class DefaultOSGIService implements OSGIService {
             final BundleContext context = framework.getBundleContext();
 
             // Install all bundles and create service mapping
+            // TODO PIERRE Could we leverage Felix fileinstall plugin to manage Killbill plugins?
+
             final List<Bundle> installedBundles = new LinkedList<Bundle>();
             installAllJavaBundles(context, installedBundles);
             installAllJRubyBundles(context, installedBundles);
@@ -186,8 +178,12 @@ public class DefaultOSGIService implements OSGIService {
         final Map<Object, Object> felixConfig = new HashMap<Object, Object>();
         felixConfig.putAll(config);
 
-        // Install default bundles: killbill and slf4j ones
-        felixConfig.put(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP, ImmutableList.<BundleActivator>of(killbillActivator, new Activator()));
+        // Install default bundles in the Framework: Killbill bundle and Felix fileinstall bundle
+        // Note! Think twice before adding a bundle here as it will run inside the System bundle. This means the bundle
+        // context that the bundle will see is the System bundle one, which will break e.g. resources lookup
+        felixConfig.put(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP,
+                        ImmutableList.<BundleActivator>of(new FileInstall(),
+                                                          killbillActivator));
 
         final Framework felix = new Felix(felixConfig);
         felix.init();
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 701e8dc..6b9ff07 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,9 +16,11 @@
 
 package com.ning.billing.osgi.glue;
 
+import javax.servlet.Servlet;
 import javax.servlet.http.HttpServlet;
 import javax.sql.DataSource;
 
+import org.osgi.service.http.HttpService;
 import org.skife.config.ConfigurationObjectFactory;
 
 import com.ning.billing.osgi.DefaultOSGIKillbill;
@@ -28,6 +30,7 @@ import com.ning.billing.osgi.api.OSGIKillbill;
 import com.ning.billing.osgi.api.OSGIService;
 import com.ning.billing.osgi.api.OSGIServiceRegistration;
 import com.ning.billing.osgi.api.config.PluginConfigServiceApi;
+import com.ning.billing.osgi.http.DefaultHttpService;
 import com.ning.billing.osgi.http.DefaultServletRouter;
 import com.ning.billing.osgi.http.OSGIServlet;
 import com.ning.billing.osgi.pluginconf.DefaultPluginConfigServiceApi;
@@ -51,14 +54,19 @@ public class DefaultOSGIModule extends AbstractModule {
     }
 
     protected void installOSGIServlet() {
-        bind(new TypeLiteral<OSGIServiceRegistration<HttpServlet>>() {}).to(DefaultServletRouter.class).asEagerSingleton();
+        bind(new TypeLiteral<OSGIServiceRegistration<Servlet>>() {}).to(DefaultServletRouter.class).asEagerSingleton();
         bind(HttpServlet.class).annotatedWith(Names.named(OSGI_NAMED)).to(OSGIServlet.class).asEagerSingleton();
     }
 
+    protected void installHttpService() {
+        bind(HttpService.class).to(DefaultHttpService.class).asEagerSingleton();
+    }
+
     @Override
     protected void configure() {
         installConfig();
         installOSGIServlet();
+        installHttpService();
 
         bind(OSGIService.class).to(DefaultOSGIService.class).asEagerSingleton();
 
diff --git a/osgi/src/main/java/com/ning/billing/osgi/http/DefaultHttpContext.java b/osgi/src/main/java/com/ning/billing/osgi/http/DefaultHttpContext.java
new file mode 100644
index 0000000..9ded549
--- /dev/null
+++ b/osgi/src/main/java/com/ning/billing/osgi/http/DefaultHttpContext.java
@@ -0,0 +1,45 @@
+/*
+ * 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.http;
+
+import java.io.IOException;
+import java.net.URL;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.osgi.service.http.HttpContext;
+
+public class DefaultHttpContext implements HttpContext {
+
+    @Override
+    public boolean handleSecurity(final HttpServletRequest request, final HttpServletResponse response) throws IOException {
+        // Security should have already been handled by Shiro
+        return true;
+    }
+
+    @Override
+    public URL getResource(final String name) {
+        // Maybe it's in our classpath?
+        return DefaultHttpContext.class.getClassLoader().getResource(name);
+    }
+
+    @Override
+    public String getMimeType(final String name) {
+        return null;
+    }
+}
diff --git a/osgi/src/main/java/com/ning/billing/osgi/http/DefaultHttpService.java b/osgi/src/main/java/com/ning/billing/osgi/http/DefaultHttpService.java
new file mode 100644
index 0000000..a5d606c
--- /dev/null
+++ b/osgi/src/main/java/com/ning/billing/osgi/http/DefaultHttpService.java
@@ -0,0 +1,71 @@
+/*
+ * 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.http;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.HttpService;
+import org.osgi.service.http.NamespaceException;
+
+@Singleton
+public class DefaultHttpService implements HttpService {
+
+    private final DefaultServletRouter servletRouter;
+
+    @Inject
+    public DefaultHttpService(final DefaultServletRouter servletRouter) {
+        this.servletRouter = servletRouter;
+    }
+
+    @Override
+    public void registerServlet(final String alias, final Servlet servlet, final Dictionary initparams, final HttpContext httpContext) throws ServletException, NamespaceException {
+        if (alias == null) {
+            throw new IllegalArgumentException("Invalid alias (null)");
+        } else if (servlet == null) {
+            throw new IllegalArgumentException("Invalid servlet (null)");
+        }
+
+        servletRouter.registerService(alias, servlet);
+    }
+
+    @Override
+    public void registerResources(final String alias, final String name, final HttpContext httpContext) throws NamespaceException {
+        final Servlet staticServlet = new StaticServlet(httpContext);
+        try {
+            registerServlet(alias, staticServlet, new Hashtable(), httpContext);
+        } catch (ServletException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    @Override
+    public void unregister(final String alias) {
+        servletRouter.unregisterService(alias);
+    }
+
+    @Override
+    public HttpContext createDefaultHttpContext() {
+        return new DefaultHttpContext();
+    }
+}
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 fc36943..e822259 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
@@ -21,28 +21,44 @@ import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
 import javax.inject.Singleton;
-import javax.servlet.http.HttpServlet;
+import javax.servlet.Servlet;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.ning.billing.osgi.api.OSGIServiceRegistration;
 
 @Singleton
-public class DefaultServletRouter implements OSGIServiceRegistration<HttpServlet> {
+public class DefaultServletRouter implements OSGIServiceRegistration<Servlet> {
+
+    private static final Logger logger = LoggerFactory.getLogger(DefaultServletRouter.class);
 
-    private final Map<String, HttpServlet> pluginServlets = new ConcurrentHashMap<String, HttpServlet>();
+    // Internal Servlet routing table: map of plugin prefixes to servlet instances.
+    // A plugin prefix can be /foo, /foo/bar, /foo/bar/baz, ... and is mounted on /plugins/<pluginPrefix>
+    private final Map<String, Servlet> pluginServlets = new ConcurrentHashMap<String, Servlet>();
 
     @Override
-    public void registerService(final String pluginName, final HttpServlet httpServlet) {
-        pluginServlets.put(pluginName, httpServlet);
+    public void registerService(final String originalPathPrefix, final Servlet httpServlet) {
+        // Enforce each route to start with /
+        final String pathPrefix;
+        if (originalPathPrefix.charAt(0) != '/') {
+            pathPrefix = "/" + originalPathPrefix;
+        } else {
+            pathPrefix = originalPathPrefix;
+        }
+        logger.info("Registering OSGI servlet at " + pathPrefix);
+        pluginServlets.put(pathPrefix, httpServlet);
     }
 
     @Override
-    public void unregisterService(final String pluginName) {
-        pluginServlets.remove(pluginName);
+    public void unregisterService(final String pathPrefix) {
+        logger.info("Unregistering OSGI servlet at " + pathPrefix);
+        pluginServlets.remove(pathPrefix);
     }
 
     @Override
-    public HttpServlet getServiceForPluginName(final String pluginName) {
-        return pluginServlets.get(pluginName);
+    public Servlet getServiceForPluginName(final String pathPrefix) {
+        return getServletForPathPrefix(pathPrefix);
     }
 
     @Override
@@ -51,7 +67,23 @@ public class DefaultServletRouter implements OSGIServiceRegistration<HttpServlet
     }
 
     @Override
-    public Class<HttpServlet> getServiceType() {
-        return HttpServlet.class;
+    public Class<Servlet> getServiceType() {
+        return Servlet.class;
+    }
+
+    // TODO PIERRE Naive implementation - we should rather switch to e.g. heap tree
+    public String getPluginPrefixForPath(final String pathPrefix) {
+        String bestMatch = null;
+        for (final String potentialMatch : pluginServlets.keySet()) {
+            if (pathPrefix.startsWith(potentialMatch) && (bestMatch == null || bestMatch.length() < potentialMatch.length())) {
+                bestMatch = potentialMatch;
+            }
+        }
+        return bestMatch;
+    }
+
+    private Servlet getServletForPathPrefix(final String pathPrefix) {
+        final String bestMatch = getPluginPrefixForPath(pathPrefix);
+        return bestMatch == null ? null : pluginServlets.get(bestMatch);
     }
 }
diff --git a/osgi/src/main/java/com/ning/billing/osgi/http/OSGIServlet.java b/osgi/src/main/java/com/ning/billing/osgi/http/OSGIServlet.java
index 182d142..a9af086 100644
--- a/osgi/src/main/java/com/ning/billing/osgi/http/OSGIServlet.java
+++ b/osgi/src/main/java/com/ning/billing/osgi/http/OSGIServlet.java
@@ -17,21 +17,26 @@
 package com.ning.billing.osgi.http;
 
 import java.io.IOException;
+import java.util.Vector;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
 import javax.servlet.http.HttpServletResponse;
 
-import com.ning.billing.osgi.api.OSGIServiceRegistration;
-
 @Singleton
 public class OSGIServlet extends HttpServlet {
 
+    private final Vector<Servlet> initializedServlets = new Vector<Servlet>();
+    private final Object servletsMonitor = new Object();
+
     @Inject
-    private OSGIServiceRegistration<HttpServlet> servletRouter;
+    private DefaultServletRouter servletRouter;
 
     @Override
     protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
@@ -64,16 +69,57 @@ public class OSGIServlet extends HttpServlet {
     }
 
     private void serviceViaPlugin(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
-        final HttpServlet pluginServlet = getPluginServlet(req);
+        // requestPath is the full path minus the JAX-RS prefix (/plugins)
+        final String requestPath = req.getServletPath() + req.getPathInfo();
+
+        final Servlet pluginServlet = getPluginServlet(requestPath);
         if (pluginServlet != null) {
-            pluginServlet.service(req, resp);
+            initializeServletIfNeeded(req, pluginServlet);
+            final OSGIServletRequestWrapper requestWrapper = new OSGIServletRequestWrapper(req, servletRouter.getPluginPrefixForPath(requestPath));
+            pluginServlet.service(requestWrapper, resp);
         } else {
             resp.sendError(404);
         }
     }
 
-    private HttpServlet getPluginServlet(final HttpServletRequest req) {
-        final String pluginName = (String) req.getAttribute("killbill.osgi.pluginName");
+    // Request wrapper to hide the plugin prefix to OSGI servlets (the plugin prefix serves as a servlet path)
+    private static final class OSGIServletRequestWrapper extends HttpServletRequestWrapper {
+
+        private final String pluginPrefix;
+
+        public OSGIServletRequestWrapper(final HttpServletRequest request, final String pluginPrefix) {
+            super(request);
+            this.pluginPrefix = pluginPrefix;
+        }
+
+        @Override
+        public String getPathInfo() {
+            return super.getPathInfo().replace(pluginPrefix, "");
+        }
+
+        @Override
+        public String getContextPath() {
+            return super.getContextPath() + pluginPrefix;
+        }
+    }
+
+    // Hack to bridge the gap between the web container and the OSGI servlets
+    private void initializeServletIfNeeded(final HttpServletRequest req, final Servlet pluginServlet) throws ServletException {
+        if (!initializedServlets.contains(pluginServlet)) {
+            synchronized (servletsMonitor) {
+                if (!initializedServlets.contains(pluginServlet)) {
+                    final ServletConfig servletConfig = (ServletConfig) req.getAttribute("killbill.osgi.servletConfig");
+                    if (servletConfig != null) {
+                        // TODO PIERRE The servlet will never be destroyed!
+                        pluginServlet.init(servletConfig);
+                        initializedServlets.add(pluginServlet);
+                    }
+                }
+            }
+        }
+    }
+
+    private Servlet getPluginServlet(final String pluginName) {
         if (pluginName != null) {
             return servletRouter.getServiceForPluginName(pluginName);
         } else {
diff --git a/osgi/src/main/java/com/ning/billing/osgi/http/StaticServlet.java b/osgi/src/main/java/com/ning/billing/osgi/http/StaticServlet.java
new file mode 100644
index 0000000..9f8d5ac
--- /dev/null
+++ b/osgi/src/main/java/com/ning/billing/osgi/http/StaticServlet.java
@@ -0,0 +1,86 @@
+/*
+ * 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.http;
+
+import java.io.IOException;
+import java.net.URL;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+
+import org.osgi.service.http.HttpContext;
+
+import com.google.common.io.Resources;
+
+// Simple servlet to serve OSGI resources
+public class StaticServlet extends HttpServlet {
+
+    private final HttpContext httpContext;
+
+    public StaticServlet(final HttpContext httpContext) {
+        this.httpContext = httpContext;
+    }
+
+    @Override
+    protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+        final URL url = findResourceURL(req);
+        if (url != null) {
+            Resources.copy(url, resp.getOutputStream());
+            resp.setStatus(200);
+            return;
+        }
+
+        // If we can't find it, the container might
+        final RequestDispatcher rd = getServletContext().getNamedDispatcher("default");
+        final HttpServletRequest wrapped = new HttpServletRequestWrapper(req) {
+            public String getServletPath() { return ""; }
+        };
+        rd.forward(wrapped, resp);
+    }
+
+    // TODO PIERRE HUGE HACK
+    // We don't really know at this point the resource path to look for
+    // e.g. if the request is for /plugins/foo/bar/baz/qux.css, should
+    // we look for /qux.css? /baz/qux.css? /bar/baz/qux.css? /foo/bar/baz/qux.css?
+    private URL findResourceURL(final HttpServletRequest request) {
+        final String url = request.getRequestURI();
+        for (int i = 0; i < url.lastIndexOf('/'); i++) {
+            final int idx = url.indexOf('/', i);
+            if (idx > -1) {
+                final String resourceName = url.substring(idx);
+                final URL match = findResourceURL(resourceName);
+                if (match != null) {
+                    return match;
+                }
+            }
+        }
+        return null;
+    }
+
+    private URL findResourceURL(final String resourceName) {
+        URL url = httpContext.getResource(resourceName);
+        if (url == null) {
+            // Look into the OSGI bundle JAR
+            url = httpContext.getClass().getResource(resourceName);
+        }
+        return url;
+    }
+}
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 33b320f..79f082b 100644
--- a/osgi/src/main/java/com/ning/billing/osgi/KillbillActivator.java
+++ b/osgi/src/main/java/com/ning/billing/osgi/KillbillActivator.java
@@ -19,7 +19,7 @@ package com.ning.billing.osgi;
 import java.util.List;
 
 import javax.inject.Inject;
-import javax.servlet.http.HttpServlet;
+import javax.servlet.Servlet;
 
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
@@ -27,6 +27,7 @@ import org.osgi.framework.ServiceEvent;
 import org.osgi.framework.ServiceListener;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.http.HttpService;
 
 import com.ning.billing.osgi.api.OSGIKillbill;
 import com.ning.billing.osgi.api.OSGIPluginProperties;
@@ -38,6 +39,7 @@ import com.google.common.collect.ImmutableList;
 public class KillbillActivator implements BundleActivator, ServiceListener {
 
     private final OSGIKillbill osgiKillbill;
+    private final HttpService defaultHttpService;
     private final List<OSGIServiceRegistration> allRegistrationHandlers;
 
     private volatile ServiceRegistration osgiKillbillRegistration;
@@ -46,9 +48,11 @@ public class KillbillActivator implements BundleActivator, ServiceListener {
 
     @Inject
     public KillbillActivator(final OSGIKillbill osgiKillbill,
-                             final OSGIServiceRegistration<HttpServlet> servletRouter,
+                             final HttpService defaultHttpService,
+                             final OSGIServiceRegistration<Servlet> servletRouter,
                              final OSGIServiceRegistration<PaymentPluginApi> paymentProviderPluginRegistry) {
         this.osgiKillbill = osgiKillbill;
+        this.defaultHttpService = defaultHttpService;
         this.allRegistrationHandlers = ImmutableList.<OSGIServiceRegistration>of(servletRouter, paymentProviderPluginRegistry);
     }
 
@@ -81,45 +85,39 @@ public class KillbillActivator implements BundleActivator, ServiceListener {
         }
     }
 
-    private <T> boolean listenForServiceType(final ServiceEvent event, Class<T> claz, final OSGIServiceRegistration<T> registation) {
-
-        // Is that for us ?
-        final String[] objectClass = (String[]) event.getServiceReference().getProperty("objectClass");
-        if (objectClass == null || objectClass.length == 0 || !claz.getName().equals(objectClass[0])) {
-            return false;
-        }
-
+    private <T> boolean listenForServiceType(final ServiceEvent event, final Class<T> claz, final OSGIServiceRegistration<T> registration) {
         // Make sure we can retrieve the plugin name
         final ServiceReference serviceReference = event.getServiceReference();
         final String pluginName = (String) serviceReference.getProperty(OSGIPluginProperties.PLUGIN_NAME_PROP);
         if (pluginName == null) {
-            // STEPH logger ?
+            // TODO STEPH logger ?
             return true;
         }
 
         final T theService = (T) context.getService(serviceReference);
-        if (theService == null) {
-            return true;
+        // Is that for us? We look for a subclass here for greater flexibility (e.g. HttpServlet for a Servlet service)
+        if (theService == null || !claz.isAssignableFrom(theService.getClass())) {
+            return false;
         }
 
         switch (event.getType()) {
             case ServiceEvent.REGISTERED:
-                registation.registerService(pluginName, theService);
-
+                registration.registerService(pluginName, theService);
                 break;
             case ServiceEvent.UNREGISTERING:
-                registation.unregisterService(pluginName);
+                registration.unregisterService(pluginName);
                 break;
-
             default:
                 break;
         }
+
         return true;
     }
 
-
     private void registerServices(final BundleContext context) {
         osgiKillbillRegistration = context.registerService(OSGIKillbill.class.getName(), osgiKillbill, null);
+
+        context.registerService(HttpService.class.getName(), defaultHttpService, null);
     }
 
     private void unregisterServices() {
diff --git a/osgi-bundles/defaultbundles/pom.xml b/osgi-bundles/defaultbundles/pom.xml
new file mode 100644
index 0000000..dd0c884
--- /dev/null
+++ b/osgi-bundles/defaultbundles/pom.xml
@@ -0,0 +1,163 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+
+<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>com.ning.billing</groupId>
+        <artifactId>killbill-osgi-bundles</artifactId>
+        <version>0.1.56-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <artifactId>killbill-osgi-bundles-defaultbundles</artifactId>
+    <name>Killbill billing platform: OSGI default bundles</name>
+    <packaging>pom</packaging>
+    <dependencies>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-osgi-bundles-webconsolebranding</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.bundlerepository</artifactId>
+            <version>1.6.6</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.configadmin</artifactId>
+            <version>1.6.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.dependencymanager</artifactId>
+            <version>3.1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.dependencymanager.annotation</artifactId>
+            <version>3.1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.dependencymanager.compat</artifactId>
+            <version>3.0.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.dependencymanager.runtime</artifactId>
+            <version>3.1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.dependencymanager.shell</artifactId>
+            <version>3.0.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.deploymentadmin</artifactId>
+            <version>0.9.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.gogo.runtime</artifactId>
+            <version>0.10.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.log</artifactId>
+            <version>1.0.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.metatype</artifactId>
+            <version>1.0.6</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr</artifactId>
+            <version>1.6.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+            <version>1.7.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.ds-annotations</artifactId>
+            <version>1.2.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.shell</artifactId>
+            <version>1.4.3</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.shell.remote</artifactId>
+            <version>1.1.2</version>
+        </dependency>
+        <!--
+        TODO PIERRE Update to 4.0.1 or above when released.
+        4.0.0 is missing various dependencies, see https://issues.apache.org/jira/browse/FELIX-3778
+        See also https://issues.apache.org/jira/browse/FELIX-3666
+        -->
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.webconsole</artifactId>
+            <version>3.1.8</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>osgi-over-slf4j</artifactId>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <!-- Hack to avoid generating jar and test-jar artifacts -->
+                        <phase>none</phase>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                        <phase>package</phase>
+                        <configuration>
+                            <appendAssemblyId>false</appendAssemblyId>
+                            <tarLongFileMode>gnu</tarLongFileMode>
+                            <descriptors>
+                                <descriptor>src/main/assembly/assembly.xml</descriptor>
+                            </descriptors>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/osgi-bundles/defaultbundles/README.md b/osgi-bundles/defaultbundles/README.md
new file mode 100644
index 0000000..c2d1faa
--- /dev/null
+++ b/osgi-bundles/defaultbundles/README.md
@@ -0,0 +1,12 @@
+Killbill default OSGI bundles
+-----------------------------
+
+This module is simply used as a build utility. It produces a .tar.gz
+artifact containing various useful OSGI bundles one may want to have
+available by default.
+
+For example, to install these default bundles with the start-server script, unpack
+the .tar.gz artifact under the *server/load* directory.
+
+Killbill uses the Felix fileinstall bundle to detect bundles to install, see [here](http://felix.apache.org/documentation/subprojects/apache-felix-file-install.html)
+for more information.
\ No newline at end of file
diff --git a/osgi-bundles/defaultbundles/src/main/assembly/assembly.xml b/osgi-bundles/defaultbundles/src/main/assembly/assembly.xml
new file mode 100644
index 0000000..5913e1a
--- /dev/null
+++ b/osgi-bundles/defaultbundles/src/main/assembly/assembly.xml
@@ -0,0 +1,18 @@
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+    <id>jar-with-dependencies</id>
+    <formats>
+        <format>tar.gz</format>
+    </formats>
+    <includeBaseDirectory>false</includeBaseDirectory>
+    <dependencySets>
+        <dependencySet>
+            <outputDirectory>/</outputDirectory>
+            <useProjectArtifact>false</useProjectArtifact>
+            <useProjectAttachments>true</useProjectAttachments>
+            <useTransitiveDependencies>false</useTransitiveDependencies>
+            <unpack>false</unpack>
+        </dependencySet>
+    </dependencySets>
+</assembly>
diff --git a/osgi-bundles/jruby/pom.xml b/osgi-bundles/jruby/pom.xml
index 356c70a..f3dba84 100644
--- a/osgi-bundles/jruby/pom.xml
+++ b/osgi-bundles/jruby/pom.xml
@@ -49,6 +49,10 @@
             <artifactId>org.osgi.core</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>osgi-over-slf4j</artifactId>
         </dependency>
diff --git a/osgi-bundles/jruby/README.md b/osgi-bundles/jruby/README.md
index f1f240d..d550a73 100644
--- a/osgi-bundles/jruby/README.md
+++ b/osgi-bundles/jruby/README.md
@@ -3,7 +3,7 @@ Testing the JRuby OSGI bridge
 
 1. Build the jruby OSGI module and copy the bundle to:
 
-        /var/tmp/killbill-osgi-bundles-jruby-*-jar-with-dependencies.jar 
+        /var/tmp/killbill-osgi-bundles-jruby.jar
 
 2. Build a ruby plugin, e.g. killbill-paypal-express:
 
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 f2e4e92..78d4cde 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
@@ -36,15 +36,16 @@ import com.ning.billing.osgi.api.config.PluginRubyConfig;
 public class Activator implements BundleActivator {
 
     private final List<ServiceReference<?>> serviceReferences = new ArrayList<ServiceReference<?>>();
+    private final Logger logger = new Logger();
 
     private OSGIKillbill osgiKillbill;
-    private LogService logger = null;
     private JRubyPlugin plugin = null;
 
     public void start(final BundleContext context) throws Exception {
-        logger = retrieveApi(context, LogService.class);
+        logger.start(context);
+
         osgiKillbill = retrieveApi(context, OSGIKillbill.class);
-        log(LogService.LOG_INFO, "JRuby bundle activated");
+        logger.log(LogService.LOG_INFO, "JRuby bundle activated");
 
         doMagicToMakeJRubyAndFelixHappy();
 
@@ -66,7 +67,7 @@ public class Activator implements BundleActivator {
         killbillServices.put("logger", logger);
         plugin.instantiatePlugin(killbillServices);
 
-        log(LogService.LOG_INFO, "Starting JRuby plugin " + plugin.getPluginMainClass());
+        logger.log(LogService.LOG_INFO, "Starting JRuby plugin " + plugin.getPluginMainClass());
         plugin.startPlugin(context);
     }
 
@@ -92,12 +93,14 @@ public class Activator implements BundleActivator {
     }
 
     public void stop(final BundleContext context) throws Exception {
-        log(LogService.LOG_INFO, "Stopping JRuby plugin " + plugin.getPluginMainClass());
+        logger.log(LogService.LOG_INFO, "Stopping JRuby plugin " + plugin.getPluginMainClass());
         plugin.stopPlugin(context);
 
         for (final ServiceReference apiReference : serviceReferences) {
             context.ungetService(apiReference);
         }
+
+        logger.close();
     }
 
     private Map<String, Object> retrieveKillbillApis(final BundleContext context) {
@@ -146,10 +149,4 @@ public class Activator implements BundleActivator {
             return null;
         }
     }
-
-    private void log(final int level, final String message) {
-        if (logger != null) {
-            logger.log(level, message);
-        }
-    }
 }
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 3887a80..134e4b5 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
@@ -16,8 +16,6 @@
 
 package com.ning.billing.osgi.bundles.jruby;
 
-import javax.annotation.Nullable;
-
 import org.jruby.embed.ScriptingContainer;
 import org.jruby.javasupport.JavaEmbedUtils;
 import org.osgi.framework.BundleContext;
@@ -33,7 +31,7 @@ import com.google.common.eventbus.Subscribe;
 public class JRubyNotificationPlugin extends JRubyPlugin {
 
     public JRubyNotificationPlugin(final PluginRubyConfig config, final ScriptingContainer container,
-                                   final BundleContext bundleContext, @Nullable final LogService logger) {
+                                   final BundleContext bundleContext, final Logger logger) {
         super(config, container, bundleContext, logger);
     }
 
@@ -47,7 +45,7 @@ public class JRubyNotificationPlugin extends JRubyPlugin {
             final ExternalBus externalBus = context.getService(externalBusReference);
             externalBus.register(this);
         } catch (Exception e) {
-            log(LogService.LOG_WARNING, "Error registering notification plugin service", e);
+            logger.log(LogService.LOG_WARNING, "Error registering notification plugin service", e);
         } finally {
             if (externalBusReference != null) {
                 context.ungetService(externalBusReference);
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 3002635..01dbf38 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
@@ -22,14 +22,11 @@ import java.util.Hashtable;
 import java.util.List;
 import java.util.UUID;
 
-import javax.annotation.Nullable;
-
 import org.jruby.Ruby;
 import org.jruby.embed.ScriptingContainer;
 import org.jruby.javasupport.JavaEmbedUtils;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.log.LogService;
 
 import com.ning.billing.osgi.api.config.PluginRubyConfig;
 import com.ning.billing.payment.api.PaymentMethodPlugin;
@@ -46,7 +43,7 @@ public class JRubyPaymentPlugin extends JRubyPlugin implements PaymentPluginApi 
     private volatile ServiceRegistration<PaymentPluginApi> paymentInfoPluginRegistration;
 
     public JRubyPaymentPlugin(final PluginRubyConfig config, final ScriptingContainer container,
-                              final BundleContext bundleContext, @Nullable final LogService logger) {
+                              final BundleContext bundleContext, final Logger logger) {
         super(config, container, bundleContext, logger);
     }
 
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 562b2ea..4aaa263 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
@@ -20,7 +20,6 @@ import java.util.Arrays;
 import java.util.Hashtable;
 import java.util.Map;
 
-import javax.annotation.Nullable;
 import javax.servlet.http.HttpServlet;
 
 import org.jruby.Ruby;
@@ -46,7 +45,7 @@ public abstract class JRubyPlugin {
     private static final String KILLBILL_SERVICES = "java_apis";
     private static final String ACTIVE = "@active";
 
-    protected final LogService logger;
+    protected final Logger logger;
     protected final BundleContext bundleContext;
     protected final String pluginGemName;
     protected final String rubyRequire;
@@ -60,7 +59,7 @@ public abstract class JRubyPlugin {
     private String cachedRequireLine = null;
 
     public JRubyPlugin(final PluginRubyConfig config, final ScriptingContainer container,
-                       final BundleContext bundleContext, @Nullable final LogService logger) {
+                       final BundleContext bundleContext, final Logger logger) {
         this.logger = logger;
         this.bundleContext = bundleContext;
         this.pluginGemName = config.getPluginName();
@@ -114,7 +113,7 @@ public abstract class JRubyPlugin {
         // 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()));
+            logger.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<String, String>();
@@ -217,16 +216,4 @@ public abstract class JRubyPlugin {
     protected Ruby getRuntime() {
         return pluginInstance.getMetaClass().getRuntime();
     }
-
-    protected void log(final int level, final String message) {
-        if (logger != null) {
-            logger.log(level, message);
-        }
-    }
-
-    protected void log(final int level, final String message, final Throwable throwable) {
-        if (logger != null) {
-            logger.log(level, message, throwable);
-        }
-    }
 }
diff --git a/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/Logger.java b/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/Logger.java
new file mode 100644
index 0000000..2b648b0
--- /dev/null
+++ b/osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/Logger.java
@@ -0,0 +1,70 @@
+/*
+ * 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 javax.annotation.Nullable;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class Logger {
+
+    // The name of the LogService
+    private static final String LOG_SERVICE_NAME = "org.osgi.service.log.LogService";
+
+    // The ServiceTracker to emit log services
+    private ServiceTracker logTracker;
+
+    public void start(final BundleContext context) {
+        // Track the log service using a ServiceTracker
+        logTracker = new ServiceTracker(context, LOG_SERVICE_NAME, null);
+        logTracker.open();
+    }
+
+    public void close() {
+        if (logTracker != null) {
+            logTracker.close();
+        }
+    }
+
+    public void log(final int level, final String message) {
+        log(level, message, null);
+    }
+
+    public void log(final int level, final String message, @Nullable final Throwable t) {
+        // log using the LogService if available
+        final Object log = logTracker.getService();
+        if (log != null) {
+            if (t == null) {
+                ((LogService) log).log(level, message);
+            } else {
+                ((LogService) log).log(level, message, t);
+            }
+        } else {
+            if (level >= 2) {
+                System.out.println(message);
+            } else {
+                System.err.println(message);
+            }
+
+            if (t != null) {
+                t.printStackTrace(System.err);
+            }
+        }
+    }
+}
diff --git a/osgi-bundles/pom.xml b/osgi-bundles/pom.xml
index 7911ac8..dc984c2 100644
--- a/osgi-bundles/pom.xml
+++ b/osgi-bundles/pom.xml
@@ -31,5 +31,7 @@
         <module>jruby</module>
         <module>meter</module>
         <module>test</module>
+        <module>webconsolebranding</module>
+        <module>defaultbundles</module>
     </modules>
 </project>
diff --git a/osgi-bundles/test/pom.xml b/osgi-bundles/test/pom.xml
index 4bcf392..deed751 100644
--- a/osgi-bundles/test/pom.xml
+++ b/osgi-bundles/test/pom.xml
@@ -41,6 +41,10 @@
             <artifactId>org.osgi.core</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>osgi-over-slf4j</artifactId>
         </dependency>
diff --git a/osgi-bundles/test/src/test/java/com/ning/billing/osgi/bundles/test/Logger.java b/osgi-bundles/test/src/test/java/com/ning/billing/osgi/bundles/test/Logger.java
new file mode 100644
index 0000000..4b85926
--- /dev/null
+++ b/osgi-bundles/test/src/test/java/com/ning/billing/osgi/bundles/test/Logger.java
@@ -0,0 +1,70 @@
+/*
+ * 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.test;
+
+import javax.annotation.Nullable;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class Logger {
+
+    // The name of the LogService
+    private static final String LOG_SERVICE_NAME = "org.osgi.service.log.LogService";
+
+    // The ServiceTracker to emit log services
+    private ServiceTracker logTracker;
+
+    public void start(final BundleContext context) {
+        // Track the log service using a ServiceTracker
+        logTracker = new ServiceTracker(context, LOG_SERVICE_NAME, null);
+        logTracker.open();
+    }
+
+    public void close() {
+        if (logTracker != null) {
+            logTracker.close();
+        }
+    }
+
+    public void log(final int level, final String message) {
+        log(level, message, null);
+    }
+
+    public void log(final int level, final String message, @Nullable final Throwable t) {
+        // log using the LogService if available
+        final Object log = logTracker.getService();
+        if (log != null) {
+            if (t == null) {
+                ((LogService) log).log(level, message);
+            } else {
+                ((LogService) log).log(level, message, t);
+            }
+        } else {
+            if (level >= 2) {
+                System.out.println(message);
+            } else {
+                System.err.println(message);
+            }
+
+            if (t != null) {
+                t.printStackTrace(System.err);
+            }
+        }
+    }
+}
diff --git a/osgi-bundles/test/src/test/java/com/ning/billing/osgi/bundles/test/TestActivator.java b/osgi-bundles/test/src/test/java/com/ning/billing/osgi/bundles/test/TestActivator.java
index 83576ca..06f1308 100644
--- a/osgi-bundles/test/src/test/java/com/ning/billing/osgi/bundles/test/TestActivator.java
+++ b/osgi-bundles/test/src/test/java/com/ning/billing/osgi/bundles/test/TestActivator.java
@@ -53,9 +53,7 @@ public class TestActivator implements BundleActivator {
     private OSGIKillbill osgiKillbill;
     private volatile ServiceReference<OSGIKillbill> osgiKillbillReference;
 
-    private LogService logService;
-    private volatile ServiceReference<LogService> logServiceReference;
-
+    private final Logger logger = new Logger();
 
     private volatile boolean isRunning;
     private volatile ServiceRegistration paymentInfoPluginRegistration;
@@ -69,7 +67,7 @@ public class TestActivator implements BundleActivator {
         System.out.println("TestActivator starting bundle = " + bundleName);
 
         fetchOSGIKIllbill(context);
-        fetchLogService(context);
+        logger.start(context);
 
         final IDBI dbi = new DBI(osgiKillbill.getDataSource());
         testDao = new TestDao(dbi);
@@ -89,16 +87,15 @@ public class TestActivator implements BundleActivator {
         this.isRunning = false;
         releaseOSGIKIllbill(context);
         this.osgiKillbill = null;
-        releaseLogService(context);
-        this.logService = null;
         unregisterPlaymentPluginApi(context);
+        logger.close();
         System.out.println("Good bye world from TestActivator!");
     }
 
     @Subscribe
     public void handleKillbillEvent(final ExtBusEvent killbillEvent) {
 
-        logService.log(LogService.LOG_INFO, "Received external event " + killbillEvent.toString());
+        logger.log(LogService.LOG_INFO, "Received external event " + killbillEvent.toString());
 
         // Only looking at account creation
         if (killbillEvent.getEventType() != ExtBusEventType.ACCOUNT_CREATION) {
@@ -118,7 +115,7 @@ public class TestActivator implements BundleActivator {
             testDao.insertAccountExternalKey(account.getExternalKey());
 
         } catch (AccountApiException e) {
-            logService.log(LogService.LOG_ERROR, e.getMessage());
+            logger.log(LogService.LOG_ERROR, e.getMessage());
         }
     }
 
@@ -148,22 +145,6 @@ public class TestActivator implements BundleActivator {
         }
     }
 
-
-    private void fetchLogService(final BundleContext context) {
-        this.logServiceReference = (ServiceReference<LogService>) context.getServiceReference(LogService.class.getName());
-        try {
-            this.logService = context.getService(logServiceReference);
-        } catch (Exception e) {
-            System.err.println("Error in TestActivator: " + e.getLocalizedMessage());
-        }
-    }
-
-    private void releaseLogService(final BundleContext context) {
-        if (logServiceReference != null) {
-            context.ungetService(logServiceReference);
-        }
-    }
-
     private void registerPaymentApi(final BundleContext context, final TestDao dao) {
 
         final Dictionary props = new Hashtable();
diff --git a/osgi-bundles/webconsolebranding/pom.xml b/osgi-bundles/webconsolebranding/pom.xml
new file mode 100644
index 0000000..ed92524
--- /dev/null
+++ b/osgi-bundles/webconsolebranding/pom.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+
+<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>com.ning.billing</groupId>
+        <artifactId>killbill-osgi-bundles</artifactId>
+        <version>0.1.56-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <artifactId>killbill-osgi-bundles-webconsolebranding</artifactId>
+    <name>Killbill billing platform: OSGI Web Console branding bundle</name>
+    <packaging>bundle</packaging>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Fragment-Host>
+                            org.apache.felix.webconsole
+                        </Fragment-Host>
+                        <Export-Package>
+                            !*
+                        </Export-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/osgi-bundles/webconsolebranding/src/main/resources/META-INF/webconsole.properties b/osgi-bundles/webconsolebranding/src/main/resources/META-INF/webconsole.properties
new file mode 100644
index 0000000..3993219
--- /dev/null
+++ b/osgi-bundles/webconsolebranding/src/main/resources/META-INF/webconsole.properties
@@ -0,0 +1,16 @@
+# This file contains branding properties to overwrite the default
+# branding of the Apache Felix Web Console when deployed in Killbill
+
+webconsole.brand.name = Killbill Web Console
+webconsole.product.name = Killbill
+webconsole.product.url = http://killbilling.org
+webconsole.product.image = /res/killbill/logo.png
+
+# webconsole.vendor.name = The Apache Software Foundation
+# webconsole.vendor.url = http://www.apache.org
+# webconsole.vendor.image = /res/imgs/logo.png
+
+webconsole.favicon = /res/killbill/favicon.ico
+
+# We don't have a different stylesheet yet
+# webconsole.stylesheet = /res/ui/webconsole.css
diff --git a/osgi-bundles/webconsolebranding/src/main/resources/res/killbill/favicon.ico b/osgi-bundles/webconsolebranding/src/main/resources/res/killbill/favicon.ico
new file mode 100644
index 0000000..675f906
Binary files /dev/null and b/osgi-bundles/webconsolebranding/src/main/resources/res/killbill/favicon.ico differ
diff --git a/osgi-bundles/webconsolebranding/src/main/resources/res/killbill/logo.png b/osgi-bundles/webconsolebranding/src/main/resources/res/killbill/logo.png
new file mode 100644
index 0000000..ed80710
Binary files /dev/null and b/osgi-bundles/webconsolebranding/src/main/resources/res/killbill/logo.png differ

pom.xml 511(+334 -177)

diff --git a/pom.xml b/pom.xml
index 07f7162..c9d75f8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,7 +14,8 @@
   ~ 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">
+<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">
     <parent>
         <groupId>org.sonatype.oss</groupId>
         <artifactId>oss-parent</artifactId>
@@ -72,11 +73,26 @@
                 <version>4.0.3</version>
             </dependency>
             <dependency>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>org.osgi.core</artifactId>
+                <version>1.0.1</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>org.apache.felix.fileinstall</artifactId>
+                <version>3.2.6</version>
+            </dependency>
+            <dependency>
                 <groupId>org.osgi</groupId>
                 <artifactId>org.osgi.core</artifactId>
                 <version>4.3.1</version>
             </dependency>
             <dependency>
+                <groupId>org.osgi</groupId>
+                <artifactId>org.osgi.compendium</artifactId>
+                <version>4.3.1</version>
+            </dependency>
+            <dependency>
                 <groupId>net.sf.ehcache</groupId>
                 <artifactId>ehcache-core</artifactId>
                 <version>${ehcache.version}</version>
@@ -94,12 +110,6 @@
                 <scope>provided</scope>
             </dependency>
             <dependency>
-                <groupId>javax.servlet</groupId>
-                <artifactId>javax.servlet-api</artifactId>
-                <version>3.0.1</version>
-                <scope>test</scope>
-            </dependency>
-            <dependency>
                 <groupId>com.ning.billing</groupId>
                 <artifactId>killbill-beatrix</artifactId>
                 <version>${project.version}</version>
@@ -261,6 +271,16 @@
             </dependency>
             <dependency>
                 <groupId>com.ning.billing</groupId>
+                <artifactId>killbill-osgi-bundles-webconsolebranding</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.ning.billing</groupId>
+                <artifactId>killbill-osgi-bundles-defaultbundles</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.ning.billing</groupId>
                 <artifactId>killbill-osgi-bundles-jruby</artifactId>
                 <version>${project.version}</version>
             </dependency>
@@ -293,12 +313,12 @@
             <dependency>
                 <groupId>com.fasterxml.jackson.core</groupId>
                 <artifactId>jackson-core</artifactId>
-                <version>2.0.0</version>
+                <version>2.1.0</version>
             </dependency>
             <dependency>
                 <groupId>com.fasterxml.jackson.core</groupId>
                 <artifactId>jackson-annotations</artifactId>
-                <version>2.0.0</version>
+                <version>2.1.0</version>
             </dependency>
             <dependency>
                 <groupId>com.fasterxml.jackson.dataformat</groupId>
@@ -313,7 +333,7 @@
             <dependency>
                 <groupId>com.fasterxml.jackson.core</groupId>
                 <artifactId>jackson-databind</artifactId>
-                <version>2.0.0</version>
+                <version>2.1.0</version>
             </dependency>
             <dependency>
                 <groupId>com.fasterxml.jackson.dataformat</groupId>
@@ -378,6 +398,11 @@
                 <version>1.2</version>
             </dependency>
             <dependency>
+                <groupId>commons-io</groupId>
+                <artifactId>commons-io</artifactId>
+                <version>2.1</version>
+            </dependency>
+            <dependency>
                 <groupId>org.apache.shiro</groupId>
                 <artifactId>shiro-core</artifactId>
                 <version>1.2.1</version>
@@ -477,6 +502,57 @@
         <pluginManagement>
             <plugins>
                 <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <version>3.0</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-enforcer-plugin</artifactId>
+                    <version>1.2</version>
+                    <executions>
+                        <execution>
+                            <id>enforce-versions</id>
+                            <goals>
+                                <goal>enforce</goal>
+                            </goals>
+                            <configuration>
+                                <rules>
+                                    <requireMavenVersion>
+                                        <version>3.0.0</version>
+                                    </requireMavenVersion>
+                                    <requireJavaVersion>
+                                        <version>1.6</version>
+                                    </requireJavaVersion>
+                                    <bannedDependencies>
+                                        <excludes>
+                                            <exclude>com.google.collections:google-collections</exclude>
+                                            <exclude>com.google.guava:guava</exclude>
+                                        </excludes>
+                                        <includes>
+                                            <include>com.google.guava:guava:[12,)</include>
+                                        </includes>
+                                    </bannedDependencies>
+                                </rules>
+                            </configuration>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <version>2.13</version>
+                    <configuration>
+                        <argLine>-Xms512m -Xmx1024m -XX:MaxPermSize=512m -XX:MaxDirectMemorySize=512m</argLine>
+                        <useManifestOnlyJar>false</useManifestOnlyJar>
+                        <systemPropertyVariables>
+                            <file.encoding>UTF-8</file.encoding>
+                            <user.timezone>GMT</user.timezone>
+                            <killbill.version>${project.version}</killbill.version>
+                        </systemPropertyVariables>
+                    </configuration>
+                </plugin>
+                <plugin>
                     <groupId>org.apache.felix</groupId>
                     <artifactId>maven-bundle-plugin</artifactId>
                     <version>2.3.7</version>
@@ -484,63 +560,160 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-shade-plugin</artifactId>
-                    <version>1.4</version>
+                    <version>2.0</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-dependency-plugin</artifactId>
+                    <version>2.3</version>
+                    <executions>
+                        <execution>
+                            <id>analyze</id>
+                            <goals>
+                                <goal>analyze-only</goal>
+                            </goals>
+                            <configuration>
+                                <ignoreNonCompile>true</ignoreNonCompile>
+                                <failOnWarning>false</failOnWarning>
+                            </configuration>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>com.ning.maven.plugins</groupId>
+                    <artifactId>maven-dependency-versions-check-plugin</artifactId>
+                    <version>2.0.2</version>
+                    <configuration>
+                        <failBuildInCaseOfConflict>true</failBuildInCaseOfConflict>
+                    </configuration>
+                    <executions>
+                        <execution>
+                            <phase>verify</phase>
+                            <goals>
+                                <goal>check</goal>
+                            </goals>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>com.ning.maven.plugins</groupId>
+                    <artifactId>maven-duplicate-finder-plugin</artifactId>
+                    <version>1.0.4</version>
+                    <configuration>
+                        <failBuildInCaseOfConflict>false</failBuildInCaseOfConflict>
+                        <!-- That's for Jetty -->
+                        <ignoredResources>
+                            <ignoredResource>about.html</ignoredResource>
+                        </ignoredResources>
+                    </configuration>
+                    <executions>
+                        <execution>
+                            <phase>verify</phase>
+                            <goals>
+                                <goal>check</goal>
+                            </goals>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.rat</groupId>
+                    <artifactId>apache-rat-plugin</artifactId>
+                    <version>0.7</version>
+                    <executions>
+                        <execution>
+                            <phase>verify</phase>
+                            <goals>
+                                <goal>check</goal>
+                            </goals>
+                            <configuration>
+                                <useEclipseDefaultExcludes>true</useEclipseDefaultExcludes>
+                                <useIdeaDefaultExcludes>true</useIdeaDefaultExcludes>
+                                <useMavenDefaultExcludes>true</useMavenDefaultExcludes>
+                                <excludes>
+                                    <!-- For some reason, useIdeaDefaultExcludes
+                                        doesn't pick up .idea directory -->
+                                    <exclude>.idea/**</exclude>
+                                    <exclude>**/*.iml</exclude>
+                                    <exclude>**/.project</exclude>
+                                    <exclude>.git/**</exclude>
+                                    <exclude>.gitignore</exclude>
+                                    <exclude>**/.classpath</exclude>
+                                    <exclude>ignore/**</exclude>
+                                    <exclude>API.txt</exclude>
+                                    <exclude>RELEASE.sh</exclude>
+                                    <exclude>deploy.sh</exclude>
+                                    <exclude>run.sh</exclude>
+                                    <exclude>run-local.sh</exclude>
+                                    <exclude>release-script</exclude>
+                                    <exclude>doc/**</exclude>
+                                    <exclude>src/site/**</exclude>
+                                    <exclude>*.log</exclude>
+                                    <exclude>README.*</exclude>
+                                    <exclude>TODO</exclude>
+                                    <exclude>logs/**</exclude>
+                                    <exclude>**/*.xsd</exclude>
+                                    <exclude>**/*.xml</exclude>
+                                    <exclude>**/*.stg</exclude>
+                                    <exclude>**/*.sql</exclude>
+                                    <exclude>**/*.properties</exclude>
+                                    <exclude>**/*.dont-let-git-remove-this-directory</exclude>
+                                    <exclude>**/test-output/**</exclude>
+                                    <exclude>**/bin/**</exclude>
+                                    <exclude>**/target/**</exclude>
+                                    <exclude>**/.settings/**</exclude>
+                                    <exclude>.travis.yml</exclude>
+                                    <exclude>bin/**</exclude>
+                                    <!-- exclude mustache template files -->
+                                    <exclude>**/*.mustache</exclude>
+                                    <exclude>examples/**</exclude>
+                                    <exclude>Gemfile.lock</exclude>
+                                    <exclude>src/main/ruby/kbadmin/**</exclude>
+                                </excludes>
+                            </configuration>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-source-plugin</artifactId>
+                    <version>2.2.1</version>
+                    <executions>
+                        <execution>
+                            <id>attach-sources</id>
+                            <phase>verify</phase>
+                            <goals>
+                                <goal>jar</goal>
+                                <goal>test-jar</goal>
+                            </goals>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-assembly-plugin</artifactId>
+                    <version>2.4</version>
                 </plugin>
             </plugins>
         </pluginManagement>
         <plugins>
             <plugin>
-                <groupId>com.ning.maven.plugins</groupId>
-                <artifactId>maven-dependency-versions-check-plugin</artifactId>
-                <version>2.0.2</version>
-                <configuration>
-                    <failBuildInCaseOfConflict>true</failBuildInCaseOfConflict>
-                </configuration>
-                <executions>
-                    <execution>
-                        <phase>none</phase>
-                        <goals>
-                            <goal>check</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
-                <groupId>com.ning.maven.plugins</groupId>
-                <artifactId>maven-duplicate-finder-plugin</artifactId>
-                <version>1.0.2</version>
-                <configuration>
-                    <failBuildInCaseOfConflict>false</failBuildInCaseOfConflict>
-                    <!-- That's for Jetty -->
-                    <ignoredResources>
-                        <ignoredResource>about.html</ignoredResource>
-                    </ignoredResources>
-                </configuration>
-                <executions>
-                    <execution>
-                        <phase>verify</phase>
-                        <goals>
-                            <goal>check</goal>
-                        </goals>
-                    </execution>
-                </executions>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-clean-plugin</artifactId>
+                <version>2.5</version>
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-compiler-plugin</artifactId>
-                <version>2.3.2</version>
-                <configuration>
-                    <source>1.6</source>
-                    <target>1.6</target>
-                </configuration>
+                <artifactId>maven-resources-plugin</artifactId>
+                <version>2.6</version>
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-jar-plugin</artifactId>
-                <version>2.2</version>
+                <version>2.4</version>
                 <executions>
                     <execution>
                         <goals>
+                            <goal>jar</goal>
                             <goal>test-jar</goal>
                         </goals>
                     </execution>
@@ -548,81 +721,6 @@
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-dependency-plugin</artifactId>
-                <version>2.3</version>
-                <executions>
-                    <execution>
-                        <id>analyze</id>
-                        <goals>
-                            <goal>analyze-only</goal>
-                        </goals>
-                        <configuration>
-                            <ignoreNonCompile>true</ignoreNonCompile>
-                            <failOnWarning>false</failOnWarning>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.rat</groupId>
-                <artifactId>apache-rat-plugin</artifactId>
-                <version>0.7</version>
-                <executions>
-                    <execution>
-                        <phase>verify</phase>
-                        <goals>
-                            <goal>check</goal>
-                        </goals>
-                        <configuration>
-                            <useEclipseDefaultExcludes>true</useEclipseDefaultExcludes>
-                            <useIdeaDefaultExcludes>true</useIdeaDefaultExcludes>
-                            <useMavenDefaultExcludes>true</useMavenDefaultExcludes>
-                            <excludes>
-                                <!-- For some reason, useIdeaDefaultExcludes 
-                                    doesn't pick up .idea directory -->
-                                <exclude>.idea/**</exclude>
-                                <exclude>**/*.iml</exclude>
-                                <exclude>**/.project</exclude>
-                                <exclude>.git/**</exclude>
-                                <exclude>.gitignore</exclude>
-                                <exclude>**/.classpath</exclude>
-                                <exclude>ignore/**</exclude>
-                                <exclude>API.txt</exclude>
-                                <exclude>RELEASE.sh</exclude>
-                                <exclude>deploy.sh</exclude>
-                                <exclude>run.sh</exclude>
-                                <exclude>run-local.sh</exclude>
-                                <exclude>release-script</exclude>
-                                <exclude>doc/**</exclude>
-                                <exclude>src/site/**</exclude>
-                                <exclude>*.log</exclude>
-                                <exclude>README.*</exclude>
-                                <exclude>TODO</exclude>
-                                <exclude>logs/**</exclude>
-                                <exclude>**/*.xsd</exclude>
-                                <exclude>**/*.xml</exclude>
-                                <exclude>**/*.stg</exclude>
-                                <exclude>**/*.sql</exclude>
-                                <exclude>**/*.properties</exclude>
-                                <exclude>**/*.dont-let-git-remove-this-directory</exclude>
-                                <exclude>**/test-output/**</exclude>
-                                <exclude>**/bin/**</exclude>
-                                <exclude>**/target/**</exclude>
-                                <exclude>**/.settings/**</exclude>
-                                <exclude>.travis.yml</exclude>
-                                <exclude>bin/**</exclude>
-                                <!-- exclude mustache template files -->
-                                <exclude>**/*.mustache</exclude>
-                                <exclude>examples/**</exclude>
-                                <exclude>Gemfile.lock</exclude>
-                                <exclude>src/main/ruby/kbadmin/**</exclude>
-                            </excludes>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-release-plugin</artifactId>
                 <version>2.2.1</version>
                 <configuration>
@@ -650,15 +748,8 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <version>2.11</version>
                 <configuration>
-                    <useManifestOnlyJar>false</useManifestOnlyJar>
                     <groups>fast,slow,mysql</groups>
-                    <systemPropertyVariables>
-                        <propertyName>propertyValue</propertyName>
-                        <log4j.configuration>file:${project.basedir}/src/test/resources/log4j.xml</log4j.configuration>
-                        <killbill.version>${project.version}</killbill.version>
-                    </systemPropertyVariables>
                 </configuration>
             </plugin>
             <plugin>
@@ -669,21 +760,6 @@
                     <attachClasses>true</attachClasses>
                 </configuration>
             </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-source-plugin</artifactId>
-                <version>2.1.2</version>
-                <executions>
-                    <execution>
-                        <id>attach-sources</id>
-                        <phase>verify</phase>
-                        <goals>
-                            <goal>jar</goal>
-                            <goal>test-jar</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
         </plugins>
     </build>
     <profiles>
@@ -694,14 +770,11 @@
                     <plugin>
                         <groupId>org.apache.maven.plugins</groupId>
                         <artifactId>maven-surefire-plugin</artifactId>
-                        <version>2.11</version>
                         <configuration>
                             <groups>fast,slow</groups>
                             <excludedGroups>mysql</excludedGroups>
                             <systemPropertyVariables>
                                 <com.ning.billing.dbi.test.h2>true</com.ning.billing.dbi.test.h2>
-                                <file.encoding>UTF-8</file.encoding>
-                                <user.timezone>GMT</user.timezone>
                             </systemPropertyVariables>
                         </configuration>
                     </plugin>
@@ -715,19 +788,12 @@
                     <plugin>
                         <groupId>org.apache.maven.plugins</groupId>
                         <artifactId>maven-surefire-plugin</artifactId>
-                        <version>2.11</version>
                         <configuration>
                             <groups>fast,slow,mysql</groups>
-                            <useManifestOnlyJar>false</useManifestOnlyJar>
                             <systemPropertyVariables>
-                                <log4j.configuration>file:${project.basedir}/src/test/resources/log4j.xml
-                                </log4j.configuration>
                                 <com.ning.billing.dbi.test.useLocalDb>true</com.ning.billing.dbi.test.useLocalDb>
                                 <com.ning.billing.dbi.jdbc.url>jdbc:mysql://127.0.0.1:3306/killbill
                                 </com.ning.billing.dbi.jdbc.url>
-                                <file.encoding>UTF-8</file.encoding>
-                                <user.timezone>GMT</user.timezone>
-                                <killbill.version>${project.version}</killbill.version>
                             </systemPropertyVariables>
                         </configuration>
                     </plugin>
@@ -740,16 +806,35 @@
                 <plugins>
                     <plugin>
                         <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-enforcer-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
                         <artifactId>maven-surefire-plugin</artifactId>
-                        <version>2.11</version>
                         <configuration>
                             <groups>fast</groups>
-                            <systemPropertyVariables>
-                                <file.encoding>UTF-8</file.encoding>
-                                <user.timezone>GMT</user.timezone>
-                            </systemPropertyVariables>
                         </configuration>
                     </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-dependency-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>com.ning.maven.plugins</groupId>
+                        <artifactId>maven-dependency-versions-check-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>com.ning.maven.plugins</groupId>
+                        <artifactId>maven-duplicate-finder-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.rat</groupId>
+                        <artifactId>apache-rat-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-source-plugin</artifactId>
+                    </plugin>
                 </plugins>
             </build>
         </profile>
@@ -760,7 +845,6 @@
                     <plugin>
                         <groupId>org.apache.maven.plugins</groupId>
                         <artifactId>maven-surefire-plugin</artifactId>
-                        <version>2.11</version>
                         <configuration>
                             <groups>stress</groups>
                         </configuration>
@@ -774,6 +858,10 @@
                 <plugins>
                     <plugin>
                         <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-enforcer-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
                         <artifactId>maven-gpg-plugin</artifactId>
                         <version>1.4</version>
                         <executions>
@@ -789,13 +877,82 @@
                     <plugin>
                         <groupId>org.apache.maven.plugins</groupId>
                         <artifactId>maven-surefire-plugin</artifactId>
-                        <version>2.11</version>
                         <configuration>
-                            <groups>fast</groups>
-                            <systemPropertyVariables>
-                                <file.encoding>UTF-8</file.encoding>
-                                <user.timezone>GMT</user.timezone>
-                            </systemPropertyVariables>
+                            <skipTests>true</skipTests>
+                        </configuration>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-dependency-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>com.ning.maven.plugins</groupId>
+                        <artifactId>maven-dependency-versions-check-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>com.ning.maven.plugins</groupId>
+                        <artifactId>maven-duplicate-finder-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.rat</groupId>
+                        <artifactId>apache-rat-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-source-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>jdk16</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-compiler-plugin</artifactId>
+                        <configuration>
+                            <source>1.6</source>
+                            <target>1.6</target>
+                            <showDeprecation>true</showDeprecation>
+                            <showWarnings>true</showWarnings>
+                            <fork>true</fork>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>jdk17</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-jar-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <configuration>
+                                    <classifier>jdk17</classifier>
+                                </configuration>
+                                <goals>
+                                    <goal>jar</goal>
+                                    <goal>test-jar</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-compiler-plugin</artifactId>
+                        <configuration>
+                            <source>1.7</source>
+                            <target>1.7</target>
+                            <showDeprecation>true</showDeprecation>
+                            <showWarnings>true</showWarnings>
+                            <fork>true</fork>
                         </configuration>
                     </plugin>
                 </plugins>
@@ -812,7 +969,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-javadoc-plugin</artifactId>
-                <version>2.8</version>
+                <version>2.9</version>
                 <configuration>
                     <source>1.6</source>
                     <encoding>UTF-8</encoding>
@@ -843,22 +1000,22 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-report-plugin</artifactId>
-                <version>2.6</version>
+                <version>2.13</version>
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-project-info-reports-plugin</artifactId>
-                <version>2.4</version>
+                <version>2.6</version>
             </plugin>
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>cobertura-maven-plugin</artifactId>
-                <version>2.5.1</version>
+                <version>2.5.2</version>
             </plugin>
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>findbugs-maven-plugin</artifactId>
-                <version>2.3.2</version>
+                <version>2.5.2</version>
                 <configuration>
                     <threshold>Low</threshold>
                     <effort>Max</effort>
@@ -892,6 +1049,6 @@
     </reporting>
     <issueManagement>
         <system>Github</system>
-        <url>http://github.com/ning/killbill</url>
+        <url>http://github.com/killbill/killbill</url>
     </issueManagement>
 </project>

server/pom.xml 92(+0 -92)

diff --git a/server/pom.xml b/server/pom.xml
index 4f388a4..7640aae 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -117,7 +117,6 @@
         <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>
-            <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>com.ning.jetty</groupId>
@@ -217,7 +216,6 @@
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
-            <version>11.0.2</version>
             <scope>compile</scope>
         </dependency>
         <dependency>
@@ -322,7 +320,6 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-shade-plugin</artifactId>
-                <version>1.4</version>
                 <executions>
                     <execution>
                         <id>assemble-killbill</id>
@@ -344,98 +341,9 @@
                 </executions>
             </plugin>
             <plugin>
-                <groupId>com.ning.maven.plugins</groupId>
-                <artifactId>maven-dependency-versions-check-plugin</artifactId>
-                <version>2.0.2</version>
-                <configuration>
-                    <failBuildInCaseOfConflict>true</failBuildInCaseOfConflict>
-                </configuration>
-                <executions>
-                    <execution>
-                        <phase>verify</phase>
-                        <goals>
-                            <goal>check</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
                 <!-- To make eclipse happy -->
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-resources-plugin</artifactId>
-                <version>2.5</version>
-            </plugin>
-            <plugin>
-                <groupId>com.ning.maven.plugins</groupId>
-                <artifactId>maven-duplicate-finder-plugin</artifactId>
-                <version>1.0.2</version>
-                <configuration>
-                    <failBuildInCaseOfConflict>false</failBuildInCaseOfConflict>
-                    <!-- That's for Jetty -->
-                    <ignoredResources>
-                        <ignoredResource>about.html</ignoredResource>
-                    </ignoredResources>
-                </configuration>
-                <executions>
-                    <execution>
-                        <phase>verify</phase>
-                        <goals>
-                            <goal>check</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-compiler-plugin</artifactId>
-                <version>2.3.2</version>
-                <configuration>
-                    <source>1.6</source>
-                    <target>1.6</target>
-                </configuration>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-dependency-plugin</artifactId>
-                <version>2.3</version>
-                <executions>
-                    <execution>
-                        <id>analyze</id>
-                        <goals>
-                            <goal>analyze-only</goal>
-                        </goals>
-                        <configuration>
-                            <ignoreNonCompile>true</ignoreNonCompile>
-                            <failOnWarning>false</failOnWarning>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-release-plugin</artifactId>
-                <version>2.2.1</version>
-                <configuration>
-                    <mavenExecutorId>forked-path</mavenExecutorId>
-                </configuration>
-                <dependencies>
-                    <dependency>
-                        <groupId>org.apache.maven.scm</groupId>
-                        <artifactId>maven-scm-provider-gitexe</artifactId>
-                        <version>1.4</version>
-                    </dependency>
-                    <dependency>
-                        <groupId>org.codehaus.plexus</groupId>
-                        <artifactId>plexus-utils</artifactId>
-                        <version>1.5.9</version>
-                    </dependency>
-                </dependencies>
-            </plugin>
-            <plugin>
-                <!-- TODO: fix for http://jira.codehaus.org/browse/MSITE-286? -->
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-site-plugin</artifactId>
-                <version>3.0</version>
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
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 2cd6f49..9eb93c3 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
@@ -24,7 +24,7 @@ import java.util.Iterator;
 import java.util.Map;
 
 import javax.inject.Inject;
-import javax.servlet.http.HttpServlet;
+import javax.servlet.Servlet;
 
 import org.eclipse.jetty.servlet.FilterHolder;
 import org.joda.time.LocalDate;
@@ -89,7 +89,7 @@ public class TestJaxrsBase extends KillbillClient {
     protected static final int DEFAULT_HTTP_TIMEOUT_SEC = 6000; // 5;
 
     @Inject
-    protected OSGIServiceRegistration<HttpServlet> servletRouter;
+    protected OSGIServiceRegistration<Servlet> servletRouter;
 
     protected static TestKillbillGuiceListener listener;
 
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestPlugin.java b/server/src/test/java/com/ning/billing/jaxrs/TestPlugin.java
index bebe50a..edd5333 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestPlugin.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestPlugin.java
@@ -29,7 +29,6 @@ import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import com.ning.billing.jaxrs.resources.JaxrsResource;
 import com.ning.http.client.Response;
 
 public class TestPlugin extends TestJaxrsBase {
@@ -161,7 +160,7 @@ public class TestPlugin extends TestJaxrsBase {
         servletRouter.registerService(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())) {
+                if (("/" + TEST_PLUGIN_VALID_GET_PATH).equals(req.getPathInfo())) {
                     requestGETMarker.set(true);
                     resp.getOutputStream().write(TEST_PLUGIN_RESPONSE_BYTES);
                     resp.setStatus(230);
@@ -170,14 +169,14 @@ public class TestPlugin extends TestJaxrsBase {
 
             @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())) {
+                if (("/" + 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())) {
+                if (("/" + TEST_PLUGIN_VALID_POST_PATH).equals(req.getPathInfo())) {
                     requestPOSTMarker.set(true);
                     resp.getOutputStream().write(TEST_PLUGIN_RESPONSE_BYTES);
                     resp.setStatus(230);
@@ -186,7 +185,7 @@ public class TestPlugin extends TestJaxrsBase {
 
             @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())) {
+                if (("/" + TEST_PLUGIN_VALID_PUT_PATH).equals(req.getPathInfo())) {
                     requestPUTMarker.set(true);
                     resp.getOutputStream().write(TEST_PLUGIN_RESPONSE_BYTES);
                     resp.setStatus(230);
@@ -195,7 +194,7 @@ public class TestPlugin extends TestJaxrsBase {
 
             @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())) {
+                if (("/" + TEST_PLUGIN_VALID_DELETE_PATH).equals(req.getPathInfo())) {
                     requestDELETEMarker.set(true);
                     resp.getOutputStream().write(TEST_PLUGIN_RESPONSE_BYTES);
                     resp.setStatus(230);
@@ -204,7 +203,7 @@ public class TestPlugin extends TestJaxrsBase {
 
             @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())) {
+                if (("/" + TEST_PLUGIN_VALID_OPTIONS_PATH).equals(req.getPathInfo())) {
                     requestOPTIONSMarker.set(true);
                     resp.getOutputStream().write(TEST_PLUGIN_RESPONSE_BYTES);
                     resp.setStatus(230);
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 c135656..2e18f38 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
@@ -63,12 +63,20 @@ public interface OSGIConfig extends KillbillConfig {
              // 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;version=1.3")
+             "org.osgi.service.log;version=1.3," +
+             // Let the world know the System bundle exposes (via org.osgi.compendium) the requirement (osgi.wiring.package=org.osgi.service.http)
+             "org.osgi.service.http," +
+             // Let the world know the System bundle exposes (via org.osgi.compendium) the requirement (&(osgi.wiring.package=org.osgi.service.deploymentadmin)(version>=1.1.0)(!(version>=2.0.0)))
+             "org.osgi.service.deploymentadmin;version=1.1.0," +
+             // Let the world know the System bundle exposes (via org.osgi.compendium) the requirement (&(osgi.wiring.package=org.osgi.service.event)(version>=1.2.0)(!(version>=2.0.0)))
+             "org.osgi.service.event;version=1.2.0," +
+             // Let the world know the System bundle exposes the requirement (&(osgi.wiring.package=org.slf4j)(version>=1.7.0)(!(version>=2.0.0)))
+             "org.slf4j;version=1.7.2")
     public String getSystemBundleExportPackages();
 
     // TODO FIXME OSGI
     @Config("killbill.osgi.jruby.bundle.path")
-    @Default("file:/var/tmp/killbill-osgi-bundles-jruby-0.1.52-SNAPSHOT-jar-with-dependencies.jar")
+    @Default("file:/var/tmp/killbill-osgi-bundles-jruby.jar")
     public String getJrubyBundlePath();
 
 }