killbill-memoizeit

Changes

.gitignore 1(+1 -0)

.travis.yml 2(+1 -1)

account/pom.xml 15(+0 -15)

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

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

beatrix/pom.xml 26(+23 -3)

bin/start-server 22(+13 -9)

catalog/pom.xml 37(+24 -13)

entitlement/pom.xml 15(+0 -15)

invoice/pom.xml 2(+0 -2)

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

junction/pom.xml 15(+0 -15)

osgi/pom.xml 39(+18 -21)

osgi-bundles/hello/src/main/java/com/ning/billing/osgi/bundles/hello/HelloActivator.java 181(+0 -181)

osgi-bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/Activator.java 170(+0 -170)

overdue/pom.xml 22(+19 -3)

pom.xml 560(+390 -170)

server/pom.xml 126(+25 -101)

server/src/main/resources/catalog-demo.xml 641(+0 -641)

tenant/pom.xml 15(+0 -15)

util/pom.xml 15(+0 -15)

Details

.gitignore 1(+1 -0)

diff --git a/.gitignore b/.gitignore
index 8f8d9f9..041eea5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,4 @@ logs/
 catalog/src/test/resources/CatalogSchema.xsd
 */test-output/
 */src/test/resources/*.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

account/pom.xml 15(+0 -15)

diff --git a/account/pom.xml b/account/pom.xml
index 8bacbed..54f83c2 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -102,19 +102,4 @@
             <scope>test</scope>
         </dependency>
     </dependencies>
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-jar-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>test-jar</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-        </plugins>
-    </build>
 </project>
diff --git a/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java b/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
index 1368547..40f09f4 100644
--- a/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
+++ b/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
@@ -40,8 +40,8 @@ public class DefaultAccount extends EntityBase implements Account {
     public static final Integer DEFAULT_INTEGER_VALUE = 0;
     public static final Currency DEFAULT_CURRENCY_VALUE = Currency.USD;
     public static final DateTimeZone DEFAULT_TIMEZONE_VALUE = DateTimeZone.UTC;
-    private static final Boolean DEFAULT_MIGRATED_VALUE = true;
-    private static final Boolean DEFAULT_NOTIFIED_FOR_INVOICES_VALUE = false;
+    public static final Boolean DEFAULT_MIGRATED_VALUE = false;
+    public static final Boolean DEFAULT_NOTIFIED_FOR_INVOICES_VALUE = false;
 
     private final String externalKey;
     private final String email;
diff --git a/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java b/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java
index 80b03c9..93d7cf9 100644
--- a/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java
+++ b/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java
@@ -59,12 +59,8 @@ public class DefaultAccountInternalApi implements AccountInternalApi {
 
     @Override
     public Account getAccountByRecordId(final Long recordId, final InternalTenantContext context) throws AccountApiException {
-        try {
-            final AccountModelDao account = accountDao.getByRecordId(recordId, context);
-            return new DefaultAccount(account);
-        } catch (NullPointerException e) {
-            return null;
-        }
+        final AccountModelDao accountModelDao = getAccountModelDaoByRecordId(recordId, context);
+        return new DefaultAccount(accountModelDao);
     }
 
     @Override
@@ -115,11 +111,16 @@ public class DefaultAccountInternalApi implements AccountInternalApi {
     }
 
     @Override
-    public UUID getByRecordId(final Long recordId, final InternalCallContext context) throws AccountApiException {
+    public UUID getByRecordId(final Long recordId, final InternalTenantContext context) throws AccountApiException {
+        final AccountModelDao accountModelDao = getAccountModelDaoByRecordId(recordId, context);
+        return accountModelDao.getId();
+    }
+
+    private AccountModelDao getAccountModelDaoByRecordId(final Long recordId, final InternalTenantContext context) throws AccountApiException {
         final AccountModelDao accountModelDao = accountDao.getByRecordId(recordId, context);
         if (accountModelDao == null) {
             throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_RECORD_ID, recordId);
         }
-        return accountModelDao.getId();
+        return accountModelDao;
     }
 }
diff --git a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java
index 933fb64..417eefa 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java
@@ -112,7 +112,7 @@ public class DefaultAccountCreationEvent extends DefaultBusInternalEvent impleme
         private final String name;
         private final Integer firstNameLength;
         private final String email;
-        private final int billCycleDayLocal;
+        private final Integer billCycleDayLocal;
         private final String currency;
         private final UUID paymentMethodId;
         private final String timeZone;
@@ -125,8 +125,8 @@ public class DefaultAccountCreationEvent extends DefaultBusInternalEvent impleme
         private final String postalCode;
         private final String country;
         private final String phone;
-        private final boolean isMigrated;
-        private final boolean isNotifiedForInvoices;
+        private final Boolean isMigrated;
+        private final Boolean isNotifiedForInvoices;
 
         public DefaultAccountData(final AccountModelDao d) {
             this(d.getExternalKey() != null ? d.getExternalKey() : null,
@@ -155,7 +155,7 @@ public class DefaultAccountCreationEvent extends DefaultBusInternalEvent impleme
                                   @JsonProperty("name") final String name,
                                   @JsonProperty("firstNameLength") final Integer firstNameLength,
                                   @JsonProperty("email") final String email,
-                                  @JsonProperty("billCycleDayLocal") final int billCycleDayLocal,
+                                  @JsonProperty("billCycleDayLocal") final Integer billCycleDayLocal,
                                   @JsonProperty("currency") final String currency,
                                   @JsonProperty("paymentMethodId") final UUID paymentMethodId,
                                   @JsonProperty("timeZone") final String timeZone,
@@ -168,8 +168,8 @@ public class DefaultAccountCreationEvent extends DefaultBusInternalEvent impleme
                                   @JsonProperty("postalCode") final String postalCode,
                                   @JsonProperty("country") final String country,
                                   @JsonProperty("phone") final String phone,
-                                  @JsonProperty("isMigrated") final boolean isMigrated,
-                                  @JsonProperty("isNotifiedForInvoices") final boolean isNotifiedForInvoices) {
+                                  @JsonProperty("isMigrated") final Boolean isMigrated,
+                                  @JsonProperty("isNotifiedForInvoices") final Boolean isNotifiedForInvoices) {
             this.externalKey = externalKey;
             this.name = name;
             this.firstNameLength = firstNameLength;
@@ -218,7 +218,7 @@ public class DefaultAccountCreationEvent extends DefaultBusInternalEvent impleme
 
         @Override
         public Currency getCurrency() {
-            return Currency.valueOf(currency);
+            return currency == null ? null : Currency.valueOf(currency);
         }
 
         @JsonIgnore
@@ -294,6 +294,16 @@ public class DefaultAccountCreationEvent extends DefaultBusInternalEvent impleme
             return isNotifiedForInvoices;
         }
 
+        // These two getters are for Jackson serialization only
+
+        public Boolean getIsMigrated() {
+            return isMigrated;
+        }
+
+        public Boolean getIsNotifiedForInvoices() {
+            return isNotifiedForInvoices;
+        }
+
         @Override
         public boolean equals(final Object o) {
             if (this == o) {
@@ -305,13 +315,13 @@ public class DefaultAccountCreationEvent extends DefaultBusInternalEvent impleme
 
             final DefaultAccountData that = (DefaultAccountData) o;
 
-            if (billCycleDayLocal != that.billCycleDayLocal) {
+            if (billCycleDayLocal != null ? !billCycleDayLocal.equals(that.billCycleDayLocal) : that.billCycleDayLocal != null) {
                 return false;
             }
-            if (isMigrated != that.isMigrated) {
+            if (isMigrated != null ? !isMigrated.equals(that.isMigrated) : that.isMigrated != null) {
                 return false;
             }
-            if (isNotifiedForInvoices != that.isNotifiedForInvoices) {
+            if (isNotifiedForInvoices != null ? !isNotifiedForInvoices.equals(that.isNotifiedForInvoices) : that.isNotifiedForInvoices != null) {
                 return false;
             }
             if (address1 != null ? !address1.equals(that.address1) : that.address1 != null) {
@@ -372,7 +382,7 @@ public class DefaultAccountCreationEvent extends DefaultBusInternalEvent impleme
             result = 31 * result + (name != null ? name.hashCode() : 0);
             result = 31 * result + (firstNameLength != null ? firstNameLength.hashCode() : 0);
             result = 31 * result + (email != null ? email.hashCode() : 0);
-            result = 31 * result + billCycleDayLocal;
+            result = 31 * result + (billCycleDayLocal != null ? billCycleDayLocal.hashCode() : 0);
             result = 31 * result + (currency != null ? currency.hashCode() : 0);
             result = 31 * result + (paymentMethodId != null ? paymentMethodId.hashCode() : 0);
             result = 31 * result + (timeZone != null ? timeZone.hashCode() : 0);
@@ -385,8 +395,8 @@ public class DefaultAccountCreationEvent extends DefaultBusInternalEvent impleme
             result = 31 * result + (postalCode != null ? postalCode.hashCode() : 0);
             result = 31 * result + (country != null ? country.hashCode() : 0);
             result = 31 * result + (phone != null ? phone.hashCode() : 0);
-            result = 31 * result + (isMigrated ? 1 : 0);
-            result = 31 * result + (isNotifiedForInvoices ? 1 : 0);
+            result = 31 * result + (isMigrated != null ? isMigrated.hashCode() : 0);
+            result = 31 * result + (isNotifiedForInvoices != null ? isNotifiedForInvoices.hashCode() : 0);
             return result;
         }
     }
diff --git a/account/src/main/java/com/ning/billing/account/glue/DefaultAccountModule.java b/account/src/main/java/com/ning/billing/account/glue/DefaultAccountModule.java
index 96c45ef..eb31347 100644
--- a/account/src/main/java/com/ning/billing/account/glue/DefaultAccountModule.java
+++ b/account/src/main/java/com/ning/billing/account/glue/DefaultAccountModule.java
@@ -16,6 +16,8 @@
 
 package com.ning.billing.account.glue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.account.api.AccountService;
 import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.account.api.DefaultAccountService;
@@ -31,6 +33,12 @@ import com.google.inject.AbstractModule;
 
 public class DefaultAccountModule extends AbstractModule implements AccountModule {
 
+    protected final ConfigSource configSource;
+
+    public DefaultAccountModule(final ConfigSource configSource) {
+        this.configSource = configSource;
+    }
+
     private void installConfig() {
     }
 
diff --git a/account/src/test/java/com/ning/billing/account/AccountTestSuiteNoDB.java b/account/src/test/java/com/ning/billing/account/AccountTestSuiteNoDB.java
index 285e46c..1171013 100644
--- a/account/src/test/java/com/ning/billing/account/AccountTestSuiteNoDB.java
+++ b/account/src/test/java/com/ning/billing/account/AccountTestSuiteNoDB.java
@@ -66,18 +66,18 @@ public abstract class AccountTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     protected NonEntityDao nonEntityDao;
 
     @BeforeClass(groups = "fast")
-    protected void setup() throws Exception {
-        final Injector injector = Guice.createInjector(new TestAccountModuleNoDB());
+    protected void beforeClass() throws Exception {
+        final Injector injector = Guice.createInjector(new TestAccountModuleNoDB(configSource));
         injector.injectMembers(this);
     }
 
     @BeforeMethod(groups = "fast")
-    public void setupTest() {
+    public void beforeMethod() throws Exception {
         bus.start();
     }
 
     @AfterMethod(groups = "fast")
-    public void cleanupTest() {
+    public void afterMethod() throws Exception {
         bus.stop();
     }
 }
diff --git a/account/src/test/java/com/ning/billing/account/AccountTestSuiteWithEmbeddedDB.java b/account/src/test/java/com/ning/billing/account/AccountTestSuiteWithEmbeddedDB.java
index f784c0f..74df794 100644
--- a/account/src/test/java/com/ning/billing/account/AccountTestSuiteWithEmbeddedDB.java
+++ b/account/src/test/java/com/ning/billing/account/AccountTestSuiteWithEmbeddedDB.java
@@ -66,18 +66,19 @@ public abstract class AccountTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
     protected NonEntityDao nonEntityDao;
 
     @BeforeClass(groups = "slow")
-    protected void setup() throws Exception {
-        final Injector injector = Guice.createInjector(new TestAccountModuleWithEmbeddedDB());
+    protected void beforeClass() throws Exception {
+        final Injector injector = Guice.createInjector(new TestAccountModuleWithEmbeddedDB(configSource));
         injector.injectMembers(this);
     }
 
     @BeforeMethod(groups = "slow")
-    public void setupTest() {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         bus.start();
     }
 
     @AfterMethod(groups = "slow")
-    public void cleanupTest() {
+    public void afterMethod() throws Exception {
         bus.stop();
     }
 }
diff --git a/account/src/test/java/com/ning/billing/account/api/TestDefaultAccount.java b/account/src/test/java/com/ning/billing/account/api/TestDefaultAccount.java
new file mode 100644
index 0000000..e9fe421
--- /dev/null
+++ b/account/src/test/java/com/ning/billing/account/api/TestDefaultAccount.java
@@ -0,0 +1,164 @@
+/*
+ * 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.account.api;
+
+import java.util.UUID;
+
+import org.joda.time.DateTimeZone;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.account.AccountTestSuiteNoDB;
+import com.ning.billing.catalog.api.Currency;
+
+public class TestDefaultAccount extends AccountTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testConstructorAcceptsNullValues() throws Exception {
+        final AccountData accountData = getNullAccountData();
+        final Account account = new DefaultAccount(UUID.randomUUID(), accountData);
+
+        Assert.assertEquals(account.getExternalKey(), DefaultAccount.DEFAULT_STRING_VALUE);
+        Assert.assertEquals(account.getEmail(), DefaultAccount.DEFAULT_STRING_VALUE);
+        Assert.assertEquals(account.getName(), DefaultAccount.DEFAULT_STRING_VALUE);
+        Assert.assertEquals(account.getFirstNameLength(), DefaultAccount.DEFAULT_INTEGER_VALUE);
+        Assert.assertEquals(account.getCurrency(), DefaultAccount.DEFAULT_CURRENCY_VALUE);
+        Assert.assertEquals(account.getBillCycleDayLocal(), DefaultAccount.DEFAULT_INTEGER_VALUE);
+        Assert.assertNull(account.getPaymentMethodId());
+        Assert.assertEquals(account.getTimeZone(), DefaultAccount.DEFAULT_TIMEZONE_VALUE);
+        Assert.assertEquals(account.getLocale(), DefaultAccount.DEFAULT_STRING_VALUE);
+        Assert.assertEquals(account.getAddress1(), DefaultAccount.DEFAULT_STRING_VALUE);
+        Assert.assertEquals(account.getAddress2(), DefaultAccount.DEFAULT_STRING_VALUE);
+        Assert.assertEquals(account.getCompanyName(), DefaultAccount.DEFAULT_STRING_VALUE);
+        Assert.assertEquals(account.getCity(), DefaultAccount.DEFAULT_STRING_VALUE);
+        Assert.assertEquals(account.getStateOrProvince(), DefaultAccount.DEFAULT_STRING_VALUE);
+        Assert.assertEquals(account.getCountry(), DefaultAccount.DEFAULT_STRING_VALUE);
+        Assert.assertEquals(account.getPostalCode(), DefaultAccount.DEFAULT_STRING_VALUE);
+        Assert.assertEquals(account.getPhone(), DefaultAccount.DEFAULT_STRING_VALUE);
+        Assert.assertEquals(account.isMigrated(), DefaultAccount.DEFAULT_MIGRATED_VALUE);
+        Assert.assertEquals(account.isNotifiedForInvoices(), DefaultAccount.DEFAULT_NOTIFIED_FOR_INVOICES_VALUE);
+    }
+
+    @Test(groups = "fast")
+    public void testMergeWithDelegate() throws Exception {
+        final AccountData accountData = getNullAccountData();
+        final Account account = new DefaultAccount(UUID.randomUUID(), accountData);
+
+        final AccountData secondAccountData = getAccountData();
+        final Account secondAccount = new DefaultAccount(UUID.randomUUID(), secondAccountData);
+
+        final Account finalAccount = account.mergeWithDelegate(secondAccount);
+        checkAccountEquals(finalAccount, secondAccount);
+    }
+
+    @Test(groups = "fast")
+    public void testBCDMerges() throws Exception {
+        final UUID accountId = UUID.randomUUID();
+        final Currency currency = Currency.BRL;
+        final String externalKey = UUID.randomUUID().toString();
+
+        final AccountData accountDataWithNullBCD = getAccountData(null, currency, externalKey);
+        final Account accountWithNullBCD = new DefaultAccount(accountId, accountDataWithNullBCD);
+        // Null BCD -> 0 BCD
+        Assert.assertEquals(accountWithNullBCD.getBillCycleDayLocal(), (Integer) 0);
+
+        final AccountData accountDataWithZeroBCD = getAccountData(0, currency, externalKey);
+        final Account accountWithZeroBCD = new DefaultAccount(accountId, accountDataWithZeroBCD);
+        // Null BCD and 0 BCD -> 0 BCD
+        Assert.assertEquals(accountWithNullBCD.mergeWithDelegate(accountWithZeroBCD).getBillCycleDayLocal(), (Integer) 0);
+
+        final AccountData accountDataWithRealBCD = getAccountData(12, currency, externalKey);
+        final Account accountWithRealBCD = new DefaultAccount(accountId, accountDataWithRealBCD);
+        // Null BCD and real BCD -> real BCD
+        Assert.assertEquals(accountWithNullBCD.mergeWithDelegate(accountWithRealBCD).getBillCycleDayLocal(), (Integer) 12);
+
+        final AccountData accountDataWithAnotherRealBCD = getAccountData(20, currency, externalKey);
+        final Account accountWithAnotherBCD = new DefaultAccount(accountId, accountDataWithAnotherRealBCD);
+        // Same BCD
+        Assert.assertEquals(accountWithAnotherBCD.mergeWithDelegate(accountWithAnotherBCD).getBillCycleDayLocal(), (Integer) 20);
+        try {
+            // Different BCD
+            Assert.assertEquals(accountWithAnotherBCD.mergeWithDelegate(accountWithRealBCD).getBillCycleDayLocal(), (Integer) 20);
+            Assert.fail();
+        } catch (IllegalArgumentException e) {
+            Assert.assertTrue(true);
+        }
+    }
+
+    private void checkAccountEquals(final Account finalAccount, final Account delegateAccount) {
+        Assert.assertEquals(finalAccount.getExternalKey(), delegateAccount.getExternalKey());
+        Assert.assertEquals(finalAccount.getEmail(), delegateAccount.getEmail());
+        Assert.assertEquals(finalAccount.getName(), delegateAccount.getName());
+        Assert.assertEquals(finalAccount.getFirstNameLength(), delegateAccount.getFirstNameLength());
+        Assert.assertEquals(finalAccount.getCurrency(), delegateAccount.getCurrency());
+        Assert.assertEquals(finalAccount.getBillCycleDayLocal(), delegateAccount.getBillCycleDayLocal());
+        Assert.assertEquals(finalAccount.getPaymentMethodId(), delegateAccount.getPaymentMethodId());
+        Assert.assertEquals(finalAccount.getTimeZone(), delegateAccount.getTimeZone());
+        Assert.assertEquals(finalAccount.getLocale(), delegateAccount.getLocale());
+        Assert.assertEquals(finalAccount.getAddress1(), delegateAccount.getAddress1());
+        Assert.assertEquals(finalAccount.getAddress2(), delegateAccount.getAddress2());
+        Assert.assertEquals(finalAccount.getCompanyName(), delegateAccount.getCompanyName());
+        Assert.assertEquals(finalAccount.getCity(), delegateAccount.getCity());
+        Assert.assertEquals(finalAccount.getStateOrProvince(), delegateAccount.getStateOrProvince());
+        Assert.assertEquals(finalAccount.getCountry(), delegateAccount.getCountry());
+        Assert.assertEquals(finalAccount.getPostalCode(), delegateAccount.getPostalCode());
+        Assert.assertEquals(finalAccount.getPhone(), delegateAccount.getPhone());
+        Assert.assertEquals(finalAccount.isMigrated(), delegateAccount.isMigrated());
+        Assert.assertEquals(finalAccount.isNotifiedForInvoices(), delegateAccount.isNotifiedForInvoices());
+    }
+
+    private AccountData getAccountData() {
+        return getAccountData(Integer.MIN_VALUE, Currency.AUD, UUID.randomUUID().toString());
+    }
+
+    private AccountData getAccountData(final Integer bcd, final Currency currency, final String externalKey) {
+        final AccountData secondAccountData = Mockito.mock(AccountData.class);
+        Mockito.when(secondAccountData.getExternalKey()).thenReturn(externalKey);
+        Mockito.when(secondAccountData.getEmail()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(secondAccountData.getName()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(secondAccountData.getFirstNameLength()).thenReturn(Integer.MAX_VALUE);
+        Mockito.when(secondAccountData.getCurrency()).thenReturn(currency);
+        Mockito.when(secondAccountData.getBillCycleDayLocal()).thenReturn(bcd);
+        Mockito.when(secondAccountData.getPaymentMethodId()).thenReturn(UUID.randomUUID());
+        Mockito.when(secondAccountData.getTimeZone()).thenReturn(DateTimeZone.forID("EST"));
+        Mockito.when(secondAccountData.getLocale()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(secondAccountData.getAddress1()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(secondAccountData.getAddress2()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(secondAccountData.getCompanyName()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(secondAccountData.getCity()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(secondAccountData.getStateOrProvince()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(secondAccountData.getCountry()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(secondAccountData.getPostalCode()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(secondAccountData.getPhone()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(secondAccountData.isMigrated()).thenReturn(true);
+        Mockito.when(secondAccountData.isNotifiedForInvoices()).thenReturn(true);
+        return secondAccountData;
+    }
+
+    private AccountData getNullAccountData() {
+        // Make Mockito return null for all values
+        return Mockito.mock(AccountData.class, new Answer<Object>() {
+            @Override
+            public Object answer(final InvocationOnMock invocation) throws Throwable {
+                return null;
+            }
+        });
+    }
+}
diff --git a/account/src/test/java/com/ning/billing/account/glue/TestAccountModule.java b/account/src/test/java/com/ning/billing/account/glue/TestAccountModule.java
index b6cb812..a2bbf6d 100644
--- a/account/src/test/java/com/ning/billing/account/glue/TestAccountModule.java
+++ b/account/src/test/java/com/ning/billing/account/glue/TestAccountModule.java
@@ -16,10 +16,7 @@
 
 package com.ning.billing.account.glue;
 
-import java.util.Properties;
-
 import org.skife.config.ConfigSource;
-import org.skife.config.SimplePropertyConfigSource;
 
 import com.ning.billing.mock.glue.MockEntitlementModule;
 import com.ning.billing.util.glue.AuditModule;
@@ -30,17 +27,8 @@ import com.ning.billing.util.glue.TagStoreModule;
 
 public class TestAccountModule extends DefaultAccountModule {
 
-    protected final ConfigSource configSource;
-
-    public TestAccountModule() {
-        final Properties properties = new Properties(System.getProperties());
-        // Speed up the bus
-        properties.put("killbill.billing.util.persistent.bus.sleep", "10");
-        properties.put("killbill.billing.util.persistent.bus.nbThreads", "1");
-        configSource = new SimplePropertyConfigSource(properties);
-
-        // Ignore ehcache checks. Unfortunately, ehcache looks at system properties directly...
-        System.setProperty("net.sf.ehcache.skipUpdateCheck", "true");
+    public TestAccountModule(final ConfigSource configSource) {
+        super(configSource);
     }
 
     @Override
@@ -48,7 +36,7 @@ public class TestAccountModule extends DefaultAccountModule {
         super.configure();
 
         install(new AuditModule());
-        install(new CacheModule());
+        install(new CacheModule(configSource));
         install(new CallContextModule());
         install(new CustomFieldModule());
         // Needed for Audit
diff --git a/account/src/test/java/com/ning/billing/account/glue/TestAccountModuleNoDB.java b/account/src/test/java/com/ning/billing/account/glue/TestAccountModuleNoDB.java
index 53ed416..dcb9665 100644
--- a/account/src/test/java/com/ning/billing/account/glue/TestAccountModuleNoDB.java
+++ b/account/src/test/java/com/ning/billing/account/glue/TestAccountModuleNoDB.java
@@ -16,6 +16,8 @@
 
 package com.ning.billing.account.glue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.GuicyKillbillTestNoDBModule;
 import com.ning.billing.account.dao.AccountDao;
 import com.ning.billing.account.dao.MockAccountDao;
@@ -24,6 +26,10 @@ import com.ning.billing.util.bus.InMemoryBusModule;
 
 public class TestAccountModuleNoDB extends TestAccountModule {
 
+    public TestAccountModuleNoDB(final ConfigSource configSource) {
+        super(configSource);
+    }
+
     @Override
     protected void installAccountDao() {
         bind(AccountDao.class).to(MockAccountDao.class);
diff --git a/account/src/test/java/com/ning/billing/account/glue/TestAccountModuleWithEmbeddedDB.java b/account/src/test/java/com/ning/billing/account/glue/TestAccountModuleWithEmbeddedDB.java
index 3a30677..dfd1b29 100644
--- a/account/src/test/java/com/ning/billing/account/glue/TestAccountModuleWithEmbeddedDB.java
+++ b/account/src/test/java/com/ning/billing/account/glue/TestAccountModuleWithEmbeddedDB.java
@@ -16,12 +16,18 @@
 
 package com.ning.billing.account.glue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.GuicyKillbillTestWithEmbeddedDBModule;
 import com.ning.billing.util.glue.BusModule;
 import com.ning.billing.util.glue.NonEntityDaoModule;
 
 public class TestAccountModuleWithEmbeddedDB extends TestAccountModule {
 
+    public TestAccountModuleWithEmbeddedDB(final ConfigSource configSource) {
+        super(configSource);
+    }
+
     @Override
     public void configure() {
         super.configure();
diff --git a/analytics/pom.xml b/analytics/pom.xml
index 811741a..be310fa 100644
--- a/analytics/pom.xml
+++ b/analytics/pom.xml
@@ -152,6 +152,4 @@
             <scope>test</scope>
         </dependency>
     </dependencies>
-    <build>
-    </build>
 </project>
diff --git a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
index c394e7f..858fcc6 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
@@ -183,8 +183,6 @@ public class AnalyticsListener {
             public Void call() throws Exception {
                 bipDao.invoicePaymentPosted(paymentInfo.getAccountId(),
                                             paymentInfo.getPaymentId(),
-                                            paymentInfo.getExtFirstPaymentRefId(),
-                                            paymentInfo.getExtSecondPaymentRefId(),
                                             paymentInfo.getStatus().toString(),
                                             createCallContext(paymentInfo));
                 return null;
@@ -199,8 +197,6 @@ public class AnalyticsListener {
             public Void call() throws Exception {
                 bipDao.invoicePaymentPosted(paymentError.getAccountId(),
                                             paymentError.getPaymentId(),
-                                            null,
-                                            null,
                                             paymentError.getMessage(),
                                             createCallContext(paymentError));
                 return null;
diff --git a/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java b/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java
index b5544ee..f177991 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java
@@ -343,8 +343,6 @@ public class DefaultAnalyticsUserApi implements AnalyticsUserApi {
             final Payment paymentInfo = payments.get(paymentId);
             bipDao.invoicePaymentPosted(paymentInfo.getAccountId(),
                                         paymentInfo.getId(),
-                                        paymentInfo.getExtFirstPaymentIdRef(),
-                                        paymentInfo.getExtSecondPaymentIdRef(),
                                         paymentInfo.getPaymentStatus().toString(),
                                         internalCallContext);
         }
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountDao.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountDao.java
index 7e2fce4..d6260e1 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountDao.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountDao.java
@@ -128,7 +128,7 @@ public class BusinessAccountDao {
             }
 
             // Retrieve payment methods
-            for (final PaymentMethod paymentMethod : paymentApi.getPaymentMethods(account, true, context)) {
+            for (final PaymentMethod paymentMethod : paymentApi.getPaymentMethods(account, context)) {
                 if (paymentMethod.getId().equals(account.getPaymentMethodId()) && paymentMethod.getPluginDetail() != null) {
                     paymentMethodType = PaymentMethodUtils.getPaymentMethodType(paymentMethod.getPluginDetail());
                     creditCardType = PaymentMethodUtils.getCardType(paymentMethod.getPluginDetail());
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoicePaymentDao.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoicePaymentDao.java
index 4de765d..99a7533 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoicePaymentDao.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoicePaymentDao.java
@@ -72,8 +72,7 @@ public class BusinessInvoicePaymentDao {
         this.accountDao = accountDao;
     }
 
-    public void invoicePaymentPosted(final UUID accountId, @Nullable final UUID paymentId, @Nullable final String extFirstPaymentRefId,
-                                     @Nullable final String extSecondPaymentRefId, final String message, final InternalCallContext context) {
+    public void invoicePaymentPosted(final UUID accountId, @Nullable final UUID paymentId, final String message, final InternalCallContext context) {
         // Payment attempt with no default payment method. Ignore.
         if (paymentId == null) {
             return;
@@ -97,7 +96,7 @@ public class BusinessInvoicePaymentDao {
 
         PaymentMethod paymentMethod = null;
         try {
-            paymentMethod = paymentApi.getPaymentMethod(account, payment.getPaymentMethodId(), true, context);
+            paymentMethod = paymentApi.getPaymentMethodById(payment.getPaymentMethodId(), context);
         } catch (PaymentApiException e) {
             log.info("For payment {}: payment method {} does not exist", paymentId, payment.getPaymentMethodId());
         }
@@ -114,11 +113,11 @@ public class BusinessInvoicePaymentDao {
                      invoicePayment != null ? invoicePayment.getInvoiceId() : "unknown", paymentId);
         }
 
-        createPayment(account, invoice, invoicePayment, payment, paymentMethod, extFirstPaymentRefId, extSecondPaymentRefId, message, context);
+        createPayment(account, invoice, invoicePayment, payment, paymentMethod, message, context);
     }
 
     private void createPayment(final Account account, @Nullable final Invoice invoice, @Nullable final InvoicePayment invoicePayment, final Payment payment,
-                               @Nullable final PaymentMethod paymentMethod, final String extFirstPaymentRefId, final String extSecondPaymentRefId,
+                               @Nullable final PaymentMethod paymentMethod,
                                final String message, final InternalCallContext context) {
         // paymentMethod may be null if the payment method has been deleted
         final String cardCountry;
@@ -148,6 +147,7 @@ public class BusinessInvoicePaymentDao {
         } else {
             invoicePaymentType = null;
             linkedInvoicePaymentId = null;
+            // TODO PIERRE
             createdDate = clock.getUTCNow();
             updatedDate = createdDate;
         }
@@ -155,8 +155,6 @@ public class BusinessInvoicePaymentDao {
         final BusinessInvoicePaymentModelDao businessInvoicePayment = new BusinessInvoicePaymentModelDao(
                 account.getExternalKey(),
                 payment.getAmount(),
-                extFirstPaymentRefId,
-                extSecondPaymentRefId,
                 cardCountry,
                 cardType,
                 createdDate,
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessInvoicePaymentMapper.java b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessInvoicePaymentMapper.java
index 16c3d55..65361a6 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessInvoicePaymentMapper.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessInvoicePaymentMapper.java
@@ -61,7 +61,7 @@ public class BusinessInvoicePaymentMapper implements ResultSetMapper<BusinessInv
             linkedInvoicePaymentId = null;
         }
 
-        return new BusinessInvoicePaymentModelDao(accountKey, amount, extFirstPaymentRefId, extSecondPaymentRefId, cardCountry, cardType, createdDate, currency,
+        return new BusinessInvoicePaymentModelDao(accountKey, amount, cardCountry, cardType, createdDate, currency,
                                                   effectiveDate, invoiceId, paymentError, paymentId, paymentMethod, paymentType,
                                                   pluginName, processingStatus, requestedAmount, updatedDate, invoicePaymentType,
                                                   linkedInvoicePaymentId);
diff --git a/analytics/src/main/java/com/ning/billing/analytics/model/BusinessInvoicePaymentModelDao.java b/analytics/src/main/java/com/ning/billing/analytics/model/BusinessInvoicePaymentModelDao.java
index 5f646d6..bf1ec7f 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/model/BusinessInvoicePaymentModelDao.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/model/BusinessInvoicePaymentModelDao.java
@@ -48,7 +48,7 @@ public class BusinessInvoicePaymentModelDao extends EntityBase {
     private final String invoicePaymentType;
     private final UUID linkedInvoicePaymentId;
 
-    public BusinessInvoicePaymentModelDao(final String accountKey, final BigDecimal amount, final String extFirstPaymentRefId, final String extSecondPaymentRefId,
+    public BusinessInvoicePaymentModelDao(final String accountKey, final BigDecimal amount,
                                           final String cardCountry, final String cardType, final DateTime createdDate,
                                           final Currency currency, final DateTime effectiveDate, final UUID invoiceId,
                                           final String paymentError, final UUID paymentId, final String paymentMethod,
@@ -58,8 +58,9 @@ public class BusinessInvoicePaymentModelDao extends EntityBase {
         super(paymentId, createdDate, updatedDate);
         this.accountKey = accountKey;
         this.amount = amount;
-        this.extFirstPaymentRefId = extFirstPaymentRefId;
-        this.extSecondPaymentRefId = extSecondPaymentRefId;
+        // TODO For backward compatibility
+        this.extFirstPaymentRefId = null;
+        this.extSecondPaymentRefId = null;
         this.cardCountry = cardCountry;
         this.cardType = cardType;
         this.currency = currency;
diff --git a/analytics/src/main/java/com/ning/billing/analytics/setup/AnalyticsModule.java b/analytics/src/main/java/com/ning/billing/analytics/setup/AnalyticsModule.java
index dfedeeb..0867140 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/setup/AnalyticsModule.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/setup/AnalyticsModule.java
@@ -16,6 +16,8 @@
 
 package com.ning.billing.analytics.setup;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.analytics.AnalyticsListener;
 import com.ning.billing.analytics.BusinessAccountDao;
 import com.ning.billing.analytics.BusinessSubscriptionTransitionDao;
@@ -50,6 +52,12 @@ import com.google.inject.AbstractModule;
 
 public class AnalyticsModule extends AbstractModule {
 
+    protected final ConfigSource configSource;
+
+    public AnalyticsModule(final ConfigSource configSource) {
+        this.configSource = configSource;
+    }
+
     @Override
     protected void configure() {
         installAnalyticsUserApi();
diff --git a/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestSuiteNoDB.java b/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestSuiteNoDB.java
index c31930e..ba36b48 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestSuiteNoDB.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestSuiteNoDB.java
@@ -122,18 +122,18 @@ public abstract class AnalyticsTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB 
     protected BusinessTagDao tagDao;
 
     @BeforeClass(groups = "fast")
-    protected void setup() throws Exception {
-        final Injector injector = Guice.createInjector(new TestAnalyticsModuleNoDB());
+    protected void beforeClass() throws Exception {
+        final Injector injector = Guice.createInjector(new TestAnalyticsModuleNoDB(configSource));
         injector.injectMembers(this);
     }
 
     @BeforeMethod(groups = "fast")
-    public void setupTest() {
+    public void beforeMethod() throws Exception {
         bus.start();
     }
 
     @AfterMethod(groups = "fast")
-    public void cleanupTest() {
+    public void afterMethod() throws Exception {
         bus.stop();
     }
 }
diff --git a/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestSuiteWithEmbeddedDB.java b/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestSuiteWithEmbeddedDB.java
index 6541125..0f70b21 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestSuiteWithEmbeddedDB.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestSuiteWithEmbeddedDB.java
@@ -125,19 +125,20 @@ public abstract class AnalyticsTestSuiteWithEmbeddedDB extends GuicyKillbillTest
     protected BusinessTagDao tagDao;
 
     @BeforeClass(groups = "slow")
-    protected void setup() throws Exception {
-        final Injector injector = Guice.createInjector(new TestAnalyticsModuleWithEmbeddedDB());
+    protected void beforeClass() throws Exception {
+        final Injector injector = Guice.createInjector(new TestAnalyticsModuleWithEmbeddedDB(configSource));
         injector.injectMembers(this);
     }
 
     @BeforeMethod(groups = "slow")
-    public void setupTest() throws Exception {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         bus.start();
         restartAnalyticsService();
     }
 
     @AfterMethod(groups = "slow")
-    public void cleanupTest() throws Exception {
+    public void afterMethod() throws Exception {
         bus.stop();
         stopAnalyticsService();
     }
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 411a66e..ace0c73 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
@@ -93,7 +93,8 @@ public class TestAnalyticsService extends AnalyticsTestSuiteWithEmbeddedDB {
     private PaymentInfoInternalEvent paymentInfoNotification;
 
     @BeforeMethod(groups = "slow")
-    public void createMocks() throws AccountApiException {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         Mockito.when(catalogService.getFullCatalog()).thenReturn(new MockCatalog());
 
         final PaymentMethod paymentMethod = Mockito.mock(PaymentMethod.class);
@@ -177,7 +178,7 @@ public class TestAnalyticsService extends AnalyticsTestSuiteWithEmbeddedDB {
                                                                       INVOICE_AMOUNT, ACCOUNT_CURRENCY, null, 1L, 1L);
 
         paymentInfoNotification = new DefaultPaymentInfoEvent(account.getId(), invoice.getId(), null, INVOICE_AMOUNT, -1,
-                                                              PaymentStatus.UNKNOWN, null, null, null, clock.getUTCNow(), 1L, 1L);
+                                                              PaymentStatus.UNKNOWN, null, clock.getUTCNow(), 1L, 1L);
     }
 
     @Test(groups = "slow")
diff --git a/analytics/src/test/java/com/ning/billing/analytics/dao/TestAnalyticsDao.java b/analytics/src/test/java/com/ning/billing/analytics/dao/TestAnalyticsDao.java
index 58e6d46..0219eac 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/dao/TestAnalyticsDao.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/dao/TestAnalyticsDao.java
@@ -67,7 +67,8 @@ public class TestAnalyticsDao extends AnalyticsTestSuiteWithEmbeddedDB {
     private final Catalog catalog = Mockito.mock(Catalog.class);
 
     @BeforeClass(groups = "slow")
-    public void setupMocks() throws IOException, ClassNotFoundException, SQLException, CatalogApiException {
+    public void beforeClass() throws Exception {
+        super.beforeClass();
         Mockito.when(catalog.findPlan(Mockito.anyString(), Mockito.<DateTime>any())).thenReturn(plan);
         Mockito.when(catalog.findPlan(Mockito.anyString(), Mockito.<DateTime>any(), Mockito.<DateTime>any())).thenReturn(plan);
         Mockito.when(catalog.findPhase(Mockito.anyString(), Mockito.<DateTime>any(), Mockito.<DateTime>any())).thenReturn(phase);
diff --git a/analytics/src/test/java/com/ning/billing/analytics/dao/TestBusinessInvoicePaymentSqlDao.java b/analytics/src/test/java/com/ning/billing/analytics/dao/TestBusinessInvoicePaymentSqlDao.java
index 7e76cf9..4eee2d2 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/dao/TestBusinessInvoicePaymentSqlDao.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/dao/TestBusinessInvoicePaymentSqlDao.java
@@ -32,10 +32,8 @@ public class TestBusinessInvoicePaymentSqlDao extends AnalyticsTestSuiteWithEmbe
 
     @Test(groups = "slow")
     public void testCRUD() throws Exception {
-        final String extFirstPaymentRefId = UUID.randomUUID().toString();
-        final String extSecondPaymentRefId = UUID.randomUUID().toString();
         final String accountKey = UUID.randomUUID().toString();
-        final BusinessInvoicePaymentModelDao invoicePayment = createInvoicePayment(extFirstPaymentRefId, extSecondPaymentRefId, accountKey);
+        final BusinessInvoicePaymentModelDao invoicePayment = createInvoicePayment(accountKey);
 
         // Verify initial state
         Assert.assertNull(invoicePaymentSqlDao.getInvoicePayment(invoicePayment.getPaymentId().toString(), internalCallContext));
@@ -57,14 +55,10 @@ public class TestBusinessInvoicePaymentSqlDao extends AnalyticsTestSuiteWithEmbe
 
     @Test(groups = "slow")
     public void testSegmentation() throws Exception {
-        final String extFirstPaymentRefId1 = UUID.randomUUID().toString();
-        final String extSecondPaymentRefId1 = UUID.randomUUID().toString();
         final String accountKey1 = UUID.randomUUID().toString();
-        final BusinessInvoicePaymentModelDao invoicePayment1 = createInvoicePayment(extFirstPaymentRefId1, extSecondPaymentRefId1, accountKey1);
-        final String extFirstPaymentRefId2 = UUID.randomUUID().toString();
-        final String extSecondPaymentRefId2 = UUID.randomUUID().toString();
+        final BusinessInvoicePaymentModelDao invoicePayment1 = createInvoicePayment(accountKey1);
         final String accountKey2 = UUID.randomUUID().toString();
-        final BusinessInvoicePaymentModelDao invoicePayment2 = createInvoicePayment(extFirstPaymentRefId2, extSecondPaymentRefId2, accountKey2);
+        final BusinessInvoicePaymentModelDao invoicePayment2 = createInvoicePayment(accountKey2);
 
         // Create both invoice payments
         Assert.assertEquals(invoicePaymentSqlDao.createInvoicePayment(invoicePayment1, internalCallContext), 1);
@@ -94,7 +88,7 @@ public class TestBusinessInvoicePaymentSqlDao extends AnalyticsTestSuiteWithEmbe
         }
     }
 
-    private BusinessInvoicePaymentModelDao createInvoicePayment(final String extFirstPaymentRefId, final String extSecondPaymentRefId, final String accountKey) {
+    private BusinessInvoicePaymentModelDao createInvoicePayment(final String accountKey) {
         final BigDecimal amount = BigDecimal.ONE;
         final String cardCountry = UUID.randomUUID().toString().substring(0, 20);
         final String cardType = UUID.randomUUID().toString().substring(0, 20);
@@ -113,7 +107,7 @@ public class TestBusinessInvoicePaymentSqlDao extends AnalyticsTestSuiteWithEmbe
         final String invoicePaymentType = UUID.randomUUID().toString().substring(0, 10);
         final UUID linkedInvoicePaymentId = UUID.randomUUID();
 
-        return new BusinessInvoicePaymentModelDao(accountKey, amount, extFirstPaymentRefId, extSecondPaymentRefId,
+        return new BusinessInvoicePaymentModelDao(accountKey, amount,
                                                   cardCountry, cardType, createdDate,
                                                   currency, effectiveDate, invoiceId,
                                                   paymentError, paymentId, paymentMethod,
diff --git a/analytics/src/test/java/com/ning/billing/analytics/glue/TestAnalyticsModule.java b/analytics/src/test/java/com/ning/billing/analytics/glue/TestAnalyticsModule.java
index 5956954..a414303 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/glue/TestAnalyticsModule.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/glue/TestAnalyticsModule.java
@@ -16,11 +16,8 @@
 
 package com.ning.billing.analytics.glue;
 
-import java.util.Properties;
-
 import org.mockito.Mockito;
 import org.skife.config.ConfigSource;
-import org.skife.config.SimplePropertyConfigSource;
 
 import com.ning.billing.analytics.setup.AnalyticsModule;
 import com.ning.billing.catalog.MockCatalogModule;
@@ -42,17 +39,8 @@ import com.ning.billing.util.glue.TagStoreModule;
 
 public class TestAnalyticsModule extends AnalyticsModule {
 
-    protected final ConfigSource configSource;
-
-    public TestAnalyticsModule() {
-        final Properties properties = new Properties(System.getProperties());
-        // Speed up the bus
-        properties.put("killbill.billing.util.persistent.bus.sleep", "10");
-        properties.put("killbill.billing.util.persistent.bus.nbThreads", "1");
-        configSource = new SimplePropertyConfigSource(properties);
-
-        // Ignore ehcache checks. Unfortunately, ehcache looks at system properties directly...
-        System.setProperty("net.sf.ehcache.skipUpdateCheck", "true");
+    public TestAnalyticsModule(final ConfigSource configSource) {
+        super(configSource);
     }
 
     @Override
@@ -60,7 +48,7 @@ public class TestAnalyticsModule extends AnalyticsModule {
         super.configure();
 
         install(new AuditModule());
-        install(new CacheModule());
+        install(new CacheModule(configSource));
         install(new CallContextModule());
         install(new CustomFieldModule());
         install(new MockAccountModule());
@@ -71,7 +59,7 @@ public class TestAnalyticsModule extends AnalyticsModule {
         install(new MockOverdueModule());
         install(new MockPaymentModule());
         install(new MockGlobalLockerModule());
-        install(new NotificationQueueModule());
+        install(new NotificationQueueModule(configSource));
         install(new TagStoreModule());
 
         bind(InvoiceDao.class).toInstance(Mockito.mock(InvoiceDao.class));
diff --git a/analytics/src/test/java/com/ning/billing/analytics/glue/TestAnalyticsModuleNoDB.java b/analytics/src/test/java/com/ning/billing/analytics/glue/TestAnalyticsModuleNoDB.java
index 55f675e..5a6e851 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/glue/TestAnalyticsModuleNoDB.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/glue/TestAnalyticsModuleNoDB.java
@@ -17,6 +17,7 @@
 package com.ning.billing.analytics.glue;
 
 import org.mockito.Mockito;
+import org.skife.config.ConfigSource;
 
 import com.ning.billing.GuicyKillbillTestNoDBModule;
 import com.ning.billing.analytics.MockBusinessSubscriptionTransitionSqlDao;
@@ -39,6 +40,10 @@ import com.ning.billing.util.bus.InMemoryBusModule;
 
 public class TestAnalyticsModuleNoDB extends TestAnalyticsModule {
 
+    public TestAnalyticsModuleNoDB(final ConfigSource configSource) {
+        super(configSource);
+    }
+
     @Override
     protected void installAnalyticsSqlDao() {
         bind(BusinessSubscriptionTransitionSqlDao.class).to(MockBusinessSubscriptionTransitionSqlDao.class).asEagerSingleton();
diff --git a/analytics/src/test/java/com/ning/billing/analytics/glue/TestAnalyticsModuleWithEmbeddedDB.java b/analytics/src/test/java/com/ning/billing/analytics/glue/TestAnalyticsModuleWithEmbeddedDB.java
index f749c31..fc9ecaf 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/glue/TestAnalyticsModuleWithEmbeddedDB.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/glue/TestAnalyticsModuleWithEmbeddedDB.java
@@ -16,12 +16,18 @@
 
 package com.ning.billing.analytics.glue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.GuicyKillbillTestWithEmbeddedDBModule;
 import com.ning.billing.util.glue.BusModule;
 import com.ning.billing.util.glue.NonEntityDaoModule;
 
 public class TestAnalyticsModuleWithEmbeddedDB extends TestAnalyticsModule {
 
+    public TestAnalyticsModuleWithEmbeddedDB(final ConfigSource configSource) {
+        super(configSource);
+    }
+
     @Override
     public void configure() {
         super.configure();
diff --git a/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessAccount.java b/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessAccount.java
index 7ea70a2..9bff5c7 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessAccount.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessAccount.java
@@ -32,7 +32,8 @@ public class TestBusinessAccount extends AnalyticsTestSuiteNoDB {
     private BusinessAccountModelDao account;
 
     @BeforeMethod(groups = "fast")
-    public void setUp() throws Exception {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         account = new BusinessAccountModelDao(UUID.randomUUID(), "pierre", UUID.randomUUID().toString(), BigDecimal.ONE, clock.getUTCToday(),
                                               BigDecimal.TEN, "ERROR_NOT_ENOUGH_FUNDS", "CreditCard", "Visa", "", UUID.randomUUID().toString(),
                                               clock.getUTCNow(), clock.getUTCNow());
diff --git a/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessInvoicePayment.java b/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessInvoicePayment.java
index 116a2ad..c8835bd 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessInvoicePayment.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessInvoicePayment.java
@@ -33,8 +33,6 @@ public class TestBusinessInvoicePayment extends AnalyticsTestSuiteNoDB {
     public void testEquals() throws Exception {
         final String accountKey = UUID.randomUUID().toString();
         final BigDecimal amount = BigDecimal.ONE;
-        final String extFirstPaymentRefId = UUID.randomUUID().toString();
-        final String extSecondPaymentRefId = UUID.randomUUID().toString();
         final String cardCountry = UUID.randomUUID().toString();
         final String cardType = UUID.randomUUID().toString();
         final DateTime createdDate = new DateTime(DateTimeZone.UTC);
@@ -51,7 +49,7 @@ public class TestBusinessInvoicePayment extends AnalyticsTestSuiteNoDB {
         final DateTime updatedDate = new DateTime(DateTimeZone.UTC);
         final String invoicePaymentType = UUID.randomUUID().toString();
         final UUID linkedInvoicePaymentId = UUID.randomUUID();
-        final BusinessInvoicePaymentModelDao invoicePayment = new BusinessInvoicePaymentModelDao(accountKey, amount, extFirstPaymentRefId, extSecondPaymentRefId,
+        final BusinessInvoicePaymentModelDao invoicePayment = new BusinessInvoicePaymentModelDao(accountKey, amount,
                                                                                                  cardCountry, cardType, createdDate,
                                                                                                  currency, effectiveDate, invoiceId,
                                                                                                  paymentError, paymentId, paymentMethod,
@@ -63,8 +61,6 @@ public class TestBusinessInvoicePayment extends AnalyticsTestSuiteNoDB {
         Assert.assertTrue(invoicePayment.equals(invoicePayment));
         Assert.assertEquals(invoicePayment.getAccountKey(), accountKey);
         Assert.assertEquals(invoicePayment.getAmount(), amount);
-        Assert.assertEquals(invoicePayment.getExtFirstPaymentRefId(), extFirstPaymentRefId);
-        Assert.assertEquals(invoicePayment.getExtSecondPaymentRefId(), extSecondPaymentRefId);
         Assert.assertEquals(invoicePayment.getCardCountry(), cardCountry);
         Assert.assertEquals(invoicePayment.getCardType(), cardType);
         Assert.assertEquals(invoicePayment.getCreatedDate(), createdDate);
@@ -82,7 +78,7 @@ public class TestBusinessInvoicePayment extends AnalyticsTestSuiteNoDB {
         Assert.assertEquals(invoicePayment.getInvoicePaymentType(), invoicePaymentType);
         Assert.assertEquals(invoicePayment.getLinkedInvoicePaymentId(), linkedInvoicePaymentId);
 
-        final BusinessInvoicePaymentModelDao otherInvoicePayment = new BusinessInvoicePaymentModelDao(null, null, extFirstPaymentRefId, extSecondPaymentRefId, null, null, createdDate,
+        final BusinessInvoicePaymentModelDao otherInvoicePayment = new BusinessInvoicePaymentModelDao(null, null, null, null, createdDate,
                                                                                                       null, null, null, null, paymentId, null,
                                                                                                       null, null, null, null, null, null, null);
         Assert.assertFalse(invoicePayment.equals(otherInvoicePayment));
diff --git a/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessSubscription.java b/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessSubscription.java
index 14a4fc9..f69c32a 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessSubscription.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessSubscription.java
@@ -116,8 +116,11 @@ public class TestBusinessSubscription extends AnalyticsTestSuiteNoDB {
 
     private final Catalog catalog = Mockito.mock(Catalog.class);
 
+
+    @Override
     @BeforeMethod(groups = "fast")
-    public void setUp() throws Exception {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         product = new MockProduct("platinium", "subscription", ProductCategory.BASE);
         plan = new MockPlan("platinum-monthly", product);
         phase = new MockPhase(PhaseType.EVERGREEN, plan, MockDuration.UNLIMITED(), 25.95);
diff --git a/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessSubscriptionEvent.java b/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessSubscriptionEvent.java
index 7b85925..2608fe4 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessSubscriptionEvent.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessSubscriptionEvent.java
@@ -45,8 +45,10 @@ public class TestBusinessSubscriptionEvent extends AnalyticsTestSuiteNoDB {
 
     private final Catalog catalog = Mockito.mock(Catalog.class);
 
+    @Override
     @BeforeMethod(groups = "fast")
-    public void setUp() throws Exception {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         product = new MockProduct("platinium", "subscription", ProductCategory.BASE);
         plan = new MockPlan("platinum-monthly", product);
         phase = new MockPhase(PhaseType.EVERGREEN, plan, MockDuration.UNLIMITED(), 25.95);
diff --git a/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessSubscriptionTransition.java b/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessSubscriptionTransition.java
index 1440516..716a189 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessSubscriptionTransition.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/model/TestBusinessSubscriptionTransition.java
@@ -57,8 +57,10 @@ public class TestBusinessSubscriptionTransition extends AnalyticsTestSuiteNoDB {
 
     private final Catalog catalog = Mockito.mock(Catalog.class);
 
-    @BeforeMethod(alwaysRun = true)
-    public void setUp() throws Exception {
+    @Override
+    @BeforeMethod(groups = "fast")
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         final Product product = new MockProduct("platinium", "subscription", ProductCategory.BASE);
         final Plan plan = new MockPlan("platinum-monthly", product);
         final PlanPhase phase = new MockPhase(PhaseType.EVERGREEN, plan, MockDuration.UNLIMITED(), 25.95);

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

diff --git a/api/pom.xml b/api/pom.xml
index 161848f..7f6309a 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -28,18 +28,16 @@
     <packaging>jar</packaging>
     <dependencies>
         <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
-            <scope>provided</scope>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.jdbi</groupId>
-            <artifactId>jdbi</artifactId>
+            <groupId>com.google.code.findbugs</groupId>
+            <artifactId>jsr305</artifactId>
         </dependency>
         <dependency>
-            <groupId>com.google.inject</groupId>
-            <artifactId>guice</artifactId>
-            <scope>provided</scope>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
         </dependency>
         <dependency>
             <groupId>joda-time</groupId>
@@ -53,11 +51,5 @@
             <groupId>org.skife.config</groupId>
             <artifactId>config-magic</artifactId>
         </dependency>
-        <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-annotations</artifactId>
-        </dependency>
     </dependencies>
-    <build>
-    </build>
 </project>
diff --git a/api/src/main/java/com/ning/billing/lifecycle/LifecycleHandlerType.java b/api/src/main/java/com/ning/billing/lifecycle/LifecycleHandlerType.java
index 6948773..d0b9453 100644
--- a/api/src/main/java/com/ning/billing/lifecycle/LifecycleHandlerType.java
+++ b/api/src/main/java/com/ning/billing/lifecycle/LifecycleHandlerType.java
@@ -20,10 +20,9 @@ import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.util.ArrayList;
 import java.util.List;
 
-import com.google.common.collect.Lists;
-
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.METHOD)
 public @interface LifecycleHandlerType {
@@ -97,7 +96,7 @@ public @interface LifecycleHandlerType {
         // Returns an ordered list of level for a particular sequence
         //
         public static List<LifecycleLevel> getLevelsForSequence(final Sequence seq) {
-            final List<LifecycleLevel> result = Lists.newLinkedList();
+            final List<LifecycleLevel> result = new ArrayList<LifecycleLevel>();
             for (final LifecycleLevel level : LifecycleLevel.values()) {
                 if (level.getSequence() == seq) {
                     result.add(level);
diff --git a/api/src/main/java/com/ning/billing/osgi/api/config/PluginConfig.java b/api/src/main/java/com/ning/billing/osgi/api/config/PluginConfig.java
index ced4e47..02cca4e 100644
--- a/api/src/main/java/com/ning/billing/osgi/api/config/PluginConfig.java
+++ b/api/src/main/java/com/ning/billing/osgi/api/config/PluginConfig.java
@@ -16,6 +16,8 @@
 
 package com.ning.billing.osgi.api.config;
 
+import java.io.File;
+
 public interface PluginConfig {
 
     public enum PluginType {
@@ -37,5 +39,10 @@ public interface PluginConfig {
 
     public String getPluginVersionnedName();
 
+    /**
+     * @return root directory of the deployed plugin
+     */
+    public File getPluginVersionRoot();
+
     public PluginLanguage getPluginLanguage();
 }
diff --git a/api/src/main/java/com/ning/billing/osgi/api/config/PluginRubyConfig.java b/api/src/main/java/com/ning/billing/osgi/api/config/PluginRubyConfig.java
index 0e9ca2a..2350304 100644
--- a/api/src/main/java/com/ning/billing/osgi/api/config/PluginRubyConfig.java
+++ b/api/src/main/java/com/ning/billing/osgi/api/config/PluginRubyConfig.java
@@ -21,5 +21,6 @@ public interface PluginRubyConfig extends PluginConfig {
     public String getRubyMainClass();
 
     public String getRubyLoadDir();
-    
+
+    public String getRubyRequire();
 }
diff --git a/api/src/main/java/com/ning/billing/osgi/api/OSGIKillbill.java b/api/src/main/java/com/ning/billing/osgi/api/OSGIKillbill.java
new file mode 100644
index 0000000..646afd9
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/osgi/api/OSGIKillbill.java
@@ -0,0 +1,94 @@
+/*
+ * 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.api;
+
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.analytics.api.sanity.AnalyticsSanityApi;
+import com.ning.billing.analytics.api.user.AnalyticsUserApi;
+import com.ning.billing.catalog.api.CatalogUserApi;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
+import com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi;
+import com.ning.billing.entitlement.api.transfer.EntitlementTransferApi;
+import com.ning.billing.entitlement.api.user.EntitlementUserApi;
+import com.ning.billing.invoice.api.InvoiceMigrationApi;
+import com.ning.billing.invoice.api.InvoicePaymentApi;
+import com.ning.billing.invoice.api.InvoiceUserApi;
+import com.ning.billing.osgi.api.config.PluginConfigServiceApi;
+import com.ning.billing.overdue.OverdueUserApi;
+import com.ning.billing.payment.api.PaymentApi;
+import com.ning.billing.tenant.api.TenantUserApi;
+import com.ning.billing.usage.api.UsageUserApi;
+import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.api.CustomFieldUserApi;
+import com.ning.billing.util.api.ExportUserApi;
+import com.ning.billing.util.api.TagUserApi;
+
+/**
+ * This interface encapsulates all the OSGI interfaces seen by the Killbill OSGI plugins
+ */
+public interface OSGIKillbill {
+
+    /**
+     * Used  by the OSGI bundles to interact with Killbill through APIs
+     *
+     * @return the matching API
+     */
+    public AccountUserApi getAccountUserApi();
+
+    public AnalyticsSanityApi getAnalyticsSanityApi();
+
+    public AnalyticsUserApi getAnalyticsUserApi();
+
+    public CatalogUserApi getCatalogUserApi();
+
+    public EntitlementMigrationApi getEntitlementMigrationApi();
+
+    public EntitlementTimelineApi getEntitlementTimelineApi();
+
+    public EntitlementTransferApi getEntitlementTransferApi();
+
+    public EntitlementUserApi getEntitlementUserApi();
+
+    public InvoiceMigrationApi getInvoiceMigrationApi();
+
+    public InvoicePaymentApi getInvoicePaymentApi();
+
+    public InvoiceUserApi getInvoiceUserApi();
+
+    public OverdueUserApi getOverdueUserApi();
+
+    public PaymentApi getPaymentApi();
+
+    public TenantUserApi getTenantUserApi();
+
+    public UsageUserApi getUsageUserApi();
+
+    public AuditUserApi getAuditUserApi();
+
+    public CustomFieldUserApi getCustomFieldUserApi();
+
+    public ExportUserApi getExportUserApi();
+
+    public TagUserApi getTagUserApi();
+
+    /**
+     * Used by the OSGI bundles to discover their configuration
+     *
+     * @return the PluginConfigServiceApi
+     */
+    public PluginConfigServiceApi getPluginConfigServiceApi();
+}
diff --git a/api/src/main/java/com/ning/billing/osgi/api/OSGIPluginProperties.java b/api/src/main/java/com/ning/billing/osgi/api/OSGIPluginProperties.java
new file mode 100644
index 0000000..e1873e0
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/osgi/api/OSGIPluginProperties.java
@@ -0,0 +1,37 @@
+/*
+ * 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.api;
+
+/**
+ * Those represents the properties that plugin can use when registering services
+ * and that Killbill knows how to interpret. At a minimum, the plugin should use PLUGIN_NAME_PROP
+ */
+public interface OSGIPluginProperties {
+
+    /** Name of the plugin when it registers itself */
+    // TODO We should make sure that this mataches the 'symbolic name' of the plugin, or if not how those two play together
+    public static final String PLUGIN_NAME_PROP = "killbill.pluginName";
+
+    /** Name of the instnace of the plugin; if 2 instances of the same plugin register */
+    public static final String PLUGIN_INSTANCE_PROP = "killbill.pluginInstance";
+
+    /** Used to export an additional configuration string for that service
+     *  For instance for Servlet services this is used to specify the path of the servlet.
+     */
+    public static final String PLUGIN_SERVICE_INFO = "killbill.pluginServiceInfo";
+
+}
diff --git a/api/src/main/java/com/ning/billing/osgi/api/OSGIServiceDescriptor.java b/api/src/main/java/com/ning/billing/osgi/api/OSGIServiceDescriptor.java
new file mode 100644
index 0000000..d281471
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/osgi/api/OSGIServiceDescriptor.java
@@ -0,0 +1,43 @@
+/*
+ * 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.api;
+
+public interface OSGIServiceDescriptor {
+
+    /**
+     * @return the symbolic name of the OSGI plugin registering that service
+     */
+    public String getPluginSymbolicName();
+
+    /**
+     *
+     * @return the unique of that service-- plugin should rely on namespace to enforce the uniqueness
+     */
+    public String getServiceName();
+
+    /**
+     *
+     * @return additional service info that can be interpreted by the OSGIServiceRegistration system
+     */
+    public String getServiceInfo();
+
+    /**
+     *
+     * @return the type of the service being registered
+     */
+    public String getServiceType();
+}
diff --git a/api/src/main/java/com/ning/billing/osgi/api/OSGIServiceRegistration.java b/api/src/main/java/com/ning/billing/osgi/api/OSGIServiceRegistration.java
new file mode 100644
index 0000000..cfa3127
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/osgi/api/OSGIServiceRegistration.java
@@ -0,0 +1,51 @@
+/*
+ * 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.api;
+
+import java.util.Set;
+
+/**
+ * The purpose is to register within Killbill OSGI services
+ * that were exported by specific Killbill plugins
+ *
+ * @param <T> The OSGI service exported by Killbill bundles
+ */
+public interface OSGIServiceRegistration<T> {
+
+    void registerService(OSGIServiceDescriptor desc, T service);
+
+    /**
+     * @param serviceName the name of the service as it was registered
+     */
+    void unregisterService(String serviceName);
+
+    /**
+     * @param serviceName the name of the service as it was registered
+     * @return the instance that was registered under that name
+     */
+    T getServiceForName(String serviceName);
+
+    /**
+     * @return the set of all the service registered
+     */
+    Set<String> getAllServices();
+
+    /**
+     * @return the type of service that is registered under that OSGIServiceRegistration
+     */
+    Class<T> getServiceType();
+}
diff --git a/api/src/main/java/com/ning/billing/osgi/api/OSGIUserApi.java b/api/src/main/java/com/ning/billing/osgi/api/OSGIUserApi.java
new file mode 100644
index 0000000..b0bdfed
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/osgi/api/OSGIUserApi.java
@@ -0,0 +1,20 @@
+/*
+ * 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.api;
+
+public interface OSGIUserApi {
+}
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java b/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
index ca7febb..833b4d4 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentApi.java
@@ -111,25 +111,13 @@ public interface PaymentApi {
      */
     public Set<String> getAvailablePlugins();
 
-    public String initializeAccountPlugin(String pluginName, Account account, CallContext context)
-            throws PaymentApiException;
-
     public UUID addPaymentMethod(String pluginName, Account account, boolean setDefault, PaymentMethodPlugin paymentMethodInfo, CallContext context)
             throws PaymentApiException;
 
-    public List<PaymentMethod> refreshPaymentMethods(String pluginName, Account account, CallContext context)
-            throws PaymentApiException;
-
-    public List<PaymentMethod> getPaymentMethods(Account account, boolean withPluginDetail, TenantContext context)
-            throws PaymentApiException;
-
-    public PaymentMethod getPaymentMethodById(UUID paymentMethodId, TenantContext context)
-            throws PaymentApiException;
-
-    public PaymentMethod getPaymentMethod(Account account, UUID paymentMethodId, boolean withPluginDetail, TenantContext context)
+    public List<PaymentMethod> getPaymentMethods(Account account, TenantContext context)
             throws PaymentApiException;
 
-    public void updatePaymentMethod(Account account, UUID paymentMethodId, PaymentMethodPlugin paymentMethodInfo, CallContext context)
+    public PaymentMethod getPaymentMethodById(UUID paymentMethodId, final boolean withPluginInfo, TenantContext context)
             throws PaymentApiException;
 
     public void deletedPaymentMethod(Account account, UUID paymentMethodId, boolean deleteDefaultPaymentMethodWithAutoPayOff, CallContext context)
@@ -138,4 +126,7 @@ public interface PaymentApi {
     public void setDefaultPaymentMethod(Account account, UUID paymentMethodId, CallContext context)
             throws PaymentApiException;
 
+    public List<PaymentMethod> refreshPaymentMethods(String pluginName, Account account, CallContext context)
+            throws PaymentApiException;
+
 }
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentMethodPlugin.java b/api/src/main/java/com/ning/billing/payment/api/PaymentMethodPlugin.java
index 690584a..0433003 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentMethodPlugin.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentMethodPlugin.java
@@ -20,6 +20,7 @@ import java.util.List;
 
 public interface PaymentMethodPlugin {
 
+
     public String getExternalPaymentMethodId();
 
     public boolean isDefaultPaymentMethod();
diff --git a/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentInfoPlugin.java b/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentInfoPlugin.java
index 826c304..50a2ac9 100644
--- a/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentInfoPlugin.java
+++ b/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentInfoPlugin.java
@@ -13,6 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
+
 package com.ning.billing.payment.plugin.api;
 
 import java.math.BigDecimal;
@@ -27,19 +28,33 @@ public interface PaymentInfoPlugin {
         ERROR
     }
 
+    /**
+     * @return payment amount
+     */
     public BigDecimal getAmount();
 
+    /**
+     * @return date when the payment was created
+     */
     public DateTime getCreatedDate();
 
+    /**
+     * @return date when the payment is effective
+     */
     public DateTime getEffectiveDate();
 
+    /**
+     * @return payment status in the gateway
+     */
     public PaymentPluginStatus getStatus();
 
+    /**
+     * @return gateway error, if any
+     */
     public String getGatewayError();
 
+    /**
+     * @return gateway error code, if any
+     */
     public String getGatewayErrorCode();
-
-    public String getExtFirstReferenceId();
-
-    public String getExtSecondReferenceId();
 }
diff --git a/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentMethodInfoPlugin.java b/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentMethodInfoPlugin.java
new file mode 100644
index 0000000..cd25e95
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentMethodInfoPlugin.java
@@ -0,0 +1,49 @@
+/*
+ * 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.payment.plugin.api;
+
+import java.util.UUID;
+
+/**
+ * Returns the plugin view of existing payment methods
+ */
+public interface PaymentMethodInfoPlugin {
+
+    /**
+     *
+     * @return the Killbill accountId
+     */
+    public UUID getAccountId();
+
+    /**
+     *
+     * @return the killbillPaymentMethodId
+     */
+    public UUID getPaymentMethodId();
+
+    /**
+     *
+     * @return default payment method set on the gateway
+     */
+    public boolean isDefault();
+
+    /**
+     *
+     * @return the external paymentMethodId on the gateway
+     */
+    public String getExternalPaymentMethodId();
+}
diff --git a/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentPluginApi.java b/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentPluginApi.java
index 4007d0b..0a0815b 100644
--- a/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentPluginApi.java
+++ b/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentPluginApi.java
@@ -20,45 +20,123 @@ import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
 
-import com.ning.billing.account.api.Account;
 import com.ning.billing.payment.api.PaymentMethodPlugin;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.TenantContext;
 
 public interface PaymentPluginApi {
 
+    /**
+     * @return plugin name
+     */
     public String getName();
 
-    public PaymentInfoPlugin processPayment(String externalAccountKey, UUID paymentId, BigDecimal amount, CallContext context)
+    /**
+     * Charge a specific amount in the Gateway. Required.
+     *
+     * @param kbPaymentId            killbill payment id (for reference)
+     * @param kbPaymentMethodId      killbill payment method id
+     * @param amount                 amount to charge
+     * @param context                call context
+     * @return information about the payment in the gateway
+     * @throws PaymentPluginApiException
+     */
+    public PaymentInfoPlugin processPayment(UUID kbPaymentId, UUID kbPaymentMethodId, BigDecimal amount, CallContext context)
             throws PaymentPluginApiException;
 
-    public PaymentInfoPlugin getPaymentInfo(UUID paymentId, TenantContext context)
+    /**
+     * Retrieve information about a given payment. Optional (not all gateways will support it).
+     *
+     *
+     * @param kbPaymentId      killbill payment id (for reference)
+     * @param context          call context
+     * @return information about the payment in the gateway
+     * @throws PaymentPluginApiException
+     */
+    public PaymentInfoPlugin getPaymentInfo(UUID kbPaymentId, TenantContext context)
             throws PaymentPluginApiException;
 
-    public void processRefund(final Account account, final UUID paymentId, BigDecimal refundAmount, CallContext context)
+    /**
+     * Process a refund against a given payment. Required.
+     *
+     *
+     * @param kbPaymentId      killbill payment id (for reference)
+     * @param refundAmount     call context
+     * @param context          call context
+     * @return information about the refund in the gateway
+     * @throws PaymentPluginApiException
+     */
+    public RefundInfoPlugin processRefund(UUID kbPaymentId, BigDecimal refundAmount, CallContext context)
             throws PaymentPluginApiException;
 
-    public int getNbRefundForPaymentAmount(final Account account, final UUID paymentId, final BigDecimal refundAmount, TenantContext context)
+    /**
+     * Add a payment method for a Killbill account in the gateway. Optional.
+     *
+     * Note: the payment method doesn't exist yet in Killbill when receiving the call in
+     * the plugin (kbPaymentMethodId is a placeholder).
+     *
+     * @param kbAccountId        killbill accountId
+     * @param paymentMethodProps payment method details
+     * @param setDefault         set it as the default payment method in the gateway
+     * @param context            call context
+     * @throws PaymentPluginApiException
+     */
+    public void addPaymentMethod(UUID kbAccountId, UUID kbPaymentMethodId, PaymentMethodPlugin paymentMethodProps, boolean setDefault, CallContext context)
             throws PaymentPluginApiException;
 
-    public String createPaymentProviderAccount(Account account, CallContext context)
+    /**
+     * Delete a payment method in the gateway. Optional.
+     *
+     * @param kbPaymentMethodId      killbill payment method id
+     * @param context                call context
+     * @throws PaymentPluginApiException
+     */
+    public void deletePaymentMethod(UUID kbPaymentMethodId, CallContext context)
             throws PaymentPluginApiException;
 
-    public List<PaymentMethodPlugin> getPaymentMethodDetails(String accountKey, TenantContext context)
+    /**
+     * Get payment method details for a given payment method. Optional.
+     *
+     * @param kbAccountId       killbill account id
+     * @param kbPaymentMethodId killbill payment method id.
+     * @param context           call context
+     * @return PaymentMethodPlugin info for the payment method
+     * @throws PaymentPluginApiException
+     */
+    public PaymentMethodPlugin getPaymentMethodDetail(UUID kbAccountId, UUID kbPaymentMethodId, TenantContext context)
             throws PaymentPluginApiException;
 
-    public PaymentMethodPlugin getPaymentMethodDetail(String accountKey, String externalPaymentMethodId, TenantContext context)
+    /**
+     * Set a payment method as default in the gateway. Optional.
+     *
+     * @param kbPaymentMethodId      killbill payment method id
+     * @param context                call context
+     * @throws PaymentPluginApiException
+     */
+    public void setDefaultPaymentMethod(UUID kbPaymentMethodId, CallContext context)
             throws PaymentPluginApiException;
 
-    public String addPaymentMethod(String accountKey, PaymentMethodPlugin paymentMethodProps, boolean setDefault, CallContext context)
+    /**
+     *
+     * This is used to see the view of paymentMethods kept by the plugin or the view of
+     * existing payment method on the gateway.
+     *
+     * Sometimes payment methods have to be added directly to the gateway for PCI compliance issues
+     * and so Killbill needs to refresh its state.
+     *
+     * @param kbAccountId           killbill accountId
+     * @param refreshFromGateway    fetch the list of existing  payment methods from gateway-- if supported
+     * @param context               call context
+     * @return
+     */
+    public List<PaymentMethodInfoPlugin> getPaymentMethods(UUID kbAccountId, boolean refreshFromGateway, CallContext context)
             throws PaymentPluginApiException;
 
-    public void updatePaymentMethod(String accountKey, PaymentMethodPlugin paymentMethodProps, CallContext context)
-            throws PaymentPluginApiException;
-
-    public void deletePaymentMethod(String accountKey, String externalPaymentMethodId, CallContext context)
-            throws PaymentPluginApiException;
-
-    public void setDefaultPaymentMethod(String accountKey, String externalPaymentId, CallContext context)
+    /**
+     * This is used after Killbill decided to refresh its state from the gateway
+     *
+     * @param paymentMethods        the list of payment methods
+     */
+    public void resetPaymentMethods(List<PaymentMethodInfoPlugin> paymentMethods)
             throws PaymentPluginApiException;
 }
diff --git a/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentPluginApiException.java b/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentPluginApiException.java
index 607be20..f9a2000 100644
--- a/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentPluginApiException.java
+++ b/api/src/main/java/com/ning/billing/payment/plugin/api/PaymentPluginApiException.java
@@ -16,17 +16,30 @@
 
 package com.ning.billing.payment.plugin.api;
 
+import com.ning.billing.BillingExceptionBase;
+
 public class PaymentPluginApiException extends Exception {
     private static final long serialVersionUID = 15642965L;
 
     private final String errorType;
     private final String errorMessage;
 
+    public PaymentPluginApiException(final String msg, final Throwable e) {
+        super(msg, e);
+        errorMessage = msg;
+        errorType = e.getMessage();
+    }
+
     public PaymentPluginApiException(final String errorType, final String errorMessage) {
         this.errorMessage = errorMessage;
         this.errorType = errorType;
     }
 
+    public PaymentPluginApiException(final String errorType, BillingExceptionBase billingExceptionBase) {
+        this.errorMessage = billingExceptionBase.getMessage();
+        this.errorType = errorType;
+    }
+
     public String getErrorType() {
         return errorType;
     }
@@ -39,8 +52,12 @@ public class PaymentPluginApiException extends Exception {
     public String toString() {
         final StringBuilder sb = new StringBuilder();
         sb.append("PaymentPluginApiException");
-        sb.append("{errorMessage='").append(errorMessage).append('\'');
-        sb.append(", errorType='").append(errorType).append('\'');
+        if (errorMessage != null) {
+            sb.append("{errorMessage='").append(errorMessage).append('\'');
+        }
+        if (errorType != null) {
+            sb.append(", errorType='").append(errorType).append('\'');
+        }
         sb.append('}');
         return sb.toString();
     }
diff --git a/api/src/main/java/com/ning/billing/payment/plugin/api/RefundInfoPlugin.java b/api/src/main/java/com/ning/billing/payment/plugin/api/RefundInfoPlugin.java
new file mode 100644
index 0000000..c0c6662
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/payment/plugin/api/RefundInfoPlugin.java
@@ -0,0 +1,60 @@
+/*
+ * 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.payment.plugin.api;
+
+import java.math.BigDecimal;
+
+import org.joda.time.DateTime;
+
+public interface RefundInfoPlugin {
+
+    public enum RefundPluginStatus {
+        UNDEFINED,
+        PROCESSED,
+        ERROR
+    }
+
+    /**
+     * @return refund amount
+     */
+    public BigDecimal getAmount();
+
+    /**
+     * @return date when the refund was created
+     */
+    public DateTime getCreatedDate();
+
+    /**
+     * @return date when the refund is effective
+     */
+    public DateTime getEffectiveDate();
+
+    /**
+     * @return refund status in the gateway
+     */
+    public RefundPluginStatus getStatus();
+
+    /**
+     * @return gateway error, if any
+     */
+    public String getGatewayError();
+
+    /**
+     * @return gateway error code, if any
+     */
+    public String getGatewayErrorCode();
+}
diff --git a/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java b/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java
index 64fe06b..3101622 100644
--- a/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java
+++ b/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java
@@ -17,20 +17,19 @@
 package com.ning.billing.util.tag;
 
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.ObjectType;
 
-import com.google.common.collect.ImmutableList;
-
 public enum ControlTagType {
 
-    AUTO_PAY_OFF(new UUID(0, 1), "Suspends payments until removed.", true, false, ImmutableList.<ObjectType>of(ObjectType.ACCOUNT)),
-    AUTO_INVOICING_OFF(new UUID(0, 2), "Suspends invoicing until removed.", false, true, ImmutableList.<ObjectType>of(ObjectType.ACCOUNT)),
-    OVERDUE_ENFORCEMENT_OFF(new UUID(0, 3), "Suspends overdue enforcement behaviour until removed.", false, false, ImmutableList.<ObjectType>of(ObjectType.ACCOUNT)),
-    WRITTEN_OFF(new UUID(0, 4), "Indicates that an invoice is written off. No billing or payment effect.", false, false, ImmutableList.<ObjectType>of(ObjectType.INVOICE)),
-    MANUAL_PAY(new UUID(0, 5), "Indicates that Killbill doesn't process payments for that account (external payments only)", true, false, ImmutableList.<ObjectType>of(ObjectType.ACCOUNT));
+    AUTO_PAY_OFF(new UUID(0, 1), "Suspends payments until removed.", true, false, Collections.<ObjectType>singletonList(ObjectType.ACCOUNT)),
+    AUTO_INVOICING_OFF(new UUID(0, 2), "Suspends invoicing until removed.", false, true, Collections.<ObjectType>singletonList(ObjectType.ACCOUNT)),
+    OVERDUE_ENFORCEMENT_OFF(new UUID(0, 3), "Suspends overdue enforcement behaviour until removed.", false, false, Collections.<ObjectType>singletonList(ObjectType.ACCOUNT)),
+    WRITTEN_OFF(new UUID(0, 4), "Indicates that an invoice is written off. No billing or payment effect.", false, false, Collections.<ObjectType>singletonList(ObjectType.INVOICE)),
+    MANUAL_PAY(new UUID(0, 5), "Indicates that Killbill doesn't process payments for that account (external payments only)", true, false, Collections.<ObjectType>singletonList(ObjectType.ACCOUNT));
 
     private final UUID id;
     private final String description;
diff --git a/api/src/main/java/com/ning/billing/util/tag/TagDefinition.java b/api/src/main/java/com/ning/billing/util/tag/TagDefinition.java
index 7f24b4a..4f673ed 100644
--- a/api/src/main/java/com/ning/billing/util/tag/TagDefinition.java
+++ b/api/src/main/java/com/ning/billing/util/tag/TagDefinition.java
@@ -24,7 +24,6 @@ import com.ning.billing.util.entity.Entity;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 
 // TODO: needs to surface created date, created by
-
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
 public interface TagDefinition extends Entity {
 
diff --git a/api/src/main/java/com/ning/billing/util/template/translation/TranslatorConfig.java b/api/src/main/java/com/ning/billing/util/template/translation/TranslatorConfig.java
index bd33887..5557241 100644
--- a/api/src/main/java/com/ning/billing/util/template/translation/TranslatorConfig.java
+++ b/api/src/main/java/com/ning/billing/util/template/translation/TranslatorConfig.java
@@ -23,34 +23,40 @@ import org.skife.config.Description;
 import com.ning.billing.invoice.api.formatters.InvoiceFormatterFactory;
 
 public interface TranslatorConfig {
+
     // Common
 
     @Config("killbill.default.locale")
     @Default("en_US")
+    @Description("Default Killbill locale")
     public String getDefaultLocale();
 
     // Catalog
 
     @Config("killbill.catalog.bundlePath")
     @Default("com/ning/billing/util/template/translation/CatalogTranslation")
+    @Description("Path to the catalog translation bundle")
     String getCatalogBundlePath();
 
     // Invoices
 
     @Config("killbill.template.bundlePath")
     @Default("com/ning/billing/util/template/translation/InvoiceTranslation")
+    @Description("Path to the invoice template translation bundle")
     public String getInvoiceTemplateBundlePath();
 
     @Config("killbill.template.name")
     @Default("com/ning/billing/util/email/templates/HtmlInvoiceTemplate.mustache")
+    @Description("Path to the HTML invoice template")
     String getTemplateName();
 
     @Config("killbill.manualPayTemplate.name")
     @Default("com/ning/billing/util/email/templates/HtmlInvoiceTemplate.mustache")
-    @Description("Invoice template for accounts with MANUAL_PAY tag")
+    @Description("Path to the invoice template for accounts with MANUAL_PAY tag")
     String getManualPayTemplateName();
 
     @Config("killbill.template.invoiceFormatterFactoryClass")
     @Default("com.ning.billing.invoice.template.formatters.DefaultInvoiceFormatterFactory")
+    @Description("Invoice formatter class")
     Class<? extends InvoiceFormatterFactory> getInvoiceFormatterFactoryClass();
 }

beatrix/pom.xml 26(+23 -3)

diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index 0848de4..7472253 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -64,6 +64,14 @@
             <artifactId>killbill-usage</artifactId>
         </dependency>
         <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-osgi-bundles-test-beatrix</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-osgi-bundles-test-payment</artifactId>
+        </dependency>
+        <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         </dependency>
@@ -77,6 +85,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>
@@ -173,12 +185,20 @@
     <build>
         <plugins>
             <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-jar-plugin</artifactId>
+                <artifactId>maven-antrun-plugin</artifactId>
+                <version>1.4</version>
                 <executions>
                     <execution>
+                        <id>copy</id>
+                        <phase>initialize</phase>
+                        <configuration>
+                            <tasks>
+                                <copy file="${basedir}/../osgi-bundles/tests/beatrix/target/killbill-osgi-bundles-test-beatrix-${project.version}-jar-with-dependencies.jar" tofile="${basedir}/src/test/resources/killbill-osgi-bundles-test-beatrix-${project.version}-jar-with-dependencies.jar"/>
+                                <copy file="${basedir}/../osgi-bundles/tests/payment/target/killbill-osgi-bundles-test-payment-${project.version}-jar-with-dependencies.jar" tofile="${basedir}/src/test/resources/killbill-osgi-bundles-test-payment-${project.version}-jar-with-dependencies.jar"/>
+                            </tasks>
+                        </configuration>
                         <goals>
-                            <goal>test-jar</goal>
+                            <goal>run</goal>
                         </goals>
                     </execution>
                 </executions>
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/BeatrixTestSuite.java b/beatrix/src/test/java/com/ning/billing/beatrix/BeatrixTestSuite.java
index 80922a9..1e391ad 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/BeatrixTestSuite.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/BeatrixTestSuite.java
@@ -16,7 +16,7 @@
 
 package com.ning.billing.beatrix;
 
-import com.ning.billing.KillbillTestSuite;
+import com.ning.billing.GuicyKillbillTestSuite;
 
-public abstract class BeatrixTestSuite extends KillbillTestSuite {
+public abstract class BeatrixTestSuite extends GuicyKillbillTestSuite {
 }
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/BeatrixIntegrationModule.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/BeatrixIntegrationModule.java
index 35acacf..ae1fc99 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/BeatrixIntegrationModule.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/BeatrixIntegrationModule.java
@@ -20,6 +20,8 @@ import java.io.IOException;
 import java.net.URL;
 import java.util.Set;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.GuicyKillbillTestWithEmbeddedDBModule;
 import com.ning.billing.account.api.AccountService;
 import com.ning.billing.account.glue.DefaultAccountModule;
@@ -77,7 +79,16 @@ import static org.testng.Assert.assertNotNull;
 
 public class BeatrixIntegrationModule extends AbstractModule {
 
-    public static final String PLUGIN_NAME = "yoyo";
+    public static final String NON_OSGI_PLUGIN_NAME = "yoyo";
+
+    // Same name the osgi-payment-test plugin uses to register its service
+    public static final String OSGI_PLUGIN_NAME = "osgiPaymentPlugin";
+
+    private final ConfigSource configSource;
+
+    public BeatrixIntegrationModule(final ConfigSource configSource) {
+        this.configSource = configSource;
+    }
 
     @Override
     protected void configure() {
@@ -89,27 +100,27 @@ public class BeatrixIntegrationModule extends AbstractModule {
         install(new GuicyKillbillTestWithEmbeddedDBModule());
 
         install(new GlobalLockerModule());
-        install(new CacheModule());
-        install(new EmailModule());
+        install(new CacheModule(configSource));
+        install(new EmailModule(configSource));
         install(new CallContextModule());
-        install(new BusModule());
-        install(new NotificationQueueModule());
+        install(new BusModule(configSource));
+        install(new NotificationQueueModule(configSource));
         install(new TagStoreModule());
         install(new CustomFieldModule());
-        install(new DefaultAccountModule());
-        install(new AnalyticsModule());
-        install(new CatalogModule());
-        install(new DefaultEntitlementModule());
-        install(new DefaultInvoiceModule());
+        install(new DefaultAccountModule(configSource));
+        install(new AnalyticsModule(configSource));
+        install(new CatalogModule(configSource));
+        install(new DefaultEntitlementModule(configSource));
+        install(new DefaultInvoiceModule(configSource));
         install(new TemplateModule());
-        install(new PaymentPluginMockModule());
-        install(new DefaultJunctionModule());
-        install(new IntegrationTestOverdueModule());
+        install(new PaymentPluginMockModule(configSource));
+        install(new DefaultJunctionModule(configSource));
+        install(new IntegrationTestOverdueModule(configSource));
         install(new AuditModule());
-        install(new UsageModule());
-        install(new TenantModule());
+        install(new UsageModule(configSource));
+        install(new TenantModule(configSource));
         install(new ExportModule());
-        install(new DefaultOSGIModule());
+        install(new DefaultOSGIModule(configSource));
         install(new NonEntityDaoModule());
 
         bind(AccountChecker.class).asEagerSingleton();
@@ -130,9 +141,13 @@ public class BeatrixIntegrationModule extends AbstractModule {
 
     private static final class PaymentPluginMockModule extends PaymentModule {
 
+        public PaymentPluginMockModule(final ConfigSource configSource) {
+            super(configSource);
+        }
+
         @Override
         protected void installPaymentProviderPlugins(final PaymentConfig config) {
-            install(new MockPaymentProviderPluginModule(PLUGIN_NAME, TestIntegrationBase.getClock()));
+            install(new MockPaymentProviderPluginModule(NON_OSGI_PLUGIN_NAME, TestIntegrationBase.getClock()));
         }
     }
 
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
new file mode 100644
index 0000000..1630a41
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/osgi/TestBasicOSGIWithTestBundle.java
@@ -0,0 +1,252 @@
+/*
+ * 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.beatrix.integration.osgi;
+
+import java.math.BigDecimal;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+
+import org.skife.jdbi.v2.Handle;
+import org.skife.jdbi.v2.IDBI;
+import org.skife.jdbi.v2.Query;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.TransactionCallback;
+import org.skife.jdbi.v2.TransactionStatus;
+import org.skife.jdbi.v2.tweak.ResultSetMapper;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.beatrix.osgi.SetupBundleWithAssertion;
+import com.ning.billing.dbi.DBTestingHelper;
+import com.ning.billing.osgi.api.OSGIServiceRegistration;
+import com.ning.billing.osgi.glue.OSGIDataSourceConfig;
+import com.ning.billing.payment.plugin.api.PaymentInfoPlugin;
+import com.ning.billing.payment.plugin.api.PaymentPluginApi;
+
+import static com.jayway.awaitility.Awaitility.await;
+
+/**
+ * Basic OSGI test that relies on the 'test' bundle (com.ning.billing.osgi.bundles.test.TestActivator)
+ * <p/>
+ * The test checks that the bundle:
+ * - gets started
+ * - can make API call
+ * - can listen to KB events
+ * - can register a service (PaymentPluginApi) that this test calls
+ * - can write in the DB using the DataSource (this is how the assertion work)
+ */
+public class TestBasicOSGIWithTestBundle extends TestOSGIBase {
+
+    private final String BUNDLE_TEST_RESOURCE = "killbill-osgi-bundles-test-beatrix";
+
+    @Inject
+    private OSGIServiceRegistration<PaymentPluginApi> paymentPluginApiOSGIServiceRegistration;
+
+    @BeforeClass(groups = "slow")
+    public void beforeClass() throws Exception {
+
+        final String jdbcConnection = getDBTestingHelper().getJdbcConnectionString();
+        final String userName = DBTestingHelper.USERNAME;
+        final String userPwd = DBTestingHelper.PASSWORD;
+
+        System.setProperty(OSGIDataSourceConfig.DATA_SOURCE_PROP_PREFIX + "jdbc.url", jdbcConnection);
+        System.setProperty(OSGIDataSourceConfig.DATA_SOURCE_PROP_PREFIX + "jdbc.user", userName);
+        System.setProperty(OSGIDataSourceConfig.DATA_SOURCE_PROP_PREFIX + "jdbc.password", userPwd);
+
+        // OSGIDataSourceConfig
+        super.beforeClass();
+
+        // 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();
+
+    }
+
+    @Test(groups = "slow")
+    public void testBundleTest() throws Exception {
+
+        // At this point test bundle should have been started already
+        final TestActivatorWithAssertion assertTor = new TestActivatorWithAssertion(getDBI());
+        assertTor.assertPluginInitialized();
+
+        // Create an account and expect test bundle listen to KB events and write the external name in its table
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+        assertTor.assertPluginReceievdAccountCreationEvent(account.getExternalKey());
+
+        // Retrieve the PaymentPluginApi that the test bundle registered
+        final PaymentPluginApi paymentPluginApi = getTestPluginPaymentApi();
+
+        // Make a payment and expect test bundle to correcly write in its table the input values
+        final UUID paymentId = UUID.randomUUID();
+        final BigDecimal paymentAmount = new BigDecimal("14.32");
+        final PaymentInfoPlugin r = paymentPluginApi.processPayment(paymentId, account.getPaymentMethodId(), paymentAmount, callContext);
+        assertTor.assertPluginCreatedPayment(paymentId, account.getPaymentMethodId(), paymentAmount);
+    }
+
+    private PaymentPluginApi getTestPluginPaymentApi() {
+        PaymentPluginApi result = paymentPluginApiOSGIServiceRegistration.getServiceForName("test");
+        Assert.assertNotNull(result);
+        return result;
+    }
+
+    private static final class TestActivatorWithAssertion {
+
+        private final IDBI dbi;
+
+        public TestActivatorWithAssertion(final IDBI dbi) {
+            this.dbi = dbi;
+        }
+
+        public void assertPluginInitialized() {
+            assertWithCallback(new AwaitCallback() {
+                @Override
+                public boolean isSuccess() {
+                    return isPluginInitialized();
+                }
+            }, "Plugin did not complete initialization");
+        }
+
+        public void assertPluginReceievdAccountCreationEvent(final String expectedExternalKey) {
+            assertWithCallback(new AwaitCallback() {
+                @Override
+                public boolean isSuccess() {
+                    return isValidAccountExternalKey(expectedExternalKey);
+                }
+            }, "Plugin did not receive account creation event");
+        }
+
+        public void assertPluginCreatedPayment(final UUID expectedPaymentId, final UUID expectedPaymentMethodId, final BigDecimal expectedAmount) {
+            assertWithCallback(new AwaitCallback() {
+                @Override
+                public boolean isSuccess() {
+                    return isValidPayment(expectedPaymentId, expectedPaymentMethodId, expectedAmount);
+                }
+            }, "Plugin did not create the payment");
+        }
+
+
+        private void assertWithCallback(final AwaitCallback callback, final String error) {
+            try {
+                await().atMost(5, TimeUnit.SECONDS).until(new Callable<Boolean>() {
+                    @Override
+                    public Boolean call() throws Exception {
+                        return callback.isSuccess();
+                    }
+                });
+            } catch (Exception e) {
+                Assert.fail(error, e);
+            }
+        }
+
+        private boolean isValidPayment(final UUID expectedPaymentId, final UUID expectedPaymentMethodId, final BigDecimal expectedAmount) {
+            TestModel test = getTestModelFirstRecord();
+            return expectedPaymentId.equals(test.getPaymentId()) &&
+                   expectedPaymentMethodId.equals(test.getPaymentMethodId()) &&
+                   expectedAmount.compareTo(test.getAmount()) == 0;
+        }
+
+
+        private boolean isPluginInitialized() {
+            TestModel test = getTestModelFirstRecord();
+            return test.isStarted();
+        }
+
+        private boolean isValidAccountExternalKey(final String expectedExternalKey) {
+            TestModel test = getTestModelFirstRecord();
+            return expectedExternalKey.equals(test.getAccountExternalKey());
+        }
+
+        private TestModel getTestModelFirstRecord() {
+            TestModel test = dbi.inTransaction(new TransactionCallback<TestModel>() {
+                @Override
+                public TestModel inTransaction(final Handle conn, final TransactionStatus status) throws Exception {
+                    Query<Map<String, Object>> q = conn.createQuery("SELECT is_started, external_key, payment_id, payment_method_id, payment_amount FROM test_bundle WHERE record_id = 1;");
+                    TestModel test = q.map(new TestMapper()).first();
+                    return test;
+                }
+            });
+            return test;
+        }
+    }
+
+
+    private final static class TestModel {
+
+        private final Boolean isStarted;
+        private final String accountExternalKey;
+        private final UUID paymentId;
+        private final UUID paymentMethodId;
+        private final BigDecimal amount;
+
+        private TestModel(final Boolean started, final String accountExternalKey, final UUID paymentId, final UUID paymentMethodId, final BigDecimal amount) {
+            isStarted = started;
+            this.accountExternalKey = accountExternalKey;
+            this.paymentId = paymentId;
+            this.paymentMethodId = paymentMethodId;
+            this.amount = amount;
+        }
+
+        public Boolean isStarted() {
+            return isStarted;
+        }
+
+        public String getAccountExternalKey() {
+            return accountExternalKey;
+        }
+
+        public UUID getPaymentId() {
+            return paymentId;
+        }
+
+        public UUID getPaymentMethodId() {
+            return paymentMethodId;
+        }
+
+        public BigDecimal getAmount() {
+            return amount;
+        }
+
+    }
+
+
+    private static class TestMapper implements ResultSetMapper<TestModel> {
+
+        @Override
+        public TestModel map(final int index, final ResultSet r, final StatementContext ctx) throws SQLException {
+
+            final Boolean isStarted = r.getBoolean("is_started");
+            final String externalKey = r.getString("external_key");
+            final UUID paymentId = r.getString("payment_id") != null ? UUID.fromString(r.getString("payment_id")) : null;
+            final UUID paymentMethodId = r.getString("payment_method_id") != null ? UUID.fromString(r.getString("payment_method_id")) : null;
+            final BigDecimal amount = r.getBigDecimal("payment_amount");
+            return new TestModel(isStarted, externalKey, paymentId, paymentMethodId, amount);
+        }
+    }
+
+    private interface AwaitCallback {
+        boolean isSuccess();
+    }
+}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/osgi/TestOSGIBase.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/osgi/TestOSGIBase.java
index 437ebc0..661e490 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/osgi/TestOSGIBase.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/osgi/TestOSGIBase.java
@@ -23,6 +23,4 @@ import com.google.inject.Inject;
 
 public class TestOSGIBase extends TestIntegrationBase {
 
-    @Inject
-    protected OSGIConfig config;
 }
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/osgi/TestOSGIIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/osgi/TestOSGIIntegration.java
index 2be1879..36fd2e5 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/osgi/TestOSGIIntegration.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/osgi/TestOSGIIntegration.java
@@ -16,15 +16,12 @@
 
 package com.ning.billing.beatrix.integration.osgi;
 
-import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
-import com.ning.billing.beatrix.integration.BeatrixIntegrationModule;
-
 public class TestOSGIIntegration extends TestOSGIBase {
 
     @Test(groups = "slow")
     public void testJRubyIntegration() throws Exception {
-        createAccountWithPaymentMethod(getAccountData(1));
+        createAccountWithNonOsgiPaymentMethod(getAccountData(1));
     }
 }
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/osgi/TestPaymentOSGIWithTestPaymentBundle.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/osgi/TestPaymentOSGIWithTestPaymentBundle.java
new file mode 100644
index 0000000..bbe1df6
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/osgi/TestPaymentOSGIWithTestPaymentBundle.java
@@ -0,0 +1,186 @@
+/*
+ * 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.beatrix.integration.osgi;
+
+import java.math.BigDecimal;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import org.joda.time.LocalDate;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountData;
+import com.ning.billing.api.TestApiListener.NextEvent;
+import com.ning.billing.beatrix.integration.BeatrixIntegrationModule;
+import com.ning.billing.beatrix.osgi.SetupBundleWithAssertion;
+import com.ning.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
+import com.ning.billing.beatrix.util.PaymentChecker.ExpectedPaymentCheck;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItemType;
+import com.ning.billing.osgi.api.OSGIServiceRegistration;
+import com.ning.billing.payment.api.PaymentStatus;
+import com.ning.billing.payment.plugin.api.PaymentPluginApi;
+import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
+import com.ning.billing.payment.plugin.api.PaymentPluginApiWithTestControl;
+
+public class TestPaymentOSGIWithTestPaymentBundle extends TestOSGIBase {
+
+    private final String BUNDLE_TEST_RESOURCE = "killbill-osgi-bundles-test-payment";
+
+    @Inject
+    private OSGIServiceRegistration<PaymentPluginApi> paymentPluginApiOSGIServiceRegistration;
+
+    @BeforeClass(groups = "slow")
+    public void beforeClass() throws Exception {
+
+        super.beforeClass();
+
+        // 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();
+
+    }
+
+    @BeforeMethod(groups = "slow")
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
+        getTestPluginPaymentApi().resetToNormalbehavior();
+    }
+
+    @Test(groups = "slow")
+    public void testBasicProcessPaymentOK() throws Exception {
+        final PaymentPluginApiWithTestControl paymentPluginApi = getTestPluginPaymentApi();
+        paymentPluginApi.processPayment(UUID.randomUUID(), UUID.randomUUID(), BigDecimal.TEN, callContext);
+    }
+
+    @Test(groups = "slow")
+    public void testBasicProcessPaymentWithPaymentPluginApiException() throws Exception {
+
+        boolean gotException = false;
+        try {
+            final PaymentPluginApiWithTestControl paymentPluginApi = getTestPluginPaymentApi();
+            final PaymentPluginApiException e = new PaymentPluginApiException("test-error", "foo");
+
+            paymentPluginApi.setPaymentPluginApiExceptionOnNextCalls(e);
+            paymentPluginApi.processPayment(UUID.randomUUID(), UUID.randomUUID(), BigDecimal.TEN, callContext);
+            Assert.fail("Expected to fail with " + e.toString());
+        } catch (PaymentPluginApiException e) {
+            gotException = true;
+        }
+        Assert.assertTrue(gotException);
+    }
+
+    @Test(groups = "slow")
+    public void testBasicProcessPaymentWithRuntimeException() throws Exception {
+
+        boolean gotException = false;
+        try {
+            final PaymentPluginApiWithTestControl paymentPluginApi = getTestPluginPaymentApi();
+            final RuntimeException e = new RuntimeException("test-error");
+
+            paymentPluginApi.setPaymentRuntimeExceptionOnNextCalls(e);
+            paymentPluginApi.processPayment(UUID.randomUUID(), UUID.randomUUID(), BigDecimal.TEN, callContext);
+            Assert.fail("Expected to fail with " + e.toString());
+        } catch (RuntimeException e) {
+            gotException = true;
+        }
+        Assert.assertTrue(gotException);
+    }
+
+
+    @Test(groups = "slow")
+    public void testIntegrationOK() throws Exception {
+        setupIntegration(null, null);
+    }
+
+    @Test(groups = "slow")
+    public void testIntegrationWithPaymentPluginApiException() throws Exception {
+        final PaymentPluginApiException e = new PaymentPluginApiException("test-error", "foo");
+        setupIntegration(e, null);
+    }
+
+    @Test(groups = "slow")
+    public void testIntegrationWithRuntimeException() throws Exception {
+        final RuntimeException e = new RuntimeException("test-error");
+        setupIntegration(null, e);
+    }
+
+
+    private void setupIntegration(final PaymentPluginApiException expectedException, final RuntimeException expectedRuntimeException) throws Exception {
+
+        final PaymentPluginApiWithTestControl paymentPluginApi = getTestPluginPaymentApi();
+
+        final AccountData accountData = getAccountData(1);
+        final Account account = createAccountWithOsgiPaymentMethod(accountData);
+
+        // We take april as it has 30 days (easier to play with BCD)
+        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+        clock.setDay(new LocalDate(2012, 4, 1));
+
+        final SubscriptionBundle bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever", callContext);
+        //
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        //
+        final Subscription bpSubscription = createSubscriptionAndCheckForCompletion(bundle.getId(), "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        //
+        // ADD ADD_ON ON THE SAME DAY TO TRIGGER PAYMENT
+        //
+
+        final List<NextEvent> expectedEvents = new LinkedList<NextEvent>();
+        expectedEvents.add(NextEvent.CREATE);
+        expectedEvents.add(NextEvent.INVOICE);
+        if (expectedException == null && expectedRuntimeException == null) {
+            expectedEvents.add(NextEvent.PAYMENT);
+        } else if (expectedException != null) {
+            paymentPluginApi.setPaymentPluginApiExceptionOnNextCalls(expectedException);
+        } else if (expectedRuntimeException != null) {
+            paymentPluginApi.setPaymentRuntimeExceptionOnNextCalls(expectedRuntimeException);
+
+        }
+
+        createSubscriptionAndCheckForCompletion(bundle.getId(), "Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, expectedEvents.toArray(new NextEvent[expectedEvents.size()]));
+        Invoice invoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("399.95")));
+
+        if (expectedException == null && expectedRuntimeException == null) {
+            paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 4, 1), new BigDecimal("399.95"), PaymentStatus.SUCCESS, invoice.getId(), Currency.USD));
+        } else if (expectedException != null) {
+            paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 4, 1), new BigDecimal("399.95"), PaymentStatus.PLUGIN_FAILURE, invoice.getId(), Currency.USD));
+        } else if (expectedRuntimeException != null) {
+            paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 4, 1), new BigDecimal("399.95"), PaymentStatus.UNKNOWN, invoice.getId(), Currency.USD));
+        }
+    }
+
+
+    private PaymentPluginApiWithTestControl getTestPluginPaymentApi() {
+        PaymentPluginApiWithTestControl result = (PaymentPluginApiWithTestControl) paymentPluginApiOSGIServiceRegistration.getServiceForName(BeatrixIntegrationModule.OSGI_PLUGIN_NAME);
+        Assert.assertNotNull(result);
+        return result;
+    }
+}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/IntegrationTestOverdueModule.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/IntegrationTestOverdueModule.java
index 1290f7e..2f80d30 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/IntegrationTestOverdueModule.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/IntegrationTestOverdueModule.java
@@ -16,15 +16,18 @@
 
 package com.ning.billing.beatrix.integration.overdue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.overdue.OverdueService;
 import com.ning.billing.overdue.glue.DefaultOverdueModule;
 
 public class IntegrationTestOverdueModule extends DefaultOverdueModule {
 
+    public IntegrationTestOverdueModule(final ConfigSource configSource) {
+        super(configSource);
+    }
 
     protected void installOverdueService() {
         bind(OverdueService.class).to(MockOverdueService.class);
     }
-
-
 }
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestBillingAlignment.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestBillingAlignment.java
index c4984c9..1fecc27 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestBillingAlignment.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestBillingAlignment.java
@@ -19,12 +19,10 @@ package com.ning.billing.beatrix.integration.overdue;
 import java.math.BigDecimal;
 
 import org.joda.time.LocalDate;
-import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
 import com.ning.billing.account.api.Account;
 import com.ning.billing.api.TestApiListener.NextEvent;
-import com.ning.billing.beatrix.integration.BeatrixIntegrationModule;
 import com.ning.billing.beatrix.integration.TestIntegrationBase;
 import com.ning.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
 import com.ning.billing.catalog.api.BillingPeriod;
@@ -45,7 +43,7 @@ public class TestBillingAlignment extends TestIntegrationBase {
     @Test(groups = "slow", enabled = false)
     public void testTransitonAccountBAToSubscriptionBA() throws Exception {
 
-        final Account account = createAccountWithPaymentMethod(getAccountData(1));
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
 
         // We take april as it has 30 days (easier to play with BCD)
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueBase.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueBase.java
index a3e0b6a..3594ec2 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueBase.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueBase.java
@@ -70,17 +70,19 @@ public abstract class TestOverdueBase extends TestIntegrationBase {
         }
     };
 
+    @Override
     @BeforeMethod(groups = "slow")
-    public void setupOverdue() throws Exception {
+    public void beforeMethod( ) throws Exception {
+        super.beforeMethod();
         final String configXml = getOverdueConfig();
         final InputStream is = new ByteArrayInputStream(configXml.getBytes());
         final OverdueConfig config = XMLLoader.getObjectFromStreamNoValidation(is, OverdueConfig.class);
         overdueWrapperFactory.setOverdueConfig(config);
 
-        account = createAccountWithPaymentMethod(getAccountData(0));
+        account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
         assertNotNull(account);
 
-        paymentApi.addPaymentMethod(BeatrixIntegrationModule.PLUGIN_NAME, account, true, paymentMethodPlugin, callContext);
+        paymentApi.addPaymentMethod(BeatrixIntegrationModule.NON_OSGI_PLUGIN_NAME, account, true, paymentMethodPlugin, callContext);
 
         bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever", callContext);
 
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java
index b66e107..5ad8c97 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java
@@ -21,7 +21,6 @@ import java.util.Collection;
 
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
-import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
 import com.ning.billing.api.TestApiListener.NextEvent;
@@ -255,7 +254,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
         checkChangePlanWithOverdueState(baseSubscription, true);
 
         // Add a payment method and set it as default
-        paymentApi.addPaymentMethod(BeatrixIntegrationModule.PLUGIN_NAME, account, true, paymentMethodPlugin, callContext);
+        paymentApi.addPaymentMethod(BeatrixIntegrationModule.NON_OSGI_PLUGIN_NAME, account, true, paymentMethodPlugin, callContext);
 
         // Pay all invoices
         final Collection<Invoice> invoices = invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext);
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java
index 9caa951..a139e9e 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestAnalytics.java
@@ -27,7 +27,6 @@ import org.joda.time.LocalDate;
 import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
 import com.ning.billing.ObjectType;
@@ -71,8 +70,10 @@ public class TestAnalytics extends TestIntegrationBase {
 
     private Plan subscriptionPlan;
 
+    @Override
     @BeforeMethod(groups = "slow")
-    public void setUpAnalyticsHandler() throws Exception {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         final String configXml = "<overdueConfig>" +
                                  "   <bundleOverdueStates>" +
                                  "       <state name=\"OD3\">" +
@@ -128,8 +129,10 @@ public class TestAnalytics extends TestIntegrationBase {
         account = verifyAccountCreation(initialDate.plusDays(30).getDayOfMonth());
     }
 
+    @Override
     @AfterMethod(groups = "slow")
-    public void tearDownAnalyticsHandler() throws Exception {
+    public void afterMethod() throws Exception {
+        super.afterMethod();
         busService.getBus().unregister(analyticsListener);
         // Reset the payment plugin for other tests
         paymentPlugin.clear();
@@ -247,7 +250,7 @@ public class TestAnalytics extends TestIntegrationBase {
         Assert.assertNull(invoicePaymentsForAccount.get(0).getExtFirstPaymentRefId());
         Assert.assertNull(invoicePaymentsForAccount.get(0).getExtSecondPaymentRefId());
         Assert.assertEquals(invoicePaymentsForAccount.get(0).getProcessingStatus(), PaymentStatus.PAYMENT_FAILURE.toString());
-        Assert.assertEquals(invoicePaymentsForAccount.get(0).getPluginName(), BeatrixIntegrationModule.PLUGIN_NAME);
+        Assert.assertEquals(invoicePaymentsForAccount.get(0).getPluginName(), BeatrixIntegrationModule.NON_OSGI_PLUGIN_NAME);
 
         // Verify the account object has been updated
         Assert.assertEquals(analyticsUserApi.getAccountByKey(account.getExternalKey(), callContext).getBalance(),
@@ -336,7 +339,7 @@ public class TestAnalytics extends TestIntegrationBase {
         Assert.assertNull(analyticsUserApi.getAccountByKey(accountData.getExternalKey(), callContext));
 
         // Create an account
-        final Account account = createAccountWithPaymentMethod(accountData);
+        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
         Assert.assertNotNull(account);
 
         waitALittle();
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBundleTransfer.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBundleTransfer.java
index c27ac26..2c6b654 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBundleTransfer.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBundleTransfer.java
@@ -47,7 +47,7 @@ public class TestBundleTransfer extends TestIntegrationBase {
     @Test(groups = "slow")
     public void testBundleTransferWithBPAnnualOnly() throws Exception {
 
-        final Account account = createAccountWithPaymentMethod(getAccountData(3));
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(3));
 
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
 
@@ -84,7 +84,7 @@ public class TestBundleTransfer extends TestIntegrationBase {
         assertListenerStatus();
 
         // BUNDLE TRANSFER
-        final Account newAccount = createAccountWithPaymentMethod(getAccountData(17));
+        final Account newAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(17));
 
         busHandler.pushExpectedEvent(NextEvent.TRANSFER);
         busHandler.pushExpectedEvent(NextEvent.INVOICE);
@@ -107,7 +107,7 @@ public class TestBundleTransfer extends TestIntegrationBase {
     @Test(groups = "slow")
     public void testBundleTransferWithBPMonthlyOnly() throws Exception {
 
-        final Account account = createAccountWithPaymentMethod(getAccountData(0));
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
 
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
 
@@ -144,7 +144,7 @@ public class TestBundleTransfer extends TestIntegrationBase {
         assertListenerStatus();
 
         // BUNDLE TRANSFER
-        final Account newAccount = createAccountWithPaymentMethod(getAccountData(0));
+        final Account newAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
 
         busHandler.pushExpectedEvent(NextEvent.TRANSFER);
         busHandler.pushExpectedEvent(NextEvent.INVOICE);
@@ -174,7 +174,7 @@ public class TestBundleTransfer extends TestIntegrationBase {
     @Test(groups = "slow")
     public void testBundleTransferWithBPMonthlyOnlyWIthCancellationImm() throws Exception {
 
-        final Account account = createAccountWithPaymentMethod(getAccountData(9));
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(9));
 
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
 
@@ -211,7 +211,7 @@ public class TestBundleTransfer extends TestIntegrationBase {
         assertListenerStatus();
 
         // BUNDLE TRANSFER
-        final Account newAccount = createAccountWithPaymentMethod(getAccountData(15));
+        final Account newAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(15));
 
         busHandler.pushExpectedEvent(NextEvent.CANCEL);
         busHandler.pushExpectedEvent(NextEvent.TRANSFER);
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestEntitlement.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestEntitlement.java
index af1f885..adea394 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestEntitlement.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestEntitlement.java
@@ -49,7 +49,7 @@ public class TestEntitlement extends TestIntegrationBase {
     public void testForcePolicy() throws Exception {
         // We take april as it has 30 days (easier to play with BCD)
         final LocalDate today = new LocalDate(2012, 4, 1);
-        final Account account = createAccountWithPaymentMethod(getAccountData(1));
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
 
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
         clock.setDeltaFromReality(today.toDateTimeAtCurrentTime(DateTimeZone.UTC).getMillis() - clock.getUTCNow().getMillis());
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
index b7817af..8453fd7 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
@@ -23,7 +23,6 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 import org.joda.time.Interval;
 import org.joda.time.LocalDate;
-import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
 import com.ning.billing.account.api.Account;
@@ -54,7 +53,7 @@ public class TestIntegration extends TestIntegrationBase {
     public void testCancelBPWithAOTheSameDay() throws Exception {
 
         final AccountData accountData = getAccountData(1);
-        final Account account = createAccountWithPaymentMethod(accountData);
+        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
         accountChecker.checkAccount(account.getId(), accountData, callContext);
 
         // We take april as it has 30 days (easier to play with BCD)
@@ -101,7 +100,7 @@ public class TestIntegration extends TestIntegrationBase {
         final DateTime initialCreationDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
 
         log.info("Beginning test with BCD of " + billingDay);
-        final Account account = createAccountWithPaymentMethod(getAccountData(billingDay));
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
 
         // set clock to the initial start date
         clock.setTime(initialCreationDate);
@@ -183,7 +182,7 @@ public class TestIntegration extends TestIntegrationBase {
         final DateTime initialCreationDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
 
         log.info("Beginning test with BCD of " + billingDay);
-        final Account account = createAccountWithPaymentMethod(getAccountData(billingDay));
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
 
         // set clock to the initial start date
         clock.setTime(initialCreationDate);
@@ -265,7 +264,7 @@ public class TestIntegration extends TestIntegrationBase {
         final DateTime initialCreationDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
 
         log.info("Beginning test with BCD of " + billingDay);
-        final Account account = createAccountWithPaymentMethod(getAccountData(billingDay));
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
 
         // set clock to the initial start date
         clock.setTime(initialCreationDate);
@@ -355,23 +354,23 @@ public class TestIntegration extends TestIntegrationBase {
         final int maxIterations = 100;
         for (int curIteration = 0; curIteration < maxIterations; curIteration++) {
             if (curIteration != 0) {
-                setupTest();
+                beforeMethod();
             }
 
             log.info("################################  ITERATION " + curIteration + "  #########################");
-            cleanupTest();
-            setupTest();
+            afterMethod();
+            beforeMethod();
             testBasePlanCompleteWithBillingDayInPast();
             Thread.sleep(1000);
-            cleanupTest();
-            setupTest();
+            afterMethod();
+            beforeMethod();
             testBasePlanCompleteWithBillingDayAlignedWithTrial();
             Thread.sleep(1000);
-            cleanupTest();
-            setupTest();
+            afterMethod();
+            beforeMethod();
             testBasePlanCompleteWithBillingDayInFuture();
             if (curIteration < maxIterations - 1) {
-                cleanupTest();
+                afterMethod();
                 Thread.sleep(1000);
             }
         }
@@ -383,11 +382,11 @@ public class TestIntegration extends TestIntegrationBase {
         for (int curIteration = 0; curIteration < maxIterations; curIteration++) {
             log.info("################################  ITERATION " + curIteration + "  #########################");
             if (curIteration != 0) {
-                setupTest();
+                beforeMethod();
             }
             testAddonsWithMultipleAlignments();
             if (curIteration < maxIterations - 1) {
-                cleanupTest();
+                afterMethod();
                 Thread.sleep(1000);
             }
         }
@@ -398,7 +397,7 @@ public class TestIntegration extends TestIntegrationBase {
         final DateTime initialDate = new DateTime(2012, 4, 25, 0, 13, 42, 0, testTimeZone);
         clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
 
-        final Account account = createAccountWithPaymentMethod(getAccountData(25));
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(25));
         assertNotNull(account);
 
         final SubscriptionBundle bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever", callContext);
@@ -477,7 +476,7 @@ public class TestIntegration extends TestIntegrationBase {
 
         log.info("Starting testRepairForInvoicing");
 
-        final Account account = createAccountWithPaymentMethod(getAccountData(1));
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
         final UUID accountId = account.getId();
         assertNotNull(account);
 
@@ -510,7 +509,7 @@ public class TestIntegration extends TestIntegrationBase {
         final int billingDay = 2;
 
         log.info("Beginning test with BCD of " + billingDay);
-        final Account account = createAccountWithPaymentMethod(getAccountData(billingDay));
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
         final UUID accountId = account.getId();
         assertNotNull(account);
 
@@ -582,7 +581,7 @@ public class TestIntegration extends TestIntegrationBase {
         final DateTime initialCreationDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
         clock.setDeltaFromReality(initialCreationDate.getMillis() - clock.getUTCNow().getMillis());
 
-        final Account account = createAccountWithPaymentMethod(getAccountData(2));
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(2));
         final UUID accountId = account.getId();
 
         final String productName = "Blowdart";
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
index 6886bf6..d1c30e4 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
@@ -45,6 +45,7 @@ import com.ning.billing.api.TestListenerStatus;
 import com.ning.billing.beatrix.BeatrixTestSuiteWithEmbeddedDB;
 import com.ning.billing.beatrix.bus.api.ExternalBus;
 import com.ning.billing.beatrix.lifecycle.Lifecycle;
+import com.ning.billing.beatrix.osgi.SetupBundleWithAssertion;
 import com.ning.billing.beatrix.util.AccountChecker;
 import com.ning.billing.beatrix.util.EntitlementChecker;
 import com.ning.billing.beatrix.util.InvoiceChecker;
@@ -79,6 +80,7 @@ import com.ning.billing.payment.api.PaymentApiException;
 import com.ning.billing.payment.api.PaymentMethodPlugin;
 import com.ning.billing.payment.provider.MockPaymentProviderPlugin;
 import com.ning.billing.util.api.TagUserApi;
+import com.ning.billing.util.config.OSGIConfig;
 import com.ning.billing.util.svcapi.account.AccountInternalApi;
 import com.ning.billing.util.svcapi.junction.BlockingInternalApi;
 import com.ning.billing.util.svcsapi.bus.BusService;
@@ -152,7 +154,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
     @Inject
     protected PaymentApi paymentApi;
 
-    @Named(BeatrixIntegrationModule.PLUGIN_NAME)
+    @Named(BeatrixIntegrationModule.NON_OSGI_PLUGIN_NAME)
     @Inject
     protected MockPaymentProviderPlugin paymentPlugin;
 
@@ -192,6 +194,10 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
     @Inject
     protected AccountInternalApi accountInternalApi;
 
+    @Inject
+    protected OSGIConfig osgiConfig;
+
+
     protected TestApiListener busHandler;
 
     private boolean isListenerFailed;
@@ -217,16 +223,21 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
     }
 
     @BeforeClass(groups = "slow")
-    public void setup() throws Exception {
-        final Injector g = Guice.createInjector(Stage.PRODUCTION, new BeatrixIntegrationModule());
+    public void beforeClass() throws Exception {
+        final Injector g = Guice.createInjector(Stage.PRODUCTION, new BeatrixIntegrationModule(configSource));
         g.injectMembers(this);
         busHandler = new TestApiListener(this);
 
+        SetupBundleWithAssertion setupTest = new SetupBundleWithAssertion("whatever", osgiConfig, "whatever");
+        setupTest.cleanBundleInstallDir();
+
     }
 
 
     @BeforeMethod(groups = "slow")
-    public void setupTest() throws Exception {
+    public void beforeMethod() throws Exception {
+
+        super.beforeMethod();
         log.warn("\n");
         log.warn("RESET TEST FRAMEWORK\n\n");
 
@@ -243,7 +254,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
     }
 
     @AfterMethod(groups = "slow")
-    public void cleanupTest() throws Exception {
+    public void afterMethod() throws Exception {
         lifecycle.fireShutdownSequencePriorEventUnRegistration();
         busService.getBus().unregister(busHandler);
         lifecycle.fireShutdownSequencePostEventUnRegistration();
@@ -295,7 +306,15 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
         return (SubscriptionData) ((BlockingSubscription) sub).getDelegateSubscription();
     }
 
-    protected Account createAccountWithPaymentMethod(final AccountData accountData) throws Exception {
+    protected Account createAccountWithOsgiPaymentMethod(final AccountData accountData) throws Exception {
+        return createAccountWithPaymentMethod(accountData, BeatrixIntegrationModule.OSGI_PLUGIN_NAME);
+    }
+
+    protected Account createAccountWithNonOsgiPaymentMethod(final AccountData accountData) throws Exception {
+        return createAccountWithPaymentMethod(accountData, BeatrixIntegrationModule.NON_OSGI_PLUGIN_NAME);
+    }
+
+    private Account createAccountWithPaymentMethod(final AccountData accountData, final String paymentPluginName) throws Exception {
         final Account account = accountUserApi.createAccount(accountData, callContext);
         assertNotNull(account);
 
@@ -321,7 +340,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
             }
         };
 
-        paymentApi.addPaymentMethod(BeatrixIntegrationModule.PLUGIN_NAME, account, true, info, callContext);
+        paymentApi.addPaymentMethod(paymentPluginName, account, true, info, callContext);
         return accountUserApi.getAccountById(account.getId(), callContext);
     }
 
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
index d79e09c..3fe7604 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
@@ -22,7 +22,6 @@ import java.util.UUID;
 
 import org.joda.time.DateTime;
 import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
 import com.ning.billing.ObjectType;
@@ -62,10 +61,11 @@ public class TestIntegrationWithAutoInvoiceOffTag extends TestIntegrationBase {
     private BillingPeriod term;
     private String planSetName;
 
+    @Override
     @BeforeMethod(groups = {"slow"})
-    public void setupBeforeTest() throws Exception {
-
-        account = createAccountWithPaymentMethod(getAccountData(25));
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
+        account = createAccountWithNonOsgiPaymentMethod(getAccountData(25));
         assertNotNull(account);
 
         bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever", callContext);
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java
index 07c48e4..ae23993 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java
@@ -22,7 +22,6 @@ import java.util.UUID;
 
 import org.joda.time.DateTime;
 import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
 import com.ning.billing.ObjectType;
@@ -69,10 +68,11 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
     private String planSetName;
 
 
+    @Override
     @BeforeMethod(groups = {"slow"})
-    public void setupBeforeTest() throws Exception {
-
-        account = createAccountWithPaymentMethod(getAccountData(25));
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
+        account = createAccountWithNonOsgiPaymentMethod(getAccountData(25));
         assertNotNull(account);
 
         bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever", callContext);
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestPaymentRefund.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestPaymentRefund.java
index f2ead6a..f3e829b 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestPaymentRefund.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestPaymentRefund.java
@@ -26,7 +26,6 @@ import javax.annotation.Nullable;
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
 import com.ning.billing.account.api.Account;
@@ -59,16 +58,18 @@ public class TestPaymentRefund extends TestIntegrationBase {
     private DateTime initialCreationDate;
     private int invoiceItemCount;
 
+    @Override
     @BeforeMethod(groups = "slow")
-    public void setupTest() throws Exception {
-        super.setupTest();
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         invoiceItemCount = 1;
         setupRefundTest();
     }
 
     @Test(groups = "slow")
     public void testRefundWithNoAdjustments() throws Exception {
-        refundPaymentAndCheckForCompletion(account, payment);
+        // Although we don't adjust the invoice, the invoicing system sends an event because invoice balance changes and overdue system-- in particular-- needs to know about it.
+        refundPaymentAndCheckForCompletion(account, payment, NextEvent.INVOICE_ADJUSTMENT);
         refundChecker.checkRefund(payment.getId(), callContext, new ExpectedRefundCheck(payment.getId(), false, new BigDecimal("233.83"), Currency.USD, initialCreationDate.toLocalDate()));
     }
 
@@ -98,7 +99,7 @@ public class TestPaymentRefund extends TestIntegrationBase {
         final int billingDay = 31;
         initialCreationDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
 
-        account = createAccountWithPaymentMethod(getAccountData(billingDay));
+        account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
 
         // set clock to the initial start date
         clock.setTime(initialCreationDate);
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestPublicBus.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestPublicBus.java
index 37800f0..a7349b9 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestPublicBus.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestPublicBus.java
@@ -22,7 +22,6 @@ import java.util.UUID;
 
 import org.joda.time.DateTime;
 import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
 import com.ning.billing.account.api.Account;
@@ -51,7 +50,8 @@ public class TestPublicBus extends TestIntegrationBase {
 
     @Override
     @BeforeMethod(groups = "slow")
-    public void setupTest() throws Exception {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
 
         publicListener = new PublicListener();
 
@@ -77,7 +77,7 @@ public class TestPublicBus extends TestIntegrationBase {
         final int billingDay = 2;
 
         log.info("Beginning test with BCD of " + billingDay);
-        final Account account = createAccountWithPaymentMethod(getAccountData(billingDay));
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
         final UUID accountId = account.getId();
         assertNotNull(account);
 
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestRepairIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestRepairIntegration.java
index 170f015..59d4cae 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestRepairIntegration.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestRepairIntegration.java
@@ -24,7 +24,6 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 import org.joda.time.Interval;
 import org.testng.Assert;
-import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
 import com.ning.billing.account.api.Account;
@@ -69,7 +68,7 @@ public class TestRepairIntegration extends TestIntegrationBase {
         final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
         clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
 
-        final Account account = createAccountWithPaymentMethod(getAccountData(25));
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(25));
 
         final SubscriptionBundle bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever", callContext);
 
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/osgi/SetupBundleWithAssertion.java b/beatrix/src/test/java/com/ning/billing/beatrix/osgi/SetupBundleWithAssertion.java
new file mode 100644
index 0000000..1b65c63
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/osgi/SetupBundleWithAssertion.java
@@ -0,0 +1,205 @@
+/*
+ * 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.beatrix.osgi;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.URL;
+import java.nio.channels.FileChannel;
+
+import org.testng.Assert;
+
+import com.ning.billing.osgi.api.config.PluginConfig.PluginType;
+import com.ning.billing.osgi.api.config.PluginJavaConfig;
+import com.ning.billing.util.config.OSGIConfig;
+
+import com.google.common.io.Resources;
+
+public class SetupBundleWithAssertion {
+
+    private final String bundleName;
+    private final OSGIConfig config;
+    private final String killbillVersion;
+
+    public SetupBundleWithAssertion(final String bundleName, final OSGIConfig config, final String killbillVersion) {
+        this.bundleName = bundleName;
+        this.config = config;
+        this.killbillVersion = killbillVersion;
+    }
+
+    public void setupBundle() {
+
+        try {
+            // Retrieve PluginConfig info from classpath
+            // test bundle should have been exported under Beatrix resource by the maven maven-dependency-plugin
+            final PluginJavaConfig pluginConfig = extractBundleTestResource();
+            Assert.assertNotNull(pluginConfig);
+
+            // Create OSGI install bundle directory
+            setupDirectoryStructure(pluginConfig);
+
+            // Copy the jar
+            copyFile(new File(pluginConfig.getBundleJarPath()), new File(pluginConfig.getPluginVersionRoot().getAbsolutePath(), pluginConfig.getPluginVersionnedName() + ".jar"));
+
+            // Create the osgiConfig file
+            createConfigFile(pluginConfig);
+
+        } catch (IOException e) {
+            Assert.fail(e.getMessage());
+        }
+    }
+
+    public void cleanBundleInstallDir() {
+        final File rootDir  = new File(config.getRootInstallationDir());
+        if (rootDir.exists()) {
+            deleteDirectory(rootDir, false);
+        }
+    }
+
+    private void createConfigFile(final PluginJavaConfig pluginConfig) throws IOException {
+
+        PrintStream printStream = null;
+        try {
+            final File configFile = new File(pluginConfig.getPluginVersionRoot(), config.getOSGIKillbillPropertyName());
+            configFile.createNewFile();
+            printStream = new PrintStream(new FileOutputStream(configFile));
+            printStream.print("pluginType=" + PluginType.NOTIFICATION);
+        } finally {
+            if (printStream != null) {
+                printStream.close();
+            }
+        }
+    }
+
+    private void setupDirectoryStructure(final PluginJavaConfig pluginConfig) {
+        cleanBundleInstallDir();
+        pluginConfig.getPluginVersionRoot().mkdirs();
+    }
+
+    private static void deleteDirectory(final File path, final boolean deleteParent) {
+        if (path == null) {
+            return;
+        }
+
+        if (path.exists()) {
+            final File[] files = path.listFiles();
+            if (files != null) {
+                for (final File f : files) {
+                    if (f.isDirectory()) {
+                        deleteDirectory(f, true);
+                    }
+                    f.delete();
+                }
+            }
+
+            if (deleteParent) {
+                path.delete();
+            }
+        }
+    }
+
+
+
+    private PluginJavaConfig extractBundleTestResource() {
+
+
+        final String resourceName = bundleName + "-" + killbillVersion + "-jar-with-dependencies.jar";
+        final URL resourceUrl = Resources.getResource(resourceName);
+        if (resourceUrl != null) {
+            final String[] parts = resourceUrl.getPath().split("/");
+            final String lastPart = parts[parts.length - 1];
+            if (lastPart.startsWith(bundleName)) {
+                return createPluginConfig(resourceUrl.getPath(), lastPart);
+            }
+        }
+        return null;
+
+    }
+
+    private PluginJavaConfig createPluginConfig(final String bundleTestResourcePath, final String fileName) {
+
+        return new PluginJavaConfig() {
+            @Override
+            public String getBundleJarPath() {
+                return bundleTestResourcePath;
+            }
+
+            @Override
+            public String getPluginName() {
+                return bundleName;
+            }
+
+            @Override
+            public PluginType getPluginType() {
+                return PluginType.PAYMENT;
+            }
+
+            @Override
+            public String getVersion() {
+                return killbillVersion;
+            }
+
+            @Override
+            public String getPluginVersionnedName() {
+                return bundleName + "-" + killbillVersion;
+            }
+
+            @Override
+            public File getPluginVersionRoot() {
+                final StringBuilder tmp = new StringBuilder(config.getRootInstallationDir());
+                tmp.append("/plugins/")
+                   .append(PluginLanguage.JAVA.toString().toLowerCase())
+                   .append("/")
+                   .append(bundleName)
+                   .append("/")
+                   .append(killbillVersion);
+                final File result = new File(tmp.toString());
+                return result;
+            }
+
+            @Override
+            public PluginLanguage getPluginLanguage() {
+                return PluginLanguage.JAVA;
+            }
+        };
+    }
+
+    public static void copyFile(File sourceFile, File destFile) throws IOException {
+        if (!destFile.exists()) {
+            destFile.createNewFile();
+        }
+
+        FileChannel source = null;
+        FileChannel destination = null;
+
+        try {
+            source = new FileInputStream(sourceFile).getChannel();
+            destination = new FileOutputStream(destFile).getChannel();
+            destination.transferFrom(source, 0, source.size());
+        } finally {
+            if (source != null) {
+                source.close();
+            }
+            if (destination != null) {
+                destination.close();
+            }
+        }
+    }
+}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/util/PaymentChecker.java b/beatrix/src/test/java/com/ning/billing/beatrix/util/PaymentChecker.java
index 233679e..2763aed 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/util/PaymentChecker.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/util/PaymentChecker.java
@@ -53,7 +53,11 @@ public class PaymentChecker {
         final List<Payment> payments = paymentApi.getAccountPayments(accountId, context);
         Assert.assertEquals(payments.size(), paymentOrderingNumber);
         final Payment payment = payments.get(paymentOrderingNumber - 1);
-        checkPayment(accountId, payment, context, expected);
+        if (payment.getPaymentStatus() == PaymentStatus.UNKNOWN) {
+            checkPaymentNoAuditForRuntimeException(accountId, payment, context, expected);
+        } else {
+            checkPayment(accountId, payment, context, expected);
+        }
         return payment;
     }
 
@@ -66,6 +70,14 @@ public class PaymentChecker {
         auditChecker.checkPaymentCreated(payment, context);
     }
 
+    private void checkPaymentNoAuditForRuntimeException(final UUID accountId, final Payment payment, final CallContext context, final ExpectedPaymentCheck expected) {
+        Assert.assertEquals(payment.getAccountId(), accountId);
+        Assert.assertTrue(payment.getAmount().compareTo(expected.getAmount()) == 0);
+        Assert.assertEquals(payment.getPaymentStatus(),expected.getStatus());
+        Assert.assertEquals(payment.getInvoiceId(), expected.getInvoiceId());
+        Assert.assertEquals(payment.getCurrency(), expected.getCurrency());
+    }
+
     public static class ExpectedPaymentCheck {
 
         private final LocalDate paymentDate;

bin/start-server 22(+13 -9)

diff --git a/bin/start-server b/bin/start-server
index ce254da..2b42e28 100755
--- a/bin/start-server
+++ b/bin/start-server
@@ -28,9 +28,11 @@ 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/log4j.xml"
+LOG="$SERVER/src/main/resources/logback.xml"
+LOG_DIR="$SERVER/logs"
 
 # From Argument Options
 PORT=8080
@@ -42,16 +44,16 @@ WAIT_DEBUGGER=
 function usage() {
     echo -n "./start-server "
     echo -n " -s (start server)"
-    echo -n " -d (debugger turned on)"    
+    echo -n " -d (debugger turned on)"
     echo -n " -w (along with -d, wait for debugger before starting)"
     echo -n " -p <port_number> default 8080"
-    echo -n "-h this message"        
+    echo -n "-h this message"
     exit 1
 }
 
 function build_properties() {
     local opts=
-    local prop= 
+    local prop=
     for prop in `cat  $PROPERTIES | grep =`; do
         local k=`echo $prop | awk '  BEGIN {FS="="} { print $1 }'`
         local v=`echo $prop | awk 'BEGIN {FS="="} { print $2 }'`
@@ -61,10 +63,12 @@ function build_properties() {
 }
 
 function start() {
+    mkdir -p $LOG_DIR
+
     local opts=`build_properties`
-    local start_cmd="mvn $opts -Dlog4j.configuration=file://$LOG -Dning.jmx.http.port=$PORT -Dxn.host.external.port=$PORT -DjettyPort=$PORT -Dxn.server.port=$PORT jetty:run"    
+    local start_cmd="mvn $opts -Dlogback.configurationFile=$LOG -Dning.jmx.http.port=$PORT -Dxn.host.external.port=$PORT -DjettyPort=$PORT -Dxn.server.port=$PORT jetty:run"
 
-    local debug_opts_eclipse= 
+    local debug_opts_eclipse=
     if [ ! -z $DEBUG ]; then
         if  [ ! -z $WAIT_DEBUGGER ]; then
             debug_opts_eclipse=$DEBUG_OPTS_ECLIPSE_WAIT
@@ -72,8 +76,8 @@ 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"
     cd $SERVER

catalog/pom.xml 37(+24 -13)

diff --git a/catalog/pom.xml b/catalog/pom.xml
index ad053c3..854d35c 100644
--- a/catalog/pom.xml
+++ b/catalog/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>
@@ -78,17 +79,6 @@
         <plugins>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-jar-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>test-jar</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-shade-plugin</artifactId>
                 <executions>
                     <execution>
@@ -102,7 +92,8 @@
                             <shadedArtifactAttached>true</shadedArtifactAttached>
                             <shadedClassifierName>load-tool</shadedClassifierName>
                             <transformers>
-                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                <transformer
+                                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                     <manifestEntries>
                                         <Main-Class>com.ning.billing.catalog.LoadCatalog</Main-Class>
                                     </manifestEntries>
@@ -110,6 +101,26 @@
                             </transformers>
                         </configuration>
                     </execution>
+                    <execution>
+                        <id>assemble-xsd-tool-catalog</id>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <phase>package</phase>
+                        <configuration>
+                            <createSourcesJar>false</createSourcesJar>
+                            <shadedArtifactAttached>true</shadedArtifactAttached>
+                            <shadedClassifierName>xsd-tool</shadedClassifierName>
+                            <transformers>
+                                <transformer
+                                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                    <manifestEntries>
+                                        <Main-Class>com.ning.billing.catalog.CreateCatalogSchema</Main-Class>
+                                    </manifestEntries>
+                                </transformer>
+                            </transformers>
+                        </configuration>
+                    </execution>
                 </executions>
             </plugin>
         </plugins>
diff --git a/catalog/src/main/java/com/ning/billing/catalog/CreateCatalogSchema.java b/catalog/src/main/java/com/ning/billing/catalog/CreateCatalogSchema.java
index 1ccaf64..7e25177 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/CreateCatalogSchema.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/CreateCatalogSchema.java
@@ -25,7 +25,7 @@ import com.ning.billing.util.config.catalog.XMLSchemaGenerator;
 public class CreateCatalogSchema {
 
     /**
-     * @param args
+     * @param args output file path
      */
     public static void main(final String[] args) throws Exception {
         if (args.length != 1) {
@@ -37,7 +37,5 @@ public class CreateCatalogSchema {
         final Writer w = new FileWriter(f);
         w.write(XMLSchemaGenerator.xmlSchemaAsString(StandaloneCatalog.class));
         w.close();
-
     }
-
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/glue/CatalogModule.java b/catalog/src/main/java/com/ning/billing/catalog/glue/CatalogModule.java
index 0227fc5..21ccadc 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/glue/CatalogModule.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/glue/CatalogModule.java
@@ -16,11 +16,8 @@
 
 package com.ning.billing.catalog.glue;
 
-import java.util.Properties;
-
 import org.skife.config.ConfigSource;
 import org.skife.config.ConfigurationObjectFactory;
-import org.skife.config.SimplePropertyConfigSource;
 
 import com.ning.billing.catalog.DefaultCatalogService;
 import com.ning.billing.catalog.api.CatalogService;
@@ -34,15 +31,7 @@ import com.google.inject.AbstractModule;
 
 public class CatalogModule extends AbstractModule {
 
-    final ConfigSource configSource;
-
-    public CatalogModule() {
-        this(System.getProperties());
-    }
-
-    public CatalogModule(final Properties properties) {
-        this(new SimplePropertyConfigSource(properties));
-    }
+    protected final ConfigSource configSource;
 
     public CatalogModule(final ConfigSource configSource) {
         this.configSource = configSource;
diff --git a/catalog/src/test/java/com/ning/billing/catalog/CatalogTestSuiteNoDB.java b/catalog/src/test/java/com/ning/billing/catalog/CatalogTestSuiteNoDB.java
index 3687ad1..fa03508 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/CatalogTestSuiteNoDB.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/CatalogTestSuiteNoDB.java
@@ -32,8 +32,8 @@ public abstract class CatalogTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     protected VersionedCatalogLoader loader;
 
     @BeforeClass(groups = "fast")
-    protected void setup() throws Exception {
-        final Injector injector = Guice.createInjector(new TestCatalogModuleNoDB());
+    protected void beforeClass() throws Exception {
+        final Injector injector = Guice.createInjector(new TestCatalogModuleNoDB(configSource));
         injector.injectMembers(this);
     }
 }
diff --git a/catalog/src/test/java/com/ning/billing/catalog/glue/TestCatalogModule.java b/catalog/src/test/java/com/ning/billing/catalog/glue/TestCatalogModule.java
index 0b7dec4..41b5bb4 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/glue/TestCatalogModule.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/glue/TestCatalogModule.java
@@ -16,10 +16,16 @@
 
 package com.ning.billing.catalog.glue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.GuicyKillbillTestNoDBModule;
 
 public class TestCatalogModule extends CatalogModule {
 
+    public TestCatalogModule(final ConfigSource configSource) {
+        super(configSource);
+    }
+
     @Override
     public void configure() {
         super.configure();
diff --git a/catalog/src/test/java/com/ning/billing/catalog/glue/TestCatalogModuleNoDB.java b/catalog/src/test/java/com/ning/billing/catalog/glue/TestCatalogModuleNoDB.java
index b895a87..a0608bf 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/glue/TestCatalogModuleNoDB.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/glue/TestCatalogModuleNoDB.java
@@ -16,5 +16,11 @@
 
 package com.ning.billing.catalog.glue;
 
+import org.skife.config.ConfigSource;
+
 public class TestCatalogModuleNoDB extends TestCatalogModule {
+
+    public TestCatalogModuleNoDB(final ConfigSource configSource) {
+        super(configSource);
+    }
 }
diff --git a/catalog/src/test/java/com/ning/billing/catalog/rules/TestPlanRules.java b/catalog/src/test/java/com/ning/billing/catalog/rules/TestPlanRules.java
index 58152dc..3bae0a7 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/rules/TestPlanRules.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/rules/TestPlanRules.java
@@ -17,6 +17,7 @@
 package com.ning.billing.catalog.rules;
 
 import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.BeforeTest;
 import org.testng.annotations.Test;
 
@@ -39,8 +40,8 @@ public class TestPlanRules extends CatalogTestSuiteNoDB {
 
     private MockCatalog cat = null;
 
-    @BeforeTest(groups = "fast")
-    public void setupTest() {
+    @BeforeMethod(groups = "fast")
+    public void beforeMethod() {
         cat = new MockCatalog();
 
         final DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[0];
diff --git a/catalog/src/test/java/com/ning/billing/catalog/TestLimits.java b/catalog/src/test/java/com/ning/billing/catalog/TestLimits.java
index 7761c3f..a9f1835 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/TestLimits.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/TestLimits.java
@@ -28,7 +28,8 @@ public class TestLimits extends CatalogTestSuiteNoDB {
     private VersionedCatalog catalog;
     
     @BeforeClass(groups = "fast")
-    public void setUp() throws ServiceException {
+    public void beforeClass() throws Exception {
+        super.beforeClass();
         catalog = loader.load(Resources.getResource("WeaponsHireSmall.xml").toString());
     }
 
diff --git a/catalog/src/test/java/com/ning/billing/catalog/TestVersionedCatalog.java b/catalog/src/test/java/com/ning/billing/catalog/TestVersionedCatalog.java
index 31c4782..f008c10 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/TestVersionedCatalog.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/TestVersionedCatalog.java
@@ -44,7 +44,8 @@ public class TestVersionedCatalog extends CatalogTestSuiteNoDB {
     private VersionedCatalog vc;
 
     @BeforeClass(groups = "fast")
-    public void setUp() throws ServiceException {
+    public void beforeClass() throws Exception {
+        super.beforeClass();
         vc = loader.load(Resources.getResource("versionedCatalog").toString());
     }
 
diff --git a/catalog/src/test/resources/SpyCarBasic.xml b/catalog/src/test/resources/SpyCarBasic.xml
index 9ea43ad..7db7465 100644
--- a/catalog/src/test/resources/SpyCarBasic.xml
+++ b/catalog/src/test/resources/SpyCarBasic.xml
@@ -1,4 +1,20 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+  ~ 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.
+  -->
+
 <catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
 

entitlement/pom.xml 15(+0 -15)

diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index f2d71e3..062f9b0 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -114,19 +114,4 @@
             <scope>test</scope>
         </dependency>
     </dependencies>
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-jar-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>test-jar</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-        </plugins>
-    </build>
 </project>
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java b/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java
index 83fcd7f..c23c51f 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.entitlement.glue;
 
+import org.skife.config.ConfigSource;
 import org.skife.config.ConfigurationObjectFactory;
 
 import com.ning.billing.entitlement.engine.dao.DefaultEntitlementDao;
@@ -51,8 +52,14 @@ public class DefaultEntitlementModule extends AbstractModule implements Entitlem
 
     public static final String REPAIR_NAMED = "repair";
 
+    protected final ConfigSource configSource;
+
+    public DefaultEntitlementModule(final ConfigSource configSource) {
+        this.configSource = configSource;
+    }
+
     protected void installConfig() {
-        final EntitlementConfig config = new ConfigurationObjectFactory(System.getProperties()).build(EntitlementConfig.class);
+        final EntitlementConfig config = new ConfigurationObjectFactory(configSource).build(EntitlementConfig.class);
         bind(EntitlementConfig.class).toInstance(config);
     }
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/alignment/TestPlanAligner.java b/entitlement/src/test/java/com/ning/billing/entitlement/alignment/TestPlanAligner.java
index 5528d7c..16deb9d 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/alignment/TestPlanAligner.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/alignment/TestPlanAligner.java
@@ -57,8 +57,10 @@ public class TestPlanAligner extends EntitlementTestSuiteNoDB {
     private DefaultCatalogService catalogService;
     private PlanAligner planAligner;
 
+    @Override
     @BeforeClass(groups = "fast")
-    public void setUp() throws Exception {
+    public void beforeClass() throws Exception {
+        super.beforeClass();
         final VersionedCatalogLoader versionedCatalogLoader = new VersionedCatalogLoader(clock);
         final CatalogConfig config = new ConfigurationObjectFactory(new ConfigSource() {
             final Map<String, String> properties = ImmutableMap.<String, String>of("killbill.catalog.uri", "file:src/test/resources/testInput.xml");
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairWithError.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairWithError.java
index ee50c23..6773d3f 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairWithError.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/timeline/TestRepairWithError.java
@@ -52,9 +52,10 @@ public class TestRepairWithError extends EntitlementTestSuiteNoDB {
     private TestWithException test;
     private Subscription baseSubscription;
 
+    @Override
     @BeforeMethod(alwaysRun = true)
-    public void setupTest() throws Exception {
-        super.setupTest();
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         test = new TestWithException();
         final DateTime startDate = clock.getUTCNow();
         baseSubscription = testUtil.createSubscription(bundle, baseProduct, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, startDate);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestDefaultEntitlementTransferApi.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestDefaultEntitlementTransferApi.java
index bd18ac9..237a21d 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestDefaultEntitlementTransferApi.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/transfer/TestDefaultEntitlementTransferApi.java
@@ -56,8 +56,10 @@ public class TestDefaultEntitlementTransferApi extends EntitlementTestSuiteNoDB 
 
     private DefaultEntitlementTransferApi transferApi;
 
+    @Override
     @BeforeMethod(groups = "fast")
-    public void setUp() throws Exception {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         final NonEntityDao nonEntityDao = Mockito.mock(NonEntityDao.class);
         final EntitlementDao dao = Mockito.mock(EntitlementDao.class);
         final CatalogService catalogService = new MockCatalogService(new MockCatalog());
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteNoDB.java b/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteNoDB.java
index 2348176..64b95ac 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteNoDB.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteNoDB.java
@@ -93,14 +93,14 @@ public class EntitlementTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     protected SubscriptionBundle bundle;
 
     @BeforeClass(groups = "fast")
-    public void setup() throws Exception {
+    public void beforeClass() throws Exception {
         DefaultEntitlementTestInitializer.loadSystemPropertiesFromClasspath("/entitlement.properties");
-        final Injector g = Guice.createInjector(Stage.PRODUCTION, new TestEngineModuleNoDB());
+        final Injector g = Guice.createInjector(Stage.PRODUCTION, new TestEngineModuleNoDB(configSource));
         g.injectMembers(this);
     }
 
     @BeforeMethod(groups = "fast")
-    public void setupTest() throws Exception {
+    public void beforeMethod() throws Exception {
 
         // CLEANUP ALL DB TABLES OR IN MEMORY STRUCTURES
         ((MockEntitlementDaoMemory) dao).reset();
@@ -113,7 +113,7 @@ public class EntitlementTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     }
 
     @AfterMethod(groups = "fast")
-    public void cleanupTest() throws Exception {
+    public void afterMethod() throws Exception {
         entitlementTestInitializer.stopTestFramework(testListener, busService, entitlementService);
     }
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java b/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
index 4720a01..a4b084b 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
@@ -83,7 +83,6 @@ public class EntitlementTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWi
     protected TestApiListener testListener;
     @Inject
     protected TestListenerStatus testListenerStatus;
-
     @Inject
     protected EntitlementTestInitializer entitlementTestInitializer;
 
@@ -92,14 +91,16 @@ public class EntitlementTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWi
     protected SubscriptionBundle bundle;
 
     @BeforeClass(groups = "slow")
-    public void setup() throws Exception {
+    public void beforeClass() throws Exception {
         DefaultEntitlementTestInitializer.loadSystemPropertiesFromClasspath("/entitlement.properties");
-        final Injector g = Guice.createInjector(Stage.PRODUCTION, new TestEngineModuleWithEmbeddedDB());
+        final Injector g = Guice.createInjector(Stage.PRODUCTION, new TestEngineModuleWithEmbeddedDB(configSource));
         g.injectMembers(this);
     }
 
+    @Override
     @BeforeMethod(groups = "slow")
-    public void setupTest() throws Exception {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         entitlementTestInitializer.startTestFamework(testListener, testListenerStatus, clock, busService, entitlementService);
 
         this.catalog = entitlementTestInitializer.initCatalog(catalogService);
@@ -108,7 +109,7 @@ public class EntitlementTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWi
     }
 
     @AfterMethod(groups = "slow")
-    public void cleanupTest() throws Exception {
+    public void afterMethod() throws Exception {
         entitlementTestInitializer.stopTestFramework(testListener, busService, entitlementService);
     }
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/TestEngineModule.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/TestEngineModule.java
index 5df0b4e..b648503 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/TestEngineModule.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/TestEngineModule.java
@@ -17,6 +17,7 @@
 package com.ning.billing.entitlement.glue;
 
 import org.mockito.Mockito;
+import org.skife.config.ConfigSource;
 
 import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.api.TestApiListener;
@@ -33,6 +34,9 @@ import com.ning.billing.util.glue.CallContextModule;
 
 public class TestEngineModule extends DefaultEntitlementModule {
 
+    public TestEngineModule(final ConfigSource configSource) {
+        super(configSource);
+    }
 
     @Override
     public void installEntitlementUserApi() {
@@ -42,9 +46,9 @@ public class TestEngineModule extends DefaultEntitlementModule {
     @Override
     protected void configure() {
         super.configure();
-        install(new CatalogModule());
+        install(new CatalogModule(configSource));
         install(new CallContextModule());
-        install(new CacheModule());
+        install(new CacheModule(configSource));
 
         bind(AccountUserApi.class).toInstance(Mockito.mock(AccountUserApi.class));
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/TestEngineModuleNoDB.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/TestEngineModuleNoDB.java
index 1346b54..1e9f07d 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/TestEngineModuleNoDB.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/TestEngineModuleNoDB.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.entitlement.glue;
 
+import org.skife.config.ConfigSource;
 import org.skife.config.ConfigurationObjectFactory;
 
 import com.ning.billing.GuicyKillbillTestNoDBModule;
@@ -24,8 +25,7 @@ import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.engine.dao.MockEntitlementDaoMemory;
 import com.ning.billing.entitlement.engine.dao.RepairEntitlementDao;
 import com.ning.billing.mock.glue.MockNonEntityDaoModule;
-import com.ning.billing.util.glue.BusModule;
-import com.ning.billing.util.glue.BusModule.BusType;
+import com.ning.billing.util.bus.InMemoryBusModule;
 import com.ning.billing.util.notificationq.MockNotificationQueueService;
 import com.ning.billing.util.notificationq.NotificationQueueConfig;
 import com.ning.billing.util.notificationq.NotificationQueueService;
@@ -34,6 +34,10 @@ import com.google.inject.name.Names;
 
 public class TestEngineModuleNoDB extends TestEngineModule {
 
+    public TestEngineModuleNoDB(final ConfigSource configSource) {
+        super(configSource);
+    }
+
     @Override
     protected void installEntitlementDao() {
         bind(EntitlementDao.class).to(MockEntitlementDaoMemory.class).asEagerSingleton();
@@ -48,7 +52,7 @@ public class TestEngineModuleNoDB extends TestEngineModule {
     }
 
     protected void configureNotificationQueueConfig() {
-        final NotificationQueueConfig config = new ConfigurationObjectFactory(System.getProperties()).build(NotificationQueueConfig.class);
+        final NotificationQueueConfig config = new ConfigurationObjectFactory(configSource).build(NotificationQueueConfig.class);
         bind(NotificationQueueConfig.class).toInstance(config);
     }
 
@@ -59,7 +63,7 @@ public class TestEngineModuleNoDB extends TestEngineModule {
 
         super.configure();
 
-        install(new BusModule(BusType.MEMORY));
+        install(new InMemoryBusModule(configSource));
         installNotificationQueue();
 
         install(new MockNonEntityDaoModule());
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/TestEngineModuleWithEmbeddedDB.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/TestEngineModuleWithEmbeddedDB.java
index d926521..0f1d8ae 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/TestEngineModuleWithEmbeddedDB.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/TestEngineModuleWithEmbeddedDB.java
@@ -16,13 +16,14 @@
 
 package com.ning.billing.entitlement.glue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.GuicyKillbillTestWithEmbeddedDBModule;
 import com.ning.billing.entitlement.api.timeline.RepairEntitlementLifecycleDao;
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.engine.dao.MockEntitlementDaoSql;
 import com.ning.billing.entitlement.engine.dao.RepairEntitlementDao;
 import com.ning.billing.util.glue.BusModule;
-import com.ning.billing.util.glue.BusModule.BusType;
 import com.ning.billing.util.glue.CustomFieldModule;
 import com.ning.billing.util.glue.NonEntityDaoModule;
 import com.ning.billing.util.glue.NotificationQueueModule;
@@ -30,6 +31,11 @@ import com.ning.billing.util.glue.NotificationQueueModule;
 import com.google.inject.name.Names;
 
 public class TestEngineModuleWithEmbeddedDB extends TestEngineModule {
+
+    public TestEngineModuleWithEmbeddedDB(final ConfigSource configSource) {
+        super(configSource);
+    }
+
     @Override
     protected void installEntitlementDao() {
         bind(EntitlementDao.class).to(MockEntitlementDaoSql.class).asEagerSingleton();
@@ -47,9 +53,9 @@ public class TestEngineModuleWithEmbeddedDB extends TestEngineModule {
 
         //installDBI();
 
-        install(new NotificationQueueModule());
+        install(new NotificationQueueModule(configSource));
         install(new CustomFieldModule());
-        install(new BusModule(BusType.PERSISTENT));
+        install(new BusModule(configSource));
         super.configure();
     }
 }

invoice/pom.xml 2(+0 -2)

diff --git a/invoice/pom.xml b/invoice/pom.xml
index 32ab61d..8c820ca 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -124,6 +124,4 @@
             <scope>test</scope>
         </dependency>
     </dependencies>
-    <build>
-    </build>
 </project>
diff --git a/invoice/src/main/java/com/ning/billing/invoice/glue/DefaultInvoiceModule.java b/invoice/src/main/java/com/ning/billing/invoice/glue/DefaultInvoiceModule.java
index 2338ccf..f1f1f01 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/glue/DefaultInvoiceModule.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/glue/DefaultInvoiceModule.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.invoice.glue;
 
+import org.skife.config.ConfigSource;
 import org.skife.config.ConfigurationObjectFactory;
 
 import com.ning.billing.glue.InvoiceModule;
@@ -52,6 +53,12 @@ public class DefaultInvoiceModule extends AbstractModule implements InvoiceModul
 
     InvoiceConfig config;
 
+    protected final ConfigSource configSource;
+
+    public DefaultInvoiceModule(final ConfigSource configSource) {
+        this.configSource = configSource;
+    }
+
     protected void installInvoiceDao() {
         bind(InvoiceDao.class).to(DefaultInvoiceDao.class).asEagerSingleton();
     }
@@ -72,7 +79,7 @@ public class DefaultInvoiceModule extends AbstractModule implements InvoiceModul
     }
 
     protected void installConfig() {
-        config = new ConfigurationObjectFactory(System.getProperties()).build(InvoiceConfig.class);
+        config = new ConfigurationObjectFactory(configSource).build(InvoiceConfig.class);
         bind(InvoiceConfig.class).toInstance(config);
     }
 
@@ -88,7 +95,7 @@ public class DefaultInvoiceModule extends AbstractModule implements InvoiceModul
     protected void installNotifiers() {
         bind(NextBillingDateNotifier.class).to(DefaultNextBillingDateNotifier.class).asEagerSingleton();
         bind(NextBillingDatePoster.class).to(DefaultNextBillingDatePoster.class).asEagerSingleton();
-        final TranslatorConfig config = new ConfigurationObjectFactory(System.getProperties()).build(TranslatorConfig.class);
+        final TranslatorConfig config = new ConfigurationObjectFactory(configSource).build(TranslatorConfig.class);
         bind(TranslatorConfig.class).toInstance(config);
         bind(InvoiceFormatterFactory.class).to(config.getInvoiceFormatterFactoryClass()).asEagerSingleton();
     }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java b/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
index 85cd2e9..8e665f1 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
@@ -51,9 +51,10 @@ public class TestDefaultInvoiceMigrationApi extends InvoiceTestSuiteWithEmbedded
     private static final BigDecimal MIGRATION_INVOICE_AMOUNT = new BigDecimal("100.00");
     private static final Currency MIGRATION_INVOICE_CURRENCY = Currency.USD;
 
+    @Override
     @BeforeMethod(groups = "slow")
-    public void setupTest() throws Exception {
-        super.setupTest();
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         date_migrated = clock.getUTCToday().minusYears(1);
         date_regular = clock.getUTCNow();
 
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/user/TestDefaultInvoiceUserApi.java b/invoice/src/test/java/com/ning/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
index 647437a..33d66ba 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
@@ -45,9 +45,10 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
     private UUID accountId;
     private UUID invoiceId;
 
+    @Override
     @BeforeMethod(groups = "slow")
-    public void setupTest() throws Exception {
-        super.setupTest();
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         final Account account = invoiceUtil.createAccount();
         accountId = account.getId();
         invoiceId = invoiceUtil.generateRegularInvoice(account, clock.getUTCNow());
diff --git a/invoice/src/test/java/com/ning/billing/invoice/glue/TestInvoiceModule.java b/invoice/src/test/java/com/ning/billing/invoice/glue/TestInvoiceModule.java
index 7bfc867..f094d0f 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/glue/TestInvoiceModule.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/glue/TestInvoiceModule.java
@@ -17,6 +17,7 @@
 package com.ning.billing.invoice.glue;
 
 import org.mockito.Mockito;
+import org.skife.config.ConfigSource;
 
 import com.ning.billing.catalog.glue.CatalogModule;
 import com.ning.billing.invoice.TestInvoiceHelper;
@@ -34,6 +35,10 @@ import com.ning.billing.util.svcapi.junction.BillingInternalApi;
 
 public class TestInvoiceModule extends DefaultInvoiceModule {
 
+    public TestInvoiceModule(final ConfigSource configSource) {
+        super(configSource);
+    }
+
     private void installExternalApis() {
         bind(EntitlementInternalApi.class).toInstance(Mockito.mock(EntitlementInternalApi.class));
         bind(AccountInternalApi.class).toInstance(Mockito.mock(AccountInternalApi.class));
@@ -46,12 +51,12 @@ public class TestInvoiceModule extends DefaultInvoiceModule {
 
         install(new MockGlobalLockerModule());
 
-        install(new CatalogModule());
-        install(new CacheModule());
+        install(new CatalogModule(configSource));
+        install(new CacheModule(configSource));
         install(new TemplateModule());
-        install(new EmailModule());
+        install(new EmailModule(configSource));
 
-        install(new NotificationQueueModule());
+        install(new NotificationQueueModule(configSource));
         install(new TagStoreModule());
         install(new CustomFieldModule());
 
diff --git a/invoice/src/test/java/com/ning/billing/invoice/glue/TestInvoiceModuleNoDB.java b/invoice/src/test/java/com/ning/billing/invoice/glue/TestInvoiceModuleNoDB.java
index 8946d1f..58d4a49 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/glue/TestInvoiceModuleNoDB.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/glue/TestInvoiceModuleNoDB.java
@@ -16,15 +16,20 @@
 
 package com.ning.billing.invoice.glue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.GuicyKillbillTestNoDBModule;
 import com.ning.billing.invoice.dao.InvoiceDao;
 import com.ning.billing.invoice.dao.MockInvoiceDao;
 import com.ning.billing.mock.glue.MockNonEntityDaoModule;
 import com.ning.billing.util.bus.InMemoryBusModule;
-import com.ning.billing.util.glue.NonEntityDaoModule;
 
 public class TestInvoiceModuleNoDB extends TestInvoiceModule {
 
+    public TestInvoiceModuleNoDB(final ConfigSource configSource) {
+        super(configSource);
+    }
+
     protected void installInvoiceDao() {
         bind(InvoiceDao.class).to(MockInvoiceDao.class);
     }
@@ -34,6 +39,6 @@ public class TestInvoiceModuleNoDB extends TestInvoiceModule {
         super.configure();
         install(new GuicyKillbillTestNoDBModule());
         install(new MockNonEntityDaoModule());
-        install(new InMemoryBusModule());
+        install(new InMemoryBusModule(configSource));
     }
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/glue/TestInvoiceModuleWithEmbeddedDb.java b/invoice/src/test/java/com/ning/billing/invoice/glue/TestInvoiceModuleWithEmbeddedDb.java
index 5986a0f..9517d0b 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/glue/TestInvoiceModuleWithEmbeddedDb.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/glue/TestInvoiceModuleWithEmbeddedDb.java
@@ -16,6 +16,8 @@
 
 package com.ning.billing.invoice.glue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.GuicyKillbillTestWithEmbeddedDBModule;
 import com.ning.billing.invoice.InvoiceListener;
 import com.ning.billing.invoice.TestInvoiceNotificationQListener;
@@ -24,6 +26,10 @@ import com.ning.billing.util.glue.NonEntityDaoModule;
 
 public class TestInvoiceModuleWithEmbeddedDb extends TestInvoiceModule {
 
+    public TestInvoiceModuleWithEmbeddedDb(final ConfigSource configSource) {
+        super(configSource);
+    }
+
     @Override
     protected void installInvoiceListener() {
         bind(InvoiceListener.class).to(TestInvoiceNotificationQListener.class).asEagerSingleton();
@@ -36,6 +42,6 @@ public class TestInvoiceModuleWithEmbeddedDb extends TestInvoiceModule {
 
         install(new GuicyKillbillTestWithEmbeddedDBModule());
         install(new NonEntityDaoModule());
-        install(new BusModule());
+        install(new BusModule(configSource));
     }
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/InvoiceTestSuiteNoDB.java b/invoice/src/test/java/com/ning/billing/invoice/InvoiceTestSuiteNoDB.java
index 98597ab..f11a650 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/InvoiceTestSuiteNoDB.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/InvoiceTestSuiteNoDB.java
@@ -91,21 +91,21 @@ public abstract class InvoiceTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
 
 
     @BeforeClass(groups = "fast")
-    protected void setup() throws Exception {
+    protected void beforeClass() throws Exception {
 
         loadSystemPropertiesFromClasspath("/resource.properties");
 
-        final Injector injector = Guice.createInjector(new TestInvoiceModuleNoDB());
+        final Injector injector = Guice.createInjector(new TestInvoiceModuleNoDB(configSource));
         injector.injectMembers(this);
     }
 
     @BeforeMethod(groups = "fast")
-    public void setupTest() {
+    public void beforeMethod() {
         bus.start();
     }
 
     @AfterMethod(groups = "fast")
-    public void cleanupTest() {
+    public void afterMethod() {
         bus.stop();
     }
 
diff --git a/invoice/src/test/java/com/ning/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java b/invoice/src/test/java/com/ning/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java
index 2783174..a969367 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java
@@ -107,16 +107,17 @@ public abstract class InvoiceTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
 
 
     @BeforeClass(groups = "slow")
-    protected void setup() throws Exception {
-
+    protected void beforeClass() throws Exception {
         loadSystemPropertiesFromClasspath("/resource.properties");
 
-        final Injector injector = Guice.createInjector(new TestInvoiceModuleWithEmbeddedDb());
+        final Injector injector = Guice.createInjector(new TestInvoiceModuleWithEmbeddedDb(configSource));
         injector.injectMembers(this);
     }
 
+    @Override
     @BeforeMethod(groups = "slow")
-    public void setupTest() throws Exception {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         bus.start();
         restartInvoiceService(invoiceService);
     }
@@ -133,7 +134,7 @@ public abstract class InvoiceTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
     }
 
     @AfterMethod(groups = "slow")
-    public void cleanupTest() throws Exception {
+    public void afterMethod() throws Exception {
         bus.stop();
         stopInvoiceService(invoiceService);
     }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java b/invoice/src/test/java/com/ning/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java
index fbe8e5c..7998d77 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java
@@ -51,8 +51,8 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
     private MustacheTemplateEngine templateEngine;
 
     @BeforeClass(groups = "fast")
-    public void setup() throws Exception {
-        super.setup();
+    public void beforeClass() throws Exception {
+        super.beforeClass();
         config = new ConfigurationObjectFactory(System.getProperties()).build(TranslatorConfig.class);
         templateEngine = new MustacheTemplateEngine();
     }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/template/formatters/TestDefaultInvoiceItemFormatter.java b/invoice/src/test/java/com/ning/billing/invoice/template/formatters/TestDefaultInvoiceItemFormatter.java
index 85604e7..e9ce70e 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/template/formatters/TestDefaultInvoiceItemFormatter.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/template/formatters/TestDefaultInvoiceItemFormatter.java
@@ -44,9 +44,10 @@ public class TestDefaultInvoiceItemFormatter extends InvoiceTestSuiteNoDB {
     private TranslatorConfig config;
     private MustacheTemplateEngine templateEngine;
 
+    @Override
     @BeforeClass(groups = "fast")
-    public void setup() throws Exception {
-        super.setup();
+    public void beforeClass() throws Exception {
+        super.beforeClass();
         config = new ConfigurationObjectFactory(System.getProperties()).build(TranslatorConfig.class);
         templateEngine = new MustacheTemplateEngine();
     }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/TestHtmlInvoiceGenerator.java b/invoice/src/test/java/com/ning/billing/invoice/TestHtmlInvoiceGenerator.java
index a17dd6d..fd00f39 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/TestHtmlInvoiceGenerator.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/TestHtmlInvoiceGenerator.java
@@ -46,9 +46,10 @@ public class TestHtmlInvoiceGenerator extends InvoiceTestSuiteNoDB {
 
     private HtmlInvoiceGenerator g;
 
+    @Override
     @BeforeClass(groups = "fast")
-    public void setup() throws Exception {
-        super.setup();
+    public void beforeClass() throws Exception {
+        super.beforeClass();
         final TranslatorConfig config = new ConfigurationObjectFactory(System.getProperties()).build(TranslatorConfig.class);
         final TemplateEngine templateEngine = new MustacheTemplateEngine();
         final InvoiceFormatterFactory factory = new DefaultInvoiceFormatterFactory();
diff --git a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
index bea73f8..a4f7b92 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
@@ -61,9 +61,10 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
     private Account account;
     private Subscription subscription;
 
+    @Override
     @BeforeMethod(groups = "slow")
-    public void setupTest() throws Exception {
-        super.setupTest();
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         account = invoiceUtil.createAccount();
         subscription = invoiceUtil.createSubscription();
     }

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

diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index 3902d2d..ca1b031 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>
@@ -88,19 +87,4 @@
             <scope>test</scope>
         </dependency>
     </dependencies>
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-jar-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>test-jar</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-        </plugins>
-    </build>
 </project>
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountJson.java
index f57124f..e8a3c97 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountJson.java
@@ -26,6 +26,7 @@ import com.ning.billing.catalog.api.Currency;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Objects;
 
 public class AccountJson extends AccountJsonSimple {
 
@@ -94,12 +95,12 @@ public class AccountJson extends AccountJsonSimple {
 
             @Override
             public Boolean isMigrated() {
-                return isMigrated;
+                return Objects.firstNonNull(isMigrated, false);
             }
 
             @Override
             public Boolean isNotifiedForInvoices() {
-                return isNotifiedForInvoices;
+                return Objects.firstNonNull(isNotifiedForInvoices, false);
             }
 
             @Override
@@ -119,7 +120,13 @@ public class AccountJson extends AccountJsonSimple {
 
             @Override
             public Integer getFirstNameLength() {
-                return length;
+                if (length == null && name == null) {
+                    return 0;
+                } else if (length == null) {
+                    return name.length();
+                } else {
+                    return length;
+                }
             }
 
             @Override
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/PaymentMethodJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/PaymentMethodJson.java
index f4c5657..29dbb7b 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/PaymentMethodJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/PaymentMethodJson.java
@@ -73,7 +73,7 @@ public class PaymentMethodJson {
         return new PaymentMethodJson(in.getId().toString(), account.getId().toString(), isDefault, in.getPluginName(), detail);
     }
 
-    public PaymentMethod toPaymentMethod() {
+    public PaymentMethod toPaymentMethod(final String accountId) {
         return new PaymentMethod() {
             @Override
             public Boolean isActive() {
@@ -102,7 +102,7 @@ public class PaymentMethodJson {
 
             @Override
             public UUID getAccountId() {
-                return accountId != null ? UUID.fromString(accountId) : null;
+                return UUID.fromString(accountId);
             }
 
             @Override
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
index 6b5ae86..2a7f6bc 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
@@ -361,8 +361,6 @@ public class AccountResource extends JaxRsResourceBase {
     @Path("/{accountId:\\w+-\\w+-\\w+-\\w+-\\w+}/" + PAYMENTS)
     @Produces(APPLICATION_JSON)
     public Response getPayments(@PathParam("accountId") final String accountId,
-                                @QueryParam(QUERY_PAYMENT_LAST4_CC) final String last4CC,
-                                @QueryParam(QUERY_PAYMENT_NAME_ON_CC) final String nameOnCC,
                                 @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException {
         final List<Payment> payments = paymentApi.getAccountPayments(UUID.fromString(accountId), context.createContext(request));
         final List<PaymentJsonSimple> result = new ArrayList<PaymentJsonSimple>(payments.size());
@@ -377,6 +375,7 @@ public class AccountResource extends JaxRsResourceBase {
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
     public Response createPaymentMethod(final PaymentMethodJson json,
+                                        @PathParam("accountId") final String accountId,
                                         @QueryParam(QUERY_PAYMENT_METHOD_IS_DEFAULT) @DefaultValue("false") final Boolean isDefault,
                                         @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                         @HeaderParam(HDR_REASON) final String reason,
@@ -385,7 +384,7 @@ public class AccountResource extends JaxRsResourceBase {
                                         @javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException, PaymentApiException {
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
 
-        final PaymentMethod data = json.toPaymentMethod();
+        final PaymentMethod data = json.toPaymentMethod(accountId);
         final Account account = accountApi.getAccountById(data.getAccountId(), callContext);
 
         final UUID paymentMethodId = paymentApi.addPaymentMethod(data.getPluginName(), account, isDefault, data.getPluginDetail(), callContext);
@@ -396,14 +395,11 @@ public class AccountResource extends JaxRsResourceBase {
     @Path("/{accountId:" + UUID_PATTERN + "}/" + PAYMENT_METHODS)
     @Produces(APPLICATION_JSON)
     public Response getPaymentMethods(@PathParam("accountId") final String accountId,
-                                      @QueryParam(QUERY_PAYMENT_METHOD_PLUGIN_INFO) @DefaultValue("false") final Boolean withPluginInfo,
-                                      @QueryParam(QUERY_PAYMENT_LAST4_CC) final String last4CC,
-                                      @QueryParam(QUERY_PAYMENT_NAME_ON_CC) final String nameOnCC,
                                       @javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException, PaymentApiException {
         final TenantContext tenantContext = context.createContext(request);
 
         final Account account = accountApi.getAccountById(UUID.fromString(accountId), tenantContext);
-        final List<PaymentMethod> methods = paymentApi.getPaymentMethods(account, withPluginInfo, tenantContext);
+        final List<PaymentMethod> methods = paymentApi.getPaymentMethods(account, tenantContext);
         final List<PaymentMethodJson> json = new ArrayList<PaymentMethodJson>(Collections2.transform(methods, new Function<PaymentMethod, PaymentMethodJson>() {
             @Override
             public PaymentMethodJson apply(final PaymentMethod input) {
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
index 6333cfd..74b20ab 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
@@ -60,8 +60,6 @@ public interface JaxrsResource {
     public static final String QUERY_INVOICE_WITH_ITEMS = "withItems";
 
     public static final String QUERY_PAYMENT_EXTERNAL = "externalPayment";
-    public static final String QUERY_PAYMENT_LAST4_CC = "last4CC";
-    public static final String QUERY_PAYMENT_NAME_ON_CC = "nameOnCC";
     public static final String QUERY_PAYMENT_WITH_REFUNDS_AND_CHARGEBACKS = "withRefundsAndChargebacks";
 
     public static final String QUERY_TAGS = "tagList";
@@ -133,5 +131,9 @@ public interface JaxrsResource {
     public static final String EXPORT = "export";
     public static final String EXPORT_PATH = PREFIX + "/" + EXPORT;
 
+    public static final String PLUGINS = "plugins";
+    // No PREFIX here!
+    public static final String PLUGINS_PATH = "/" + PLUGINS;
+
     public static final String CBA_REBALANCING = "cbaRebalancing";
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentMethodResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentMethodResource.java
index 3356185..e421fbb 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentMethodResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentMethodResource.java
@@ -19,12 +19,10 @@ package com.ning.billing.jaxrs.resources;
 import java.util.UUID;
 
 import javax.servlet.http.HttpServletRequest;
-import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.DefaultValue;
 import javax.ws.rs.GET;
 import javax.ws.rs.HeaderParam;
-import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
@@ -81,37 +79,13 @@ public class PaymentMethodResource extends JaxRsResourceBase {
                                      @javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException, PaymentApiException {
         final TenantContext tenantContext = context.createContext(request);
 
-        PaymentMethod paymentMethod = paymentApi.getPaymentMethodById(UUID.fromString(paymentMethodId), tenantContext);
+        final PaymentMethod paymentMethod = paymentApi.getPaymentMethodById(UUID.fromString(paymentMethodId), withPluginInfo, tenantContext);
         final Account account = accountApi.getAccountById(paymentMethod.getAccountId(), tenantContext);
-        if (withPluginInfo) {
-            paymentMethod = paymentApi.getPaymentMethod(account, paymentMethod.getId(), true, tenantContext);
-        }
         final PaymentMethodJson json = PaymentMethodJson.toPaymentMethodJson(account, paymentMethod);
 
         return Response.status(Status.OK).entity(json).build();
     }
 
-    @PUT
-    @Consumes(APPLICATION_JSON)
-    @Produces(APPLICATION_JSON)
-    @Path("/{paymentMethodId:" + UUID_PATTERN + "}")
-    public Response updatePaymentMethod(final PaymentMethodJson json,
-                                        @PathParam("paymentMethodId") final String paymentMethodId,
-                                        @HeaderParam(HDR_CREATED_BY) final String createdBy,
-                                        @HeaderParam(HDR_REASON) final String reason,
-                                        @HeaderParam(HDR_COMMENT) final String comment,
-                                        @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
-        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
-
-        final PaymentMethod input = json.toPaymentMethod();
-        final PaymentMethod paymentMethod = paymentApi.getPaymentMethodById(UUID.fromString(paymentMethodId), callContext);
-        final Account account = accountApi.getAccountById(paymentMethod.getAccountId(), callContext);
-
-        paymentApi.updatePaymentMethod(account, paymentMethod.getId(), input.getPluginDetail(), callContext);
-
-        return getPaymentMethod(paymentMethod.getId().toString(), false, request);
-    }
-
     @DELETE
     @Produces(APPLICATION_JSON)
     @Path("/{paymentMethodId:" + UUID_PATTERN + "}")
@@ -123,7 +97,7 @@ public class PaymentMethodResource extends JaxRsResourceBase {
                                         @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
 
-        final PaymentMethod paymentMethod = paymentApi.getPaymentMethodById(UUID.fromString(paymentMethodId), callContext);
+        final PaymentMethod paymentMethod = paymentApi.getPaymentMethodById(UUID.fromString(paymentMethodId), false, callContext);
         final Account account = accountApi.getAccountById(paymentMethod.getAccountId(), callContext);
 
         paymentApi.deletedPaymentMethod(account, UUID.fromString(paymentMethodId), deleteDefaultPaymentMethodWithAutoPayOff, callContext);
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PluginResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PluginResource.java
new file mode 100644
index 0000000..fae67ff
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PluginResource.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.jaxrs.resources;
+
+import java.io.IOException;
+
+import javax.servlet.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;
+import javax.ws.rs.OPTIONS;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+
+import com.ning.billing.jaxrs.util.Context;
+import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
+import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.api.CustomFieldUserApi;
+import com.ning.billing.util.api.TagUserApi;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.google.inject.name.Named;
+
+@Singleton
+@Path(JaxrsResource.PLUGINS_PATH + "{subResources:.*}")
+public class PluginResource extends JaxRsResourceBase {
+
+    private final HttpServlet osgiServlet;
+
+    @Inject
+    public PluginResource(@Named("osgi") final HttpServlet osgiServlet,
+                          final JaxrsUriBuilder uriBuilder,
+                          final TagUserApi tagUserApi,
+                          final CustomFieldUserApi customFieldUserApi,
+                          final AuditUserApi auditUserApi,
+                          final Context context) {
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, context);
+        this.osgiServlet = osgiServlet;
+    }
+
+    @DELETE
+    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
+    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
+    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
+    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
+    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
+    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 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 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);
+        }
+    }
+}
diff --git a/jaxrs/src/test/java/com/ning/billing/jaxrs/JaxrsTestSuiteNoDB.java b/jaxrs/src/test/java/com/ning/billing/jaxrs/JaxrsTestSuiteNoDB.java
index a40ec50..f1b3400 100644
--- a/jaxrs/src/test/java/com/ning/billing/jaxrs/JaxrsTestSuiteNoDB.java
+++ b/jaxrs/src/test/java/com/ning/billing/jaxrs/JaxrsTestSuiteNoDB.java
@@ -32,7 +32,7 @@ public abstract class JaxrsTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     protected ObjectMapper mapper;
 
     @BeforeClass(groups = "fast")
-    protected void setup() throws Exception {
+    protected void beforeClass() throws Exception {
         final Injector injector = Guice.createInjector(new TestJaxrsModuleNoDB());
         injector.injectMembers(this);
     }

junction/pom.xml 15(+0 -15)

diff --git a/junction/pom.xml b/junction/pom.xml
index 2a256ed..ceb9349 100644
--- a/junction/pom.xml
+++ b/junction/pom.xml
@@ -114,19 +114,4 @@
             <scope>test</scope>
         </dependency>
     </dependencies>
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-jar-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>test-jar</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-        </plugins>
-    </build>
 </project>
diff --git a/junction/src/main/java/com/ning/billing/junction/glue/DefaultJunctionModule.java b/junction/src/main/java/com/ning/billing/junction/glue/DefaultJunctionModule.java
index f0308b3..ce80386 100644
--- a/junction/src/main/java/com/ning/billing/junction/glue/DefaultJunctionModule.java
+++ b/junction/src/main/java/com/ning/billing/junction/glue/DefaultJunctionModule.java
@@ -16,7 +16,7 @@
 
 package com.ning.billing.junction.glue;
 
-import org.skife.jdbi.v2.IDBI;
+import org.skife.config.ConfigSource;
 
 import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
@@ -25,7 +25,6 @@ import com.ning.billing.junction.api.svcs.DefaultInternalBlockingApi;
 import com.ning.billing.junction.block.BlockingChecker;
 import com.ning.billing.junction.block.DefaultBlockingChecker;
 import com.ning.billing.junction.dao.BlockingStateDao;
-import com.ning.billing.junction.dao.BlockingStateSqlDao;
 import com.ning.billing.junction.dao.DefaultBlockingStateDao;
 import com.ning.billing.junction.plumbing.api.BlockingAccountUserApi;
 import com.ning.billing.junction.plumbing.api.BlockingEntitlementUserApi;
@@ -33,14 +32,17 @@ import com.ning.billing.junction.plumbing.billing.BlockingCalculator;
 import com.ning.billing.junction.plumbing.billing.DefaultInternalBillingApi;
 import com.ning.billing.util.svcapi.junction.BillingInternalApi;
 import com.ning.billing.util.svcapi.junction.BlockingInternalApi;
-import com.ning.billing.util.svcapi.junction.DefaultBlockingState;
 
 import com.google.inject.AbstractModule;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
 
 public class DefaultJunctionModule extends AbstractModule implements JunctionModule {
 
+    protected final ConfigSource configSource;
+
+    public DefaultJunctionModule(final ConfigSource configSource) {
+        this.configSource = configSource;
+    }
+
     @Override
     protected void configure() {
         // External
@@ -57,7 +59,6 @@ public class DefaultJunctionModule extends AbstractModule implements JunctionMod
 
     public void installBlockingChecker() {
         bind(BlockingChecker.class).to(DefaultBlockingChecker.class).asEagerSingleton();
-
     }
 
     public void installBillingApi() {
diff --git a/junction/src/test/java/com/ning/billing/junction/api/blocking/TestBlockingApi.java b/junction/src/test/java/com/ning/billing/junction/api/blocking/TestBlockingApi.java
index af0d587..5998311 100644
--- a/junction/src/test/java/com/ning/billing/junction/api/blocking/TestBlockingApi.java
+++ b/junction/src/test/java/com/ning/billing/junction/api/blocking/TestBlockingApi.java
@@ -33,7 +33,8 @@ import com.ning.billing.util.svcapi.junction.DefaultBlockingState;
 public class TestBlockingApi extends JunctionTestSuiteWithEmbeddedDB {
 
     @BeforeMethod(groups = "slow")
-    public void clean() {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         clock.resetDeltaFromReality();
     }
 
diff --git a/junction/src/test/java/com/ning/billing/junction/blocking/TestBlockingChecker.java b/junction/src/test/java/com/ning/billing/junction/blocking/TestBlockingChecker.java
index 39a01ec..8e4ced1 100644
--- a/junction/src/test/java/com/ning/billing/junction/blocking/TestBlockingChecker.java
+++ b/junction/src/test/java/com/ning/billing/junction/blocking/TestBlockingChecker.java
@@ -43,7 +43,8 @@ public class TestBlockingChecker extends JunctionTestSuiteNoDB {
     private Subscription subscription;
 
     @BeforeMethod(groups = "fast")
-    public void setupTest() {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         final UUID accountId = UUID.randomUUID();
         account = Mockito.mock(Account.class);
         Mockito.when(account.getId()).thenReturn(accountId);
diff --git a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModule.java b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModule.java
index 964344c..c0ad3e5 100644
--- a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModule.java
+++ b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModule.java
@@ -16,10 +16,7 @@
 
 package com.ning.billing.junction.glue;
 
-import java.util.Properties;
-
 import org.skife.config.ConfigSource;
-import org.skife.config.SimplePropertyConfigSource;
 
 import com.ning.billing.catalog.MockCatalogModule;
 import com.ning.billing.mock.glue.MockAccountModule;
@@ -29,24 +26,15 @@ import com.ning.billing.util.glue.CallContextModule;
 
 public class TestJunctionModule extends DefaultJunctionModule {
 
-    protected final ConfigSource configSource;
-
-    public TestJunctionModule() {
-        final Properties properties = new Properties(System.getProperties());
-        // Speed up the bus
-        properties.put("killbill.billing.util.persistent.bus.sleep", "10");
-        properties.put("killbill.billing.util.persistent.bus.nbThreads", "1");
-        configSource = new SimplePropertyConfigSource(properties);
-
-        // Ignore ehcache checks. Unfortunately, ehcache looks at system properties directly...
-        System.setProperty("net.sf.ehcache.skipUpdateCheck", "true");
+    public TestJunctionModule(final ConfigSource configSource) {
+        super(configSource);
     }
 
     @Override
     protected void configure() {
         super.configure();
 
-        install(new CacheModule());
+        install(new CacheModule(configSource));
         install(new CallContextModule());
         install(new MockAccountModule());
         install(new MockCatalogModule());
diff --git a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleNoDB.java b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleNoDB.java
index f188d30..2139fe6 100644
--- a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleNoDB.java
+++ b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleNoDB.java
@@ -16,6 +16,8 @@
 
 package com.ning.billing.junction.glue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.GuicyKillbillTestNoDBModule;
 import com.ning.billing.junction.dao.BlockingStateDao;
 import com.ning.billing.junction.dao.MockBlockingStateDao;
@@ -25,6 +27,10 @@ import com.ning.billing.util.bus.InMemoryBusModule;
 
 public class TestJunctionModuleNoDB extends TestJunctionModule {
 
+    public TestJunctionModuleNoDB(final ConfigSource configSource) {
+        super(configSource);
+    }
+
     @Override
     public void installBlockingStateDao() {
         bind(BlockingStateDao.class).toInstance(new MockBlockingStateDao());
diff --git a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleWithEmbeddedDB.java b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleWithEmbeddedDB.java
index 5cf5ffc..ab54197 100644
--- a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleWithEmbeddedDB.java
+++ b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleWithEmbeddedDB.java
@@ -16,6 +16,8 @@
 
 package com.ning.billing.junction.glue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.GuicyKillbillTestWithEmbeddedDBModule;
 import com.ning.billing.util.glue.BusModule;
 import com.ning.billing.util.glue.NonEntityDaoModule;
@@ -23,6 +25,10 @@ import com.ning.billing.util.glue.TagStoreModule;
 
 public class TestJunctionModuleWithEmbeddedDB extends TestJunctionModule {
 
+    public TestJunctionModuleWithEmbeddedDB(final ConfigSource configSource) {
+        super(configSource);
+    }
+
     @Override
     protected void configure() {
         super.configure();
diff --git a/junction/src/test/java/com/ning/billing/junction/JunctionTestSuiteNoDB.java b/junction/src/test/java/com/ning/billing/junction/JunctionTestSuiteNoDB.java
index 65df1f5..d9c4aa9 100644
--- a/junction/src/test/java/com/ning/billing/junction/JunctionTestSuiteNoDB.java
+++ b/junction/src/test/java/com/ning/billing/junction/JunctionTestSuiteNoDB.java
@@ -72,18 +72,18 @@ public abstract class JunctionTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     protected TagInternalApi tagInternalApi;
 
     @BeforeClass(groups = "fast")
-    protected void setup() throws Exception {
-        final Injector injector = Guice.createInjector(new TestJunctionModuleNoDB());
+    protected void beforeClass() throws Exception {
+        final Injector injector = Guice.createInjector(new TestJunctionModuleNoDB(configSource));
         injector.injectMembers(this);
     }
 
     @BeforeMethod(groups = "fast")
-    public void setupTest() {
+    public void beforeMethod() throws Exception {
         bus.start();
     }
 
     @AfterMethod(groups = "fast")
-    public void cleanupTest() {
+    public void afterMethod() {
         bus.stop();
     }
 }
diff --git a/junction/src/test/java/com/ning/billing/junction/JunctionTestSuiteWithEmbeddedDB.java b/junction/src/test/java/com/ning/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
index ebd19a7..afa132c 100644
--- a/junction/src/test/java/com/ning/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
+++ b/junction/src/test/java/com/ning/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
@@ -68,19 +68,20 @@ public abstract class JunctionTestSuiteWithEmbeddedDB extends GuicyKillbillTestS
     @Inject
     protected TagInternalApi tagInternalApi;
 
-    @BeforeClass(groups = "fast")
-    protected void setup() throws Exception {
-        final Injector injector = Guice.createInjector(new TestJunctionModuleWithEmbeddedDB());
+    @BeforeClass(groups = "slow")
+    protected void beforeClass() throws Exception {
+        final Injector injector = Guice.createInjector(new TestJunctionModuleWithEmbeddedDB(configSource));
         injector.injectMembers(this);
     }
 
-    @BeforeMethod(groups = "fast")
-    public void setupTest() {
+    @BeforeMethod(groups = "slow")
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         bus.start();
     }
 
-    @AfterMethod(groups = "fast")
-    public void cleanupTest() {
+    @AfterMethod(groups = "slow")
+    public void afterMethod() {
         bus.stop();
     }
 }
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java
index 6c27161..9d339b9 100644
--- a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java
@@ -82,7 +82,8 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
     private MockCatalog catalog;
 
     @BeforeMethod(groups = "fast")
-    public void setupEveryTime() throws EntitlementUserApiException {
+    public void beforeMethod() throws Exception  {
+        super.beforeMethod();
         final SubscriptionBundle bundle = Mockito.mock(SubscriptionBundle.class);
         Mockito.when(bundle.getId()).thenReturn(bunId);
         final List<SubscriptionBundle> bundles = ImmutableList.<SubscriptionBundle>of(bundle);
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java
index c435d42..9b56a46 100644
--- a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java
@@ -70,7 +70,8 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
     private Subscription subscription4;
 
     @BeforeMethod(groups = "fast")
-    public void setUpTest() throws Exception {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         account = Mockito.mock(Account.class);
         subscription1 = Mockito.mock(Subscription.class);
         subscription2 = Mockito.mock(Subscription.class);

osgi/pom.xml 39(+18 -21)

diff --git a/osgi/pom.xml b/osgi/pom.xml
index c850bbb..fb15528 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,14 @@
             <artifactId>org.apache.felix.framework</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>
@@ -44,11 +53,19 @@
             <artifactId>killbill-util</artifactId>
         </dependency>
         <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-osgi-bundles-lib-killbill</artifactId>
+        </dependency>
+        <dependency>
             <groupId>com.google.inject</groupId>
             <artifactId>guice</artifactId>
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.skife.config</groupId>
             <artifactId>config-magic</artifactId>
         </dependency>
@@ -60,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>
@@ -82,19 +94,4 @@
             <scope>test</scope>
         </dependency>
     </dependencies>
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-jar-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>test-jar</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-        </plugins>
-    </build>
 </project>
diff --git a/osgi/src/main/java/com/ning/billing/osgi/ContextClassLoaderHelper.java b/osgi/src/main/java/com/ning/billing/osgi/ContextClassLoaderHelper.java
new file mode 100644
index 0000000..3e4660b
--- /dev/null
+++ b/osgi/src/main/java/com/ning/billing/osgi/ContextClassLoaderHelper.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.osgi;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class ContextClassLoaderHelper {
+
+
+    /*
+      http://impalablog.blogspot.com/2008/10/using-threads-context-class-loader-in.html:
+
+      "Many existing java libraries are designed to run inside a container (J2EE container, Applet container etc).
+      Such containers explicitly define execution boundaries between the various components running within the container.
+      The container controls the execution boundaries and knows when a boundary is being crossed from one component to the next.
+
+      This level of boundary control allows a container to switch the context of a thread when a component boundary is crossed.
+      Typically when a container detects a context switch it will set the context class loader on the thread to a class loader associated with the component which is being entered.
+      When the component is exited then the container will switch the context class loader back to the previous context class loader.
+
+      The OSGi Framework specification does not define what the context class loader should be set to and does not define when it should be switched.
+      Part of the problem is the Framework is not always aware of when a component boundary is crossed."
+
+      => So our current implementation is to proxy all calls from Killbill to OSGI registered services, and set/unset classloader before/after entering the call
+
+    */
+
+    public static <T> T getWrappedServiceWithCorrectContextClassLoader(final T service) {
+
+        final Class<T> serviceClass = (Class<T>) service.getClass();
+        final List<Class> allServiceInterfaces = getAllInterfaces(serviceClass);
+        final Class[] serviceClassInterfaces = allServiceInterfaces.toArray(new Class[allServiceInterfaces.size()]);
+
+        final InvocationHandler handler = new InvocationHandler() {
+            @Override
+            public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
+                final ClassLoader initialContextClassLoader = Thread.currentThread().getContextClassLoader();
+                try {
+                    Thread.currentThread().setContextClassLoader(serviceClass.getClassLoader());
+                    return method.invoke(service, args);
+                } catch (InvocationTargetException e) {
+                    if (e.getCause() != null) {
+                        throw e.getCause();
+                    } else {
+                        throw new RuntimeException(e);
+                    }
+                } finally {
+                    Thread.currentThread().setContextClassLoader(initialContextClassLoader);
+                }
+            }
+        };
+        final T wrappedService = (T) Proxy.newProxyInstance(serviceClass.getClassLoader(),
+                                                            serviceClassInterfaces,
+                                                            handler);
+        return wrappedService;
+    }
+
+
+    // From apache-commons
+    private static List getAllInterfaces(Class cls) {
+        if (cls == null) {
+            return null;
+        }
+        List list = new ArrayList();
+        while (cls != null) {
+            Class[] interfaces = cls.getInterfaces();
+            for (int i = 0; i < interfaces.length; i++) {
+                if (list.contains(interfaces[i]) == false) {
+                    list.add(interfaces[i]);
+                }
+                List superInterfaces = getAllInterfaces(interfaces[i]);
+                for (Iterator it = superInterfaces.iterator(); it.hasNext(); ) {
+                    Class intface = (Class) it.next();
+                    if (list.contains(intface) == false) {
+                        list.add(intface);
+                    }
+                }
+            }
+            cls = cls.getSuperclass();
+        }
+        return list;
+    }
+}
diff --git a/osgi/src/main/java/com/ning/billing/osgi/DefaultOSGIKillbill.java b/osgi/src/main/java/com/ning/billing/osgi/DefaultOSGIKillbill.java
new file mode 100644
index 0000000..ea43446
--- /dev/null
+++ b/osgi/src/main/java/com/ning/billing/osgi/DefaultOSGIKillbill.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.osgi;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.sql.DataSource;
+
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.analytics.api.sanity.AnalyticsSanityApi;
+import com.ning.billing.analytics.api.user.AnalyticsUserApi;
+import com.ning.billing.catalog.api.CatalogUserApi;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
+import com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi;
+import com.ning.billing.entitlement.api.transfer.EntitlementTransferApi;
+import com.ning.billing.entitlement.api.user.EntitlementUserApi;
+import com.ning.billing.invoice.api.InvoiceMigrationApi;
+import com.ning.billing.invoice.api.InvoicePaymentApi;
+import com.ning.billing.invoice.api.InvoiceUserApi;
+import com.ning.billing.osgi.api.OSGIKillbill;
+import com.ning.billing.osgi.api.config.PluginConfigServiceApi;
+import com.ning.billing.osgi.glue.DefaultOSGIModule;
+import com.ning.billing.overdue.OverdueUserApi;
+import com.ning.billing.payment.api.PaymentApi;
+import com.ning.billing.tenant.api.TenantUserApi;
+import com.ning.billing.usage.api.UsageUserApi;
+import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.api.CustomFieldUserApi;
+import com.ning.billing.util.api.ExportUserApi;
+import com.ning.billing.util.api.TagUserApi;
+
+public class DefaultOSGIKillbill implements OSGIKillbill {
+
+    private final AccountUserApi accountUserApi;
+    private final AnalyticsSanityApi analyticsSanityApi;
+    private final AnalyticsUserApi analyticsUserApi;
+    private final CatalogUserApi catalogUserApi;
+    private final EntitlementMigrationApi entitlementMigrationApi;
+    private final EntitlementTimelineApi entitlementTimelineApi;
+    private final EntitlementTransferApi entitlementTransferApi;
+    private final EntitlementUserApi entitlementUserApi;
+    private final InvoiceMigrationApi invoiceMigrationApi;
+    private final InvoicePaymentApi invoicePaymentApi;
+    private final InvoiceUserApi invoiceUserApi;
+    private final OverdueUserApi overdueUserApi;
+    private final PaymentApi paymentApi;
+    private final TenantUserApi tenantUserApi;
+    private final UsageUserApi usageUserApi;
+    private final AuditUserApi auditUserApi;
+    private final CustomFieldUserApi customFieldUserApi;
+    private final ExportUserApi exportUserApi;
+    private final TagUserApi tagUserApi;
+    private final PluginConfigServiceApi configServiceApi;
+
+    @Inject
+    public DefaultOSGIKillbill(final AccountUserApi accountUserApi,
+                               final AnalyticsSanityApi analyticsSanityApi,
+                               final AnalyticsUserApi analyticsUserApi,
+                               final CatalogUserApi catalogUserApi,
+                               final EntitlementMigrationApi entitlementMigrationApi,
+                               final EntitlementTimelineApi entitlementTimelineApi,
+                               final EntitlementTransferApi entitlementTransferApi,
+                               final EntitlementUserApi entitlementUserApi,
+                               final InvoiceMigrationApi invoiceMigrationApi,
+                               final InvoicePaymentApi invoicePaymentApi,
+                               final InvoiceUserApi invoiceUserApi,
+                               final OverdueUserApi overdueUserApi,
+                               final PaymentApi paymentApi,
+                               final TenantUserApi tenantUserApi,
+                               final UsageUserApi usageUserApi,
+                               final AuditUserApi auditUserApi,
+                               final CustomFieldUserApi customFieldUserApi,
+                               final ExportUserApi exportUserApi,
+                               final TagUserApi tagUserApi,
+                               final PluginConfigServiceApi configServiceApi) {
+        this.accountUserApi = accountUserApi;
+        this.analyticsSanityApi = analyticsSanityApi;
+        this.analyticsUserApi = analyticsUserApi;
+        this.catalogUserApi = catalogUserApi;
+        this.entitlementMigrationApi = entitlementMigrationApi;
+        this.entitlementTimelineApi = entitlementTimelineApi;
+        this.entitlementTransferApi = entitlementTransferApi;
+        this.entitlementUserApi = entitlementUserApi;
+        this.invoiceMigrationApi = invoiceMigrationApi;
+        this.invoicePaymentApi = invoicePaymentApi;
+        this.invoiceUserApi = invoiceUserApi;
+        this.overdueUserApi = overdueUserApi;
+        this.paymentApi = paymentApi;
+        this.tenantUserApi = tenantUserApi;
+        this.usageUserApi = usageUserApi;
+        this.auditUserApi = auditUserApi;
+        this.customFieldUserApi = customFieldUserApi;
+        this.exportUserApi = exportUserApi;
+        this.tagUserApi = tagUserApi;
+        this.configServiceApi = configServiceApi;
+    }
+
+    @Override
+    public AccountUserApi getAccountUserApi() {
+        return accountUserApi;
+    }
+
+    @Override
+    public AnalyticsSanityApi getAnalyticsSanityApi() {
+        return analyticsSanityApi;
+    }
+
+    @Override
+    public AnalyticsUserApi getAnalyticsUserApi() {
+        return analyticsUserApi;
+    }
+
+    @Override
+    public CatalogUserApi getCatalogUserApi() {
+        return catalogUserApi;
+    }
+
+    @Override
+    public EntitlementMigrationApi getEntitlementMigrationApi() {
+        return entitlementMigrationApi;
+    }
+
+    @Override
+    public EntitlementTimelineApi getEntitlementTimelineApi() {
+        return entitlementTimelineApi;
+    }
+
+    @Override
+    public EntitlementTransferApi getEntitlementTransferApi() {
+        return entitlementTransferApi;
+    }
+
+    @Override
+    public EntitlementUserApi getEntitlementUserApi() {
+        return entitlementUserApi;
+    }
+
+    @Override
+    public InvoiceMigrationApi getInvoiceMigrationApi() {
+        return invoiceMigrationApi;
+    }
+
+    @Override
+    public InvoicePaymentApi getInvoicePaymentApi() {
+        return invoicePaymentApi;
+    }
+
+    @Override
+    public InvoiceUserApi getInvoiceUserApi() {
+        return invoiceUserApi;
+    }
+
+    @Override
+    public OverdueUserApi getOverdueUserApi() {
+        return overdueUserApi;
+    }
+
+    @Override
+    public PaymentApi getPaymentApi() {
+        return paymentApi;
+    }
+
+    @Override
+    public TenantUserApi getTenantUserApi() {
+        return tenantUserApi;
+    }
+
+    @Override
+    public UsageUserApi getUsageUserApi() {
+        return usageUserApi;
+    }
+
+    @Override
+    public AuditUserApi getAuditUserApi() {
+        return auditUserApi;
+    }
+
+    @Override
+    public CustomFieldUserApi getCustomFieldUserApi() {
+        return customFieldUserApi;
+    }
+
+    @Override
+    public ExportUserApi getExportUserApi() {
+        return exportUserApi;
+    }
+
+    @Override
+    public TagUserApi getTagUserApi() {
+        return tagUserApi;
+    }
+
+    @Override
+    public PluginConfigServiceApi getPluginConfigServiceApi() {
+        return configServiceApi;
+    }
+}
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 8091eff..f6ddfa0 100644
--- a/osgi/src/main/java/com/ning/billing/osgi/DefaultOSGIService.java
+++ b/osgi/src/main/java/com/ning/billing/osgi/DefaultOSGIService.java
@@ -18,31 +18,22 @@ package com.ning.billing.osgi;
 
 import java.io.File;
 import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
 import java.util.Map;
 
 import javax.inject.Inject;
 
 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.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.OSGIService;
 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.util.config.OSGIConfig;
 
@@ -54,20 +45,19 @@ public class DefaultOSGIService implements OSGIService {
 
     private static final Logger logger = LoggerFactory.getLogger(DefaultOSGIService.class);
 
-    private Framework framework;
-
     private final OSGIConfig osgiConfig;
-    private final PluginFinder pluginFinder;
-    private final PluginConfigServiceApi pluginConfigServiceApi;
     private final KillbillActivator killbillActivator;
+    private final FileInstall fileInstall;
+
+    private Framework framework;
 
     @Inject
-    public DefaultOSGIService(final OSGIConfig osgiConfig, final PluginFinder pluginFinder,
-                              final PluginConfigServiceApi pluginConfigServiceApi, final KillbillActivator killbillActivator) {
+    public DefaultOSGIService(final OSGIConfig osgiConfig, final PureOSGIBundleFinder osgiBundleFinder,
+                              final PluginFinder pluginFinder, final PluginConfigServiceApi pluginConfigServiceApi,
+                              final KillbillActivator killbillActivator) {
         this.osgiConfig = osgiConfig;
-        this.pluginFinder = pluginFinder;
-        this.pluginConfigServiceApi = pluginConfigServiceApi;
         this.killbillActivator = killbillActivator;
+        this.fileInstall = new FileInstall(osgiBundleFinder, pluginFinder, pluginConfigServiceApi);
         this.framework = null;
     }
 
@@ -87,29 +77,30 @@ public class DefaultOSGIService implements OSGIService {
             framework.start();
 
             // This will call the start() method for the bundles
-            installAndStartBundles(framework);
+            fileInstall.installAndStartBundles(framework);
         } catch (BundleException e) {
             logger.error("Failed to initialize Killbill OSGIService", e);
         }
     }
 
-    @LifecycleHandlerType(LifecycleLevel.START_SERVICE)
-    public void startFramework() {
-    }
-
     @LifecycleHandlerType(LifecycleHandlerType.LifecycleLevel.REGISTER_EVENTS)
-    public void registerForExternalEvents() {
+    public void registerForExternalEvents() throws Exception {
     }
 
     @LifecycleHandlerType(LifecycleHandlerType.LifecycleLevel.UNREGISTER_EVENTS)
     public void unregisterForExternalEvents() {
     }
 
+    @LifecycleHandlerType(LifecycleLevel.START_SERVICE)
+    public void startFramework() {
+    }
+
     @LifecycleHandlerType(LifecycleLevel.STOP_SERVICE)
     public void stop() {
         try {
             framework.stop();
             framework.waitForStop(0);
+
         } catch (BundleException e) {
             logger.error("Failed to Stop Killbill OSGIService " + e.getMessage());
         } catch (InterruptedException e) {
@@ -117,45 +108,6 @@ public class DefaultOSGIService implements OSGIService {
         }
     }
 
-    private void installAndStartBundles(final Framework framework) throws BundleException {
-        try {
-            final BundleContext context = framework.getBundleContext();
-
-            // Install all bundles and create service mapping
-            final List<Bundle> installedBundles = new LinkedList<Bundle>();
-            installAllJavaBundles(context, installedBundles);
-            installAllJRubyBundles(context, installedBundles);
-
-            // Start all the bundles
-            for (final Bundle bundle : installedBundles) {
-                logger.info("Starting bundle {}", bundle.getLocation());
-                bundle.start();
-            }
-        } catch (PluginConfigException e) {
-            logger.error("Error while parsing plugin configurations", e);
-        }
-    }
-
-    private void installAllJavaBundles(final BundleContext context, final List<Bundle> installedBundles) throws PluginConfigException, BundleException {
-        final List<PluginJavaConfig> pluginJavaConfigs = pluginFinder.getLatestJavaPlugins();
-        for (final PluginJavaConfig cur : pluginJavaConfigs) {
-            logger.info("Installing Java bundle for plugin {} in {}", cur.getPluginName(), cur.getBundleJarPath());
-            final Bundle bundle = context.installBundle("file:" + cur.getBundleJarPath());
-            ((DefaultPluginConfigServiceApi) pluginConfigServiceApi).registerBundle(bundle.getBundleId(), cur);
-            installedBundles.add(bundle);
-        }
-    }
-
-    private void installAllJRubyBundles(final BundleContext context, final List<Bundle> installedBundles) throws PluginConfigException, BundleException {
-        final List<PluginRubyConfig> pluginRubyConfigs = pluginFinder.getLatestRubyPlugins();
-        for (final PluginRubyConfig cur : pluginRubyConfigs) {
-            logger.info("Installing JRuby bundle for plugin {} in {}", cur.getPluginName(), cur.getRubyLoadDir());
-            final Bundle bundle = context.installBundle(osgiConfig.getJrubyBundlePath());
-            ((DefaultPluginConfigServiceApi) pluginConfigServiceApi).registerBundle(bundle.getBundleId(), cur);
-            installedBundles.add(bundle);
-        }
-    }
-
     private Framework createAndInitFramework() throws BundleException {
         final Map<String, String> config = new HashMap<String, String>();
         config.put("org.osgi.framework.system.packages.extra", osgiConfig.getSystemBundleExportPackages());
@@ -169,8 +121,11 @@ 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 only for now
+        // 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(killbillActivator));
 
         final Framework felix = new Felix(felixConfig);
         felix.init();
@@ -178,7 +133,7 @@ public class DefaultOSGIService implements OSGIService {
     }
 
     private void pruneOSGICache() {
-        final String path = osgiConfig.getOSGIBundleRootDir() + "/" + osgiConfig.getOSGIBundleCacheName();
+        final String path = osgiConfig.getOSGIBundleRootDir();
         deleteUnderDirectory(new File(path));
     }
 
@@ -197,8 +152,7 @@ public class DefaultOSGIService implements OSGIService {
                 for (final File f : files) {
                     if (f.isDirectory()) {
                         deleteDirectory(f, true);
-                    }
-                    if (!f.delete()) {
+                    } else if (!f.delete()) {
                         logger.warn("Unable to delete {}", f.getAbsolutePath());
                     }
                 }
@@ -207,6 +161,8 @@ public class DefaultOSGIService implements OSGIService {
             if (deleteParent) {
                 if (!path.delete()) {
                     logger.warn("Unable to delete {}", path.getAbsolutePath());
+                } else {
+                    logger.info("Deleted recursively {}", path.getAbsolutePath());
                 }
             }
         }
diff --git a/osgi/src/main/java/com/ning/billing/osgi/DefaultOSGIServiceDescriptor.java b/osgi/src/main/java/com/ning/billing/osgi/DefaultOSGIServiceDescriptor.java
new file mode 100644
index 0000000..dcbcd4d
--- /dev/null
+++ b/osgi/src/main/java/com/ning/billing/osgi/DefaultOSGIServiceDescriptor.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.osgi;
+
+import com.ning.billing.osgi.api.OSGIServiceDescriptor;
+
+public class DefaultOSGIServiceDescriptor implements OSGIServiceDescriptor {
+
+    private final String pluginSymbolicName;
+    private final String serviceName;
+    private final String serviceInfo;
+    private final String serviceType;
+
+    public DefaultOSGIServiceDescriptor(final String pluginSymbolicName, final String serviceName, final String serviceInfo, final String serviceType) {
+        this.pluginSymbolicName = pluginSymbolicName;
+        this.serviceName = serviceName;
+        this.serviceInfo = serviceInfo;
+        this.serviceType = serviceType;
+    }
+
+    @Override
+    public String getPluginSymbolicName() {
+        return pluginSymbolicName;
+    }
+
+    @Override
+    public String getServiceName() {
+        return serviceName;
+    }
+
+    @Override
+    public String getServiceInfo() {
+        return serviceInfo;
+    }
+
+    @Override
+    public String getServiceType() {
+        return serviceType;
+    }
+}
diff --git a/osgi/src/main/java/com/ning/billing/osgi/FileInstall.java b/osgi/src/main/java/com/ning/billing/osgi/FileInstall.java
new file mode 100644
index 0000000..d6138da
--- /dev/null
+++ b/osgi/src/main/java/com/ning/billing/osgi/FileInstall.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.osgi;
+
+import java.io.File;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.wiring.BundleRevision;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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;
+
+// TODO Pierre Should we leverage org.apache.felix.fileinstall.internal.FileInstall?
+public class FileInstall {
+
+    private static final Logger logger = LoggerFactory.getLogger(FileInstall.class);
+
+    private final PureOSGIBundleFinder osgiBundleFinder;
+    private final PluginFinder pluginFinder;
+    private final PluginConfigServiceApi pluginConfigServiceApi;
+
+    public FileInstall(final PureOSGIBundleFinder osgiBundleFinder, final PluginFinder pluginFinder, final PluginConfigServiceApi pluginConfigServiceApi) {
+        this.osgiBundleFinder = osgiBundleFinder;
+        this.pluginFinder = pluginFinder;
+        this.pluginConfigServiceApi = pluginConfigServiceApi;
+    }
+
+    public void installAndStartBundles(final Framework framework) {
+        try {
+            final BundleContext context = framework.getBundleContext();
+
+            final String jrubyBundlePath = findJrubyBundlePath();
+
+            // Install all bundles and create service mapping
+            final List<Bundle> installedBundles = new LinkedList<Bundle>();
+            installAllJavaBundles(context, installedBundles, jrubyBundlePath);
+            installAllJavaPluginBundles(context, installedBundles);
+            installAllJRubyPluginBundles(context, installedBundles, jrubyBundlePath);
+
+            // Start all the bundles
+            for (final Bundle bundle : installedBundles) {
+                startBundle(bundle);
+            }
+        } catch (PluginConfigException e) {
+            logger.error("Error while parsing plugin configurations", e);
+        } catch (BundleException e) {
+            logger.error("Error while parsing plugin configurations", e);
+        }
+    }
+
+    private void installAllJavaBundles(final BundleContext context, final List<Bundle> installedBundles, @Nullable final String jrubyBundlePath) throws PluginConfigException, BundleException {
+        final List<String> bundleJarPaths = osgiBundleFinder.getLatestBundles();
+        for (final String cur : bundleJarPaths) {
+            // Don't install the jruby.jar bundle
+            if (jrubyBundlePath != null && jrubyBundlePath.equals(cur)) {
+                continue;
+            }
+
+            logger.info("Installing Java OSGI bundle from {}", cur);
+            final Bundle bundle = context.installBundle("file:" + cur);
+            installedBundles.add(bundle);
+        }
+    }
+
+    private void installAllJavaPluginBundles(final BundleContext context, final List<Bundle> installedBundles) throws PluginConfigException, BundleException {
+        final List<PluginJavaConfig> pluginJavaConfigs = pluginFinder.getLatestJavaPlugins();
+        for (final PluginJavaConfig cur : pluginJavaConfigs) {
+            logger.info("Installing Java bundle for plugin {} from {}", cur.getPluginName(), cur.getBundleJarPath());
+            final Bundle bundle = context.installBundle("file:" + cur.getBundleJarPath());
+            ((DefaultPluginConfigServiceApi) pluginConfigServiceApi).registerBundle(bundle.getBundleId(), cur);
+            installedBundles.add(bundle);
+        }
+    }
+
+    private void installAllJRubyPluginBundles(final BundleContext context, final List<Bundle> installedBundles, @Nullable final String jrubyBundlePath) throws PluginConfigException, BundleException {
+        if (jrubyBundlePath == null) {
+            return;
+        }
+
+        final List<PluginRubyConfig> pluginRubyConfigs = pluginFinder.getLatestRubyPlugins();
+        for (final PluginRubyConfig cur : pluginRubyConfigs) {
+            logger.info("Installing JRuby bundle for plugin {} from {}", cur.getPluginName(), cur.getRubyLoadDir());
+            final Bundle bundle = context.installBundle("file:" + jrubyBundlePath);
+            ((DefaultPluginConfigServiceApi) pluginConfigServiceApi).registerBundle(bundle.getBundleId(), cur);
+            installedBundles.add(bundle);
+        }
+    }
+
+    private String findJrubyBundlePath() {
+        final String expectedPath = osgiBundleFinder.getPlatformOSGIBundlesRootDir() + "jruby.jar";
+        if (new File(expectedPath).isFile()) {
+            return expectedPath;
+        } else {
+            logger.warn("Unable to find the JRuby bundle for ruby plugins. If you want to install ruby plugins, copy the jar to " + expectedPath);
+            return null;
+        }
+    }
+
+    private boolean startBundle(final Bundle bundle) {
+        if (bundle.getState() == Bundle.UNINSTALLED) {
+            logger.info("Skipping uninstalled bundle {}", bundle.getLocation());
+        } else if (isFragment(bundle)) {
+            // Fragments can never be started.
+            logger.info("Skipping fragment bundle {}", bundle.getLocation());
+        } else {
+            logger.info("Starting bundle {}", bundle.getLocation());
+            try {
+                bundle.start();
+                return true;
+            } catch (BundleException e) {
+                logger.warn("Unable to start bundle", e);
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Check if a bundle is a fragment.
+     *
+     * @param bundle bundle to check
+     * @return true iff the bundle is a fragment
+     */
+    private boolean isFragment(final Bundle bundle) {
+        // Necessary cast on jdk7
+        final BundleRevision bundleRevision = (BundleRevision) bundle.adapt(BundleRevision.class);
+        return bundleRevision != null && (bundleRevision.getTypes() & BundleRevision.TYPE_FRAGMENT) != 0;
+    }
+}
diff --git a/osgi/src/main/java/com/ning/billing/osgi/glue/DefaultOSGIModule.java b/osgi/src/main/java/com/ning/billing/osgi/glue/DefaultOSGIModule.java
index b505635..a136ab0 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,28 +16,79 @@
 
 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.ConfigSource;
 import org.skife.config.ConfigurationObjectFactory;
 
+import com.ning.billing.osgi.DefaultOSGIKillbill;
+import com.ning.billing.osgi.DefaultOSGIService;
 import com.ning.billing.osgi.KillbillActivator;
+import com.ning.billing.osgi.KillbillEventObservable;
+import com.ning.billing.osgi.PureOSGIBundleFinder;
+import com.ning.billing.osgi.api.DefaultOSGIUserApi;
+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.OSGIUserApi;
 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;
 import com.ning.billing.osgi.pluginconf.PluginFinder;
 import com.ning.billing.util.config.OSGIConfig;
 
 import com.google.inject.AbstractModule;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Names;
 
 public class DefaultOSGIModule extends AbstractModule {
 
+    public static final String OSGI_NAMED = "osgi";
+
+    protected final ConfigSource configSource;
+
+    public DefaultOSGIModule(final ConfigSource configSource) {
+        this.configSource = configSource;
+    }
+
     protected void installConfig() {
-        final OSGIConfig config = new ConfigurationObjectFactory(System.getProperties()).build(OSGIConfig.class);
+        final OSGIConfig config = new ConfigurationObjectFactory(configSource).build(OSGIConfig.class);
         bind(OSGIConfig.class).toInstance(config);
+
+        final OSGIDataSourceConfig osgiDataSourceConfig = new ConfigurationObjectFactory(configSource).build(OSGIDataSourceConfig.class);
+        bind(OSGIDataSourceConfig.class).toInstance(osgiDataSourceConfig);
+    }
+
+    protected void installOSGIServlet() {
+        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();
+
+        bind(OSGIUserApi.class).to(DefaultOSGIUserApi.class).asEagerSingleton();
         bind(KillbillActivator.class).asEagerSingleton();
+        bind(PureOSGIBundleFinder.class).asEagerSingleton();
         bind(PluginFinder.class).asEagerSingleton();
         bind(PluginConfigServiceApi.class).to(DefaultPluginConfigServiceApi.class).asEagerSingleton();
+        bind(OSGIKillbill.class).to(DefaultOSGIKillbill.class).asEagerSingleton();
+        bind(OSGIDataSourceProvider.class).asEagerSingleton();
+        bind(KillbillEventObservable.class).asEagerSingleton();
+        bind(DataSource.class).annotatedWith(Names.named(OSGI_NAMED)).toProvider(OSGIDataSourceProvider.class).asEagerSingleton();
     }
 }
diff --git a/osgi/src/main/java/com/ning/billing/osgi/glue/OSGIDataSourceConfig.java b/osgi/src/main/java/com/ning/billing/osgi/glue/OSGIDataSourceConfig.java
new file mode 100644
index 0000000..ba06292
--- /dev/null
+++ b/osgi/src/main/java/com/ning/billing/osgi/glue/OSGIDataSourceConfig.java
@@ -0,0 +1,57 @@
+/*
+ * 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.glue;
+
+import org.skife.config.Config;
+import org.skife.config.Default;
+import org.skife.config.Description;
+import org.skife.config.TimeSpan;
+
+public interface OSGIDataSourceConfig {
+
+    static String DATA_SOURCE_PROP_PREFIX = "com.ning.billing.osgi.";
+
+    @Description("The jdbc url for the database")
+    @Config(DATA_SOURCE_PROP_PREFIX + "jdbc.url")
+    @Default("jdbc:mysql://127.0.0.1:3306/killbill")
+    String getJdbcUrl();
+
+    @Description("The jdbc user name for the database")
+    @Config(DATA_SOURCE_PROP_PREFIX + "jdbc.user")
+    @Default("root")
+    String getUsername();
+
+    @Description("The jdbc password for the database")
+    @Config(DATA_SOURCE_PROP_PREFIX + "jdbc.password")
+    @Default("root")
+    String getPassword();
+
+    @Description("The minimum allowed number of idle connections to the database")
+    @Config(DATA_SOURCE_PROP_PREFIX + "jdbc.minIdle")
+    @Default("1")
+    int getMinIdle();
+
+    @Description("The maximum allowed number of active connections to the database")
+    @Config(DATA_SOURCE_PROP_PREFIX + "jdbc.maxActive")
+    @Default("10")
+    int getMaxActive();
+
+    @Description("How long to wait before a connection attempt to the database is considered timed out")
+    @Config(DATA_SOURCE_PROP_PREFIX + "jdbc.connectionTimeout")
+    @Default("10s")
+    TimeSpan getConnectionTimeout();
+}
diff --git a/osgi/src/main/java/com/ning/billing/osgi/glue/OSGIDataSourceProvider.java b/osgi/src/main/java/com/ning/billing/osgi/glue/OSGIDataSourceProvider.java
new file mode 100644
index 0000000..455ff51
--- /dev/null
+++ b/osgi/src/main/java/com/ning/billing/osgi/glue/OSGIDataSourceProvider.java
@@ -0,0 +1,167 @@
+/*
+ * 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.glue;
+
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.sql.DataSource;
+
+import org.skife.config.TimeSpan;
+import org.skife.jdbi.v2.DBI;
+import org.skife.jdbi.v2.TimingCollector;
+import org.skife.jdbi.v2.tweak.SQLLog;
+import org.skife.jdbi.v2.tweak.TransactionHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.ning.billing.util.dao.DateTimeArgumentFactory;
+import com.ning.billing.util.dao.DateTimeZoneArgumentFactory;
+import com.ning.billing.util.dao.EnumArgumentFactory;
+import com.ning.billing.util.dao.LocalDateArgumentFactory;
+import com.ning.billing.util.dao.UUIDArgumentFactory;
+import com.ning.billing.util.dao.UuidMapper;
+
+import com.jolbox.bonecp.BoneCPConfig;
+import com.jolbox.bonecp.BoneCPDataSource;
+import com.mchange.v2.c3p0.ComboPooledDataSource;
+import com.yammer.metrics.core.MetricsRegistry;
+
+public class OSGIDataSourceProvider implements Provider<DataSource> {
+
+    private final OSGIDataSourceConfig config;
+    private final String jdbcUri;
+    private final String userName;
+    private final String userPwd;
+
+    private SQLLog sqlLog;
+
+    @Inject
+    public OSGIDataSourceProvider(final OSGIDataSourceConfig config) {
+        this.config = config;
+        this.jdbcUri = config.getJdbcUrl();
+        this.userName = config.getUsername();
+        this.userPwd = config.getPassword();
+    }
+
+
+    @com.google.inject.Inject(optional = true)
+    public void setSqlLog(final SQLLog sqlLog) {
+        this.sqlLog = sqlLog;
+    }
+
+    @Override
+    public DataSource get() {
+        final DataSource ds = getDataSource();
+
+        final DBI dbi = new DBI(ds);
+        dbi.registerArgumentFactory(new UUIDArgumentFactory());
+        dbi.registerArgumentFactory(new DateTimeZoneArgumentFactory());
+        dbi.registerArgumentFactory(new DateTimeArgumentFactory());
+        dbi.registerArgumentFactory(new LocalDateArgumentFactory());
+        dbi.registerArgumentFactory(new EnumArgumentFactory());
+        dbi.registerMapper(new UuidMapper());
+
+        if (sqlLog != null) {
+            dbi.setSQLLog(sqlLog);
+        }
+        return ds;
+    }
+
+    private DataSource getDataSource() {
+        final DataSource ds;
+
+        // TODO PIERRE DaoConfig is in the skeleton
+        final String dataSource = System.getProperty("com.ning.jetty.jdbi.datasource", "c3p0");
+        if (dataSource.equals("c3p0")) {
+            ds = getC3P0DataSource();
+        } else if (dataSource.equals("bonecp")) {
+            ds = getBoneCPDatSource();
+        } else {
+            throw new IllegalArgumentException("DataSource " + dataSource + " unsupported");
+        }
+
+        return ds;
+    }
+
+    private DataSource getBoneCPDatSource() {
+        final BoneCPConfig dbConfig = new BoneCPConfig();
+        dbConfig.setJdbcUrl(config.getJdbcUrl());
+        dbConfig.setUsername(config.getUsername());
+        dbConfig.setPassword(config.getPassword());
+        dbConfig.setMinConnectionsPerPartition(config.getMinIdle());
+        dbConfig.setMaxConnectionsPerPartition(config.getMaxActive());
+        dbConfig.setConnectionTimeout(config.getConnectionTimeout().getPeriod(), config.getConnectionTimeout().getUnit());
+        /*
+        dbConfig.setIdleMaxAge(config.getIdleMaxAge().getPeriod(), config.getIdleMaxAge().getUnit());
+        dbConfig.setMaxConnectionAge(config.getMaxConnectionAge().getPeriod(), config.getMaxConnectionAge().getUnit());
+        dbConfig.setIdleConnectionTestPeriod(config.getIdleConnectionTestPeriod().getPeriod(), config.getIdleConnectionTestPeriod().getUnit());
+        */
+        dbConfig.setPartitionCount(1);
+        dbConfig.setDisableJMX(false);
+
+        return new BoneCPDataSource(dbConfig);
+    }
+
+    private DataSource getC3P0DataSource() {
+        final ComboPooledDataSource cpds = new ComboPooledDataSource();
+        cpds.setJdbcUrl(config.getJdbcUrl());
+        cpds.setUser(config.getUsername());
+        cpds.setPassword(config.getPassword());
+        // http://www.mchange.com/projects/c3p0/#minPoolSize
+        // Minimum number of Connections a pool will maintain at any given time.
+        cpds.setMinPoolSize(config.getMinIdle());
+        // http://www.mchange.com/projects/c3p0/#maxPoolSize
+        // Maximum number of Connections a pool will maintain at any given time.
+        cpds.setMaxPoolSize(config.getMaxActive());
+        // http://www.mchange.com/projects/c3p0/#checkoutTimeout
+        // The number of milliseconds a client calling getConnection() will wait for a Connection to be checked-in or
+        // acquired when the pool is exhausted. Zero means wait indefinitely. Setting any positive value will cause the getConnection()
+        // call to time-out and break with an SQLException after the specified number of milliseconds.
+        cpds.setCheckoutTimeout(toMilliSeconds(config.getConnectionTimeout()));
+        // http://www.mchange.com/projects/c3p0/#maxIdleTime
+        // Seconds a Connection can remain pooled but unused before being discarded. Zero means idle connections never expire.
+//        cpds.setMaxIdleTime(toSeconds(config.getIdleMaxAge()));
+        // http://www.mchange.com/projects/c3p0/#maxConnectionAge
+        // Seconds, effectively a time to live. A Connection older than maxConnectionAge will be destroyed and purged from the pool.
+        // This differs from maxIdleTime in that it refers to absolute age. Even a Connection which has not been much idle will be purged
+        // from the pool if it exceeds maxConnectionAge. Zero means no maximum absolute age is enforced.
+//        cpds.setMaxConnectionAge(toSeconds(config.getMaxConnectionAge()));
+        // http://www.mchange.com/projects/c3p0/#idleConnectionTestPeriod
+        // If this is a number greater than 0, c3p0 will test all idle, pooled but unchecked-out connections, every this number of seconds.
+        cpds.setIdleConnectionTestPeriod(60);
+
+        return cpds;
+    }
+
+    private int toSeconds(final TimeSpan timeSpan) {
+        return toSeconds(timeSpan.getPeriod(), timeSpan.getUnit());
+    }
+
+    private int toSeconds(final long period, final TimeUnit timeUnit) {
+        return (int) TimeUnit.SECONDS.convert(period, timeUnit);
+    }
+
+    private int toMilliSeconds(final TimeSpan timeSpan) {
+        return toMilliSeconds(timeSpan.getPeriod(), timeSpan.getUnit());
+    }
+
+    private int toMilliSeconds(final long period, final TimeUnit timeUnit) {
+        return (int) TimeUnit.MILLISECONDS.convert(period, timeUnit);
+    }
+}
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..b923149
--- /dev/null
+++ b/osgi/src/main/java/com/ning/billing/osgi/http/DefaultHttpService.java
@@ -0,0 +1,75 @@
+/*
+ * 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;
+
+import com.ning.billing.osgi.ContextClassLoaderHelper;
+
+@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)");
+        }
+        final Servlet wrappedServlet = ContextClassLoaderHelper.getWrappedServiceWithCorrectContextClassLoader(servlet);
+
+        servletRouter.registerServiceFromPath(alias, wrappedServlet);
+    }
+
+    @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.unregisterServiceFromPath(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
new file mode 100644
index 0000000..68487f5
--- /dev/null
+++ b/osgi/src/main/java/com/ning/billing/osgi/http/DefaultServletRouter.java
@@ -0,0 +1,161 @@
+/*
+ * 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.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.inject.Singleton;
+import javax.servlet.Servlet;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.ning.billing.osgi.api.OSGIServiceDescriptor;
+import com.ning.billing.osgi.api.OSGIServiceRegistration;
+
+@Singleton
+public class DefaultServletRouter implements OSGIServiceRegistration<Servlet> {
+
+    private static final Logger logger = LoggerFactory.getLogger(DefaultServletRouter.class);
+
+    // 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> pluginPathServlets = new HashMap<String, Servlet>();
+    private final Map<String, OSGIServiceDescriptor> pluginRegistrations = new HashMap<String, OSGIServiceDescriptor>();
+
+    @Override
+    public void registerService(final OSGIServiceDescriptor desc, final Servlet httpServlet) {
+        // Enforce each route to start with /
+        final String pathPrefix = getPathPrefixFromDescriptor(desc);
+        if (pathPrefix == null) {
+            logger.warn("Skipping registration of OSGI servlet for service {} (service info is not specified)", desc.getServiceName());
+            return;
+        }
+
+        logger.info("Registering OSGI servlet at " + pathPrefix);
+        synchronized (this) {
+            registerServletInternal(pathPrefix, httpServlet);
+            registerServiceInternal(desc);
+        }
+    }
+
+    public void registerServiceFromPath(final String path, final Servlet httpServlet) {
+        final String pathPrefix = sanitizePathPrefix(path);
+        registerServletInternal(pathPrefix, httpServlet);
+    }
+
+    private void registerServletInternal(final String pathPrefix, final Servlet httpServlet) {
+        pluginPathServlets.put(pathPrefix, httpServlet);
+    }
+
+    private void registerServiceInternal(final OSGIServiceDescriptor desc) {
+        pluginRegistrations.put(desc.getServiceName(), desc);
+    }
+
+    @Override
+    public void unregisterService(final String serviceName) {
+        synchronized (this) {
+            final OSGIServiceDescriptor desc = pluginRegistrations.get(serviceName);
+            if (desc != null) {
+                final String pathPrefix = getPathPrefixFromDescriptor(desc);
+                if (pathPrefix == null) {
+                    logger.warn("Skipping unregistration of OSGI servlet for service {} (service info is not specified)", desc.getServiceName());
+                    return;
+                }
+
+                logger.info("Unregistering OSGI servlet " + desc.getServiceName() + " at path " + pathPrefix);
+                synchronized (this) {
+                    unRegisterServletInternal(pathPrefix);
+                    unRegisterServiceInternal(desc);
+                }
+            }
+        }
+    }
+
+    public void unregisterServiceFromPath(final String path) {
+        final String pathPrefix = sanitizePathPrefix(path);
+        unRegisterServletInternal(pathPrefix);
+    }
+
+    private Servlet unRegisterServletInternal(final String pathPrefix) {
+        return pluginPathServlets.remove(pathPrefix);
+    }
+
+    private OSGIServiceDescriptor unRegisterServiceInternal(final OSGIServiceDescriptor desc) {
+        return pluginRegistrations.remove(desc.getServiceName());
+    }
+
+    @Override
+    public Servlet getServiceForName(final String serviceName) {
+        final OSGIServiceDescriptor desc = pluginRegistrations.get(serviceName);
+        if (desc == null) {
+            return null;
+        }
+        final String registeredPath = getPathPrefixFromDescriptor(desc);
+        return pluginPathServlets.get(registeredPath);
+    }
+
+    private String getPathPrefixFromDescriptor(final OSGIServiceDescriptor desc) {
+        return sanitizePathPrefix(desc.getServiceInfo());
+    }
+
+    public Servlet getServiceForPath(final String path) {
+        return getServletForPathPrefix(path);
+    }
+
+    @Override
+    public Set<String> getAllServices() {
+        return pluginPathServlets.keySet();
+    }
+
+    @Override
+    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 : pluginPathServlets.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 : pluginPathServlets.get(bestMatch);
+    }
+
+    private static String sanitizePathPrefix(final String inputPath) {
+        if (inputPath == null) {
+            return null;
+        }
+
+        final String pathPrefix;
+        if (inputPath.charAt(0) != '/') {
+            pathPrefix = "/" + inputPath;
+        } else {
+            pathPrefix = inputPath;
+        }
+        return pathPrefix;
+    }
+}
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
new file mode 100644
index 0000000..e4f86fb
--- /dev/null
+++ b/osgi/src/main/java/com/ning/billing/osgi/http/OSGIServlet.java
@@ -0,0 +1,132 @@
+/*
+ * 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.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;
+
+@Singleton
+public class OSGIServlet extends HttpServlet {
+
+    private final Vector<Servlet> initializedServlets = new Vector<Servlet>();
+    private final Object servletsMonitor = new Object();
+
+    @Inject
+    private DefaultServletRouter servletRouter;
+
+    @Override
+    protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+        serviceViaPlugin(req, resp);
+    }
+
+    @Override
+    protected void doHead(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+        serviceViaPlugin(req, resp);
+    }
+
+    @Override
+    protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+        serviceViaPlugin(req, resp);
+    }
+
+    @Override
+    protected void doPut(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+        serviceViaPlugin(req, resp);
+    }
+
+    @Override
+    protected void doDelete(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+        serviceViaPlugin(req, resp);
+    }
+
+    @Override
+    protected void doOptions(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+        serviceViaPlugin(req, resp);
+    }
+
+    private void serviceViaPlugin(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+        // 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) {
+            initializeServletIfNeeded(req, pluginServlet);
+            final OSGIServletRequestWrapper requestWrapper = new OSGIServletRequestWrapper(req, servletRouter.getPluginPrefixForPath(requestPath));
+            pluginServlet.service(requestWrapper, resp);
+        } else {
+            resp.sendError(404);
+        }
+    }
+
+    // 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 requestPath) {
+        if (requestPath != null) {
+            return servletRouter.getServiceForPath(requestPath);
+        } else {
+            return null;
+        }
+    }
+}
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 1367f4f..c2b038c 100644
--- a/osgi/src/main/java/com/ning/billing/osgi/KillbillActivator.java
+++ b/osgi/src/main/java/com/ning/billing/osgi/KillbillActivator.java
@@ -16,262 +16,140 @@
 
 package com.ning.billing.osgi;
 
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Observable;
+
 import javax.inject.Inject;
+import javax.inject.Named;
+import javax.servlet.Servlet;
+import javax.sql.DataSource;
 
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.HttpService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.ning.billing.osgi.api.OSGIKillbill;
+import com.ning.billing.osgi.api.OSGIPluginProperties;
+import com.ning.billing.osgi.api.OSGIServiceDescriptor;
+import com.ning.billing.osgi.api.OSGIServiceRegistration;
+import com.ning.billing.osgi.glue.DefaultOSGIModule;
+import com.ning.billing.payment.plugin.api.PaymentPluginApi;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillRegistrar;
 
-import com.ning.billing.account.api.AccountUserApi;
-import com.ning.billing.analytics.api.sanity.AnalyticsSanityApi;
-import com.ning.billing.analytics.api.user.AnalyticsUserApi;
-import com.ning.billing.beatrix.bus.api.ExternalBus;
-import com.ning.billing.catalog.api.CatalogUserApi;
-import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
-import com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi;
-import com.ning.billing.entitlement.api.transfer.EntitlementTransferApi;
-import com.ning.billing.entitlement.api.user.EntitlementUserApi;
-import com.ning.billing.invoice.api.InvoiceMigrationApi;
-import com.ning.billing.invoice.api.InvoicePaymentApi;
-import com.ning.billing.invoice.api.InvoiceUserApi;
-import com.ning.billing.osgi.api.config.PluginConfigServiceApi;
-import com.ning.billing.overdue.OverdueUserApi;
-import com.ning.billing.payment.api.PaymentApi;
-import com.ning.billing.tenant.api.TenantUserApi;
-import com.ning.billing.usage.api.UsageUserApi;
-import com.ning.billing.util.api.AuditUserApi;
-import com.ning.billing.util.api.CustomFieldUserApi;
-import com.ning.billing.util.api.ExportUserApi;
-import com.ning.billing.util.api.TagUserApi;
+import com.google.common.collect.ImmutableList;
 
-public class KillbillActivator implements BundleActivator {
+public class KillbillActivator implements BundleActivator, ServiceListener {
 
-    private final AccountUserApi accountUserApi;
-    private final AnalyticsSanityApi analyticsSanityApi;
-    private final AnalyticsUserApi analyticsUserApi;
-    private final CatalogUserApi catalogUserApi;
-    private final EntitlementMigrationApi entitlementMigrationApi;
-    private final EntitlementTimelineApi entitlementTimelineApi;
-    private final EntitlementTransferApi entitlementTransferApi;
-    private final EntitlementUserApi entitlementUserApi;
-    private final InvoiceMigrationApi invoiceMigrationApi;
-    private final InvoicePaymentApi invoicePaymentApi;
-    private final InvoiceUserApi invoiceUserApi;
-    private final OverdueUserApi overdueUserApi;
-    private final PaymentApi paymentApi;
-    private final TenantUserApi tenantUserApi;
-    private final UsageUserApi usageUserApi;
-    private final AuditUserApi auditUserApi;
-    private final CustomFieldUserApi customFieldUserApi;
-    private final ExportUserApi exportUserApi;
-    private final TagUserApi tagUserApi;
+    // TODO : Is that ok for system bundle to use Killbill Logger or do we need to LoggerService like we do for any other bundle
+    private final static Logger logger = LoggerFactory.getLogger(KillbillActivator.class);
 
-    private final ExternalBus externalBus;
-    private final PluginConfigServiceApi configServiceApi;
+    private final OSGIKillbill osgiKillbill;
+    private final HttpService defaultHttpService;
+    private final DataSource dataSource;
+    private final KillbillEventObservable observable;
+    private final OSGIKillbillRegistrar registrar;
 
-    private volatile ServiceRegistration accountUserApiRegistration = null;
-    private volatile ServiceRegistration analyticsSanityApiRegistration = null;
-    private volatile ServiceRegistration analyticsUserApiRegistration = null;
-    private volatile ServiceRegistration catalogUserApiRegistration = null;
-    private volatile ServiceRegistration entitlementMigrationApiRegistration = null;
-    private volatile ServiceRegistration entitlementTimelineApiRegistration = null;
-    private volatile ServiceRegistration entitlementTransferApiRegistration = null;
-    private volatile ServiceRegistration entitlementUserApiRegistration = null;
-    private volatile ServiceRegistration invoiceMigrationApiRegistration = null;
-    private volatile ServiceRegistration invoicePaymentApiRegistration = null;
-    private volatile ServiceRegistration invoiceUserApiRegistration = null;
-    private volatile ServiceRegistration meterUserApiRegistration = null;
-    private volatile ServiceRegistration overdueUserApiRegistration = null;
-    private volatile ServiceRegistration paymentApiRegistration = null;
-    private volatile ServiceRegistration tenantUserApiRegistration = null;
-    private volatile ServiceRegistration usageUserApiRegistration = null;
-    private volatile ServiceRegistration auditUserApiRegistration = null;
-    private volatile ServiceRegistration customFieldUserApiRegistration = null;
-    private volatile ServiceRegistration exportUserApiRegistration = null;
-    private volatile ServiceRegistration tagUserApiRegistration = null;
+    private final List<OSGIServiceRegistration> allRegistrationHandlers;
 
-    private volatile ServiceRegistration externalBusRegistration = null;
-    private volatile ServiceRegistration configServiceApiRegistration = null;
+
+    private BundleContext context = null;
 
     @Inject
-    public KillbillActivator(final AccountUserApi accountUserApi,
-                             final AnalyticsSanityApi analyticsSanityApi,
-                             final AnalyticsUserApi analyticsUserApi,
-                             final CatalogUserApi catalogUserApi,
-                             final EntitlementMigrationApi entitlementMigrationApi,
-                             final EntitlementTimelineApi entitlementTimelineApi,
-                             final EntitlementTransferApi entitlementTransferApi,
-                             final EntitlementUserApi entitlementUserApi,
-                             final InvoiceMigrationApi invoiceMigrationApi,
-                             final InvoicePaymentApi invoicePaymentApi,
-                             final InvoiceUserApi invoiceUserApi,
-                             final OverdueUserApi overdueUserApi,
-                             final PaymentApi paymentApi,
-                             final TenantUserApi tenantUserApi,
-                             final UsageUserApi usageUserApi,
-                             final AuditUserApi auditUserApi,
-                             final CustomFieldUserApi customFieldUserApi,
-                             final ExportUserApi exportUserApi,
-                             final TagUserApi tagUserApi,
-                             final ExternalBus externalBus,
-                             final PluginConfigServiceApi configServiceApi) {
-        this.accountUserApi = accountUserApi;
-        this.analyticsSanityApi = analyticsSanityApi;
-        this.analyticsUserApi = analyticsUserApi;
-        this.catalogUserApi = catalogUserApi;
-        this.entitlementMigrationApi = entitlementMigrationApi;
-        this.entitlementTimelineApi = entitlementTimelineApi;
-        this.entitlementTransferApi = entitlementTransferApi;
-        this.entitlementUserApi = entitlementUserApi;
-        this.invoiceMigrationApi = invoiceMigrationApi;
-        this.invoicePaymentApi = invoicePaymentApi;
-        this.invoiceUserApi = invoiceUserApi;
-        this.overdueUserApi = overdueUserApi;
-        this.paymentApi = paymentApi;
-        this.tenantUserApi = tenantUserApi;
-        this.usageUserApi = usageUserApi;
-        this.auditUserApi = auditUserApi;
-        this.customFieldUserApi = customFieldUserApi;
-        this.exportUserApi = exportUserApi;
-        this.tagUserApi = tagUserApi;
-        this.externalBus = externalBus;
-        this.configServiceApi = configServiceApi;
+    public KillbillActivator(@Named(DefaultOSGIModule.OSGI_NAMED) final DataSource dataSource,
+                             final OSGIKillbill osgiKillbill,
+                             final HttpService defaultHttpService,
+                             final KillbillEventObservable observable,
+                             final OSGIServiceRegistration<Servlet> servletRouter,
+                             final OSGIServiceRegistration<PaymentPluginApi> paymentProviderPluginRegistry) {
+        this.osgiKillbill = osgiKillbill;
+        this.defaultHttpService = defaultHttpService;
+        this.dataSource = dataSource;
+        this.observable = observable;
+        this.registrar = new OSGIKillbillRegistrar();
+        this.allRegistrationHandlers = ImmutableList.<OSGIServiceRegistration>of(servletRouter, paymentProviderPluginRegistry);
     }
 
     @Override
     public void start(final BundleContext context) throws Exception {
-        registerServices(context);
-    }
 
-    private void registerServices(final BundleContext context) {
-        accountUserApiRegistration = context.registerService(AccountUserApi.class.getName(), accountUserApi, null);
-        analyticsSanityApiRegistration = context.registerService(AnalyticsSanityApi.class.getName(), analyticsSanityApi, null);
-        analyticsUserApiRegistration = context.registerService(AnalyticsUserApi.class.getName(), analyticsUserApi, null);
-        catalogUserApiRegistration = context.registerService(CatalogUserApi.class.getName(), catalogUserApi, null);
-        entitlementMigrationApiRegistration = context.registerService(EntitlementMigrationApi.class.getName(), entitlementMigrationApi, null);
-        entitlementTimelineApiRegistration = context.registerService(EntitlementTimelineApi.class.getName(), entitlementTimelineApi, null);
-        entitlementTransferApiRegistration = context.registerService(EntitlementTransferApi.class.getName(), entitlementTransferApi, null);
-        entitlementUserApiRegistration = context.registerService(EntitlementUserApi.class.getName(), entitlementUserApi, null);
-        invoiceMigrationApiRegistration = context.registerService(InvoiceMigrationApi.class.getName(), invoiceMigrationApi, null);
-        invoicePaymentApiRegistration = context.registerService(InvoicePaymentApi.class.getName(), invoicePaymentApi, null);
-        invoiceUserApiRegistration = context.registerService(InvoiceUserApi.class.getName(), invoiceUserApi, null);
-        overdueUserApiRegistration = context.registerService(OverdueUserApi.class.getName(), overdueUserApi, null);
-        paymentApiRegistration = context.registerService(PaymentApi.class.getName(), paymentApi, null);
-        tenantUserApiRegistration = context.registerService(TenantUserApi.class.getName(), tenantUserApi, null);
-        usageUserApiRegistration = context.registerService(UsageUserApi.class.getName(), usageUserApi, null);
-        auditUserApiRegistration = context.registerService(AuditUserApi.class.getName(), auditUserApi, null);
-        customFieldUserApiRegistration = context.registerService(CustomFieldUserApi.class.getName(), customFieldUserApi, null);
-        exportUserApiRegistration = context.registerService(ExportUserApi.class.getName(), exportUserApi, null);
-        tagUserApiRegistration = context.registerService(TagUserApi.class.getName(), tagUserApi, null);
+        this.context = context;
+        final Dictionary props = new Hashtable();
+        props.put(OSGIPluginProperties.PLUGIN_NAME_PROP, "killbill");
 
-        externalBusRegistration = context.registerService(ExternalBus.class.getName(), externalBus, null);
-        configServiceApiRegistration = context.registerService(PluginConfigServiceApi.class.getName(), configServiceApi, null);
+        observable.register();
+
+        registrar.registerService(context, OSGIKillbill.class, osgiKillbill, props);
+        registrar.registerService(context, HttpService.class, defaultHttpService, props);
+        registrar.registerService(context, Observable.class, observable, props);
+        registrar.registerService(context, DataSource.class, dataSource, props);
+
+        context.addServiceListener(this);
     }
 
     @Override
     public void stop(final BundleContext context) throws Exception {
-        if (accountUserApiRegistration != null) {
-            accountUserApiRegistration.unregister();
-            accountUserApiRegistration = null;
-        }
-        if (analyticsSanityApiRegistration != null) {
-            analyticsSanityApiRegistration.unregister();
-            analyticsSanityApiRegistration = null;
-        }
-        if (analyticsUserApiRegistration != null) {
-            analyticsUserApiRegistration.unregister();
-            analyticsUserApiRegistration = null;
-        }
-        if (catalogUserApiRegistration != null) {
-            catalogUserApiRegistration.unregister();
-            catalogUserApiRegistration = null;
-        }
-        if (entitlementMigrationApiRegistration != null) {
-            entitlementMigrationApiRegistration.unregister();
-            entitlementMigrationApiRegistration = null;
-        }
-        if (entitlementTimelineApiRegistration != null) {
-            entitlementTimelineApiRegistration.unregister();
-            entitlementTimelineApiRegistration = null;
-        }
-        if (entitlementTransferApiRegistration != null) {
-            entitlementTransferApiRegistration.unregister();
-            entitlementTransferApiRegistration = null;
-        }
-        if (entitlementUserApiRegistration != null) {
-            entitlementUserApiRegistration.unregister();
-            entitlementUserApiRegistration = null;
-        }
-        if (invoiceMigrationApiRegistration != null) {
-            invoiceMigrationApiRegistration.unregister();
-            invoiceMigrationApiRegistration = null;
-        }
-        if (invoicePaymentApiRegistration != null) {
-            invoicePaymentApiRegistration.unregister();
-            invoicePaymentApiRegistration = null;
-        }
-        if (invoiceUserApiRegistration != null) {
-            invoiceUserApiRegistration.unregister();
-            invoiceUserApiRegistration = null;
-        }
-        if (meterUserApiRegistration != null) {
-            meterUserApiRegistration.unregister();
-            meterUserApiRegistration = null;
-        }
-        if (overdueUserApiRegistration != null) {
-            overdueUserApiRegistration.unregister();
-            overdueUserApiRegistration = null;
-        }
-        if (paymentApiRegistration != null) {
-            paymentApiRegistration.unregister();
-            paymentApiRegistration = null;
-        }
-        if (tenantUserApiRegistration != null) {
-            tenantUserApiRegistration.unregister();
-            tenantUserApiRegistration = null;
-        }
-        if (usageUserApiRegistration != null) {
-            usageUserApiRegistration.unregister();
-            usageUserApiRegistration = null;
-        }
-        if (auditUserApiRegistration != null) {
-            auditUserApiRegistration.unregister();
-            auditUserApiRegistration = null;
-        }
-        if (customFieldUserApiRegistration != null) {
-            customFieldUserApiRegistration.unregister();
-            customFieldUserApiRegistration = null;
-        }
-        if (exportUserApiRegistration != null) {
-            exportUserApiRegistration.unregister();
-            exportUserApiRegistration = null;
-        }
-        if (tagUserApiRegistration != null) {
-            tagUserApiRegistration.unregister();
-            tagUserApiRegistration = null;
-        }
-        if (externalBusRegistration != null) {
-            externalBusRegistration.unregister();
-            externalBusRegistration = null;
+        this.context = null;
+        context.removeServiceListener(this);
+        observable.unregister();
+        registrar.unregisterAll();
+    }
+
+    @Override
+    public void serviceChanged(final ServiceEvent event) {
+        if (context == null || (event.getType() != ServiceEvent.REGISTERED && event.getType() != ServiceEvent.UNREGISTERING)) {
+            // We are not initialized or uninterested
+            return;
         }
 
-        if (configServiceApiRegistration != null) {
-            configServiceApiRegistration.unregister();
-            configServiceApiRegistration = null;
+        final ServiceReference serviceReference = event.getServiceReference();
+        for (OSGIServiceRegistration cur : allRegistrationHandlers) {
+            if (listenForServiceType(serviceReference, event.getType(), cur.getServiceType(), cur)) {
+                break;
+            }
         }
     }
 
-    //    public PaymentPluginApi getPaymentPluginApiForPlugin(final String pluginName) {
-    //        try {
-    //            final ServiceReference<PaymentPluginApi>[] paymentApiReferences = (ServiceReference<PaymentPluginApi>[]) context.getServiceReferences(PaymentPluginApi.class.getName(), "(name=hello)");
-    //            final PaymentPluginApi pluginApi = context.getService(paymentApiReferences[0]);
-    //            return pluginApi;
-    //        } catch (InvalidSyntaxException e) {
-    //            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
-    //        } finally {
-    //            //context.ungetService(paymentApiReferences[0]);
-    //            // STEPH TODO leak reference here
-    //        }
-    //        return null;
-    //    }
+    private <T> boolean listenForServiceType(final ServiceReference serviceReference, final int eventType, final Class<T> claz, final OSGIServiceRegistration<T> registration) {
+        // Make sure we can retrieve the plugin name
+        final String serviceName = (String) serviceReference.getProperty(OSGIPluginProperties.PLUGIN_NAME_PROP);
+        if (serviceName == null) {
+            // Quite common for non Killbill bundles
+            logger.debug("Ignoring registered OSGI service {} with no {} property", claz.getName(), OSGIPluginProperties.PLUGIN_NAME_PROP);
+            return true;
+        }
+
+        final Object theServiceObject = context.getService(serviceReference);
+        // Is that for us? We look for a subclass here for greater flexibility (e.g. HttpServlet for a Servlet service)
+        if (theServiceObject == null || !claz.isAssignableFrom(theServiceObject.getClass())) {
+            return false;
+        }
+        final T theService = (T) theServiceObject;
+
+        final String serviceInfo = (String) serviceReference.getProperty(OSGIPluginProperties.PLUGIN_SERVICE_INFO);
+        final OSGIServiceDescriptor desc = new DefaultOSGIServiceDescriptor(serviceReference.getBundle().getSymbolicName(), serviceName, serviceInfo, claz.getName());
+        switch (eventType) {
+            case ServiceEvent.REGISTERED:
+                final T wrappedService = ContextClassLoaderHelper.getWrappedServiceWithCorrectContextClassLoader(theService);
+                registration.registerService(desc, wrappedService);
+                break;
+            case ServiceEvent.UNREGISTERING:
+                registration.unregisterService(desc.getServiceName());
+                break;
+            default:
+                break;
+        }
+        return true;
+    }
 }
diff --git a/osgi/src/main/java/com/ning/billing/osgi/KillbillEventObservable.java b/osgi/src/main/java/com/ning/billing/osgi/KillbillEventObservable.java
new file mode 100644
index 0000000..14d7f4d
--- /dev/null
+++ b/osgi/src/main/java/com/ning/billing/osgi/KillbillEventObservable.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.osgi;
+
+import java.util.Observable;
+
+import javax.inject.Inject;
+
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.ning.billing.beatrix.bus.api.ExtBusEvent;
+import com.ning.billing.beatrix.bus.api.ExternalBus;
+
+import com.google.common.eventbus.Subscribe;
+
+public class KillbillEventObservable extends Observable {
+
+
+    private Logger logger = LoggerFactory.getLogger(KillbillEventObservable.class);
+
+    private final ExternalBus externalBus;
+
+    @Inject
+    public KillbillEventObservable(final ExternalBus externalBus) {
+        this.externalBus = externalBus;
+    }
+
+    public void register() {
+        externalBus.register(this);
+    }
+
+    public void unregister() {
+        deleteObservers();
+        if (externalBus != null) {
+            externalBus.unregister(this);
+        }
+    }
+
+    @Subscribe
+    public void handleKillbillEvent(final ExtBusEvent killbillEvent) {
+        logger.debug("Received external event " + killbillEvent.toString());
+        setChanged();
+        notifyObservers(killbillEvent);
+    }
+}
diff --git a/osgi/src/main/java/com/ning/billing/osgi/pluginconf/DefaultPluginConfig.java b/osgi/src/main/java/com/ning/billing/osgi/pluginconf/DefaultPluginConfig.java
index 0d428b7..2c9bd2d 100644
--- a/osgi/src/main/java/com/ning/billing/osgi/pluginconf/DefaultPluginConfig.java
+++ b/osgi/src/main/java/com/ning/billing/osgi/pluginconf/DefaultPluginConfig.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.osgi.pluginconf;
 
+import java.io.File;
 import java.util.Properties;
 
 import com.ning.billing.osgi.api.config.PluginConfig;
@@ -27,10 +28,12 @@ public abstract class DefaultPluginConfig implements PluginConfig {
     private final String pluginName;
     private final PluginType pluginType;
     private final String version;
+    private final File pluginVersionRoot;
 
-    public DefaultPluginConfig(final String pluginName, final String version, final Properties props) {
+    public DefaultPluginConfig(final String pluginName, final String version, final Properties props, final File pluginVersionRoot) {
         this.pluginName = pluginName;
         this.version = version;
+        this.pluginVersionRoot = pluginVersionRoot;
         this.pluginType = PluginType.valueOf(props.getProperty(PROP_PLUGIN_TYPE_NAME, PluginType.__UNKNOWN__.toString()));
     }
 
@@ -55,6 +58,11 @@ public abstract class DefaultPluginConfig implements PluginConfig {
     }
 
     @Override
+    public File getPluginVersionRoot() {
+        return pluginVersionRoot;
+    }
+
+    @Override
     public abstract PluginLanguage getPluginLanguage();
 
     protected abstract void validate() throws PluginConfigException;
@@ -70,13 +78,16 @@ public abstract class DefaultPluginConfig implements PluginConfig {
 
         final DefaultPluginConfig that = (DefaultPluginConfig) o;
 
-        if (!pluginName.equals(that.pluginName)) {
+        if (pluginName != null ? !pluginName.equals(that.pluginName) : that.pluginName != null) {
             return false;
         }
         if (pluginType != that.pluginType) {
             return false;
         }
-        if (!version.equals(that.version)) {
+        if (pluginVersionRoot != null ? !pluginVersionRoot.equals(that.pluginVersionRoot) : that.pluginVersionRoot != null) {
+            return false;
+        }
+        if (version != null ? !version.equals(that.version) : that.version != null) {
             return false;
         }
 
@@ -85,9 +96,10 @@ public abstract class DefaultPluginConfig implements PluginConfig {
 
     @Override
     public int hashCode() {
-        int result = pluginName.hashCode();
-        result = 31 * result + pluginType.hashCode();
-        result = 31 * result + version.hashCode();
+        int result = pluginName != null ? pluginName.hashCode() : 0;
+        result = 31 * result + (pluginType != null ? pluginType.hashCode() : 0);
+        result = 31 * result + (version != null ? version.hashCode() : 0);
+        result = 31 * result + (pluginVersionRoot != null ? pluginVersionRoot.hashCode() : 0);
         return result;
     }
 
@@ -98,8 +110,8 @@ public abstract class DefaultPluginConfig implements PluginConfig {
         sb.append("{pluginName='").append(pluginName).append('\'');
         sb.append(", pluginType=").append(pluginType);
         sb.append(", version='").append(version).append('\'');
+        sb.append(", pluginVersionRoot=").append(pluginVersionRoot);
         sb.append('}');
         return sb.toString();
     }
-
 }
diff --git a/osgi/src/main/java/com/ning/billing/osgi/pluginconf/DefaultPluginJavaConfig.java b/osgi/src/main/java/com/ning/billing/osgi/pluginconf/DefaultPluginJavaConfig.java
index 92415e6..513a9ee 100644
--- a/osgi/src/main/java/com/ning/billing/osgi/pluginconf/DefaultPluginJavaConfig.java
+++ b/osgi/src/main/java/com/ning/billing/osgi/pluginconf/DefaultPluginJavaConfig.java
@@ -26,7 +26,7 @@ public class DefaultPluginJavaConfig extends DefaultPluginConfig implements Plug
     private final String bundleJarPath;
 
     public DefaultPluginJavaConfig(final String pluginName, final String version, final File pluginVersionRoot, final Properties props) throws PluginConfigException {
-        super(pluginName, version, props);
+        super(pluginName, version, props, pluginVersionRoot);
         this.bundleJarPath = extractJarPath(pluginVersionRoot);
         validate();
     }
diff --git a/osgi/src/main/java/com/ning/billing/osgi/pluginconf/DefaultPluginRubyConfig.java b/osgi/src/main/java/com/ning/billing/osgi/pluginconf/DefaultPluginRubyConfig.java
index f39f66e..7194fd5 100644
--- a/osgi/src/main/java/com/ning/billing/osgi/pluginconf/DefaultPluginRubyConfig.java
+++ b/osgi/src/main/java/com/ning/billing/osgi/pluginconf/DefaultPluginRubyConfig.java
@@ -26,14 +26,17 @@ public class DefaultPluginRubyConfig extends DefaultPluginConfig implements Plug
     private static final String INSTALLATION_GEM_NAME = "gems";
 
     private static final String PROP_RUBY_MAIN_CLASS_NAME = "mainClass";
+    private static final String PROP_RUBY_REQUIRE = "require";
 
     private final String rubyMainClass;
     private final File rubyLoadDir;
+    private final String rubyRequire;
 
     public DefaultPluginRubyConfig(final String pluginName, final String version, final File pluginVersionRoot, final Properties props) throws PluginConfigException {
-        super(pluginName, version, props);
+        super(pluginName, version, props, pluginVersionRoot);
         this.rubyMainClass = props.getProperty(PROP_RUBY_MAIN_CLASS_NAME);
         this.rubyLoadDir = new File(pluginVersionRoot.getAbsolutePath() + "/" + INSTALLATION_GEM_NAME);
+        this.rubyRequire = props.getProperty(PROP_RUBY_REQUIRE);
         validate();
     }
 
@@ -58,6 +61,11 @@ public class DefaultPluginRubyConfig extends DefaultPluginConfig implements Plug
     }
 
     @Override
+    public String getRubyRequire() {
+        return rubyRequire;
+    }
+
+    @Override
     public PluginLanguage getPluginLanguage() {
         return PluginLanguage.RUBY;
     }
diff --git a/osgi/src/main/java/com/ning/billing/osgi/pluginconf/PluginFinder.java b/osgi/src/main/java/com/ning/billing/osgi/pluginconf/PluginFinder.java
index acdba18..23d67aa 100644
--- a/osgi/src/main/java/com/ning/billing/osgi/pluginconf/PluginFinder.java
+++ b/osgi/src/main/java/com/ning/billing/osgi/pluginconf/PluginFinder.java
@@ -41,7 +41,6 @@ import com.ning.billing.util.config.OSGIConfig;
 
 public class PluginFinder {
 
-    private static final String INSTALATION_PROPERTIES = "killbill.properties";
 
     private final Logger logger = LoggerFactory.getLogger(PluginFinder.class);
 
@@ -115,7 +114,7 @@ public class PluginFinder {
     }
 
     private <T extends PluginConfig> void loadPluginsForLanguage(final PluginLanguage pluginLanguage) throws PluginConfigException {
-        final String rootDirPath = osgiConfig.getRootInstallationDir() + "/" + pluginLanguage.toString().toLowerCase();
+        final String rootDirPath = osgiConfig.getRootInstallationDir() + "/plugins/" + pluginLanguage.toString().toLowerCase();
         final File rootDir = new File(rootDirPath);
         if (!rootDir.exists() || !rootDir.isDirectory()) {
             logger.warn("Configuration root dir {} is not a valid directory", rootDirPath);
@@ -168,7 +167,7 @@ public class PluginFinder {
             }
 
             for (final File cur : files) {
-                if (cur.isFile() && cur.getName().equals(INSTALATION_PROPERTIES)) {
+                if (cur.isFile() && cur.getName().equals(osgiConfig.getOSGIKillbillPropertyName())) {
                     props = readPluginConfigurationFile(cur);
                 }
                 if (props != null) {
diff --git a/osgi/src/main/java/com/ning/billing/osgi/PureOSGIBundleFinder.java b/osgi/src/main/java/com/ning/billing/osgi/PureOSGIBundleFinder.java
new file mode 100644
index 0000000..12a41ed
--- /dev/null
+++ b/osgi/src/main/java/com/ning/billing/osgi/PureOSGIBundleFinder.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.osgi;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.ning.billing.osgi.pluginconf.PluginConfigException;
+import com.ning.billing.util.config.OSGIConfig;
+
+import com.google.common.collect.ImmutableList;
+
+@Singleton
+public class PureOSGIBundleFinder {
+
+    private final Logger logger = LoggerFactory.getLogger(Logger.class);
+
+    private final OSGIConfig osgiConfig;
+
+    @Inject
+    public PureOSGIBundleFinder(final OSGIConfig osgiConfig) {
+        this.osgiConfig = osgiConfig;
+    }
+
+    public List<String> getLatestBundles() throws PluginConfigException {
+        final String rootDirPath = getPlatformOSGIBundlesRootDir();
+        final File rootDir = new File(rootDirPath);
+        if (!rootDir.exists() || !rootDir.isDirectory()) {
+            logger.warn("Configuration root dir {} is not a valid directory", rootDirPath);
+            return ImmutableList.<String>of();
+        }
+
+        final File[] files = rootDir.listFiles();
+        if (files == null) {
+            return ImmutableList.<String>of();
+        }
+
+        final List<String> bundles = new ArrayList<String>();
+        for (final File bundleJar : files) {
+            if (bundleJar.isFile()) {
+                bundles.add(bundleJar.getAbsolutePath());
+            }
+        }
+
+        return bundles;
+    }
+
+    public String getPlatformOSGIBundlesRootDir() {
+        return osgiConfig.getRootInstallationDir() + "/platform/";
+    }
+}
diff --git a/osgi-bundles/bundles/jruby/README.md b/osgi-bundles/bundles/jruby/README.md
new file mode 100644
index 0000000..d550a73
--- /dev/null
+++ b/osgi-bundles/bundles/jruby/README.md
@@ -0,0 +1,17 @@
+Testing the JRuby OSGI bridge
+-----------------------------
+
+1. Build the jruby OSGI module and copy the bundle to:
+
+        /var/tmp/killbill-osgi-bundles-jruby.jar
+
+2. Build a ruby plugin, e.g. killbill-paypal-express:
+
+        rake killbill:clean
+        rake build
+        rake killbill:package
+
+3. Copy the built tree to the bundles directory:
+
+        mkdir -p /var/tmp/bundles/ruby
+        mv pkg/killbill-paypal-express-*/killbill-paypal-express /var/tmp/bundles/ruby/
diff --git a/osgi-bundles/bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyActivator.java b/osgi-bundles/bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyActivator.java
new file mode 100644
index 0000000..1540012
--- /dev/null
+++ b/osgi-bundles/bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyActivator.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.osgi.bundles.jruby;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.jruby.embed.ScriptingContainer;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+import com.ning.billing.osgi.api.config.PluginConfig.PluginType;
+import com.ning.billing.osgi.api.config.PluginConfigServiceApi;
+import com.ning.billing.osgi.api.config.PluginRubyConfig;
+import com.ning.killbill.osgi.libs.killbill.KillbillActivatorBase;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillEventDispatcher.OSGIKillbillEventHandler;
+
+public class JRubyActivator extends KillbillActivatorBase {
+
+    private JRubyPlugin plugin = null;
+
+    public void start(final BundleContext context) throws Exception {
+
+        super.start(context);
+        logService.log(LogService.LOG_INFO, "JRuby bundle activated");
+
+        doMagicToMakeJRubyAndFelixHappy();
+
+        // Retrieve the plugin config
+        final PluginRubyConfig rubyConfig = retrievePluginRubyConfig(context);
+
+        // Setup JRuby
+        final ScriptingContainer scriptingContainer = setupScriptingContainer(rubyConfig);
+        if (PluginType.NOTIFICATION.equals(rubyConfig.getPluginType())) {
+            plugin = new JRubyNotificationPlugin(rubyConfig, scriptingContainer, context, logService);
+            dispatcher.registerEventHandler((OSGIKillbillEventHandler) plugin);
+        } else if (PluginType.PAYMENT.equals(rubyConfig.getPluginType())) {
+            plugin = new JRubyPaymentPlugin(rubyConfig, scriptingContainer, context, logService);
+        }
+
+        // Validate and instantiate the plugin
+
+        final Map<String, Object> killbillServices = retrieveKillbillApis(context);
+        killbillServices.put("root", rubyConfig.getPluginVersionRoot().getAbsolutePath());
+        killbillServices.put("logger", logService);
+        plugin.instantiatePlugin(killbillServices);
+
+        logService.log(LogService.LOG_INFO, "Starting JRuby plugin " + plugin.getPluginMainClass());
+        plugin.startPlugin(context);
+
+    }
+
+    private PluginRubyConfig retrievePluginRubyConfig(final BundleContext context) {
+        final PluginConfigServiceApi pluginConfigServiceApi = killbillAPI.getPluginConfigServiceApi();
+        return pluginConfigServiceApi.getPluginRubyConfig(context.getBundle().getBundleId());
+    }
+
+    // JRuby/Felix specifics, it works out of the box on Equinox.
+    // Other OSGI frameworks are untested.
+    private void doMagicToMakeJRubyAndFelixHappy() {
+        // Tell JRuby to use the correct class loader
+        Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+    }
+
+    private ScriptingContainer setupScriptingContainer(final PluginRubyConfig rubyConfig) {
+        final ScriptingContainer scriptingContainer = new ScriptingContainer();
+
+        // Set the load paths instead of adding, to avoid looking at the filesystem
+        scriptingContainer.setLoadPaths(Collections.<String>singletonList(rubyConfig.getRubyLoadDir()));
+
+        return scriptingContainer;
+    }
+
+    public void stop(final BundleContext context) throws Exception {
+        plugin.stopPlugin(context);
+        killbillAPI.close();
+        logService.close();
+    }
+
+    // We make the explicit registration in the start method by hand as this would be called too early
+    // (see OSGIKillbillEventDispatcher)
+    @Override
+    public OSGIKillbillEventHandler getOSGIKillbillEventHandler() {
+        return null;
+    }
+
+    private Map<String, Object> retrieveKillbillApis(final BundleContext context) {
+        final Map<String, Object> killbillUserApis = new HashMap<String, Object>();
+
+        // See killbill/plugin.rb for the naming convention magic
+        killbillUserApis.put("account_user_api", killbillAPI.getAccountUserApi());
+        killbillUserApis.put("analytics_sanity_api", killbillAPI.getAnalyticsSanityApi());
+        killbillUserApis.put("analytics_user_api", killbillAPI.getAnalyticsUserApi());
+        killbillUserApis.put("catalog_user_api", killbillAPI.getCatalogUserApi());
+        killbillUserApis.put("entitlement_migration_api", killbillAPI.getEntitlementMigrationApi());
+        killbillUserApis.put("entitlement_timeline_api", killbillAPI.getEntitlementMigrationApi());
+        killbillUserApis.put("entitlement_transfer_api", killbillAPI.getEntitlementTransferApi());
+        killbillUserApis.put("entitlement_user_api", killbillAPI.getEntitlementUserApi());
+        killbillUserApis.put("invoice_migration_api", killbillAPI.getInvoiceMigrationApi());
+        killbillUserApis.put("invoice_payment_api", killbillAPI.getInvoicePaymentApi());
+        killbillUserApis.put("invoice_user_api", killbillAPI.getInvoiceUserApi());
+        killbillUserApis.put("overdue_user_api", killbillAPI.getOverdueUserApi());
+        killbillUserApis.put("payment_api", killbillAPI.getPaymentApi());
+        killbillUserApis.put("tenant_user_api", killbillAPI.getTagUserApi());
+        killbillUserApis.put("usage_user_api", killbillAPI.getUsageUserApi());
+        killbillUserApis.put("audit_user_api", killbillAPI.getAuditUserApi());
+        killbillUserApis.put("custom_field_user_api", killbillAPI.getCustomFieldUserApi());
+        killbillUserApis.put("export_user_api", killbillAPI.getExportUserApi());
+        killbillUserApis.put("tag_user_api", killbillAPI.getTagUserApi());
+        return killbillUserApis;
+    }
+}
diff --git a/osgi-bundles/bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyHttpServlet.java b/osgi-bundles/bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyHttpServlet.java
new file mode 100644
index 0000000..7e576cc
--- /dev/null
+++ b/osgi-bundles/bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyHttpServlet.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.osgi.bundles.jruby;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.jruby.runtime.builtin.IRubyObject;
+
+public class JRubyHttpServlet extends HttpServlet {
+
+    private final HttpServlet delegate;
+
+    public JRubyHttpServlet(final IRubyObject rubyObject) {
+        delegate = (HttpServlet) rubyObject.toJava(HttpServlet.class);
+    }
+
+    @Override
+    protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+        delegate.service(req, resp);
+    }
+}
diff --git a/osgi-bundles/bundles/logger/src/main/java/com/ning/billing/osgi/bundles/logger/Activator.java b/osgi-bundles/bundles/logger/src/main/java/com/ning/billing/osgi/bundles/logger/Activator.java
new file mode 100644
index 0000000..7ab3d97
--- /dev/null
+++ b/osgi-bundles/bundles/logger/src/main/java/com/ning/billing/osgi/bundles/logger/Activator.java
@@ -0,0 +1,102 @@
+/*
+ * 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.logger;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.log.LogListener;
+import org.osgi.service.log.LogReaderService;
+import org.osgi.util.tracker.ServiceTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Activator implements BundleActivator {
+
+    private static final Logger logger = LoggerFactory.getLogger(Activator.class);
+
+    private final LogListener killbillLogListener = new KillbillLogWriter();
+    private final List<LogReaderService> logReaderServices = new LinkedList<LogReaderService>();
+
+    private final ServiceListener logReaderServiceListener = new ServiceListener() {
+        public void serviceChanged(final ServiceEvent event) {
+            final ServiceReference serviceReference = event.getServiceReference();
+            if (serviceReference == null || serviceReference.getBundle() == null) {
+                return;
+            }
+
+            final BundleContext bundleContext = serviceReference.getBundle().getBundleContext();
+            if (bundleContext == null) {
+                return;
+            }
+
+            final LogReaderService logReaderService = (LogReaderService) bundleContext.getService(serviceReference);
+            if (logReaderService != null) {
+                if (event.getType() == ServiceEvent.REGISTERED) {
+                    registerLogReaderService(logReaderService);
+                } else if (event.getType() == ServiceEvent.UNREGISTERING) {
+                    logReaderService.removeLogListener(killbillLogListener);
+                    logReaderServices.remove(logReaderService);
+                }
+            }
+        }
+    };
+
+    @Override
+    public void start(final BundleContext context) throws Exception {
+        // Get a list of all the registered LogReaderService, and add the killbill listener
+        final ServiceTracker logReaderTracker = new ServiceTracker(context, LogReaderService.class.getName(), null);
+        logReaderTracker.open();
+        final Object[] readers = logReaderTracker.getServices();
+        if (readers != null) {
+            for (final Object reader : readers) {
+                final LogReaderService service = (LogReaderService) reader;
+                registerLogReaderService(service);
+            }
+        }
+        logReaderTracker.close();
+
+        // Add the ServiceListener
+        final String filter = "(objectclass=" + LogReaderService.class.getName() + ")";
+        try {
+            context.addServiceListener(logReaderServiceListener, filter);
+        } catch (InvalidSyntaxException e) {
+            logger.warn("Unable to register the killbill LogReaderService listener", e);
+        }
+    }
+
+    @Override
+    public void stop(final BundleContext context) throws Exception {
+        for (final Iterator<LogReaderService> iterator = logReaderServices.iterator(); iterator.hasNext(); ) {
+            final LogReaderService service = iterator.next();
+            service.removeLogListener(killbillLogListener);
+            iterator.remove();
+        }
+    }
+
+    private void registerLogReaderService(final LogReaderService service) {
+        logReaderServices.add(service);
+        service.addLogListener(killbillLogListener);
+    }
+}
diff --git a/osgi-bundles/bundles/logger/src/main/java/com/ning/billing/osgi/bundles/logger/KillbillLogWriter.java b/osgi-bundles/bundles/logger/src/main/java/com/ning/billing/osgi/bundles/logger/KillbillLogWriter.java
new file mode 100644
index 0000000..eea8293
--- /dev/null
+++ b/osgi-bundles/bundles/logger/src/main/java/com/ning/billing/osgi/bundles/logger/KillbillLogWriter.java
@@ -0,0 +1,193 @@
+/*
+ * 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.logger;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Version;
+import org.osgi.service.log.LogEntry;
+import org.osgi.service.log.LogListener;
+import org.osgi.service.log.LogService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+// Inspired by osgi-over-slf4j
+public class KillbillLogWriter implements LogListener {
+
+    private static final String UNKNOWN = "[Unknown]";
+
+    private final Map<String, Logger> delegates = new HashMap<String, Logger>();
+
+    // Invoked by the log service implementation for each log entry
+    public void logged(final LogEntry entry) {
+        final Bundle bundle = entry.getBundle();
+        final Logger delegate = getDelegateForBundle(bundle);
+
+        final ServiceReference serviceReference = entry.getServiceReference();
+        final int level = entry.getLevel();
+        final String message = entry.getMessage();
+        final Throwable exception = entry.getException();
+
+        if (serviceReference != null && exception != null) {
+            log(delegate, serviceReference, level, message, exception);
+        } else if (serviceReference != null) {
+            log(delegate, serviceReference, level, message);
+        } else if (exception != null) {
+            log(delegate, level, message, exception);
+        } else {
+            log(delegate, level, message);
+        }
+    }
+
+    private Logger getDelegateForBundle(/* @Nullable */ final Bundle bundle) {
+        final String loggerName;
+        if (bundle != null) {
+            final String name = bundle.getSymbolicName();
+            Version version = bundle.getVersion();
+            if (version == null) {
+                version = Version.emptyVersion;
+            }
+            loggerName = name + '.' + version;
+        } else {
+            loggerName = KillbillLogWriter.class.getName();
+        }
+
+        if (delegates.get(loggerName) == null) {
+            synchronized (delegates) {
+                if (delegates.get(loggerName) == null) {
+                    delegates.put(loggerName, LoggerFactory.getLogger(loggerName));
+                }
+            }
+        }
+
+        return delegates.get(loggerName);
+    }
+
+    private void log(final Logger delegate, final int level, final String message) {
+        switch (level) {
+            case LogService.LOG_DEBUG:
+                delegate.debug(message);
+                break;
+            case LogService.LOG_ERROR:
+                delegate.error(message);
+                break;
+            case LogService.LOG_INFO:
+                delegate.info(message);
+                break;
+            case LogService.LOG_WARNING:
+                delegate.warn(message);
+                break;
+            default:
+                break;
+        }
+    }
+
+    private void log(final Logger delegate, final int level, final String message, final Throwable exception) {
+        switch (level) {
+            case LogService.LOG_DEBUG:
+                delegate.debug(message, exception);
+                break;
+            case LogService.LOG_ERROR:
+                delegate.error(message, exception);
+                break;
+            case LogService.LOG_INFO:
+                delegate.info(message, exception);
+                break;
+            case LogService.LOG_WARNING:
+                delegate.warn(message, exception);
+                break;
+            default:
+                break;
+        }
+    }
+
+    private void log(final Logger delegate, final ServiceReference sr, final int level, final String message) {
+        switch (level) {
+            case LogService.LOG_DEBUG:
+                if (delegate.isDebugEnabled()) {
+                    delegate.debug(createMessage(sr, message));
+                }
+                break;
+            case LogService.LOG_ERROR:
+                if (delegate.isErrorEnabled()) {
+                    delegate.error(createMessage(sr, message));
+                }
+                break;
+            case LogService.LOG_INFO:
+                if (delegate.isInfoEnabled()) {
+                    delegate.info(createMessage(sr, message));
+                }
+                break;
+            case LogService.LOG_WARNING:
+                if (delegate.isWarnEnabled()) {
+                    delegate.warn(createMessage(sr, message));
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    private void log(final Logger delegate, final ServiceReference sr, final int level, final String message, final Throwable exception) {
+        switch (level) {
+            case LogService.LOG_DEBUG:
+                if (delegate.isDebugEnabled()) {
+                    delegate.debug(createMessage(sr, message), exception);
+                }
+                break;
+            case LogService.LOG_ERROR:
+                if (delegate.isErrorEnabled()) {
+                    delegate.error(createMessage(sr, message), exception);
+                }
+                break;
+            case LogService.LOG_INFO:
+                if (delegate.isInfoEnabled()) {
+                    delegate.info(createMessage(sr, message), exception);
+                }
+                break;
+            case LogService.LOG_WARNING:
+                if (delegate.isWarnEnabled()) {
+                    delegate.warn(createMessage(sr, message), exception);
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    /**
+     * Formats the log message to indicate the service sending it, if known.
+     *
+     * @param sr      the ServiceReference sending the message.
+     * @param message The message to log.
+     * @return The formatted log message.
+     */
+    private String createMessage(final ServiceReference sr, final String message) {
+        final StringBuilder output = new StringBuilder();
+        if (sr != null) {
+            output.append('[').append(sr.toString()).append(']');
+        } else {
+            output.append(UNKNOWN);
+        }
+        output.append(message);
+
+        return output.toString();
+    }
+}
diff --git a/osgi-bundles/bundles/pom.xml b/osgi-bundles/bundles/pom.xml
new file mode 100644
index 0000000..ddb3703
--- /dev/null
+++ b/osgi-bundles/bundles/pom.xml
@@ -0,0 +1,35 @@
+<?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-all-bundles</artifactId>
+        <version>0.1.56-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <artifactId>killbill-osgi-bundles</artifactId>
+    <name>Killbill billing platform: OSGI bundles</name>
+    <packaging>pom</packaging>
+    <modules>
+        <module>jruby</module>
+        <module>logger</module>
+        <module>meter</module>
+        <module>webconsolebranding</module>
+    </modules>
+</project>
diff --git a/osgi-bundles/bundles/webconsolebranding/pom.xml b/osgi-bundles/bundles/webconsolebranding/pom.xml
new file mode 100644
index 0000000..ed92524
--- /dev/null
+++ b/osgi-bundles/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/bundles/webconsolebranding/src/main/resources/META-INF/webconsole.properties b/osgi-bundles/bundles/webconsolebranding/src/main/resources/META-INF/webconsole.properties
new file mode 100644
index 0000000..3993219
--- /dev/null
+++ b/osgi-bundles/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/bundles/webconsolebranding/src/main/resources/res/killbill/favicon.ico b/osgi-bundles/bundles/webconsolebranding/src/main/resources/res/killbill/favicon.ico
new file mode 100644
index 0000000..675f906
Binary files /dev/null and b/osgi-bundles/bundles/webconsolebranding/src/main/resources/res/killbill/favicon.ico differ
diff --git a/osgi-bundles/bundles/webconsolebranding/src/main/resources/res/killbill/logo.png b/osgi-bundles/bundles/webconsolebranding/src/main/resources/res/killbill/logo.png
new file mode 100644
index 0000000..ed80710
Binary files /dev/null and b/osgi-bundles/bundles/webconsolebranding/src/main/resources/res/killbill/logo.png differ
diff --git a/osgi-bundles/defaultbundles/pom.xml b/osgi-bundles/defaultbundles/pom.xml
new file mode 100644
index 0000000..ec98490
--- /dev/null
+++ b/osgi-bundles/defaultbundles/pom.xml
@@ -0,0 +1,167 @@
+<?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-all-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-jruby</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-osgi-bundles-logger</artifactId>
+        </dependency>
+        <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>
+    </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..9ffda42
--- /dev/null
+++ b/osgi-bundles/defaultbundles/src/main/assembly/assembly.xml
@@ -0,0 +1,32 @@
+<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>tar-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>
+            <excludes>
+                <exclude>com.ning.billing:killbill-osgi-bundles-jruby:jar</exclude>
+            </excludes>
+        </dependencySet>
+        <dependencySet>
+            <outputFileNameMapping>jruby.jar</outputFileNameMapping>
+            <outputDirectory>/</outputDirectory>
+            <useProjectArtifact>false</useProjectArtifact>
+            <useProjectAttachments>true</useProjectAttachments>
+            <useTransitiveDependencies>false</useTransitiveDependencies>
+            <unpack>false</unpack>
+            <includes>
+                <include>com.ning.billing:killbill-osgi-bundles-jruby:jar</include>
+            </includes>
+        </dependencySet>
+    </dependencySets>
+</assembly>
diff --git a/osgi-bundles/libs/killbill/pom.xml b/osgi-bundles/libs/killbill/pom.xml
new file mode 100644
index 0000000..eb5c542
--- /dev/null
+++ b/osgi-bundles/libs/killbill/pom.xml
@@ -0,0 +1,71 @@
+<?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-lib-bundles</artifactId>
+        <version>0.1.56-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <artifactId>killbill-osgi-bundles-lib-killbill</artifactId>
+    <name>Killbill billing platform: OSGI Killbill Library</name>
+    <packaging>jar</packaging>
+    <dependencies>
+        <dependency>
+            <groupId>com.google.code.findbugs</groupId>
+            <artifactId>jsr305</artifactId>
+            <version>1.3.9</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>osgi-over-slf4j</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/KillbillActivatorBase.java b/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/KillbillActivatorBase.java
new file mode 100644
index 0000000..3b2da79
--- /dev/null
+++ b/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/KillbillActivatorBase.java
@@ -0,0 +1,93 @@
+/*
+ * 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.killbill.osgi.libs.killbill;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillEventDispatcher.OSGIKillbillEventHandler;
+
+public abstract class KillbillActivatorBase implements BundleActivator {
+
+
+    protected OSGIKillbillAPI killbillAPI;
+    protected OSGIKillbillLogService logService;
+    protected OSGIKillbillRegistrar registrar;
+    protected OSGIKillbillDataSource dataSource;
+    protected OSGIKillbillEventDispatcher dispatcher;
+
+    @Override
+    public void start(final BundleContext context) throws Exception {
+
+        // Tracked resource
+        killbillAPI = new OSGIKillbillAPI(context);
+        logService = new OSGIKillbillLogService(context);
+        dataSource = new OSGIKillbillDataSource(context);
+        dispatcher = new OSGIKillbillEventDispatcher(context);
+
+        // Registrar for bundle
+        registrar = new OSGIKillbillRegistrar();
+
+        // Killbill events
+        final OSGIKillbillEventHandler handler = getOSGIKillbillEventHandler();
+        if (handler != null) {
+            dispatcher.registerEventHandler(handler);
+        }
+    }
+
+    @Override
+    public void stop(final BundleContext context) throws Exception {
+
+        // Close trackers
+        if (killbillAPI != null) {
+            killbillAPI.close();
+            killbillAPI = null;
+        }
+        if (dispatcher != null) {
+            dispatcher.close();
+            dispatcher = null;
+        }
+        if (dataSource != null) {
+            dataSource.close();
+            dataSource = null;
+        }
+        if (logService != null) {
+            logService.close();
+            logService = null;
+        }
+
+        try {
+            // Remove Killbill event handler
+            final OSGIKillbillEventHandler handler = getOSGIKillbillEventHandler();
+            if (handler != null && dispatcher != null) {
+                dispatcher.unregisterEventHandler(handler);
+                dispatcher = null;
+            }
+        } catch (OSGIServiceNotAvailable ignore) {
+            // If the system bundle shut down prior to that bundle, we can' unregister our Observer, which is fine.
+        }
+
+        // Unregister all servies from that bundle
+        if (registrar != null) {
+            registrar.unregisterAll();
+            registrar = null;
+        }
+    }
+
+
+    public abstract OSGIKillbillEventHandler getOSGIKillbillEventHandler();
+}
diff --git a/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/OSGIKillbillAPI.java b/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/OSGIKillbillAPI.java
new file mode 100644
index 0000000..47ca336
--- /dev/null
+++ b/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/OSGIKillbillAPI.java
@@ -0,0 +1,262 @@
+/*
+ * 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.killbill.osgi.libs.killbill;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
+
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.analytics.api.sanity.AnalyticsSanityApi;
+import com.ning.billing.analytics.api.user.AnalyticsUserApi;
+import com.ning.billing.catalog.api.CatalogUserApi;
+import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
+import com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi;
+import com.ning.billing.entitlement.api.transfer.EntitlementTransferApi;
+import com.ning.billing.entitlement.api.user.EntitlementUserApi;
+import com.ning.billing.invoice.api.InvoiceMigrationApi;
+import com.ning.billing.invoice.api.InvoicePaymentApi;
+import com.ning.billing.invoice.api.InvoiceUserApi;
+import com.ning.billing.osgi.api.OSGIKillbill;
+import com.ning.billing.osgi.api.config.PluginConfigServiceApi;
+import com.ning.billing.overdue.OverdueUserApi;
+import com.ning.billing.payment.api.PaymentApi;
+import com.ning.billing.tenant.api.TenantUserApi;
+import com.ning.billing.usage.api.UsageUserApi;
+import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.api.CustomFieldUserApi;
+import com.ning.billing.util.api.ExportUserApi;
+import com.ning.billing.util.api.TagUserApi;
+
+public class OSGIKillbillAPI extends OSGIKillbillLibraryBase implements OSGIKillbill {
+
+
+    private static final String KILLBILL_SERVICE_NAME = "com.ning.billing.osgi.api.OSGIKillbill";
+
+    private final ServiceTracker<OSGIKillbill, OSGIKillbill> killbillTracker;
+
+    public OSGIKillbillAPI(BundleContext context) {
+        killbillTracker = new ServiceTracker(context, KILLBILL_SERVICE_NAME, null);
+        killbillTracker.open();
+    }
+
+    public void close() {
+        if (killbillTracker != null) {
+            killbillTracker.close();
+        }
+    }
+
+    @Override
+    public AccountUserApi getAccountUserApi() {
+        return withServiceTracker(killbillTracker, new APICallback<AccountUserApi, OSGIKillbill>(KILLBILL_SERVICE_NAME) {
+            @Override
+            public AccountUserApi executeWithService(final OSGIKillbill service) {
+                return service.getAccountUserApi();
+            }
+        });
+    }
+
+    @Override
+    public AnalyticsSanityApi getAnalyticsSanityApi() {
+        return withServiceTracker(killbillTracker, new APICallback<AnalyticsSanityApi, OSGIKillbill>(KILLBILL_SERVICE_NAME) {
+            @Override
+            public AnalyticsSanityApi executeWithService(final OSGIKillbill service) {
+                return service.getAnalyticsSanityApi();
+            }
+        });
+    }
+
+    @Override
+    public AnalyticsUserApi getAnalyticsUserApi() {
+        return withServiceTracker(killbillTracker, new APICallback<AnalyticsUserApi, OSGIKillbill>(KILLBILL_SERVICE_NAME) {
+            @Override
+            public AnalyticsUserApi executeWithService(final OSGIKillbill service) {
+                return service.getAnalyticsUserApi();
+            }
+        });
+
+    }
+
+    @Override
+    public CatalogUserApi getCatalogUserApi() {
+        return withServiceTracker(killbillTracker, new APICallback<CatalogUserApi, OSGIKillbill>(KILLBILL_SERVICE_NAME) {
+            @Override
+            public CatalogUserApi executeWithService(final OSGIKillbill service) {
+                return service.getCatalogUserApi();
+            }
+        });
+    }
+
+    @Override
+    public EntitlementMigrationApi getEntitlementMigrationApi() {
+        return withServiceTracker(killbillTracker, new APICallback<EntitlementMigrationApi, OSGIKillbill>(KILLBILL_SERVICE_NAME) {
+            @Override
+            public EntitlementMigrationApi executeWithService(final OSGIKillbill service) {
+                return service.getEntitlementMigrationApi();
+            }
+        });
+    }
+
+    @Override
+    public EntitlementTimelineApi getEntitlementTimelineApi() {
+        return withServiceTracker(killbillTracker, new APICallback<EntitlementTimelineApi, OSGIKillbill>(KILLBILL_SERVICE_NAME) {
+            @Override
+            public EntitlementTimelineApi executeWithService(final OSGIKillbill service) {
+                return service.getEntitlementTimelineApi();
+            }
+        });
+    }
+
+    @Override
+    public EntitlementTransferApi getEntitlementTransferApi() {
+        return withServiceTracker(killbillTracker, new APICallback<EntitlementTransferApi, OSGIKillbill>(KILLBILL_SERVICE_NAME) {
+            @Override
+            public EntitlementTransferApi executeWithService(final OSGIKillbill service) {
+                return service.getEntitlementTransferApi();
+            }
+        });
+    }
+
+    @Override
+    public EntitlementUserApi getEntitlementUserApi() {
+        return withServiceTracker(killbillTracker, new APICallback<EntitlementUserApi, OSGIKillbill>(KILLBILL_SERVICE_NAME) {
+            @Override
+            public EntitlementUserApi executeWithService(final OSGIKillbill service) {
+                return service.getEntitlementUserApi();
+            }
+        });
+    }
+
+    @Override
+    public InvoiceMigrationApi getInvoiceMigrationApi() {
+        return withServiceTracker(killbillTracker, new APICallback<InvoiceMigrationApi, OSGIKillbill>(KILLBILL_SERVICE_NAME) {
+            @Override
+            public InvoiceMigrationApi executeWithService(final OSGIKillbill service) {
+                return service.getInvoiceMigrationApi();
+            }
+        });
+    }
+
+    @Override
+    public InvoicePaymentApi getInvoicePaymentApi() {
+        return withServiceTracker(killbillTracker, new APICallback<InvoicePaymentApi, OSGIKillbill>(KILLBILL_SERVICE_NAME) {
+            @Override
+            public InvoicePaymentApi executeWithService(final OSGIKillbill service) {
+                return service.getInvoicePaymentApi();
+            }
+        });
+    }
+
+    @Override
+    public InvoiceUserApi getInvoiceUserApi() {
+        return withServiceTracker(killbillTracker, new APICallback<InvoiceUserApi, OSGIKillbill>(KILLBILL_SERVICE_NAME) {
+            @Override
+            public InvoiceUserApi executeWithService(final OSGIKillbill service) {
+                return service.getInvoiceUserApi();
+            }
+        });
+    }
+
+    @Override
+    public OverdueUserApi getOverdueUserApi() {
+        return withServiceTracker(killbillTracker, new APICallback<OverdueUserApi, OSGIKillbill>(KILLBILL_SERVICE_NAME) {
+            @Override
+            public OverdueUserApi executeWithService(final OSGIKillbill service) {
+                return service.getOverdueUserApi();
+            }
+        });
+    }
+
+    @Override
+    public PaymentApi getPaymentApi() {
+        return withServiceTracker(killbillTracker, new APICallback<PaymentApi, OSGIKillbill>(KILLBILL_SERVICE_NAME) {
+            @Override
+            public PaymentApi executeWithService(final OSGIKillbill service) {
+                return service.getPaymentApi();
+            }
+        });
+    }
+
+    @Override
+    public TenantUserApi getTenantUserApi() {
+        return withServiceTracker(killbillTracker, new APICallback<TenantUserApi, OSGIKillbill>(KILLBILL_SERVICE_NAME) {
+            @Override
+            public TenantUserApi executeWithService(final OSGIKillbill service) {
+                return service.getTenantUserApi();
+            }
+        });
+    }
+
+    @Override
+    public UsageUserApi getUsageUserApi() {
+        return withServiceTracker(killbillTracker, new APICallback<UsageUserApi, OSGIKillbill>(KILLBILL_SERVICE_NAME) {
+            @Override
+            public UsageUserApi executeWithService(final OSGIKillbill service) {
+                return service.getUsageUserApi();
+            }
+        });
+    }
+
+    @Override
+    public AuditUserApi getAuditUserApi() {
+        return withServiceTracker(killbillTracker, new APICallback<AuditUserApi, OSGIKillbill>(KILLBILL_SERVICE_NAME) {
+            @Override
+            public AuditUserApi executeWithService(final OSGIKillbill service) {
+                return service.getAuditUserApi();
+            }
+        });
+    }
+
+    @Override
+    public CustomFieldUserApi getCustomFieldUserApi() {
+        return withServiceTracker(killbillTracker, new APICallback<CustomFieldUserApi, OSGIKillbill>(KILLBILL_SERVICE_NAME) {
+            @Override
+            public CustomFieldUserApi executeWithService(final OSGIKillbill service) {
+                return service.getCustomFieldUserApi();
+            }
+        });
+    }
+
+    @Override
+    public ExportUserApi getExportUserApi() {
+        return withServiceTracker(killbillTracker, new APICallback<ExportUserApi, OSGIKillbill>(KILLBILL_SERVICE_NAME) {
+            @Override
+            public ExportUserApi executeWithService(final OSGIKillbill service) {
+                return service.getExportUserApi();
+            }
+        });
+    }
+
+    @Override
+    public TagUserApi getTagUserApi() {
+        return withServiceTracker(killbillTracker, new APICallback<TagUserApi, OSGIKillbill>(KILLBILL_SERVICE_NAME) {
+            @Override
+            public TagUserApi executeWithService(final OSGIKillbill service) {
+                return service.getTagUserApi();
+            }
+        });
+    }
+
+    @Override
+    public PluginConfigServiceApi getPluginConfigServiceApi() {
+        return withServiceTracker(killbillTracker, new APICallback<PluginConfigServiceApi, OSGIKillbill>(KILLBILL_SERVICE_NAME) {
+            @Override
+            public PluginConfigServiceApi executeWithService(final OSGIKillbill service) {
+                return service.getPluginConfigServiceApi();
+            }
+        });
+    }
+}
diff --git a/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/OSGIKillbillDataSource.java b/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/OSGIKillbillDataSource.java
new file mode 100644
index 0000000..6e1c671
--- /dev/null
+++ b/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/OSGIKillbillDataSource.java
@@ -0,0 +1,50 @@
+/*
+ * 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.killbill.osgi.libs.killbill;
+
+import javax.sql.DataSource;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class OSGIKillbillDataSource extends OSGIKillbillLibraryBase {
+
+    private static final String DATASOURCE_SERVICE_NAME = "javax.sql.DataSource";
+
+    private final ServiceTracker<DataSource, DataSource> dataSourceTracker;
+
+
+    public OSGIKillbillDataSource(BundleContext context) {
+        dataSourceTracker = new ServiceTracker(context, DATASOURCE_SERVICE_NAME, null);
+        dataSourceTracker.open();
+    }
+
+    public void close() {
+        if (dataSourceTracker != null) {
+            dataSourceTracker.close();
+        }
+    }
+
+    public DataSource getDataSource() {
+        return withServiceTracker(dataSourceTracker, new APICallback<DataSource, DataSource>(DATASOURCE_SERVICE_NAME) {
+            @Override
+            public DataSource executeWithService(final DataSource service) {
+                return dataSourceTracker.getService();
+            }
+        });
+    }
+}
diff --git a/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/OSGIKillbillEventDispatcher.java b/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/OSGIKillbillEventDispatcher.java
new file mode 100644
index 0000000..1bdf9fe
--- /dev/null
+++ b/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/OSGIKillbillEventDispatcher.java
@@ -0,0 +1,95 @@
+/*
+ * 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.killbill.osgi.libs.killbill;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Observable;
+import java.util.Observer;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
+
+import com.ning.billing.beatrix.bus.api.ExtBusEvent;
+
+public class OSGIKillbillEventDispatcher extends OSGIKillbillLibraryBase {
+
+    private static final String OBSERVABLE_SERVICE_NAME = "java.util.Observable";
+
+    private final ServiceTracker<Observable, Observable> observableTracker;
+
+
+    private final Map<OSGIKillbillEventHandler, Observer> handlerToObserver;
+
+    public OSGIKillbillEventDispatcher(BundleContext context) {
+        handlerToObserver = new HashMap<OSGIKillbillEventHandler, Observer>();
+        observableTracker = new ServiceTracker(context, OBSERVABLE_SERVICE_NAME, null);
+        observableTracker.open();
+    }
+
+    public void close() {
+        if (observableTracker != null) {
+            observableTracker.close();
+        }
+        handlerToObserver.clear();
+    }
+
+    public void registerEventHandler(final OSGIKillbillEventHandler handler) {
+
+        withServiceTracker(observableTracker, new APICallback<Void, Observable>(OBSERVABLE_SERVICE_NAME) {
+            @Override
+            public Void executeWithService(final Observable service) {
+
+                final Observer observer = new Observer() {
+                    @Override
+                    public void update(final Observable o, final Object arg) {
+                        if (!(arg instanceof ExtBusEvent)) {
+                            // TODO STEPH or should we throw because that should not happen
+                            return;
+                        }
+                        handler.handleKillbillEvent((ExtBusEvent) arg);
+                    }
+                };
+                handlerToObserver.put(handler, observer);
+                service.addObserver(observer);
+                return null;
+            }
+        });
+    }
+
+    public void unregisterEventHandler(final OSGIKillbillEventHandler handler) {
+        withServiceTracker(observableTracker, new APICallback<Void, Observable>(OBSERVABLE_SERVICE_NAME) {
+            @Override
+            public Void executeWithService(final Observable service) {
+
+                final Observer observer = handlerToObserver.get(handler);
+                if (observer != null) {
+                    service.deleteObserver(observer);
+                    handlerToObserver.remove(handler);
+                }
+                return null;
+            }
+        });
+
+    }
+
+    public interface OSGIKillbillEventHandler {
+
+        public void handleKillbillEvent(final ExtBusEvent killbillEvent);
+    }
+
+}
diff --git a/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/OSGIKillbillLibraryBase.java b/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/OSGIKillbillLibraryBase.java
new file mode 100644
index 0000000..ce86b45
--- /dev/null
+++ b/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/OSGIKillbillLibraryBase.java
@@ -0,0 +1,53 @@
+/*
+ * 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.killbill.osgi.libs.killbill;
+
+import org.osgi.util.tracker.ServiceTracker;
+
+public abstract class OSGIKillbillLibraryBase {
+
+
+    public OSGIKillbillLibraryBase() {
+
+    }
+
+    public abstract void close();
+
+
+    protected abstract class APICallback<API, T> {
+
+        private final String serviceName;
+
+        protected APICallback(final String serviceName) {
+            this.serviceName = serviceName;
+        }
+
+        public abstract API executeWithService(T service);
+
+        protected API executeWithNoService() {
+            throw new OSGIServiceNotAvailable(serviceName);
+        }
+    }
+
+    protected <API, S, T> API withServiceTracker(ServiceTracker<S, T> t, APICallback<API, T> cb) {
+        T service = t.getService();
+        if (service == null) {
+            return cb.executeWithNoService();
+        }
+        return cb.executeWithService(service);
+    }
+}
diff --git a/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/OSGIKillbillLogService.java b/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/OSGIKillbillLogService.java
new file mode 100644
index 0000000..29876b3
--- /dev/null
+++ b/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/OSGIKillbillLogService.java
@@ -0,0 +1,91 @@
+/*
+ * 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.killbill.osgi.libs.killbill;
+
+import javax.annotation.Nullable;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.log.LogService;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class OSGIKillbillLogService extends OSGIKillbillLibraryBase implements LogService {
+
+    private static final String LOG_SERVICE_NAME = "org.osgi.service.log.LogService";
+
+    private final ServiceTracker<LogService, LogService> logTracker;
+
+
+    public OSGIKillbillLogService(BundleContext context) {
+        super();
+        logTracker = new ServiceTracker(context, LOG_SERVICE_NAME, null);
+        logTracker.open();
+    }
+
+    public void close() {
+        if (logTracker != null) {
+            logTracker.close();
+        }
+    }
+
+    @Override
+    public void log(final int level, final String message) {
+        logInternal(level, message, null);
+    }
+
+    @Override
+    public void log(final int level, final String message, final Throwable exception) {
+        logInternal(level, message, exception);
+    }
+
+    @Override
+    public void log(final ServiceReference sr, final int level, final String message) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void log(final ServiceReference sr, final int level, final String message, final Throwable exception) {
+        throw new UnsupportedOperationException();
+    }
+
+    private void logInternal(final int level, final String message, @Nullable final Throwable t) {
+
+        withServiceTracker(logTracker, new APICallback<Void, LogService>(LOG_SERVICE_NAME) {
+            @Override
+            public Void executeWithService(final LogService service) {
+                if (t == null) {
+                    service.log(level, message);
+                } else {
+                    service.log(level, message, t);
+                }
+                return null;
+            }
+
+            protected Void executeWithNoService() {
+                if (level >= 2) {
+                    System.out.println(message);
+                } else {
+                    System.err.println(message);
+                }
+                if (t != null) {
+                    t.printStackTrace(System.err);
+                }
+                return null;
+            }
+        });
+    }
+}
diff --git a/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/OSGIKillbillRegistrar.java b/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/OSGIKillbillRegistrar.java
new file mode 100644
index 0000000..196e5b7
--- /dev/null
+++ b/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/OSGIKillbillRegistrar.java
@@ -0,0 +1,52 @@
+/*
+ * 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.killbill.osgi.libs.killbill;
+
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+public class OSGIKillbillRegistrar {
+
+    private final Map<String, ServiceRegistration> serviceRegistrations;
+
+    public OSGIKillbillRegistrar() {
+        this.serviceRegistrations = new HashMap<String, ServiceRegistration>();
+    }
+
+    public <S> void registerService(final BundleContext context, final Class<S> svcClass, final S service, final Dictionary props) {
+        ServiceRegistration svcRegistration = context.registerService(svcClass.getName(), service, props);
+        serviceRegistrations.put(svcClass.getName(), svcRegistration);
+    }
+
+    public <S> void unregisterService(final Class<S> svcClass) {
+        ServiceRegistration svc = serviceRegistrations.remove(svcClass.getName());
+        if (svc != null) {
+            svc.unregister();
+        }
+    }
+
+    public void unregisterAll() {
+        for (ServiceRegistration cur : serviceRegistrations.values()) {
+            cur.unregister();
+        }
+        serviceRegistrations.clear();
+    }
+}
diff --git a/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/OSGIServiceNotAvailable.java b/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/OSGIServiceNotAvailable.java
new file mode 100644
index 0000000..4a86243
--- /dev/null
+++ b/osgi-bundles/libs/killbill/src/main/java/com/ning/killbill/osgi/libs/killbill/OSGIServiceNotAvailable.java
@@ -0,0 +1,38 @@
+/*
+ * 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.killbill.osgi.libs.killbill;
+
+public class OSGIServiceNotAvailable extends RuntimeException  {
+
+    private static final String FORMAT_SERVICE_NOT_AVAILABLE = "OSGI service %s is not available";
+
+    public OSGIServiceNotAvailable(String serviceName) {
+        super(toFormat(serviceName));
+    }
+
+    public OSGIServiceNotAvailable(String serviceName, Throwable cause) {
+        super(toFormat(serviceName), cause);
+    }
+
+    public OSGIServiceNotAvailable(Throwable cause) {
+        super(cause);
+    }
+
+    private static String toFormat(String serviceName) {
+        return String.format(FORMAT_SERVICE_NOT_AVAILABLE, serviceName);
+    }
+}
diff --git a/osgi-bundles/libs/pom.xml b/osgi-bundles/libs/pom.xml
new file mode 100644
index 0000000..7600274
--- /dev/null
+++ b/osgi-bundles/libs/pom.xml
@@ -0,0 +1,33 @@
+<?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-all-bundles</artifactId>
+        <version>0.1.56-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <artifactId>killbill-osgi-lib-bundles</artifactId>
+    <name>Killbill billing platform: OSGI library bundles</name>
+    <packaging>pom</packaging>
+    <modules>
+      <module>killbill</module>
+      <module>slf4j-osgi</module>
+    </modules>
+</project>
diff --git a/osgi-bundles/libs/slf4j-osgi/pom.xml b/osgi-bundles/libs/slf4j-osgi/pom.xml
new file mode 100644
index 0000000..4efc0e2
--- /dev/null
+++ b/osgi-bundles/libs/slf4j-osgi/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-lib-bundles</artifactId>
+        <version>0.1.56-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <artifactId>killbill-osgi-bundles-lib-slf4j-osgi</artifactId>
+    <name>Killbill billing platform: OSGI slf4j-osgi Library</name>
+    <dependencies>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-osgi-bundles-lib-killbill</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/osgi-bundles/libs/slf4j-osgi/src/main/java/org/slf4j/impl/OSGISlf4jLoggerAdapter.java b/osgi-bundles/libs/slf4j-osgi/src/main/java/org/slf4j/impl/OSGISlf4jLoggerAdapter.java
new file mode 100644
index 0000000..a8b5a96
--- /dev/null
+++ b/osgi-bundles/libs/slf4j-osgi/src/main/java/org/slf4j/impl/OSGISlf4jLoggerAdapter.java
@@ -0,0 +1,67 @@
+/*
+ * 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 org.slf4j.impl;
+
+import org.osgi.service.log.LogService;
+import org.slf4j.spi.LocationAwareLogger;
+
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillLogService;
+
+public final class OSGISlf4jLoggerAdapter extends SimpleLogger {
+
+    private OSGIKillbillLogService delegate;
+
+    public OSGISlf4jLoggerAdapter(final OSGIKillbillLogService logger) {
+        super(OSGISlf4jLoggerAdapter.class.getName());
+        this.delegate = logger;
+    }
+
+    public void setDelegate(final OSGIKillbillLogService delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    protected void log(final int level, final String message, final Throwable t) {
+        if (delegate == null) {
+            super.log(level, message, t);
+        } else {
+            final int logServiceLevel = convertLocationAwareLoggerToLogServiceLevel(level);
+            if (t == null) {
+                delegate.log(logServiceLevel, message);
+            } else {
+                delegate.log(logServiceLevel, message, t);
+            }
+        }
+    }
+
+    private int convertLocationAwareLoggerToLogServiceLevel(final int level) {
+        if (level == LocationAwareLogger.TRACE_INT) {
+            return LogService.LOG_DEBUG;
+        } else if (level == LocationAwareLogger.DEBUG_INT) {
+            return LogService.LOG_DEBUG;
+        } else if (level == LocationAwareLogger.INFO_INT) {
+            return LogService.LOG_INFO;
+        } else if (level == LocationAwareLogger.WARN_INT) {
+            return LogService.LOG_WARNING;
+        } else if (level == LocationAwareLogger.ERROR_INT) {
+            return LogService.LOG_ERROR;
+        } else {
+            // Be safe to avoid loosing messages
+            return LogService.LOG_INFO;
+        }
+    }
+}
diff --git a/osgi-bundles/libs/slf4j-osgi/src/main/java/org/slf4j/impl/OSGISlf4jLoggerFactory.java b/osgi-bundles/libs/slf4j-osgi/src/main/java/org/slf4j/impl/OSGISlf4jLoggerFactory.java
new file mode 100644
index 0000000..d111958
--- /dev/null
+++ b/osgi-bundles/libs/slf4j-osgi/src/main/java/org/slf4j/impl/OSGISlf4jLoggerFactory.java
@@ -0,0 +1,53 @@
+/*
+ * 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 org.slf4j.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.slf4j.ILoggerFactory;
+import org.slf4j.Logger;
+
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillLogService;
+
+public class OSGISlf4jLoggerFactory implements ILoggerFactory {
+
+    private final Map<String, OSGISlf4jLoggerAdapter> loggerMap = new HashMap<String, OSGISlf4jLoggerAdapter>();
+
+    private OSGIKillbillLogService delegate = null;
+
+    public Logger getLogger(final String name) {
+        if (loggerMap.get(name) == null) {
+            synchronized (this) {
+                if (loggerMap.get(name) == null) {
+                    final OSGISlf4jLoggerAdapter slf4jLogger = new OSGISlf4jLoggerAdapter(delegate);
+                    loggerMap.put(name, slf4jLogger);
+                }
+            }
+        }
+        return loggerMap.get(name);
+    }
+
+    public void setDelegate(final OSGIKillbillLogService delegate) {
+        this.delegate = delegate;
+        synchronized (loggerMap) {
+            for (final OSGISlf4jLoggerAdapter adapter : loggerMap.values()) {
+                adapter.setDelegate(delegate);
+            }
+        }
+    }
+}
diff --git a/osgi-bundles/libs/slf4j-osgi/src/main/java/org/slf4j/impl/SimpleLogger.java b/osgi-bundles/libs/slf4j-osgi/src/main/java/org/slf4j/impl/SimpleLogger.java
new file mode 100644
index 0000000..2e4520e
--- /dev/null
+++ b/osgi-bundles/libs/slf4j-osgi/src/main/java/org/slf4j/impl/SimpleLogger.java
@@ -0,0 +1,644 @@
+/*
+ * 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 org.slf4j.impl;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Properties;
+
+import org.slf4j.Logger;
+import org.slf4j.helpers.FormattingTuple;
+import org.slf4j.helpers.MarkerIgnoringBase;
+import org.slf4j.helpers.MessageFormatter;
+import org.slf4j.helpers.Util;
+import org.slf4j.spi.LocationAwareLogger;
+
+/**
+ * <p>Simple implementation of {@link Logger} that sends all enabled log messages,
+ * for all defined loggers, to the console ({@code System.err}).
+ * The following system properties are supported to configure the behavior of this logger:</p>
+ *
+ * <ul>
+ * <li><code>org.slf4j.simpleLogger.logFile</code> - The output target which can be the <em>path</em> to a file, or
+ * the special values "System.out" and "System.err". Default is "System.err".
+ *
+ * <li><code>org.slf4j.simpleLogger.defaultLogLevel</code> - Default log level for all instances of SimpleLogger.
+ * Must be one of ("trace", "debug", "info", "warn", or "error"). If not specified, defaults to "info". </li>
+ *
+ * <li><code>org.slf4j.simpleLogger.log.<em>a.b.c</em></code> - Logging detail level for a SimpleLogger instance
+ * named "a.b.c". Right-side value must be one of "trace", "debug", "info", "warn", or "error". When a SimpleLogger
+ * named "a.b.c" is initialized, its level is assigned from this property. If unspecified, the level of nearest parent
+ * logger will be used, and if none is set, then the value specified by
+ * <code>org.slf4j.simpleLogger.defaultLogLevel</code> will be used.</li>
+ *
+ * <li><code>org.slf4j.simpleLogger.showDateTime</code> - Set to <code>true</code> if you want the current date and
+ * time to be included in output messages. Default is <code>true</code></li>
+ *
+ * <li><code>org.slf4j.simpleLogger.dateTimeFormat</code> - The date and time format to be used in the output messages.
+ * The pattern describing the date and time format is defined by
+ * <a href="http://docs.oracle.com/javase/1.5.0/docs/api/java/text/SimpleDateFormat.html"><code>SimpleDateFormat</code></a>.
+ * If the format is not specified or is invalid, the number of milliseconds since start up will be output. </li>
+ *
+ * <li><code>org.slf4j.simpleLogger.showThreadName</code> -Set to <code>true</code> if you want to output the current
+ * thread name. Defaults to <code>true</code>.</li>
+ *
+ * <li><code>org.slf4j.simpleLogger.showLogName</code> - Set to <code>true</code> if you want the Logger instance name
+ * to be included in output messages. Defaults to <code>true</code>.</li>
+ *
+ * <li><code>org.slf4j.simpleLogger.showShortLogName</code> - Set to <code>true</code> if you want the last component
+ * of the name to be included in output messages. Defaults to <code>false</code>.</li>
+ *
+ * <li><code>org.slf4j.simpleLogger.levelInBrackets</code> - Should the level string be output in brackets? Defaults
+ * to <code>false</code>.</li>
+ *
+ * <li><code>org.slf4j.simpleLogger.warnLevelString</code> - The string value output for the warn level. Defaults
+ * to <code>WARN</code>.</li>
+
+ * </ul>
+ *
+ * <p>In addition to looking for system properties with the names specified above, this implementation also checks for
+ * a class loader resource named <code>"simplelogger.properties"</code>, and includes any matching definitions
+ * from this resource (if it exists).</p>
+ *
+ * <p>With no configuration, the default output includes the relative time in milliseconds, thread name, the level,
+ * logger name, and the message followed by the line separator for the host.  In log4j terms it amounts to the "%r [%t]
+ * %level %logger - %m%n" pattern. </p>
+ * <p>Sample output follows.</p>
+ * <pre>
+ * 176 [main] INFO examples.Sort - Populating an array of 2 elements in reverse order.
+ * 225 [main] INFO examples.SortAlgo - Entered the sort method.
+ * 304 [main] INFO examples.SortAlgo - Dump of integer array:
+ * 317 [main] INFO examples.SortAlgo - Element [0] = 0
+ * 331 [main] INFO examples.SortAlgo - Element [1] = 1
+ * 343 [main] INFO examples.Sort - The next log statement should be an error message.
+ * 346 [main] ERROR examples.SortAlgo - Tried to dump an uninitialized array.
+ *   at org.log4j.examples.SortAlgo.dump(SortAlgo.java:58)
+ *   at org.log4j.examples.Sort.main(Sort.java:64)
+ * 467 [main] INFO  examples.Sort - Exiting main method.
+ * </pre>
+ *
+ * <p>This implementation is heavily inspired by
+ * <a href="http://commons.apache.org/logging/">Apache Commons Logging</a>'s SimpleLog.</p>
+ *
+ * @author Ceki G&uuml;lc&uuml;
+ * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
+ * @author Rod Waldhoff
+ * @author Robert Burrell Donkin
+ * @author C&eacute;drik LIME
+ */
+public class SimpleLogger extends MarkerIgnoringBase {
+
+  private static final long serialVersionUID = -632788891211436180L;
+  private static final String CONFIGURATION_FILE = "simplelogger.properties";
+
+  private static long START_TIME = System.currentTimeMillis();
+  private static final Properties SIMPLE_LOGGER_PROPS = new Properties();
+
+  private static final int LOG_LEVEL_TRACE = LocationAwareLogger.TRACE_INT;
+  private static final int LOG_LEVEL_DEBUG = LocationAwareLogger.DEBUG_INT;
+  private static final int LOG_LEVEL_INFO = LocationAwareLogger.INFO_INT;
+  private static final int LOG_LEVEL_WARN = LocationAwareLogger.WARN_INT;
+  private static final int LOG_LEVEL_ERROR = LocationAwareLogger.ERROR_INT;
+
+  private static boolean INITIALIZED = false;
+
+  private static int DEFAULT_LOG_LEVEL = LOG_LEVEL_INFO;
+  private static boolean SHOW_DATE_TIME = false;
+  private static String DATE_TIME_FORMAT_STR = null;
+  private static DateFormat DATE_FORMATTER = null;
+  private static boolean SHOW_THREAD_NAME = true;
+  private static boolean SHOW_LOG_NAME = true;
+  private static boolean SHOW_SHORT_LOG_NAME = false;
+  private static String LOG_FILE = "System.err";
+  private static PrintStream TARGET_STREAM = null;
+  private static boolean LEVEL_IN_BRACKETS = false;
+  private static String WARN_LEVEL_STRING = "WARN";
+
+
+  /** All system properties used by <code>SimpleLogger</code> start with this prefix */
+  public static final String SYSTEM_PREFIX = "org.slf4j.simpleLogger.";
+
+  public static final String DEFAULT_LOG_LEVEL_KEY = SYSTEM_PREFIX + "defaultLogLevel";
+  public static final String SHOW_DATE_TIME_KEY = SYSTEM_PREFIX + "showDateTime";
+  public static final String DATE_TIME_FORMAT_KEY = SYSTEM_PREFIX + "dateTimeFormat";
+  public static final String SHOW_THREAD_NAME_KEY = SYSTEM_PREFIX + "showThreadName";
+  public static final String SHOW_LOG_NAME_KEY = SYSTEM_PREFIX + "showLogName";
+  public static final String SHOW_SHORT_LOG_NAME_KEY = SYSTEM_PREFIX + "showShortLogName";
+  public static final String LOG_FILE_KEY = SYSTEM_PREFIX + "logFile";
+  public static final String LEVEL_IN_BRACKETS_KEY = SYSTEM_PREFIX + "levelInBrackets";
+  public static final String WARN_LEVEL_STRING_KEY = SYSTEM_PREFIX + "warnLevelString";
+
+
+  public static final String LOG_KEY_PREFIX = SYSTEM_PREFIX + "log.";
+
+
+  private static String getStringProperty(String name) {
+    String prop = null;
+    try {
+      prop = System.getProperty(name);
+    } catch (SecurityException e) {
+      ; // Ignore
+    }
+    return (prop == null) ? SIMPLE_LOGGER_PROPS.getProperty(name) : prop;
+  }
+
+  private static String getStringProperty(String name, String defaultValue) {
+    String prop = getStringProperty(name);
+    return (prop == null) ? defaultValue : prop;
+  }
+
+  private static boolean getBooleanProperty(String name, boolean defaultValue) {
+    String prop = getStringProperty(name);
+    return (prop == null) ? defaultValue : "true".equalsIgnoreCase(prop);
+  }
+
+
+  // Initialize class attributes.
+  // Load properties file, if found.
+  // Override with system properties.
+  static void init() {
+    INITIALIZED = true;
+    loadProperties();
+
+    String defaultLogLevelString = getStringProperty(DEFAULT_LOG_LEVEL_KEY, null);
+    if (defaultLogLevelString != null)
+      DEFAULT_LOG_LEVEL = stringToLevel(defaultLogLevelString);
+
+    SHOW_LOG_NAME = getBooleanProperty(SHOW_LOG_NAME_KEY, SHOW_LOG_NAME);
+    SHOW_SHORT_LOG_NAME = getBooleanProperty(SHOW_SHORT_LOG_NAME_KEY, SHOW_SHORT_LOG_NAME);
+    SHOW_DATE_TIME = getBooleanProperty(SHOW_DATE_TIME_KEY, SHOW_DATE_TIME);
+    SHOW_THREAD_NAME = getBooleanProperty(SHOW_THREAD_NAME_KEY, SHOW_THREAD_NAME);
+    DATE_TIME_FORMAT_STR = getStringProperty(DATE_TIME_FORMAT_KEY, DATE_TIME_FORMAT_STR);
+    LEVEL_IN_BRACKETS = getBooleanProperty(LEVEL_IN_BRACKETS_KEY, LEVEL_IN_BRACKETS);
+    WARN_LEVEL_STRING = getStringProperty(WARN_LEVEL_STRING_KEY, WARN_LEVEL_STRING);
+
+    LOG_FILE = getStringProperty(LOG_FILE_KEY, LOG_FILE);
+    TARGET_STREAM = computeTargetStream(LOG_FILE);
+
+    if (DATE_TIME_FORMAT_STR != null) {
+      try {
+        DATE_FORMATTER = new SimpleDateFormat(DATE_TIME_FORMAT_STR);
+      } catch (IllegalArgumentException e) {
+        Util.report("Bad date format in " + CONFIGURATION_FILE + "; will output relative time", e);
+      }
+    }
+  }
+
+
+  private static PrintStream computeTargetStream(String logFile) {
+    if ("System.err".equalsIgnoreCase(logFile))
+      return System.err;
+    else if ("System.out".equalsIgnoreCase(logFile)) {
+      return System.out;
+    } else {
+      try {
+        FileOutputStream fos = new FileOutputStream(logFile);
+        PrintStream printStream = new PrintStream(fos);
+        return printStream;
+      } catch (FileNotFoundException e) {
+        Util.report("Could not open [" + logFile + "]. Defaulting to System.err", e);
+        return System.err;
+      }
+    }
+  }
+
+  private static void loadProperties() {
+    // Add props from the resource simplelogger.properties
+    InputStream in = (InputStream) AccessController.doPrivileged(
+            new PrivilegedAction() {
+              public Object run() {
+                ClassLoader threadCL = Thread.currentThread().getContextClassLoader();
+                if (threadCL != null) {
+                  return threadCL.getResourceAsStream(CONFIGURATION_FILE);
+                } else {
+                  return ClassLoader.getSystemResourceAsStream(CONFIGURATION_FILE);
+                }
+              }
+            });
+    if (null != in) {
+      try {
+        SIMPLE_LOGGER_PROPS.load(in);
+        in.close();
+      } catch (java.io.IOException e) {
+        // ignored
+      }
+    }
+  }
+
+  /** The current log level */
+  protected int currentLogLevel = LOG_LEVEL_INFO;
+  /** The short name of this simple log instance */
+  private transient String shortLogName = null;
+
+  /**
+   * Package access allows only {@link SimpleLoggerFactory} to instantiate
+   * SimpleLogger instances.
+   */
+  SimpleLogger(String name) {
+    if (!INITIALIZED) {
+      init();
+    }
+    this.name = name;
+
+    String levelString = recursivelyComputeLevelString();
+    if (levelString != null) {
+      this.currentLogLevel = stringToLevel(levelString);
+    } else {
+      this.currentLogLevel = DEFAULT_LOG_LEVEL;
+    }
+  }
+
+  String recursivelyComputeLevelString() {
+    String tempName = name;
+    String levelString = null;
+    int indexOfLastDot = tempName.length();
+    while ((levelString == null) && (indexOfLastDot > -1)) {
+      tempName = tempName.substring(0, indexOfLastDot);
+      levelString = getStringProperty(LOG_KEY_PREFIX + tempName, null);
+      indexOfLastDot = String.valueOf(tempName).lastIndexOf(".");
+    }
+    return levelString;
+  }
+
+  private static int stringToLevel(String levelStr) {
+    if ("trace".equalsIgnoreCase(levelStr)) {
+      return LOG_LEVEL_TRACE;
+    } else if ("debug".equalsIgnoreCase(levelStr)) {
+      return LOG_LEVEL_DEBUG;
+    } else if ("info".equalsIgnoreCase(levelStr)) {
+      return LOG_LEVEL_INFO;
+    } else if ("warn".equalsIgnoreCase(levelStr)) {
+      return LOG_LEVEL_WARN;
+    } else if ("error".equalsIgnoreCase(levelStr)) {
+      return LOG_LEVEL_ERROR;
+    }
+    // assume INFO by default
+    return LOG_LEVEL_INFO;
+  }
+
+
+  /**
+   * This is our internal implementation for logging regular (non-parameterized)
+   * log messages.
+   *
+   * @param level   One of the LOG_LEVEL_XXX constants defining the log level
+   * @param message The message itself
+   * @param t       The exception whose stack trace should be logged
+   */
+  protected void log(int level, String message, Throwable t) {
+    if (!isLevelEnabled(level)) {
+      return;
+    }
+
+    StringBuffer buf = new StringBuffer(32);
+
+    // Append date-time if so configured
+    if (SHOW_DATE_TIME) {
+      if (DATE_FORMATTER != null) {
+        buf.append(getFormattedDate());
+        buf.append(' ');
+      } else {
+        buf.append(System.currentTimeMillis() - START_TIME);
+        buf.append(' ');
+      }
+    }
+
+    // Append current thread name if so configured
+    if (SHOW_THREAD_NAME) {
+      buf.append('[');
+      buf.append(Thread.currentThread().getName());
+      buf.append("] ");
+    }
+
+    if (LEVEL_IN_BRACKETS) buf.append('[');
+
+    // Append a readable representation of the log level
+    switch (level) {
+      case LOG_LEVEL_TRACE:
+        buf.append("TRACE");
+        break;
+      case LOG_LEVEL_DEBUG:
+        buf.append("DEBUG");
+        break;
+      case LOG_LEVEL_INFO:
+        buf.append("INFO");
+        break;
+      case LOG_LEVEL_WARN:
+        buf.append(WARN_LEVEL_STRING);
+        break;
+      case LOG_LEVEL_ERROR:
+        buf.append("ERROR");
+        break;
+    }
+    if (LEVEL_IN_BRACKETS) buf.append(']');
+    buf.append(' ');
+
+    // Append the name of the log instance if so configured
+    if (SHOW_SHORT_LOG_NAME) {
+      if (shortLogName == null) shortLogName = computeShortName();
+      buf.append(String.valueOf(shortLogName)).append(" - ");
+    } else if (SHOW_LOG_NAME) {
+      buf.append(String.valueOf(name)).append(" - ");
+    }
+
+    // Append the message
+    buf.append(message);
+
+    write(buf, t);
+
+  }
+
+  void write(StringBuffer buf, Throwable t) {
+    TARGET_STREAM.println(buf.toString());
+    if (t != null) {
+      t.printStackTrace(TARGET_STREAM);
+    }
+    TARGET_STREAM.flush();
+  }
+
+  private String getFormattedDate() {
+    Date now = new Date();
+    String dateText;
+    synchronized (DATE_FORMATTER) {
+      dateText = DATE_FORMATTER.format(now);
+    }
+    return dateText;
+  }
+
+  private String computeShortName() {
+    return name.substring(name.lastIndexOf(".") + 1);
+  }
+
+  /**
+   * For formatted messages, first substitute arguments and then log.
+   *
+   * @param level
+   * @param format
+   * @param arg1
+   * @param arg2
+   */
+  private void formatAndLog(int level, String format, Object arg1,
+                            Object arg2) {
+    if (!isLevelEnabled(level)) {
+      return;
+    }
+    FormattingTuple tp = MessageFormatter.format(format, arg1, arg2);
+    log(level, tp.getMessage(), tp.getThrowable());
+  }
+
+  /**
+   * For formatted messages, first substitute arguments and then log.
+   *
+   * @param level
+   * @param format
+   * @param arguments a list of 3 ore more arguments
+   */
+  private void formatAndLog(int level, String format, Object... arguments) {
+    if (!isLevelEnabled(level)) {
+      return;
+    }
+    FormattingTuple tp = MessageFormatter.arrayFormat(format, arguments);
+    log(level, tp.getMessage(), tp.getThrowable());
+  }
+
+  /**
+   * Is the given log level currently enabled?
+   *
+   * @param logLevel is this level enabled?
+   */
+  protected boolean isLevelEnabled(int logLevel) {
+    // log level are numerically ordered so can use simple numeric
+    // comparison
+    return (logLevel >= currentLogLevel);
+  }
+
+  /** Are {@code trace} messages currently enabled? */
+  public boolean isTraceEnabled() {
+    return isLevelEnabled(LOG_LEVEL_TRACE);
+  }
+
+  /**
+   * A simple implementation which logs messages of level TRACE according
+   * to the format outlined above.
+   */
+  public void trace(String msg) {
+    log(LOG_LEVEL_TRACE, msg, null);
+  }
+
+  /**
+   * Perform single parameter substitution before logging the message of level
+   * TRACE according to the format outlined above.
+   */
+  public void trace(String format, Object param1) {
+    formatAndLog(LOG_LEVEL_TRACE, format, param1, null);
+  }
+
+  /**
+   * Perform double parameter substitution before logging the message of level
+   * TRACE according to the format outlined above.
+   */
+  public void trace(String format, Object param1, Object param2) {
+    formatAndLog(LOG_LEVEL_TRACE, format, param1, param2);
+  }
+
+  /**
+   * Perform double parameter substitution before logging the message of level
+   * TRACE according to the format outlined above.
+   */
+  public void trace(String format, Object... argArray) {
+    formatAndLog(LOG_LEVEL_TRACE, format, argArray);
+  }
+
+  /** Log a message of level TRACE, including an exception. */
+  public void trace(String msg, Throwable t) {
+    log(LOG_LEVEL_TRACE, msg, t);
+  }
+
+  /** Are {@code debug} messages currently enabled? */
+  public boolean isDebugEnabled() {
+    return isLevelEnabled(LOG_LEVEL_DEBUG);
+  }
+
+  /**
+   * A simple implementation which logs messages of level DEBUG according
+   * to the format outlined above.
+   */
+  public void debug(String msg) {
+    log(LOG_LEVEL_DEBUG, msg, null);
+  }
+
+  /**
+   * Perform single parameter substitution before logging the message of level
+   * DEBUG according to the format outlined above.
+   */
+  public void debug(String format, Object param1) {
+    formatAndLog(LOG_LEVEL_DEBUG, format, param1, null);
+  }
+
+  /**
+   * Perform double parameter substitution before logging the message of level
+   * DEBUG according to the format outlined above.
+   */
+  public void debug(String format, Object param1, Object param2) {
+    formatAndLog(LOG_LEVEL_DEBUG, format, param1, param2);
+  }
+
+  /**
+   * Perform double parameter substitution before logging the message of level
+   * DEBUG according to the format outlined above.
+   */
+  public void debug(String format, Object... argArray) {
+    formatAndLog(LOG_LEVEL_DEBUG, format, argArray);
+  }
+
+  /** Log a message of level DEBUG, including an exception. */
+  public void debug(String msg, Throwable t) {
+    log(LOG_LEVEL_DEBUG, msg, t);
+  }
+
+  /** Are {@code info} messages currently enabled? */
+  public boolean isInfoEnabled() {
+    return isLevelEnabled(LOG_LEVEL_INFO);
+  }
+
+  /**
+   * A simple implementation which logs messages of level INFO according
+   * to the format outlined above.
+   */
+  public void info(String msg) {
+    log(LOG_LEVEL_INFO, msg, null);
+  }
+
+  /**
+   * Perform single parameter substitution before logging the message of level
+   * INFO according to the format outlined above.
+   */
+  public void info(String format, Object arg) {
+    formatAndLog(LOG_LEVEL_INFO, format, arg, null);
+  }
+
+  /**
+   * Perform double parameter substitution before logging the message of level
+   * INFO according to the format outlined above.
+   */
+  public void info(String format, Object arg1, Object arg2) {
+    formatAndLog(LOG_LEVEL_INFO, format, arg1, arg2);
+  }
+
+  /**
+   * Perform double parameter substitution before logging the message of level
+   * INFO according to the format outlined above.
+   */
+  public void info(String format, Object... argArray) {
+    formatAndLog(LOG_LEVEL_INFO, format, argArray);
+  }
+
+  /** Log a message of level INFO, including an exception. */
+  public void info(String msg, Throwable t) {
+    log(LOG_LEVEL_INFO, msg, t);
+  }
+
+  /** Are {@code warn} messages currently enabled? */
+  public boolean isWarnEnabled() {
+    return isLevelEnabled(LOG_LEVEL_WARN);
+  }
+
+  /**
+   * A simple implementation which always logs messages of level WARN according
+   * to the format outlined above.
+   */
+  public void warn(String msg) {
+    log(LOG_LEVEL_WARN, msg, null);
+  }
+
+  /**
+   * Perform single parameter substitution before logging the message of level
+   * WARN according to the format outlined above.
+   */
+  public void warn(String format, Object arg) {
+    formatAndLog(LOG_LEVEL_WARN, format, arg, null);
+  }
+
+  /**
+   * Perform double parameter substitution before logging the message of level
+   * WARN according to the format outlined above.
+   */
+  public void warn(String format, Object arg1, Object arg2) {
+    formatAndLog(LOG_LEVEL_WARN, format, arg1, arg2);
+  }
+
+  /**
+   * Perform double parameter substitution before logging the message of level
+   * WARN according to the format outlined above.
+   */
+  public void warn(String format, Object... argArray) {
+    formatAndLog(LOG_LEVEL_WARN, format, argArray);
+  }
+
+  /** Log a message of level WARN, including an exception. */
+  public void warn(String msg, Throwable t) {
+    log(LOG_LEVEL_WARN, msg, t);
+  }
+
+  /** Are {@code error} messages currently enabled? */
+  public boolean isErrorEnabled() {
+    return isLevelEnabled(LOG_LEVEL_ERROR);
+  }
+
+  /**
+   * A simple implementation which always logs messages of level ERROR according
+   * to the format outlined above.
+   */
+  public void error(String msg) {
+    log(LOG_LEVEL_ERROR, msg, null);
+  }
+
+  /**
+   * Perform single parameter substitution before logging the message of level
+   * ERROR according to the format outlined above.
+   */
+  public void error(String format, Object arg) {
+    formatAndLog(LOG_LEVEL_ERROR, format, arg, null);
+  }
+
+  /**
+   * Perform double parameter substitution before logging the message of level
+   * ERROR according to the format outlined above.
+   */
+  public void error(String format, Object arg1, Object arg2) {
+    formatAndLog(LOG_LEVEL_ERROR, format, arg1, arg2);
+  }
+
+  /**
+   * Perform double parameter substitution before logging the message of level
+   * ERROR according to the format outlined above.
+   */
+  public void error(String format, Object... argArray) {
+    formatAndLog(LOG_LEVEL_ERROR, format, argArray);
+  }
+
+  /** Log a message of level ERROR, including an exception. */
+  public void error(String msg, Throwable t) {
+    log(LOG_LEVEL_ERROR, msg, t);
+  }
+}
diff --git a/osgi-bundles/libs/slf4j-osgi/src/main/java/org/slf4j/impl/StaticLoggerBinder.java b/osgi-bundles/libs/slf4j-osgi/src/main/java/org/slf4j/impl/StaticLoggerBinder.java
new file mode 100644
index 0000000..764ce79
--- /dev/null
+++ b/osgi-bundles/libs/slf4j-osgi/src/main/java/org/slf4j/impl/StaticLoggerBinder.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 org.slf4j.impl;
+
+import org.slf4j.ILoggerFactory;
+import org.slf4j.spi.LoggerFactoryBinder;
+
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillLogService;
+
+public class StaticLoggerBinder implements LoggerFactoryBinder {
+
+    /**
+     * The unique instance of this class.
+     */
+    private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
+
+    /**
+     * Return the singleton of this class.
+     *
+     * @return the StaticLoggerBinder singleton
+     */
+    public static StaticLoggerBinder getSingleton() {
+        return SINGLETON;
+    }
+
+    /**
+     * Declare the version of the SLF4J API this implementation is compiled
+     * against. The value of this field is usually modified with each release.
+     */
+    // to avoid constant folding by the compiler, this field must *not* be final
+    public static String REQUESTED_API_VERSION = "1.7.2"; // !final
+
+    private static final String loggerFactoryClassStr = OSGISlf4jLoggerFactory.class.getName();
+
+    /**
+     * The ILoggerFactory instance returned by the {@link #getLoggerFactory}
+     * method should always be the same object
+     */
+    private final OSGISlf4jLoggerFactory loggerFactory;
+
+    private StaticLoggerBinder() {
+        loggerFactory = new OSGISlf4jLoggerFactory();
+    }
+
+    public ILoggerFactory getLoggerFactory() {
+        return loggerFactory;
+    }
+
+    public String getLoggerFactoryClassStr() {
+        return loggerFactoryClassStr;
+    }
+
+    public void setLogService(final OSGIKillbillLogService logService) {
+        loggerFactory.setDelegate(logService);
+    }
+}
diff --git a/osgi-bundles/pom.xml b/osgi-bundles/pom.xml
index b8e21ae..3c5e4f2 100644
--- a/osgi-bundles/pom.xml
+++ b/osgi-bundles/pom.xml
@@ -23,12 +23,13 @@
         <version>0.1.56-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
-    <artifactId>killbill-osgi-bundles</artifactId>
-    <name>Killbill billing platform: OSGI bundles</name>
+    <artifactId>killbill-osgi-all-bundles</artifactId>
+    <name>Killbill billing platform: OSGI ALL bundles</name>
     <packaging>pom</packaging>
     <modules>
-        <module>hello</module>
-        <module>jruby</module>
-        <module>meter</module>
+        <module>libs</module>
+        <module>bundles</module>
+        <module>tests</module>
+        <module>defaultbundles</module>
     </modules>
 </project>
diff --git a/osgi-bundles/tests/beatrix/pom.xml b/osgi-bundles/tests/beatrix/pom.xml
new file mode 100644
index 0000000..069db77
--- /dev/null
+++ b/osgi-bundles/tests/beatrix/pom.xml
@@ -0,0 +1,159 @@
+<?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-test-bundles</artifactId>
+        <version>0.1.56-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <artifactId>killbill-osgi-bundles-test-beatrix</artifactId>
+    <name>Killbill billing platform: OSGI Beatrix Test bundle</name>
+    <packaging>bundle</packaging>
+    <dependencies>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-osgi-bundles-lib-killbill</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <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>
+        <dependency>
+            <groupId>org.jdbi</groupId>
+            <artifactId>jdbi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <version>1.5</version>
+                <executions>
+                    <execution>
+                        <id>add-source</id>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>add-source</goal>
+                        </goals>
+                        <configuration>
+                            <sources>
+                                <source>src/test/java</source>
+                            </sources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Bundle-Activator>com.ning.billing.osgi.bundles.test.TestActivator</Bundle-Activator>
+                        <Import-Package>
+                            <!-- maven-bundle-plugin does not seem to detect that the library is using OSGIKillbill, this is annoying... -->
+                            *;resolution:=optional,com.ning.billing.osgi.api
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>manifest</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>assemble-killbill-osgi-bundles-test-beatrix</id>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <phase>package</phase>
+                        <configuration>
+                            <shadedArtifactAttached>true</shadedArtifactAttached>
+                            <shadedClassifierName>jar-with-dependencies</shadedClassifierName>
+                            <filters>
+                                <filter>
+                                    <artifact>${project.groupId}:${project.artifactId}</artifact>
+                                </filter>
+                            </filters>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-clean-plugin</artifactId>
+                <version>2.5</version>
+                <executions>
+                    <execution>
+                        <id>auto-clean</id>
+                        <phase>install</phase>
+                        <goals>
+                            <goal>clean</goal>
+                        </goals>
+                        <configuration>
+                            <excludeDefaultDirectories>true</excludeDefaultDirectories>
+                            <filesets>
+                                <fileset>
+                                    <directory>${project.build.directory}</directory>
+                                    <includes>
+                                        <include>${project.artifactId}-${project.version}.jar</include>
+                                        <include>${project.artifactId}-${project.version}-sources.jar</include>
+                                    </includes>
+                                </fileset>
+                            </filesets>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/osgi-bundles/tests/beatrix/src/test/java/com/ning/billing/osgi/bundles/test/dao/TestDao.java b/osgi-bundles/tests/beatrix/src/test/java/com/ning/billing/osgi/bundles/test/dao/TestDao.java
new file mode 100644
index 0000000..fe63c4c
--- /dev/null
+++ b/osgi-bundles/tests/beatrix/src/test/java/com/ning/billing/osgi/bundles/test/dao/TestDao.java
@@ -0,0 +1,89 @@
+/*
+ * 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.dao;
+
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.skife.jdbi.v2.Handle;
+import org.skife.jdbi.v2.IDBI;
+import org.skife.jdbi.v2.TransactionCallback;
+import org.skife.jdbi.v2.TransactionStatus;
+
+public class TestDao {
+
+    private final IDBI dbi;
+
+    public TestDao(final IDBI dbi) {
+        this.dbi = dbi;
+    }
+
+    public void createTable() {
+
+        dbi.inTransaction(new TransactionCallback<Object>() {
+            @Override
+            public Object inTransaction(final Handle conn, final TransactionStatus status) throws Exception {
+                conn.execute("DROP TABLE IF EXISTS test_bundle;");
+                conn.execute("CREATE TABLE test_bundle (" +
+                "record_id int(11) unsigned NOT NULL AUTO_INCREMENT, " +
+                "is_started bool DEFAULT false, " +
+                "is_logged bool DEFAULT false, " +
+                "external_key varchar(128) NULL, " +
+                "payment_id char(36) NULL," +
+                "payment_method_id char(36) NULL," +
+                "payment_amount decimal(10,4) NULL," +
+                "PRIMARY KEY(record_id)" +
+                ");");
+                return null;
+            }
+        });
+    }
+
+    public void insertStarted() {
+        dbi.inTransaction(new TransactionCallback<Object>() {
+            @Override
+            public Object inTransaction(final Handle conn, final TransactionStatus status) throws Exception {
+                conn.execute("INSERT INTO test_bundle (is_started) VALUES (1);");
+                return null;
+            }
+        });
+    }
+
+    public void insertAccountExternalKey(final String externalKey) {
+        dbi.inTransaction(new TransactionCallback<Object>() {
+            @Override
+            public Object inTransaction(final Handle conn, final TransactionStatus status) throws Exception {
+                conn.execute("UPDATE test_bundle SET external_key = '" + externalKey + "' WHERE record_id = 1;");
+                return null;
+            }
+        });
+    }
+
+    public void insertProcessedPayment(final UUID paymentId, final UUID paymentMethodId, final BigDecimal paymentAmount) {
+        dbi.inTransaction(new TransactionCallback<Object>() {
+            @Override
+            public Object inTransaction(final Handle conn, final TransactionStatus status) throws Exception {
+                conn.execute("UPDATE test_bundle SET payment_id = '" + paymentId.toString() +
+                             "', payment_method_id = '" + paymentMethodId.toString() +
+                             "', payment_amount = " + paymentAmount +
+                             " WHERE record_id = 1;");
+                return null;
+            }
+        });
+    }
+}
diff --git a/osgi-bundles/tests/beatrix/src/test/java/com/ning/billing/osgi/bundles/test/TestActivator.java b/osgi-bundles/tests/beatrix/src/test/java/com/ning/billing/osgi/bundles/test/TestActivator.java
new file mode 100644
index 0000000..53a7ea4
--- /dev/null
+++ b/osgi-bundles/tests/beatrix/src/test/java/com/ning/billing/osgi/bundles/test/TestActivator.java
@@ -0,0 +1,108 @@
+/*
+ * 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 java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.UUID;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+import org.skife.jdbi.v2.DBI;
+import org.skife.jdbi.v2.IDBI;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.beatrix.bus.api.ExtBusEvent;
+import com.ning.billing.beatrix.bus.api.ExtBusEventType;
+import com.ning.billing.osgi.api.OSGIPluginProperties;
+import com.ning.billing.osgi.bundles.test.dao.TestDao;
+import com.ning.billing.payment.plugin.api.PaymentPluginApi;
+import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.killbill.osgi.libs.killbill.KillbillActivatorBase;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillEventDispatcher.OSGIKillbillEventHandler;
+
+/**
+ * Test class used by Beatrix OSGI test to verify that:
+ * - "test" bundle is started
+ * - test bundle is able to make API call
+ * - test bundle is able to register a fake PaymentApi service
+ * - test bundle can use the DataSource from Killbill and write on disk
+ */
+public class TestActivator extends KillbillActivatorBase implements OSGIKillbillEventHandler {
+
+    private TestDao testDao;
+
+    @Override
+    public void start(final BundleContext context) throws Exception {
+
+        final String bundleName = context.getBundle().getSymbolicName();
+        System.out.println("TestActivator starting bundle = " + bundleName);
+
+        super.start(context);
+
+
+        final IDBI dbi = new DBI(dataSource.getDataSource());
+        testDao = new TestDao(dbi);
+        testDao.createTable();
+        testDao.insertStarted();
+        registerPaymentApi(context, testDao);
+    }
+
+    @Override
+    public void stop(final BundleContext context) throws Exception {
+        super.stop(context);
+        System.out.println("Good bye world from TestActivator!");
+    }
+
+    @Override
+    public OSGIKillbillEventHandler getOSGIKillbillEventHandler() {
+        return this;
+    }
+
+    private void registerPaymentApi(final BundleContext context, final TestDao dao) {
+        final Dictionary props = new Hashtable();
+        props.put(OSGIPluginProperties.PLUGIN_NAME_PROP, "test");
+        registrar.registerService(context, PaymentPluginApi.class, new TestPaymentPluginApi("test", dao), props);
+    }
+
+    @Override
+    public void handleKillbillEvent(final ExtBusEvent killbillEvent) {
+
+        logService.log(LogService.LOG_INFO, "Received external event " + killbillEvent.toString());
+
+        // Only looking at account creation
+        if (killbillEvent.getEventType() != ExtBusEventType.ACCOUNT_CREATION) {
+            return;
+        }
+
+        final TenantContext tenantContext = new TenantContext() {
+            @Override
+            public UUID getTenantId() {
+                return null;
+            }
+        };
+
+        try {
+            Account account = killbillAPI.getAccountUserApi().getAccountById(killbillEvent.getAccountId(), tenantContext);
+            testDao.insertAccountExternalKey(account.getExternalKey());
+
+        } catch (AccountApiException e) {
+            logService.log(LogService.LOG_ERROR, e.getMessage());
+        }
+    }
+}
diff --git a/osgi-bundles/tests/beatrix/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java b/osgi-bundles/tests/beatrix/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java
new file mode 100644
index 0000000..2b05d87
--- /dev/null
+++ b/osgi-bundles/tests/beatrix/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java
@@ -0,0 +1,118 @@
+/*
+ * 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 java.math.BigDecimal;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.osgi.bundles.test.dao.TestDao;
+import com.ning.billing.payment.api.PaymentMethodPlugin;
+import com.ning.billing.payment.plugin.api.PaymentInfoPlugin;
+import com.ning.billing.payment.plugin.api.PaymentMethodInfoPlugin;
+import com.ning.billing.payment.plugin.api.PaymentPluginApi;
+import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
+import com.ning.billing.payment.plugin.api.RefundInfoPlugin;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.TenantContext;
+
+public class TestPaymentPluginApi implements PaymentPluginApi {
+
+    private final TestDao testDao;
+    private final String name;
+
+    public TestPaymentPluginApi(final String name, final TestDao testDao) {
+        this.testDao = testDao;
+        this.name = name;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public PaymentInfoPlugin processPayment(final UUID kbPaymentId, final UUID kbPaymentMethodId, final BigDecimal amount, final CallContext context) throws PaymentPluginApiException {
+        testDao.insertProcessedPayment(kbPaymentId, kbPaymentMethodId, amount);
+        return new PaymentInfoPlugin() {
+            @Override
+            public BigDecimal getAmount() {
+                return amount;
+            }
+            @Override
+            public DateTime getCreatedDate() {
+                return new DateTime();
+            }
+            @Override
+            public DateTime getEffectiveDate() {
+                return new DateTime();
+            }
+            @Override
+            public PaymentPluginStatus getStatus() {
+                return PaymentPluginStatus.PROCESSED;
+            }
+            @Override
+            public String getGatewayError() {
+                return null;
+            }
+            @Override
+            public String getGatewayErrorCode() {
+                return null;
+            }
+        };
+    }
+
+    @Override
+    public PaymentInfoPlugin getPaymentInfo(final UUID kbPaymentId, final TenantContext context) throws PaymentPluginApiException {
+        return null;
+    }
+
+    @Override
+    public RefundInfoPlugin processRefund(final UUID kbPaymentId, final BigDecimal refundAmount, final CallContext context) throws PaymentPluginApiException {
+        return null;
+    }
+
+    @Override
+    public void addPaymentMethod(final UUID kbAccountId, final UUID kbPaymentMethodId, final PaymentMethodPlugin paymentMethodProps, final boolean setDefault, final CallContext context) throws PaymentPluginApiException {
+    }
+
+    @Override
+    public void deletePaymentMethod(final UUID kbPaymentMethodId, final CallContext context) throws PaymentPluginApiException {
+    }
+
+    @Override
+    public PaymentMethodPlugin getPaymentMethodDetail(final UUID kbAccountId, final UUID kbPaymentMethodId, final TenantContext context) throws PaymentPluginApiException {
+        return null;
+    }
+
+    @Override
+    public void setDefaultPaymentMethod(final UUID kbPaymentMethodId, final CallContext context) throws PaymentPluginApiException {
+    }
+
+    @Override
+    public List<PaymentMethodInfoPlugin> getPaymentMethods(final UUID kbAccountId, final boolean refreshFromGateway, final CallContext context) throws PaymentPluginApiException {
+        return null;
+    }
+
+    @Override
+    public void resetPaymentMethods(final List<PaymentMethodInfoPlugin> paymentMethods) throws PaymentPluginApiException {
+    }
+
+
+}
diff --git a/osgi-bundles/tests/beatrix/src/test/resources/ddl_test.sql b/osgi-bundles/tests/beatrix/src/test/resources/ddl_test.sql
new file mode 100644
index 0000000..e4bfa5d
--- /dev/null
+++ b/osgi-bundles/tests/beatrix/src/test/resources/ddl_test.sql
@@ -0,0 +1,10 @@
+/*! SET storage_engine=INNODB */;
+
+DROP TABLE IF EXISTS test_bundle;
+CREATE TABLE test_bundle (
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    is_started bool DEFAULT false,
+    is_logged bool DEFAULT false,
+    external_key varchar(128) NULL
+    PRIMARY KEY(record_id)
+);
diff --git a/osgi-bundles/tests/payment/pom.xml b/osgi-bundles/tests/payment/pom.xml
new file mode 100644
index 0000000..c63ad49
--- /dev/null
+++ b/osgi-bundles/tests/payment/pom.xml
@@ -0,0 +1,166 @@
+<?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-test-bundles</artifactId>
+        <version>0.1.56-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <artifactId>killbill-osgi-bundles-test-payment</artifactId>
+    <name>Killbill billing platform: OSGI Beatrix Test payment</name>
+    <packaging>bundle</packaging>
+    <dependencies>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-osgi-bundles-lib-killbill</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <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>
+        <dependency>
+            <groupId>org.jdbi</groupId>
+            <artifactId>jdbi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-util</artifactId>
+            <type>test-jar</type>
+            <!-- Override the scope since we build this test as 'source' -->
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <version>1.5</version>
+                <executions>
+                    <execution>
+                        <id>add-source</id>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>add-source</goal>
+                        </goals>
+                        <configuration>
+                            <sources>
+                                <source>src/test/java</source>
+                            </sources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Bundle-Activator>com.ning.billing.osgi.bundles.test.PaymentActivator</Bundle-Activator>
+                        <Import-Package>
+                            <!-- maven-bundle-plugin does not seem to detect that the library is using OSGIKillbill, this is annoying... -->
+                            *;resolution:=optional,com.ning.billing.osgi.api
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>manifest</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>assemble-killbill-osgi-bundles-test</id>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <phase>package</phase>
+                        <configuration>
+                            <shadedArtifactAttached>true</shadedArtifactAttached>
+                            <shadedClassifierName>jar-with-dependencies</shadedClassifierName>
+                            <filters>
+                                <filter>
+                                    <artifact>${project.groupId}:${project.artifactId}</artifact>
+                                </filter>
+                            </filters>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-clean-plugin</artifactId>
+                <version>2.5</version>
+                <executions>
+                    <execution>
+                        <id>auto-clean</id>
+                        <phase>install</phase>
+                        <goals>
+                            <goal>clean</goal>
+                        </goals>
+                        <configuration>
+                            <excludeDefaultDirectories>true</excludeDefaultDirectories>
+                            <filesets>
+                                <fileset>
+                                    <directory>${project.build.directory}</directory>
+                                    <includes>
+                                        <include>${project.artifactId}-${project.version}.jar</include>
+                                        <include>${project.artifactId}-${project.version}-sources.jar</include>
+                                    </includes>
+                                </fileset>
+                            </filesets>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/osgi-bundles/tests/payment/src/test/java/com/ning/billing/osgi/bundles/test/PaymentActivator.java b/osgi-bundles/tests/payment/src/test/java/com/ning/billing/osgi/bundles/test/PaymentActivator.java
new file mode 100644
index 0000000..00d2113
--- /dev/null
+++ b/osgi-bundles/tests/payment/src/test/java/com/ning/billing/osgi/bundles/test/PaymentActivator.java
@@ -0,0 +1,63 @@
+/*
+ * 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 java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.osgi.framework.BundleContext;
+
+import com.ning.billing.osgi.api.OSGIPluginProperties;
+import com.ning.billing.payment.plugin.api.PaymentPluginApi;
+import com.ning.billing.payment.plugin.api.PaymentPluginApiWithTestControl;
+import com.ning.killbill.osgi.libs.killbill.KillbillActivatorBase;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillEventDispatcher.OSGIKillbillEventHandler;
+
+/**
+ * Test class used by Payment tests-- to test fake OSGI payment bundle
+ */
+public class PaymentActivator extends KillbillActivatorBase {
+
+    @Override
+    public void start(final BundleContext context) throws Exception {
+
+        final String bundleName = context.getBundle().getSymbolicName();
+        System.out.println("PaymentActivator starting bundle = " + bundleName);
+
+        super.start(context);
+        registerPaymentApi(context);
+    }
+
+    @Override
+    public void stop(final BundleContext context) throws Exception {
+        super.stop(context);
+        System.out.println("Good bye world from PaymentActivator!");
+    }
+
+    @Override
+    public OSGIKillbillEventHandler getOSGIKillbillEventHandler() {
+        return null;
+    }
+
+    private void registerPaymentApi(final BundleContext context) {
+
+        final Dictionary props = new Hashtable();
+        // Same name the beatrix tests expect when using that payment plugin
+        props.put(OSGIPluginProperties.PLUGIN_NAME_PROP, "osgiPaymentPlugin");
+        registrar.registerService(context, PaymentPluginApiWithTestControl.class, new TestPaymentPluginApi("test"), props);
+    }
+}
diff --git a/osgi-bundles/tests/payment/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java b/osgi-bundles/tests/payment/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java
new file mode 100644
index 0000000..0c77cf2
--- /dev/null
+++ b/osgi-bundles/tests/payment/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java
@@ -0,0 +1,200 @@
+/*
+ * 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 java.math.BigDecimal;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.payment.api.PaymentMethodPlugin;
+import com.ning.billing.payment.plugin.api.PaymentInfoPlugin;
+import com.ning.billing.payment.plugin.api.PaymentMethodInfoPlugin;
+import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
+import com.ning.billing.payment.plugin.api.PaymentPluginApiWithTestControl;
+import com.ning.billing.payment.plugin.api.RefundInfoPlugin;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.TenantContext;
+
+public class TestPaymentPluginApi implements PaymentPluginApiWithTestControl {
+
+    private final String name;
+
+    private PaymentPluginApiException paymentPluginApiExceptionOnNextCalls;
+    private RuntimeException runtimeExceptionOnNextCalls;
+
+    public TestPaymentPluginApi(final String name) {
+        this.name = name;
+        resetToNormalbehavior();
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public PaymentInfoPlugin processPayment(final UUID kbPaymentId, final UUID kbPaymentMethodId, final BigDecimal amount, final CallContext context) throws PaymentPluginApiException {
+        return withRuntimeCheckForExceptions(new PaymentInfoPlugin() {
+            @Override
+            public BigDecimal getAmount() {
+                return amount;
+            }
+            @Override
+            public DateTime getCreatedDate() {
+                return new DateTime();
+            }
+            @Override
+            public DateTime getEffectiveDate() {
+                return new DateTime();
+            }
+            @Override
+            public PaymentPluginStatus getStatus() {
+                return PaymentPluginStatus.PROCESSED;
+            }
+            @Override
+            public String getGatewayError() {
+                return null;
+            }
+            @Override
+            public String getGatewayErrorCode() {
+                return null;
+            }
+        });
+    }
+
+    @Override
+    public PaymentInfoPlugin getPaymentInfo(final UUID kbPaymentId, final TenantContext context) throws PaymentPluginApiException {
+
+        final BigDecimal someAmount = new BigDecimal("12.45");
+        return withRuntimeCheckForExceptions(new PaymentInfoPlugin() {
+            @Override
+            public BigDecimal getAmount() {
+                return someAmount;
+            }
+            @Override
+            public DateTime getCreatedDate() {
+                return new DateTime();
+            }
+            @Override
+            public DateTime getEffectiveDate() {
+                return new DateTime();
+            }
+            @Override
+            public PaymentPluginStatus getStatus() {
+                return PaymentPluginStatus.PROCESSED;
+            }
+            @Override
+            public String getGatewayError() {
+                return null;
+            }
+            @Override
+            public String getGatewayErrorCode() {
+                return null;
+            }
+        });
+    }
+
+    @Override
+    public RefundInfoPlugin processRefund(final UUID kbPaymentId, final BigDecimal refundAmount, final CallContext context) throws PaymentPluginApiException {
+
+        final BigDecimal someAmount = new BigDecimal("12.45");
+        return withRuntimeCheckForExceptions(new RefundInfoPlugin() {
+            @Override
+            public BigDecimal getAmount() {
+                return null;
+            }
+            @Override
+            public DateTime getCreatedDate() {
+                return null;
+            }
+            @Override
+            public DateTime getEffectiveDate() {
+                return null;
+            }
+            @Override
+            public RefundPluginStatus getStatus() {
+                return null;
+            }
+            @Override
+            public String getGatewayError() {
+                return null;
+            }
+            @Override
+            public String getGatewayErrorCode() {
+                return null;
+            }
+        });
+    }
+
+    @Override
+    public void addPaymentMethod(final UUID kbAccountId, final UUID kbPaymentMethodId, final PaymentMethodPlugin paymentMethodProps, final boolean setDefault, final CallContext context) throws PaymentPluginApiException {
+    }
+
+    @Override
+    public void deletePaymentMethod(final UUID kbPaymentMethodId, final CallContext context) throws PaymentPluginApiException {
+    }
+
+    @Override
+    public PaymentMethodPlugin getPaymentMethodDetail(final UUID kbAccountId, final UUID kbPaymentMethodId, final TenantContext context) throws PaymentPluginApiException {
+        return null;
+    }
+
+    @Override
+    public void setDefaultPaymentMethod(final UUID kbPaymentMethodId, final CallContext context) throws PaymentPluginApiException {
+    }
+
+    @Override
+    public List<PaymentMethodInfoPlugin> getPaymentMethods(final UUID kbAccountId, final boolean refreshFromGateway, final CallContext context) throws PaymentPluginApiException {
+        return null;
+    }
+
+    @Override
+    public void resetPaymentMethods(final List<PaymentMethodInfoPlugin> paymentMethods) throws PaymentPluginApiException {
+    }
+
+
+    private <T> T withRuntimeCheckForExceptions(final T result) throws PaymentPluginApiException {
+        if (paymentPluginApiExceptionOnNextCalls != null) {
+            throw paymentPluginApiExceptionOnNextCalls;
+
+        } else if (runtimeExceptionOnNextCalls != null) {
+            throw runtimeExceptionOnNextCalls;
+        } else {
+            return result;
+        }
+    }
+
+    @Override
+    public void setPaymentPluginApiExceptionOnNextCalls(final PaymentPluginApiException e) {
+        resetToNormalbehavior();
+        paymentPluginApiExceptionOnNextCalls = e;
+    }
+
+    @Override
+    public void setPaymentRuntimeExceptionOnNextCalls(final RuntimeException e) {
+        resetToNormalbehavior();
+        runtimeExceptionOnNextCalls = e;
+    }
+
+    @Override
+    public void resetToNormalbehavior() {
+        paymentPluginApiExceptionOnNextCalls = null;
+        runtimeExceptionOnNextCalls = null;
+    }
+}
diff --git a/osgi-bundles/tests/payment/src/test/resources/ddl_test.sql b/osgi-bundles/tests/payment/src/test/resources/ddl_test.sql
new file mode 100644
index 0000000..e4bfa5d
--- /dev/null
+++ b/osgi-bundles/tests/payment/src/test/resources/ddl_test.sql
@@ -0,0 +1,10 @@
+/*! SET storage_engine=INNODB */;
+
+DROP TABLE IF EXISTS test_bundle;
+CREATE TABLE test_bundle (
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    is_started bool DEFAULT false,
+    is_logged bool DEFAULT false,
+    external_key varchar(128) NULL
+    PRIMARY KEY(record_id)
+);
diff --git a/osgi-bundles/tests/pom.xml b/osgi-bundles/tests/pom.xml
new file mode 100644
index 0000000..79083cf
--- /dev/null
+++ b/osgi-bundles/tests/pom.xml
@@ -0,0 +1,33 @@
+<?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-all-bundles</artifactId>
+        <version>0.1.56-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <artifactId>killbill-osgi-test-bundles</artifactId>
+    <name>Killbill billing platform: OSGI Test bundles</name>
+    <packaging>pom</packaging>
+    <modules>
+        <module>beatrix</module>
+        <module>payment</module>
+    </modules>
+</project>
diff --git a/osgi-bundles/tests/src/assemble/assembly.xml b/osgi-bundles/tests/src/assemble/assembly.xml
new file mode 100644
index 0000000..44d5bf3
--- /dev/null
+++ b/osgi-bundles/tests/src/assemble/assembly.xml
@@ -0,0 +1,58 @@
+<!--
+            POM
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <version>2.3</version>
+                <configuration>
+                    <tarLongFileMode>gnu</tarLongFileMode>
+                    <appendAssemblyId>false</appendAssemblyId>
+                    <descriptors>
+                        <descriptor>src/assemble/assembly.xml</descriptor>
+                    </descriptors>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>assemble-bundle-test</id>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                        <phase>package</phase>
+                    </execution>
+                </executions>
+            </plugin>
+
+-->
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
+          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+    <id>xoxo-test</id>
+    <formats>
+        <format>jar</format>
+    </formats>
+    <includeBaseDirectory>false</includeBaseDirectory>
+
+    <fileSets>
+        <fileSet>
+            <directory>target/classes</directory>
+            <outputDirectory>/</outputDirectory>
+            <fileMode>0755</fileMode>
+        </fileSet>
+    </fileSets>
+
+    <dependencySets>
+        <dependencySet>
+
+            <scope>compile</scope>
+            <outputFileNameMapping>
+                ${artifact.groupId}-${artifact.artifactId}-${artifact.version}${dashClassifier?}.${artifact.extension}
+            </outputFileNameMapping>
+            <useProjectArtifact>false</useProjectArtifact>
+            <outputDirectory>/</outputDirectory>
+            <unpack>true</unpack>
+            <fileMode>0644</fileMode>
+        </dependencySet>
+
+    </dependencySets>
+
+</assembly>
diff --git a/osgi-bundles/tests/src/test/resources/ddl_test.sql b/osgi-bundles/tests/src/test/resources/ddl_test.sql
new file mode 100644
index 0000000..e4bfa5d
--- /dev/null
+++ b/osgi-bundles/tests/src/test/resources/ddl_test.sql
@@ -0,0 +1,10 @@
+/*! SET storage_engine=INNODB */;
+
+DROP TABLE IF EXISTS test_bundle;
+CREATE TABLE test_bundle (
+    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    is_started bool DEFAULT false,
+    is_logged bool DEFAULT false,
+    external_key varchar(128) NULL
+    PRIMARY KEY(record_id)
+);

overdue/pom.xml 22(+19 -3)

diff --git a/overdue/pom.xml b/overdue/pom.xml
index 15e394d..f9664d3 100644
--- a/overdue/pom.xml
+++ b/overdue/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>
@@ -121,12 +122,27 @@
         <plugins>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-jar-plugin</artifactId>
+                <artifactId>maven-shade-plugin</artifactId>
                 <executions>
                     <execution>
+                        <id>assemble-xsd-tool-overdue</id>
                         <goals>
-                            <goal>test-jar</goal>
+                            <goal>shade</goal>
                         </goals>
+                        <phase>package</phase>
+                        <configuration>
+                            <createSourcesJar>false</createSourcesJar>
+                            <shadedArtifactAttached>true</shadedArtifactAttached>
+                            <shadedClassifierName>xsd-tool</shadedClassifierName>
+                            <transformers>
+                                <transformer
+                                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                    <manifestEntries>
+                                        <Main-Class>com.ning.billing.overdue.CreateOverdueConfigSchema</Main-Class>
+                                    </manifestEntries>
+                                </transformer>
+                            </transformers>
+                        </configuration>
                     </execution>
                 </executions>
             </plugin>
diff --git a/overdue/src/main/java/com/ning/billing/overdue/glue/DefaultOverdueModule.java b/overdue/src/main/java/com/ning/billing/overdue/glue/DefaultOverdueModule.java
index fb0989c..06e9e98 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/glue/DefaultOverdueModule.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/glue/DefaultOverdueModule.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.overdue.glue;
 
+import org.skife.config.ConfigSource;
 import org.skife.config.ConfigurationObjectFactory;
 
 import com.ning.billing.glue.OverdueModule;
@@ -37,6 +38,12 @@ import com.google.inject.AbstractModule;
 
 public class DefaultOverdueModule extends AbstractModule implements OverdueModule {
 
+    protected final ConfigSource configSource;
+
+    public DefaultOverdueModule(final ConfigSource configSource) {
+        this.configSource = configSource;
+    }
+
     @Override
     protected void configure() {
         installOverdueUserApi();
@@ -46,7 +53,7 @@ public class DefaultOverdueModule extends AbstractModule implements OverdueModul
         installOverdueWrapperFactory();
         installOverdueEmail();
 
-        final OverdueProperties config = new ConfigurationObjectFactory(System.getProperties()).build(OverdueProperties.class);
+        final OverdueProperties config = new ConfigurationObjectFactory(configSource).build(OverdueProperties.class);
         bind(OverdueProperties.class).toInstance(config);
         //bind(ExtendedOverdueService.class).to(DefaultOverdueService.class).asEagerSingleton();
         bind(OverdueCheckNotifier.class).to(DefaultOverdueCheckNotifier.class).asEagerSingleton();
diff --git a/overdue/src/main/java/com/ning/billing/overdue/OverdueProperties.java b/overdue/src/main/java/com/ning/billing/overdue/OverdueProperties.java
index 735b592..60fa371 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/OverdueProperties.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/OverdueProperties.java
@@ -18,13 +18,14 @@ package com.ning.billing.overdue;
 
 import org.skife.config.Config;
 import org.skife.config.Default;
+import org.skife.config.Description;
 
 import com.ning.billing.util.config.KillbillConfig;
 
-
 public interface OverdueProperties extends KillbillConfig {
 
     @Config("killbill.overdue.uri")
-    @Default("jar:///com/ning/billing/irs/overdue/Config.xml")
+    @Default("NoOverdueConfig.xml")
+    @Description("Overdue configuration location. Either in the classpath or in the filesystem")
     public String getConfigURI();
 }
diff --git a/overdue/src/test/java/com/ning/billing/ovedue/notification/TestDefaultOverdueCheckPoster.java b/overdue/src/test/java/com/ning/billing/ovedue/notification/TestDefaultOverdueCheckPoster.java
index 30e8d83..6130ddd 100644
--- a/overdue/src/test/java/com/ning/billing/ovedue/notification/TestDefaultOverdueCheckPoster.java
+++ b/overdue/src/test/java/com/ning/billing/ovedue/notification/TestDefaultOverdueCheckPoster.java
@@ -47,8 +47,10 @@ public class TestDefaultOverdueCheckPoster extends OverdueTestSuiteWithEmbeddedD
     private NotificationQueue overdueQueue;
     private DateTime testReferenceTime;
 
+    @Override
     @BeforeMethod(groups = "slow")
-    public void setUp() throws Exception {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         entitySqlDaoTransactionalJdbiWrapper = new EntitySqlDaoTransactionalJdbiWrapper(getDBI(), clock, cacheControllerDispatcher, nonEntityDao);
 
         overdueQueue = notificationQueueService.getNotificationQueue(DefaultOverdueService.OVERDUE_SERVICE_NAME,
diff --git a/overdue/src/test/java/com/ning/billing/overdue/calculator/TestBillingStateCalculator.java b/overdue/src/test/java/com/ning/billing/overdue/calculator/TestBillingStateCalculator.java
index 9d904f0..4d134d6 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/calculator/TestBillingStateCalculator.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/calculator/TestBillingStateCalculator.java
@@ -42,8 +42,10 @@ public class TestBillingStateCalculator extends OverdueTestSuiteNoDB {
 
     protected LocalDate now;
 
+    @Override
     @BeforeMethod(groups = "fast")
-    public void setUp() throws Exception {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         final Account account = Mockito.mock(Account.class);
         Mockito.when(account.getTimeZone()).thenReturn(DateTimeZone.UTC);
         Mockito.when(accountApi.getAccountById(Mockito.<UUID>any(), Mockito.<InternalTenantContext>any())).thenReturn(account);
diff --git a/overdue/src/test/java/com/ning/billing/overdue/glue/TestOverdueModule.java b/overdue/src/test/java/com/ning/billing/overdue/glue/TestOverdueModule.java
index e5a4cca..411e922 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/glue/TestOverdueModule.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/glue/TestOverdueModule.java
@@ -16,10 +16,7 @@
 
 package com.ning.billing.overdue.glue;
 
-import java.util.Properties;
-
 import org.skife.config.ConfigSource;
-import org.skife.config.SimplePropertyConfigSource;
 
 import com.ning.billing.mock.glue.MockAccountModule;
 import com.ning.billing.mock.glue.MockEntitlementModule;
@@ -36,17 +33,8 @@ import com.ning.billing.util.glue.CustomFieldModule;
 
 public class TestOverdueModule extends DefaultOverdueModule {
 
-    protected final ConfigSource configSource;
-
-    public TestOverdueModule() {
-        final Properties properties = new Properties(System.getProperties());
-        // Speed up the bus
-        properties.put("killbill.billing.util.persistent.bus.sleep", "10");
-        properties.put("killbill.billing.util.persistent.bus.nbThreads", "1");
-        configSource = new SimplePropertyConfigSource(properties);
-
-        // Ignore ehcache checks. Unfortunately, ehcache looks at system properties directly...
-        System.setProperty("net.sf.ehcache.skipUpdateCheck", "true");
+    public TestOverdueModule(final ConfigSource configSource) {
+        super(configSource);
     }
 
     @Override
@@ -54,10 +42,10 @@ public class TestOverdueModule extends DefaultOverdueModule {
         super.configure();
 
         install(new AuditModule());
-        install(new CacheModule());
+        install(new CacheModule(configSource));
         install(new CallContextModule());
         install(new CustomFieldModule());
-        install(new EmailModule());
+        install(new EmailModule(configSource));
         install(new MockAccountModule());
         install(new MockEntitlementModule());
         install(new MockInvoiceModule());
diff --git a/overdue/src/test/java/com/ning/billing/overdue/glue/TestOverdueModuleNoDB.java b/overdue/src/test/java/com/ning/billing/overdue/glue/TestOverdueModuleNoDB.java
index 5754d8f..006f79a 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/glue/TestOverdueModuleNoDB.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/glue/TestOverdueModuleNoDB.java
@@ -16,6 +16,8 @@
 
 package com.ning.billing.overdue.glue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.GuicyKillbillTestNoDBModule;
 import com.ning.billing.mock.glue.MockNonEntityDaoModule;
 import com.ning.billing.mock.glue.MockNotificationQueueModule;
@@ -23,13 +25,17 @@ import com.ning.billing.util.bus.InMemoryBusModule;
 
 public class TestOverdueModuleNoDB extends TestOverdueModule {
 
+    public TestOverdueModuleNoDB(final ConfigSource configSource) {
+        super(configSource);
+    }
+
     @Override
     public void configure() {
         super.configure();
 
         install(new GuicyKillbillTestNoDBModule());
         install(new MockNonEntityDaoModule());
-        install(new MockNotificationQueueModule());
+        install(new MockNotificationQueueModule(configSource));
         install(new InMemoryBusModule(configSource));
     }
 }
diff --git a/overdue/src/test/java/com/ning/billing/overdue/glue/TestOverdueModuleWithEmbeddedDB.java b/overdue/src/test/java/com/ning/billing/overdue/glue/TestOverdueModuleWithEmbeddedDB.java
index faa4480..863b5ab 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/glue/TestOverdueModuleWithEmbeddedDB.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/glue/TestOverdueModuleWithEmbeddedDB.java
@@ -16,6 +16,8 @@
 
 package com.ning.billing.overdue.glue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.GuicyKillbillTestWithEmbeddedDBModule;
 import com.ning.billing.util.glue.BusModule;
 import com.ning.billing.util.glue.NonEntityDaoModule;
@@ -23,13 +25,17 @@ import com.ning.billing.util.glue.NotificationQueueModule;
 
 public class TestOverdueModuleWithEmbeddedDB extends TestOverdueModule {
 
+    public TestOverdueModuleWithEmbeddedDB(final ConfigSource configSource) {
+        super(configSource);
+    }
+
     @Override
     public void configure() {
         super.configure();
 
         install(new GuicyKillbillTestWithEmbeddedDBModule());
         install(new NonEntityDaoModule());
-        install(new NotificationQueueModule());
+        install(new NotificationQueueModule(configSource));
         install(new BusModule(configSource));
     }
 }
diff --git a/overdue/src/test/java/com/ning/billing/overdue/notification/TestOverdueCheckNotifier.java b/overdue/src/test/java/com/ning/billing/overdue/notification/TestOverdueCheckNotifier.java
index b59d41c..47cdddc 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/notification/TestOverdueCheckNotifier.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/notification/TestOverdueCheckNotifier.java
@@ -71,7 +71,8 @@ public class TestOverdueCheckNotifier extends OverdueTestSuiteWithEmbeddedDB {
 
     @Override
     @BeforeMethod(groups = "slow")
-    public void setupTest() throws Exception {
+    public void beforeMethod() throws Exception {
+        //super.beforeMethod();
         // We override the parent method on purpose, because we want to register a different DefaultOverdueCheckNotifier
 
         final Account account = Mockito.mock(Account.class);
@@ -83,16 +84,15 @@ public class TestOverdueCheckNotifier extends OverdueTestSuiteWithEmbeddedDB {
         mockListener = new OverdueListenerMock(internalCallContextFactory);
         notifierForMock = new DefaultOverdueCheckNotifier(notificationQueueService, overdueProperties, mockListener);
 
-        bus.start();
         notifierForMock.initialize();
         notifierForMock.start();
     }
 
     @Override
     @AfterMethod(groups = "slow")
-    public void cleanupTest() throws Exception {
+    public void afterMethod() throws Exception {
         notifierForMock.stop();
-        bus.stop();
+        super.afterMethod();
     }
 
     @Test(groups = "slow")
diff --git a/overdue/src/test/java/com/ning/billing/overdue/OverdueTestSuiteNoDB.java b/overdue/src/test/java/com/ning/billing/overdue/OverdueTestSuiteNoDB.java
index 73ff92f..641b87e 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/OverdueTestSuiteNoDB.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/OverdueTestSuiteNoDB.java
@@ -32,7 +32,6 @@ import com.ning.billing.overdue.service.DefaultOverdueService;
 import com.ning.billing.overdue.wrapper.OverdueWrapperFactory;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.notificationq.NotificationQueueService;
-import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueAlreadyExists;
 import com.ning.billing.util.svcapi.account.AccountInternalApi;
 import com.ning.billing.util.svcapi.entitlement.EntitlementInternalApi;
 import com.ning.billing.util.svcapi.invoice.InvoiceInternalApi;
@@ -84,28 +83,22 @@ public abstract class OverdueTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     protected TestOverdueHelper testOverdueHelper;
 
     @BeforeClass(groups = "fast")
-    protected void setup() throws Exception {
-        final Injector injector = Guice.createInjector(new TestOverdueModuleNoDB());
+    protected void beforeClass() throws Exception {
+        final Injector injector = Guice.createInjector(new TestOverdueModuleNoDB(configSource));
         injector.injectMembers(this);
     }
 
     @BeforeMethod(groups = "fast")
-    public void setupTest() {
+    public void beforeMethod() throws Exception {
         bus.start();
 
         service.registerForBus();
-        try {
             service.initialize();
-        } catch (RuntimeException e) {
-            if (!(e.getCause() instanceof NotificationQueueAlreadyExists)) {
-                throw e;
-            }
-        }
         service.start();
     }
 
     @AfterMethod(groups = "fast")
-    public void cleanupTest() {
+    public void afterMethod() {
         service.stop();
         bus.stop();
     }
diff --git a/overdue/src/test/java/com/ning/billing/overdue/OverdueTestSuiteWithEmbeddedDB.java b/overdue/src/test/java/com/ning/billing/overdue/OverdueTestSuiteWithEmbeddedDB.java
index b7ab466..7eeca11 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/OverdueTestSuiteWithEmbeddedDB.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/OverdueTestSuiteWithEmbeddedDB.java
@@ -89,13 +89,14 @@ public abstract class OverdueTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
     protected TestOverdueHelper testOverdueHelper;
 
     @BeforeClass(groups = "slow")
-    protected void setup() throws Exception {
-        final Injector injector = Guice.createInjector(new TestOverdueModuleWithEmbeddedDB());
+    protected void beforeClass() throws Exception {
+        final Injector injector = Guice.createInjector(new TestOverdueModuleWithEmbeddedDB(configSource));
         injector.injectMembers(this);
     }
 
     @BeforeMethod(groups = "slow")
-    public void setupTest() throws Exception {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         bus.register(listener);
         bus.start();
 
@@ -106,7 +107,7 @@ public abstract class OverdueTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
     }
 
     @AfterMethod(groups = "slow")
-    public void cleanupTest() throws Exception {
+    public void afterMethod() throws Exception {
         service.stop();
         bus.stop();
     }
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
index c6e86d2..1f1ba8d 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
@@ -144,12 +144,6 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
-    public String initializeAccountPlugin(final String pluginName, final Account account, final CallContext context)
-            throws PaymentApiException {
-        return methodProcessor.initializeAccountPlugin(pluginName, account, internalCallContextFactory.createInternalCallContext(account.getId(), context));
-    }
-
-    @Override
     public UUID addPaymentMethod(final String pluginName, final Account account,
                                  final boolean setDefault, final PaymentMethodPlugin paymentMethodInfo, final CallContext context)
             throws PaymentApiException {
@@ -158,34 +152,15 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
-    public List<PaymentMethod> refreshPaymentMethods(final String pluginName,
-                                                     final Account account, final CallContext context)
-            throws PaymentApiException {
-        return methodProcessor.refreshPaymentMethods(pluginName, account, internalCallContextFactory.createInternalCallContext(account.getId(), context));
-    }
-
-    @Override
-    public List<PaymentMethod> getPaymentMethods(final Account account, final boolean withPluginDetail, final TenantContext context)
-            throws PaymentApiException {
-        return methodProcessor.getPaymentMethods(account, withPluginDetail, internalCallContextFactory.createInternalTenantContext(context));
-    }
-
-    @Override
-    public PaymentMethod getPaymentMethodById(final UUID paymentMethodId, final TenantContext context)
-            throws PaymentApiException {
-        return methodProcessor.getPaymentMethodById(paymentMethodId, internalCallContextFactory.createInternalTenantContext(context));
-    }
-
-    @Override
-    public PaymentMethod getPaymentMethod(final Account account, final UUID paymentMethod, final boolean withPluginDetail, final TenantContext context)
+    public List<PaymentMethod> getPaymentMethods(final Account account, final TenantContext context)
             throws PaymentApiException {
-        return methodProcessor.getPaymentMethod(account, paymentMethod, withPluginDetail, internalCallContextFactory.createInternalTenantContext(context));
+        return methodProcessor.getPaymentMethods(account, internalCallContextFactory.createInternalTenantContext(context));
     }
 
     @Override
-    public void updatePaymentMethod(final Account account, final UUID paymentMethodId, final PaymentMethodPlugin paymentMethodInfo, final CallContext context)
+    public PaymentMethod getPaymentMethodById(final UUID paymentMethodId, final boolean withPluginInfo, final TenantContext context)
             throws PaymentApiException {
-        methodProcessor.updatePaymentMethod(account, paymentMethodId, paymentMethodInfo, internalCallContextFactory.createInternalCallContext(account.getId(), context));
+        return methodProcessor.getPaymentMethodById(paymentMethodId, withPluginInfo, internalCallContextFactory.createInternalTenantContext(context));
     }
 
     @Override
@@ -199,4 +174,10 @@ public class DefaultPaymentApi implements PaymentApi {
             throws PaymentApiException {
         methodProcessor.setDefaultPaymentMethod(account, paymentMethodId, internalCallContextFactory.createInternalCallContext(account.getId(), context));
     }
+
+    @Override
+    public List<PaymentMethod> refreshPaymentMethods(final String pluginName, final Account account, final CallContext context)
+            throws PaymentApiException {
+        return methodProcessor.refreshPaymentMethods(pluginName, account, internalCallContextFactory.createInternalCallContext(account.getId(), context));
+    }
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentInfoEvent.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentInfoEvent.java
index ba3dfa9..f78eb2d 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentInfoEvent.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentInfoEvent.java
@@ -38,8 +38,6 @@ public class DefaultPaymentInfoEvent extends DefaultBusInternalEvent implements 
     private final PaymentStatus status;
     private final UUID userToken;
     private final DateTime effectiveDate;
-    private final String extFirstPaymentRefId;
-    private final String extSecondPaymentRefId;
 
     @JsonCreator
     public DefaultPaymentInfoEvent(@JsonProperty("id") final UUID id, /* not used */
@@ -49,8 +47,8 @@ public class DefaultPaymentInfoEvent extends DefaultBusInternalEvent implements 
                                    @JsonProperty("amount") final BigDecimal amount,
                                    @JsonProperty("paymentNumber") final Integer paymentNumber,
                                    @JsonProperty("status") final PaymentStatus status,
-                                   @JsonProperty("extFirstPaymentRefId") final String extFirstPaymentRefId,
-                                   @JsonProperty("extSecondPaymentRefId") final String extSecondPaymentRefId,
+                                   @JsonProperty("extFirstPaymentRefId") final String extFirstPaymentRefId /* TODO for backward compatibility only */,
+                                   @JsonProperty("extSecondPaymentRefId") final String extSecondPaymentRefId /* TODO for backward compatibility only */,
                                    @JsonProperty("userToken") final UUID userToken,
                                    @JsonProperty("effectiveDate") final DateTime effectiveDate,
                                    @JsonProperty("accountRecordId") final Long accountRecordId,
@@ -62,19 +60,16 @@ public class DefaultPaymentInfoEvent extends DefaultBusInternalEvent implements 
         this.amount = amount;
         this.paymentNumber = paymentNumber;
         this.status = status;
-        this.extFirstPaymentRefId = extFirstPaymentRefId;
-        this.extSecondPaymentRefId = extSecondPaymentRefId;
         this.userToken = userToken;
         this.effectiveDate = effectiveDate;
     }
 
-
     public DefaultPaymentInfoEvent(final UUID accountId, final UUID invoiceId,
                                    final UUID paymentId, final BigDecimal amount, final Integer paymentNumber,
-                                   final PaymentStatus status, final String extFirstPaymentRefId, final String extSecondPaymentRefId, final UUID userToken,
-                                   final DateTime effectiveDatefinal, Long accountRecordId, final Long tenantRecordId) {
-        this(UUID.randomUUID(), accountId, invoiceId, paymentId, amount, paymentNumber, status, extFirstPaymentRefId, extSecondPaymentRefId, userToken,
-                effectiveDatefinal, accountRecordId, tenantRecordId);
+                                   final PaymentStatus status, final UUID userToken,
+                                   final DateTime effectiveDatefinal, final Long accountRecordId, final Long tenantRecordId) {
+        this(UUID.randomUUID(), accountId, invoiceId, paymentId, amount, paymentNumber, status, null, null, userToken,
+             effectiveDatefinal, accountRecordId, tenantRecordId);
     }
 
     public DefaultPaymentInfoEvent(final DefaultPaymentInfoEvent src) {
@@ -85,15 +80,14 @@ public class DefaultPaymentInfoEvent extends DefaultBusInternalEvent implements 
              src.amount,
              src.paymentNumber,
              src.status,
-             src.extFirstPaymentRefId,
-             src.extSecondPaymentRefId,
+             null,
+             null,
              src.userToken,
              src.effectiveDate,
              src.getAccountRecordId(),
              src.getTenantRecordId());
     }
 
-
     @JsonIgnore
     @Override
     public BusInternalEventType getBusEventType() {
@@ -115,7 +109,6 @@ public class DefaultPaymentInfoEvent extends DefaultBusInternalEvent implements 
         return invoiceId;
     }
 
-
     @Override
     public BigDecimal getAmount() {
         return amount;
@@ -136,23 +129,12 @@ public class DefaultPaymentInfoEvent extends DefaultBusInternalEvent implements 
         return paymentNumber;
     }
 
-
     @Override
     public PaymentStatus getStatus() {
         return status;
     }
 
     @Override
-    public String getExtFirstPaymentRefId() {
-        return extFirstPaymentRefId;
-    }
-
-    @Override
-    public String getExtSecondPaymentRefId() {
-        return extSecondPaymentRefId;
-    }
-
-    @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
         sb.append("DefaultPaymentInfoEvent");
@@ -164,8 +146,6 @@ public class DefaultPaymentInfoEvent extends DefaultBusInternalEvent implements 
         sb.append(", status=").append(status);
         sb.append(", userToken=").append(userToken);
         sb.append(", effectiveDate=").append(effectiveDate);
-        sb.append(", extFirstPaymentRefId='").append(extFirstPaymentRefId).append('\'');
-        sb.append(", extSecondPaymentRefId='").append(extSecondPaymentRefId).append('\'');
         sb.append('}');
         return sb.toString();
     }
@@ -175,23 +155,22 @@ public class DefaultPaymentInfoEvent extends DefaultBusInternalEvent implements 
         final int prime = 31;
         int result = 1;
         result = prime * result
-                + ((accountId == null) ? 0 : accountId.hashCode());
+                 + ((accountId == null) ? 0 : accountId.hashCode());
         result = prime * result + ((amount == null) ? 0 : amount.hashCode());
         result = prime * result
-                + ((effectiveDate == null) ? 0 : effectiveDate.hashCode());
+                 + ((effectiveDate == null) ? 0 : effectiveDate.hashCode());
         result = prime * result
-                + ((invoiceId == null) ? 0 : invoiceId.hashCode());
+                 + ((invoiceId == null) ? 0 : invoiceId.hashCode());
         result = prime * result
-                + ((paymentId == null) ? 0 : paymentId.hashCode());
+                 + ((paymentId == null) ? 0 : paymentId.hashCode());
         result = prime * result
-                + ((paymentNumber == null) ? 0 : paymentNumber.hashCode());
+                 + ((paymentNumber == null) ? 0 : paymentNumber.hashCode());
         result = prime * result + ((status == null) ? 0 : status.hashCode());
         result = prime * result
-                + ((userToken == null) ? 0 : userToken.hashCode());
+                 + ((userToken == null) ? 0 : userToken.hashCode());
         return result;
     }
 
-
     @Override
     public boolean equals(final Object obj) {
         if (this == obj) {
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentMethod.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentMethod.java
index 0c48976..a906dc0 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentMethod.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentMethod.java
@@ -45,6 +45,10 @@ public class DefaultPaymentMethod extends EntityBase implements PaymentMethod {
         this(UUID.randomUUID(), null, null, accountId, true, pluginName, pluginDetail);
     }
 
+    public DefaultPaymentMethod(final UUID paymentMethodId, final UUID accountId, final String pluginName) {
+        this(paymentMethodId, null, null, accountId, true, pluginName, null);
+    }
+
     public DefaultPaymentMethod(final PaymentMethodModelDao input, final PaymentMethodPlugin pluginDetail) {
         this(input.getId(), input.getCreatedDate(), input.getUpdatedDate(), input.getAccountId(), input.isActive(), input.getPluginName(), pluginDetail);
     }
diff --git a/payment/src/main/java/com/ning/billing/payment/api/svcs/DefaultPaymentInternalApi.java b/payment/src/main/java/com/ning/billing/payment/api/svcs/DefaultPaymentInternalApi.java
index 72ee7dd..acad7e0 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/svcs/DefaultPaymentInternalApi.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/svcs/DefaultPaymentInternalApi.java
@@ -52,8 +52,8 @@ public class DefaultPaymentInternalApi implements PaymentInternalApi {
     }
 
     @Override
-    public PaymentMethod getPaymentMethod(final Account account, final UUID paymentMethodId, final boolean withPluginDetail, final InternalTenantContext context) throws PaymentApiException {
-        return methodProcessor.getPaymentMethod(account, paymentMethodId, withPluginDetail, context);
+    public PaymentMethod getPaymentMethodById(final UUID paymentMethodId, final InternalTenantContext context) throws PaymentApiException {
+        return methodProcessor.getPaymentMethodById(paymentMethodId, false, context);
     }
 
     @Override
@@ -62,7 +62,7 @@ public class DefaultPaymentInternalApi implements PaymentInternalApi {
     }
 
     @Override
-    public List<PaymentMethod> getPaymentMethods(final Account account, final boolean withPluginDetail, final InternalTenantContext context) throws PaymentApiException {
-        return methodProcessor.getPaymentMethods(account, withPluginDetail, context);
+    public List<PaymentMethod> getPaymentMethods(final Account account, final InternalTenantContext context) throws PaymentApiException {
+        return methodProcessor.getPaymentMethods(account, context);
     }
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
index 2252bdc..aa81ff9 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
@@ -29,19 +29,20 @@ import org.slf4j.LoggerFactory;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.osgi.api.OSGIServiceRegistration;
 import com.ning.billing.payment.api.DefaultPaymentMethod;
-import com.ning.billing.payment.api.DefaultPaymentMethodPlugin;
 import com.ning.billing.payment.api.PaymentApiException;
 import com.ning.billing.payment.api.PaymentMethod;
 import com.ning.billing.payment.api.PaymentMethodPlugin;
 import com.ning.billing.payment.api.PaymentMethodPlugin.PaymentMethodKVInfo;
 import com.ning.billing.payment.dao.PaymentDao;
 import com.ning.billing.payment.dao.PaymentMethodModelDao;
+import com.ning.billing.payment.plugin.api.PaymentMethodInfoPlugin;
 import com.ning.billing.payment.plugin.api.PaymentPluginApi;
 import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
 import com.ning.billing.payment.provider.DefaultNoOpPaymentMethodPlugin;
+import com.ning.billing.payment.provider.DefaultPaymentMethodInfoPlugin;
 import com.ning.billing.payment.provider.ExternalPaymentProviderPlugin;
-import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.globallocker.GlobalLocker;
@@ -50,6 +51,7 @@ import com.ning.billing.util.svcapi.tag.TagInternalApi;
 import com.ning.billing.util.svcsapi.bus.InternalBus;
 
 import com.google.common.base.Function;
+import com.google.common.base.Objects;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
@@ -62,7 +64,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
     private static final Logger log = LoggerFactory.getLogger(PaymentMethodProcessor.class);
 
     @Inject
-    public PaymentMethodProcessor(final PaymentProviderPluginRegistry pluginRegistry,
+    public PaymentMethodProcessor(final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
                                   final AccountInternalApi accountInternalApi,
                                   final InternalBus eventBus,
                                   final PaymentDao paymentDao,
@@ -73,29 +75,10 @@ public class PaymentMethodProcessor extends ProcessorBase {
     }
 
     public Set<String> getAvailablePlugins() {
-        return pluginRegistry.getRegisteredPluginNames();
+        return pluginRegistry.getAllServices();
     }
 
-    public String initializeAccountPlugin(final String pluginName, final Account account, final InternalCallContext context) throws PaymentApiException {
-
-        return new WithAccountLock<String>().processAccountWithLock(locker, account.getExternalKey(), new WithAccountLockCallback<String>() {
-
-            @Override
-            public String doOperation() throws PaymentApiException {
-                PaymentPluginApi pluginApi = null;
-                try {
-                    // STEPH do we want to really have a default or fail?? probably fail
-                    pluginApi = pluginRegistry.getPlugin(pluginName);
-                    return pluginApi.createPaymentProviderAccount(account, context.toCallContext());
-                } catch (PaymentPluginApiException e) {
-                    throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_ACCOUNT_INIT,
-                                                  account.getId(), pluginApi != null ? pluginApi.getName() : null, e.getErrorMessage());
-                }
-            }
-        });
-    }
-
-    public UUID addPaymentMethod(final String pluginName, final Account account,
+    public UUID addPaymentMethod(final String paymentPluginServiceName, final Account account,
                                  final boolean setDefault, final PaymentMethodPlugin paymentMethodProps, final InternalCallContext context)
             throws PaymentApiException {
 
@@ -106,11 +89,14 @@ public class PaymentMethodProcessor extends ProcessorBase {
                 PaymentMethod pm = null;
                 PaymentPluginApi pluginApi = null;
                 try {
-                    pluginApi = pluginRegistry.getPlugin(pluginName);
+                    pluginApi = pluginRegistry.getServiceForName(paymentPluginServiceName);
+                    // TODO PIERRE Should we really use the plugin instance name here?
+                    // We default to the plugin name when paymentPluginServiceName is null (i.e. for the default plugin name)
+                    final String pluginName = Objects.firstNonNull(paymentPluginServiceName, pluginApi.getName());
                     pm = new DefaultPaymentMethod(account.getId(), pluginName, paymentMethodProps);
-                    final String externalId = pluginApi.addPaymentMethod(account.getExternalKey(), paymentMethodProps, setDefault, context.toCallContext());
+                    pluginApi.addPaymentMethod(account.getId(), pm.getId(), paymentMethodProps, setDefault, context.toCallContext());
                     final PaymentMethodModelDao pmModel = new PaymentMethodModelDao(pm.getId(), pm.getCreatedDate(), pm.getUpdatedDate(),
-                                                                                    pm.getAccountId(), pm.getPluginName(), pm.isActive(), externalId);
+                                                                                    pm.getAccountId(), pm.getPluginName(), pm.isActive());
                     paymentDao.insertPaymentMethod(pmModel, context);
 
                     if (setDefault) {
@@ -127,79 +113,39 @@ public class PaymentMethodProcessor extends ProcessorBase {
         });
     }
 
-    public List<PaymentMethod> refreshPaymentMethods(final String pluginName, final Account account, final InternalCallContext context)
-            throws PaymentApiException {
-        // Don't hold the account lock while fetching the payment methods
-        final PaymentPluginApi pluginApi = pluginRegistry.getPlugin(pluginName);
-        final List<PaymentMethodPlugin> pluginPms;
-        try {
-            pluginPms = pluginApi.getPaymentMethodDetails(account.getExternalKey(), context.toCallContext());
-            // The method should never return null by convention, but let's not trust the plugin...
-            if (pluginPms == null) {
-                return ImmutableList.<PaymentMethod>of();
-            }
-        } catch (PaymentPluginApiException e) {
-            // STEPH all errors should also take a pluginName
-            throw new PaymentApiException(ErrorCode.PAYMENT_REFRESH_PAYMENT_METHOD, account.getId(), e.getErrorMessage());
-        }
-
-        return new WithAccountLock<List<PaymentMethod>>().processAccountWithLock(locker, account.getExternalKey(), new WithAccountLockCallback<List<PaymentMethod>>() {
-
-            @Override
-            public List<PaymentMethod> doOperation() throws PaymentApiException {
-                final List<PaymentMethodModelDao> finalPaymentMethods = new ArrayList<PaymentMethodModelDao>();
-                for (final PaymentMethodPlugin cur : pluginPms) {
-                    final PaymentMethod input = new DefaultPaymentMethod(account.getId(), pluginName, cur);
-                    final PaymentMethodModelDao pmModel = new PaymentMethodModelDao(input.getId(), input.getCreatedDate(), input.getUpdatedDate(),
-                                                                                    input.getAccountId(), input.getPluginName(), input.isActive(),
-                                                                                    input.getPluginDetail().getExternalPaymentMethodId());
-                    finalPaymentMethods.add(pmModel);
-                }
-
-                final List<PaymentMethodModelDao> refreshedPaymentMethods = paymentDao.refreshPaymentMethods(account.getId(),
-                                                                                                             finalPaymentMethods,
-                                                                                                             context);
-                return ImmutableList.<PaymentMethod>copyOf(Collections2.transform(refreshedPaymentMethods, new Function<PaymentMethodModelDao, PaymentMethod>() {
-                    @Override
-                    public PaymentMethod apply(final PaymentMethodModelDao input) {
-                        return new DefaultPaymentMethod(input, getPaymentMethodDetail(pluginPms, input.getExternalId()));
-                    }
-                }));
-            }
-        });
-    }
-
-    public List<PaymentMethod> getPaymentMethods(final Account account, final boolean withPluginDetail, final InternalTenantContext context) throws PaymentApiException {
+    public List<PaymentMethod> getPaymentMethods(final Account account, final InternalTenantContext context) throws PaymentApiException {
 
         final List<PaymentMethodModelDao> paymentMethodModels = paymentDao.getPaymentMethods(account.getId(), context);
         if (paymentMethodModels.size() == 0) {
             return Collections.emptyList();
         }
-        return getPaymentMethodInternal(paymentMethodModels, account.getId(), account.getExternalKey(), withPluginDetail, context);
+        return getPaymentMethodInternal(paymentMethodModels);
     }
 
-    public PaymentMethod getPaymentMethodById(final UUID paymentMethodId, final InternalTenantContext context)
+    public PaymentMethod getPaymentMethodById(final UUID paymentMethodId, final boolean withPluginInfo, final InternalTenantContext context)
             throws PaymentApiException {
         final PaymentMethodModelDao paymentMethodModel = paymentDao.getPaymentMethod(paymentMethodId, context);
         if (paymentMethodModel == null) {
             throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, paymentMethodId);
         }
-        return new DefaultPaymentMethod(paymentMethodModel, null);
-    }
 
-    public PaymentMethod getPaymentMethod(final Account account, final UUID paymentMethodId, final boolean withPluginDetail, final InternalTenantContext context)
-            throws PaymentApiException {
-        final PaymentMethodModelDao paymentMethodModel = paymentDao.getPaymentMethod(paymentMethodId, context);
-        if (paymentMethodModel == null) {
-            throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, paymentMethodId);
+        final PaymentMethodPlugin paymentMethodPlugin;
+        if (withPluginInfo) {
+            try {
+                final PaymentPluginApi pluginApi = pluginRegistry.getServiceForName(paymentMethodModel.getPluginName());
+                paymentMethodPlugin = pluginApi.getPaymentMethodDetail(paymentMethodModel.getAccountId(), paymentMethodId, context.toTenantContext());
+            } catch (PaymentPluginApiException e) {
+                throw new PaymentApiException(ErrorCode.PAYMENT_GET_PAYMENT_METHODS, paymentMethodModel.getAccountId(), paymentMethodId);
+            }
+        } else {
+            paymentMethodPlugin = null;
         }
-        final List<PaymentMethod> result = getPaymentMethodInternal(Collections.singletonList(paymentMethodModel), account.getId(),
-                                                                    account.getExternalKey(), withPluginDetail, context);
-        return (result.size() == 0) ? null : result.get(0);
+
+        return new DefaultPaymentMethod(paymentMethodModel, paymentMethodPlugin);
     }
 
     public PaymentMethod getExternalPaymentMethod(final Account account, final InternalTenantContext context) throws PaymentApiException {
-        final List<PaymentMethod> paymentMethods = getPaymentMethods(account, false, context);
+        final List<PaymentMethod> paymentMethods = getPaymentMethods(account, context);
         for (final PaymentMethod paymentMethod : paymentMethods) {
             if (ExternalPaymentProviderPlugin.PLUGIN_NAME.equals(paymentMethod.getPluginName())) {
                 return paymentMethod;
@@ -217,70 +163,20 @@ public class PaymentMethodProcessor extends ProcessorBase {
             addPaymentMethod(ExternalPaymentProviderPlugin.PLUGIN_NAME, account, false, props, context);
         }
 
-        return (ExternalPaymentProviderPlugin) pluginRegistry.getPlugin(ExternalPaymentProviderPlugin.PLUGIN_NAME);
+        return (ExternalPaymentProviderPlugin) pluginRegistry.getServiceForName(ExternalPaymentProviderPlugin.PLUGIN_NAME);
     }
 
-    private List<PaymentMethod> getPaymentMethodInternal(final List<PaymentMethodModelDao> paymentMethodModels, final UUID accountId,
-                                                         final String accountKey, final boolean withPluginDetail, final InternalTenantContext context)
+    private List<PaymentMethod> getPaymentMethodInternal(final List<PaymentMethodModelDao> paymentMethodModels)
             throws PaymentApiException {
 
         final List<PaymentMethod> result = new ArrayList<PaymentMethod>(paymentMethodModels.size());
-        PaymentPluginApi pluginApi = null;
-        try {
-            List<PaymentMethodPlugin> pluginDetails = null;
-            for (final PaymentMethodModelDao cur : paymentMethodModels) {
-
-                if (withPluginDetail) {
-                    pluginApi = pluginRegistry.getPlugin(cur.getPluginName());
-                    pluginDetails = pluginApi.getPaymentMethodDetails(accountKey, context.toTenantContext());
-                }
-
-                final PaymentMethod pm = new DefaultPaymentMethod(cur, getPaymentMethodDetail(pluginDetails, cur.getExternalId()));
-                result.add(pm);
-            }
-        } catch (PaymentPluginApiException e) {
-            throw new PaymentApiException(ErrorCode.PAYMENT_GET_PAYMENT_METHODS, accountId, e.getErrorMessage());
+        for (final PaymentMethodModelDao cur : paymentMethodModels) {
+            final PaymentMethod pm = new DefaultPaymentMethod(cur, null);
+            result.add(pm);
         }
         return result;
     }
 
-    private PaymentMethodPlugin getPaymentMethodDetail(final List<PaymentMethodPlugin> pluginDetails, final String externalId) {
-        if (pluginDetails == null) {
-            return null;
-        }
-        for (final PaymentMethodPlugin cur : pluginDetails) {
-            if (cur.getExternalPaymentMethodId().equals(externalId)) {
-                return cur;
-            }
-        }
-        return null;
-    }
-
-    public void updatePaymentMethod(final Account account, final UUID paymentMethodId,
-                                    final PaymentMethodPlugin paymentMethodProps, final InternalCallContext context)
-            throws PaymentApiException {
-
-        new WithAccountLock<Void>().processAccountWithLock(locker, account.getExternalKey(), new WithAccountLockCallback<Void>() {
-
-            @Override
-            public Void doOperation() throws PaymentApiException {
-                final PaymentMethodModelDao paymentMethodModel = paymentDao.getPaymentMethod(paymentMethodId, context);
-                if (paymentMethodModel == null) {
-                    throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, paymentMethodId);
-                }
-
-                try {
-                    final PaymentMethodPlugin inputWithId = new DefaultPaymentMethodPlugin(paymentMethodProps, paymentMethodModel.getExternalId());
-                    final PaymentPluginApi pluginApi = getPluginApi(paymentMethodId, account.getId(), context);
-                    pluginApi.updatePaymentMethod(account.getExternalKey(), inputWithId, context.toCallContext());
-                    return null;
-                } catch (PaymentPluginApiException e) {
-                    throw new PaymentApiException(ErrorCode.PAYMENT_UPD_PAYMENT_METHOD, account.getId(), e.getErrorMessage());
-                }
-            }
-        });
-    }
-
     public void deletedPaymentMethod(final Account account, final UUID paymentMethodId,
                                      final boolean deleteDefaultPaymentMethodWithAutoPayOff, final InternalCallContext context)
             throws PaymentApiException {
@@ -295,7 +191,8 @@ public class PaymentMethodProcessor extends ProcessorBase {
                 }
 
                 try {
-                    if (account.getPaymentMethodId().equals(paymentMethodId)) {
+                    // Note: account.getPaymentMethodId() may be null
+                    if (paymentMethodId.equals(account.getPaymentMethodId())) {
                         if (!deleteDefaultPaymentMethodWithAutoPayOff) {
                             throw new PaymentApiException(ErrorCode.PAYMENT_DEL_DEFAULT_PAYMENT_METHOD, account.getId());
                         } else {
@@ -307,8 +204,8 @@ public class PaymentMethodProcessor extends ProcessorBase {
                             accountInternalApi.removePaymentMethod(account.getId(), context);
                         }
                     }
-                    final PaymentPluginApi pluginApi = getPluginApi(paymentMethodId, account.getId(), context);
-                    pluginApi.deletePaymentMethod(account.getExternalKey(), paymentMethodModel.getExternalId(), context.toCallContext());
+                    final PaymentPluginApi pluginApi = getPluginApi(paymentMethodId, context);
+                    pluginApi.deletePaymentMethod(paymentMethodId, context.toCallContext());
                     paymentDao.deletedPaymentMethod(paymentMethodId, context);
                     return null;
                 } catch (PaymentPluginApiException e) {
@@ -333,9 +230,9 @@ public class PaymentMethodProcessor extends ProcessorBase {
                 }
 
                 try {
-                    final PaymentPluginApi pluginApi = getPluginApi(paymentMethodId, account.getId(), context);
+                    final PaymentPluginApi pluginApi = getPluginApi(paymentMethodId, context);
 
-                    pluginApi.setDefaultPaymentMethod(account.getExternalKey(), paymentMethodModel.getExternalId(), context.toCallContext());
+                    pluginApi.setDefaultPaymentMethod(paymentMethodId, context.toCallContext());
                     accountInternalApi.updatePaymentMethod(account.getId(), paymentMethodId, context);
                     return null;
                 } catch (PaymentPluginApiException e) {
@@ -347,12 +244,94 @@ public class PaymentMethodProcessor extends ProcessorBase {
         });
     }
 
-    private PaymentPluginApi getPluginApi(final UUID paymentMethodId, final UUID accountId, final InternalTenantContext context)
+    private PaymentPluginApi getPluginApi(final UUID paymentMethodId, final InternalTenantContext context)
             throws PaymentApiException {
         final PaymentMethodModelDao paymentMethod = paymentDao.getPaymentMethod(paymentMethodId, context);
         if (paymentMethod == null) {
             throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, paymentMethodId);
         }
-        return pluginRegistry.getPlugin(paymentMethod.getPluginName());
+        return pluginRegistry.getServiceForName(paymentMethod.getPluginName());
+    }
+
+    /**
+     * This refreshed the payment methods from the plugin for cases when adding payment method does not flow through KB because of PCI compliance
+     * issues. The logic below is not optimal because there is no atomicity in the step but the goos news is that this is idempotent so can always be
+     * replayed if necessary-- partial failure scenario.
+     *
+     * @param pluginName
+     * @param account
+     * @param context
+     * @return the list of payment methods -- should be identical between KB, the plugin view-- if it keeps a state-- and the gateway.
+     * @throws PaymentApiException
+     */
+    public List<PaymentMethod> refreshPaymentMethods(final String pluginName, final Account account, final InternalCallContext context) throws PaymentApiException {
+
+
+        // Don't hold the account lock while fetching the payment methods from the gateway as those could change anyway
+        final PaymentPluginApi pluginApi = pluginRegistry.getServiceForName(pluginName);
+        final List<PaymentMethodInfoPlugin> pluginPms;
+        try {
+            pluginPms = pluginApi.getPaymentMethods(account.getId(), true, context.toCallContext());
+            // The method should never return null by convention, but let's not trust the plugin...
+            if (pluginPms == null) {
+                log.warn("No payment methods defined on the account {} for plugin {}", account.getId(), pluginName);
+                return ImmutableList.<PaymentMethod>of();
+            }
+        } catch (PaymentPluginApiException e) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_REFRESH_PAYMENT_METHOD, account.getId(), e.getErrorMessage());
+        }
+
+        return new WithAccountLock<List<PaymentMethod>>().processAccountWithLock(locker, account.getExternalKey(), new WithAccountLockCallback<List<PaymentMethod>>() {
+
+            @Override
+            public List<PaymentMethod> doOperation() throws PaymentApiException {
+
+                UUID defaultPaymentMethodId = null;
+
+                final List<PaymentMethodInfoPlugin> pluginPmsWithId = new ArrayList<PaymentMethodInfoPlugin>();
+                final List<PaymentMethodModelDao> finalPaymentMethods = new ArrayList<PaymentMethodModelDao>();
+                for (final PaymentMethodInfoPlugin cur : pluginPms) {
+
+                    // If the kbPaymentId is NULL, the plugin does not know about it, so we create a new UUID
+                    final UUID paymentMethodId = cur.getPaymentMethodId() != null ? cur.getPaymentMethodId() : UUID.randomUUID();
+                    final PaymentMethod input = new DefaultPaymentMethod(paymentMethodId, account.getId(), pluginName);
+                    final PaymentMethodModelDao pmModel = new PaymentMethodModelDao(input.getId(), input.getCreatedDate(), input.getUpdatedDate(),
+                                                                                    input.getAccountId(), input.getPluginName(), input.isActive());
+                    finalPaymentMethods.add(pmModel);
+
+                    pluginPmsWithId.add(new DefaultPaymentMethodInfoPlugin(cur, paymentMethodId));
+
+                    if (cur.isDefault()) {
+                        defaultPaymentMethodId = paymentMethodId;
+                    }
+                }
+
+                final List<PaymentMethodModelDao> refreshedPaymentMethods = paymentDao.refreshPaymentMethods(account.getId(),
+                                                                                                             finalPaymentMethods,
+                                                                                                             context);
+                try {
+                    pluginApi.resetPaymentMethods(pluginPmsWithId);
+                } catch (PaymentPluginApiException e) {
+                    throw new PaymentApiException(ErrorCode.PAYMENT_REFRESH_PAYMENT_METHOD, account.getId(), e.getErrorMessage());
+                }
+
+                try {
+                    if (defaultPaymentMethodId != null) {
+                        accountInternalApi.updatePaymentMethod(account.getId(), defaultPaymentMethodId, context);
+                    } else {
+                        accountInternalApi.removePaymentMethod(account.getId(), context);
+                    }
+                } catch (AccountApiException e) {
+                    throw new PaymentApiException(e);
+                }
+
+                return ImmutableList.<PaymentMethod>copyOf(Collections2.transform(refreshedPaymentMethods, new Function<PaymentMethodModelDao, PaymentMethod>() {
+                    @Override
+                    public PaymentMethod apply(final PaymentMethodModelDao input) {
+                        return new DefaultPaymentMethod(input, null);
+                    }
+                }));
+            }
+        });
     }
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java
index 29158c8..2f70a84 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java
@@ -13,9 +13,8 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
-package com.ning.billing.payment.core;
 
-import static com.ning.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
+package com.ning.billing.payment.core;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
@@ -36,9 +35,9 @@ import org.slf4j.LoggerFactory;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
-import com.ning.billing.util.config.PaymentConfig;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.osgi.api.OSGIServiceRegistration;
 import com.ning.billing.payment.api.DefaultPayment;
 import com.ning.billing.payment.api.DefaultPaymentErrorEvent;
 import com.ning.billing.payment.api.DefaultPaymentInfoEvent;
@@ -53,13 +52,13 @@ import com.ning.billing.payment.dispatcher.PluginDispatcher;
 import com.ning.billing.payment.plugin.api.PaymentInfoPlugin;
 import com.ning.billing.payment.plugin.api.PaymentPluginApi;
 import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
-import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
 import com.ning.billing.payment.retry.AutoPayRetryService.AutoPayRetryServiceScheduler;
 import com.ning.billing.payment.retry.FailedPaymentRetryService.FailedPaymentRetryServiceScheduler;
 import com.ning.billing.payment.retry.PluginFailureRetryService.PluginFailureRetryServiceScheduler;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.config.PaymentConfig;
 import com.ning.billing.util.events.BusInternalEvent;
 import com.ning.billing.util.events.PaymentErrorInternalEvent;
 import com.ning.billing.util.globallocker.GlobalLocker;
@@ -72,6 +71,8 @@ import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 import com.google.inject.name.Named;
 
+import static com.ning.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
+
 public class PaymentProcessor extends ProcessorBase {
 
     private final PaymentMethodProcessor paymentMethodProcessor;
@@ -90,7 +91,7 @@ public class PaymentProcessor extends ProcessorBase {
     private static final Logger log = LoggerFactory.getLogger(PaymentProcessor.class);
 
     @Inject
-    public PaymentProcessor(final PaymentProviderPluginRegistry pluginRegistry,
+    public PaymentProcessor(final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
                             final PaymentMethodProcessor paymentMethodProcessor,
                             final AccountInternalApi accountUserApi,
                             final InvoiceInternalApi invoiceApi,
@@ -148,47 +149,47 @@ public class PaymentProcessor extends ProcessorBase {
         return result;
     }
 
-    public void process_AUTO_PAY_OFF_removal(final Account account, final InternalCallContext context) throws PaymentApiException  {
+    public void process_AUTO_PAY_OFF_removal(final Account account, final InternalCallContext context) throws PaymentApiException {
 
         try {
             voidPluginDispatcher.dispatchWithAccountLock(new CallableWithAccountLock<Void>(locker,
-                    account.getExternalKey(),
-                    new WithAccountLockCallback<Void>() {
-
-                @Override
-                public Void doOperation() throws PaymentApiException {
-
-                    final List<PaymentModelDao> payments = paymentDao.getPaymentsForAccount(account.getId(), context);
-                    final Collection<PaymentModelDao> paymentsToBeCompleted = Collections2.filter(payments, new Predicate<PaymentModelDao>() {
-                        @Override
-                        public boolean apply(final PaymentModelDao in) {
-                            // Payments left in AUTO_PAY_OFF or for which we did not retry enough
-                            return (in.getPaymentStatus() == PaymentStatus.AUTO_PAY_OFF ||
-                                    in.getPaymentStatus() == PaymentStatus.PAYMENT_FAILURE ||
-                                    in.getPaymentStatus() == PaymentStatus.PLUGIN_FAILURE);
-                        }
-                    });
-                    // Insert one retry event for each payment left in AUTO_PAY_OFF
-                    for (PaymentModelDao cur : paymentsToBeCompleted) {
-                        switch(cur.getPaymentStatus()) {
-                        case AUTO_PAY_OFF:
-                            autoPayoffRetryService.scheduleRetry(cur.getId(), clock.getUTCNow());
-                            break;
-                        case PAYMENT_FAILURE:
-                            scheduleRetryOnPaymentFailure(cur.getId(), context);
-                            break;
-                        case PLUGIN_FAILURE:
-                            scheduleRetryOnPluginFailure(cur.getId(), context);
-                            break;
-                        default:
-                            // Impossible...
-                            throw new RuntimeException("Unexpected case " + cur.getPaymentStatus());
-                        }
-
-                    }
-                    return null;
-                }
-            }));
+                                                                                           account.getExternalKey(),
+                                                                                           new WithAccountLockCallback<Void>() {
+
+                                                                                               @Override
+                                                                                               public Void doOperation() throws PaymentApiException {
+
+                                                                                                   final List<PaymentModelDao> payments = paymentDao.getPaymentsForAccount(account.getId(), context);
+                                                                                                   final Collection<PaymentModelDao> paymentsToBeCompleted = Collections2.filter(payments, new Predicate<PaymentModelDao>() {
+                                                                                                       @Override
+                                                                                                       public boolean apply(final PaymentModelDao in) {
+                                                                                                           // Payments left in AUTO_PAY_OFF or for which we did not retry enough
+                                                                                                           return (in.getPaymentStatus() == PaymentStatus.AUTO_PAY_OFF ||
+                                                                                                                   in.getPaymentStatus() == PaymentStatus.PAYMENT_FAILURE ||
+                                                                                                                   in.getPaymentStatus() == PaymentStatus.PLUGIN_FAILURE);
+                                                                                                       }
+                                                                                                   });
+                                                                                                   // Insert one retry event for each payment left in AUTO_PAY_OFF
+                                                                                                   for (PaymentModelDao cur : paymentsToBeCompleted) {
+                                                                                                       switch (cur.getPaymentStatus()) {
+                                                                                                           case AUTO_PAY_OFF:
+                                                                                                               autoPayoffRetryService.scheduleRetry(cur.getId(), clock.getUTCNow());
+                                                                                                               break;
+                                                                                                           case PAYMENT_FAILURE:
+                                                                                                               scheduleRetryOnPaymentFailure(cur.getId(), context);
+                                                                                                               break;
+                                                                                                           case PLUGIN_FAILURE:
+                                                                                                               scheduleRetryOnPluginFailure(cur.getId(), context);
+                                                                                                               break;
+                                                                                                           default:
+                                                                                                               // Impossible...
+                                                                                                               throw new RuntimeException("Unexpected case " + cur.getPaymentStatus());
+                                                                                                       }
+
+                                                                                                   }
+                                                                                                   return null;
+                                                                                               }
+                                                                                           }));
         } catch (TimeoutException e) {
             throw new PaymentApiException(ErrorCode.UNEXPECTED_ERROR, "Unexpected timeout for payment creation (AUTO_PAY_OFF)");
         }
@@ -213,43 +214,43 @@ public class PaymentProcessor extends ProcessorBase {
             // Note that at this point, we don't know the exact invoice balance (see getAndValidatePaymentAmount() below).
             // This means that events will be posted for null and zero dollar invoices (e.g. trials).
             final PaymentErrorInternalEvent event = new DefaultPaymentErrorEvent(account.getId(), invoiceId, null,
-                                                                         ErrorCode.PAYMENT_NO_DEFAULT_PAYMENT_METHOD.toString(), context.getUserToken(),
-                                                                         context.getAccountRecordId(), context.getTenantRecordId());
+                                                                                 ErrorCode.PAYMENT_NO_DEFAULT_PAYMENT_METHOD.toString(), context.getUserToken(),
+                                                                                 context.getAccountRecordId(), context.getTenantRecordId());
             postPaymentEvent(event, account.getId(), context);
             throw e;
         }
 
         try {
             return paymentPluginDispatcher.dispatchWithAccountLock(new CallableWithAccountLock<Payment>(locker,
-                    account.getExternalKey(),
-                    new WithAccountLockCallback<Payment>() {
-
-                @Override
-                public Payment doOperation() throws PaymentApiException {
-
-
-                    try {
-                        final Invoice invoice = invoiceApi.getInvoiceById(invoiceId, context);
-
-                        if (invoice.isMigrationInvoice()) {
-                            log.error("Received invoice for payment that is a migration invoice - don't know how to handle those yet: {}", invoice);
-                            return null;
-                        }
-
-                        final boolean isAccountAutoPayOff = isAccountAutoPayOff(account.getId(), context);
-                        setUnsaneAccount_AUTO_PAY_OFFWithAccountLock(account.getId(), paymentMethodId, isAccountAutoPayOff, context, isInstantPayment);
-
-                        final BigDecimal requestedAmount = getAndValidatePaymentAmount(invoice, inputAmount, isInstantPayment);
-                        if (!isInstantPayment && isAccountAutoPayOff) {
-                            return processNewPaymentForAutoPayOffWithAccountLocked(paymentMethodId, account, invoice, requestedAmount, context);
-                        } else {
-                            return processNewPaymentWithAccountLocked(paymentMethodId, plugin, account, invoice, requestedAmount, isInstantPayment, context);
-                        }
-                    } catch (InvoiceApiException e) {
-                        throw new PaymentApiException(e);
-                    }
-                }
-            }));
+                                                                                                        account.getExternalKey(),
+                                                                                                        new WithAccountLockCallback<Payment>() {
+
+                                                                                                            @Override
+                                                                                                            public Payment doOperation() throws PaymentApiException {
+
+
+                                                                                                                try {
+                                                                                                                    final Invoice invoice = invoiceApi.getInvoiceById(invoiceId, context);
+
+                                                                                                                    if (invoice.isMigrationInvoice()) {
+                                                                                                                        log.error("Received invoice for payment that is a migration invoice - don't know how to handle those yet: {}", invoice);
+                                                                                                                        return null;
+                                                                                                                    }
+
+                                                                                                                    final boolean isAccountAutoPayOff = isAccountAutoPayOff(account.getId(), context);
+                                                                                                                    setUnsaneAccount_AUTO_PAY_OFFWithAccountLock(account.getId(), paymentMethodId, isAccountAutoPayOff, context, isInstantPayment);
+
+                                                                                                                    final BigDecimal requestedAmount = getAndValidatePaymentAmount(invoice, inputAmount, isInstantPayment);
+                                                                                                                    if (!isInstantPayment && isAccountAutoPayOff) {
+                                                                                                                        return processNewPaymentForAutoPayOffWithAccountLocked(paymentMethodId, account, invoice, requestedAmount, context);
+                                                                                                                    } else {
+                                                                                                                        return processNewPaymentWithAccountLocked(paymentMethodId, plugin, account, invoice, requestedAmount, isInstantPayment, context);
+                                                                                                                    }
+                                                                                                                } catch (InvoiceApiException e) {
+                                                                                                                    throw new PaymentApiException(e);
+                                                                                                                }
+                                                                                                            }
+                                                                                                        }));
         } catch (TimeoutException e) {
             if (isInstantPayment) {
                 throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_TIMEOUT, account.getId(), invoiceId);
@@ -266,37 +267,36 @@ public class PaymentProcessor extends ProcessorBase {
 
     private void setUnsaneAccount_AUTO_PAY_OFFWithAccountLock(final UUID accountId, final UUID paymentMethodId, final boolean isAccountAutoPayOff,
                                                               final InternalCallContext context, final boolean isInstantPayment)
-    throws PaymentApiException  {
+            throws PaymentApiException {
 
         final PaymentModelDao lastPaymentForPaymentMethod = paymentDao.getLastPaymentForPaymentMethod(accountId, paymentMethodId, context);
         final boolean isLastPaymentForPaymentMethodBad = lastPaymentForPaymentMethod != null &&
-        (lastPaymentForPaymentMethod.getPaymentStatus() == PaymentStatus.PLUGIN_FAILURE_ABORTED ||
-                lastPaymentForPaymentMethod.getPaymentStatus() == PaymentStatus.UNKNOWN);
+                                                         (lastPaymentForPaymentMethod.getPaymentStatus() == PaymentStatus.PLUGIN_FAILURE_ABORTED ||
+                                                          lastPaymentForPaymentMethod.getPaymentStatus() == PaymentStatus.UNKNOWN);
 
 
         if (isLastPaymentForPaymentMethodBad &&
-                !isInstantPayment &&
-                !isAccountAutoPayOff) {
+            !isInstantPayment &&
+            !isAccountAutoPayOff) {
             log.warn(String.format("Setting account %s into AUTO_PAY_OFF because of bad payment %s", accountId, lastPaymentForPaymentMethod.getId()));
             setAccountAutoPayOff(accountId, context);
         }
     }
 
 
-
     private BigDecimal getAndValidatePaymentAmount(final Invoice invoice, @Nullable final BigDecimal inputAmount, final boolean isInstantPayment)
-    throws PaymentApiException {
+            throws PaymentApiException {
 
         if (invoice.getBalance().compareTo(BigDecimal.ZERO) <= 0) {
             throw new PaymentApiException(ErrorCode.PAYMENT_NULL_INVOICE, invoice.getId());
         }
         if (isInstantPayment &&
-                inputAmount != null &&
-                invoice.getBalance().compareTo(inputAmount) < 0) {
+            inputAmount != null &&
+            invoice.getBalance().compareTo(inputAmount) < 0) {
             throw new PaymentApiException(ErrorCode.PAYMENT_AMOUNT_DENIED,
-                    invoice.getId(), inputAmount.floatValue(), invoice.getBalance().floatValue());
+                                          invoice.getId(), inputAmount.floatValue(), invoice.getBalance().floatValue());
         }
-        final BigDecimal result =  inputAmount != null ? inputAmount : invoice.getBalance();
+        final BigDecimal result = inputAmount != null ? inputAmount : invoice.getBalance();
         return result.setScale(2, RoundingMode.HALF_UP);
     }
 
@@ -334,41 +334,41 @@ public class PaymentProcessor extends ProcessorBase {
             final PaymentPluginApi plugin = getPaymentProviderPlugin(account, context);
 
             voidPluginDispatcher.dispatchWithAccountLock(new CallableWithAccountLock<Void>(locker,
-                    account.getExternalKey(),
-                    new WithAccountLockCallback<Void>() {
-
-                @Override
-                public Void doOperation() throws PaymentApiException {
-                    try {
-                        // Fetch again with account lock this time
-                        final PaymentModelDao payment = paymentDao.getPayment(paymentId, context);
-                        boolean foundExpectedState = false;
-                        for (final PaymentStatus cur : expectedPaymentStates) {
-                            if (payment.getPaymentStatus() == cur) {
-                                foundExpectedState = true;
-                                break;
-                            }
-                        }
-                        if (!foundExpectedState) {
-                            log.info("Aborted retry for payment {} because it is {} state", paymentId, payment.getPaymentStatus());
-                            return null;
-                        }
-
-                        final Invoice invoice = invoiceApi.getInvoiceById(payment.getInvoiceId(), context);
-                        if (invoice.isMigrationInvoice()) {
-                            return null;
-                        }
-                        if (invoice.getBalance().compareTo(BigDecimal.ZERO) <= 0) {
-                            log.info("Aborted retry for payment {} because invoice has been paid", paymentId);
-                            return null;
-                        }
-                        processRetryPaymentWithAccountLocked(plugin, account, invoice, payment, invoice.getBalance(), context);
-                        return null;
-                    } catch (InvoiceApiException e) {
-                        throw new PaymentApiException(e);
-                    }
-                }
-            }));
+                                                                                           account.getExternalKey(),
+                                                                                           new WithAccountLockCallback<Void>() {
+
+                                                                                               @Override
+                                                                                               public Void doOperation() throws PaymentApiException {
+                                                                                                   try {
+                                                                                                       // Fetch again with account lock this time
+                                                                                                       final PaymentModelDao payment = paymentDao.getPayment(paymentId, context);
+                                                                                                       boolean foundExpectedState = false;
+                                                                                                       for (final PaymentStatus cur : expectedPaymentStates) {
+                                                                                                           if (payment.getPaymentStatus() == cur) {
+                                                                                                               foundExpectedState = true;
+                                                                                                               break;
+                                                                                                           }
+                                                                                                       }
+                                                                                                       if (!foundExpectedState) {
+                                                                                                           log.info("Aborted retry for payment {} because it is {} state", paymentId, payment.getPaymentStatus());
+                                                                                                           return null;
+                                                                                                       }
+
+                                                                                                       final Invoice invoice = invoiceApi.getInvoiceById(payment.getInvoiceId(), context);
+                                                                                                       if (invoice.isMigrationInvoice()) {
+                                                                                                           return null;
+                                                                                                       }
+                                                                                                       if (invoice.getBalance().compareTo(BigDecimal.ZERO) <= 0) {
+                                                                                                           log.info("Aborted retry for payment {} because invoice has been paid", paymentId);
+                                                                                                           return null;
+                                                                                                       }
+                                                                                                       processRetryPaymentWithAccountLocked(plugin, account, invoice, payment, invoice.getBalance(), context);
+                                                                                                       return null;
+                                                                                                   } catch (InvoiceApiException e) {
+                                                                                                       throw new PaymentApiException(e);
+                                                                                                   }
+                                                                                               }
+                                                                                           }));
         } catch (AccountApiException e) {
             log.error(String.format("Failed to retry payment for paymentId %s", paymentId), e);
         } catch (PaymentApiException e) {
@@ -401,7 +401,7 @@ public class PaymentProcessor extends ProcessorBase {
     }
 
     private Payment processRetryPaymentWithAccountLocked(final PaymentPluginApi plugin, final Account account, final Invoice invoice, final PaymentModelDao payment,
-            final BigDecimal requestedAmount, final InternalCallContext context) throws PaymentApiException {
+                                                         final BigDecimal requestedAmount, final InternalCallContext context) throws PaymentApiException {
         final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(account.getId(), invoice.getId(), payment.getId(), clock.getUTCNow(), requestedAmount);
         paymentDao.insertNewAttemptForPayment(payment.getId(), attempt, context);
         return processPaymentWithAccountLocked(plugin, account, invoice, payment, attempt, false, context);
@@ -409,12 +409,12 @@ public class PaymentProcessor extends ProcessorBase {
 
 
     private Payment processPaymentWithAccountLocked(final PaymentPluginApi plugin, final Account account, final Invoice invoice,
-            final PaymentModelDao paymentInput, final PaymentAttemptModelDao attemptInput, final boolean isInstantPayment, final InternalCallContext context) throws PaymentApiException {
+                                                    final PaymentModelDao paymentInput, final PaymentAttemptModelDao attemptInput, final boolean isInstantPayment, final InternalCallContext context) throws PaymentApiException {
 
 
         List<PaymentAttemptModelDao> allAttempts = null;
         if (paymentConfig.isPaymentOff()) {
-            paymentDao.updateStatusForPaymentWithAttempt(paymentInput.getId(), PaymentStatus.PAYMENT_SYSTEM_OFF, null, null, null, null, attemptInput.getId(), context);
+            paymentDao.updateStatusForPaymentWithAttempt(paymentInput.getId(), PaymentStatus.PAYMENT_SYSTEM_OFF, null, null, attemptInput.getId(), context);
             allAttempts = paymentDao.getAttemptsForPayment(paymentInput.getId(), context);
             return new DefaultPayment(paymentInput, allAttempts, Collections.<RefundModelDao>emptyList());
         }
@@ -424,53 +424,53 @@ public class PaymentProcessor extends ProcessorBase {
         PaymentStatus paymentStatus;
         try {
 
-            final PaymentInfoPlugin paymentPluginInfo = plugin.processPayment(account.getExternalKey(), paymentInput.getId(), attemptInput.getRequestedAmount(), context.toCallContext());
+            final PaymentInfoPlugin paymentPluginInfo = plugin.processPayment(paymentInput.getId(), paymentInput.getPaymentMethodId(), attemptInput.getRequestedAmount(), context.toCallContext());
             switch (paymentPluginInfo.getStatus()) {
-            case PROCESSED:
-                // Update Payment/PaymentAttempt status
-                paymentStatus = PaymentStatus.SUCCESS;
-                paymentDao.updateStatusForPaymentWithAttempt(paymentInput.getId(), paymentStatus, paymentPluginInfo.getGatewayErrorCode(), null, paymentPluginInfo.getExtFirstReferenceId(), paymentPluginInfo.getExtSecondReferenceId(), attemptInput.getId(), context);
-
-                // Fetch latest objects
-                allAttempts = paymentDao.getAttemptsForPayment(paymentInput.getId(), context);
-
-                payment = paymentDao.getPayment(paymentInput.getId(), context);
-                invoiceApi.notifyOfPayment(invoice.getId(),
-                        payment.getAmount(),
-                        paymentStatus == PaymentStatus.SUCCESS ? payment.getCurrency() : null,
-                                payment.getId(),
-                                payment.getEffectiveDate(),
-                                context);
-
-                // Create Bus event
-                event = new DefaultPaymentInfoEvent(account.getId(),
-                        invoice.getId(), payment.getId(), payment.getAmount(), payment.getPaymentNumber(), paymentStatus,
-                        paymentPluginInfo.getExtFirstReferenceId(), paymentPluginInfo.getExtSecondReferenceId(), context.getUserToken(), payment.getEffectiveDate(),
-                        context.getAccountRecordId(), context.getTenantRecordId());
-                break;
-
-            case ERROR:
-                allAttempts = paymentDao.getAttemptsForPayment(paymentInput.getId(), context);
-                // Schedule if non instant payment and max attempt for retry not reached yet
-                if (!isInstantPayment) {
-                    paymentStatus = scheduleRetryOnPaymentFailure(paymentInput.getId(), context);
-                } else {
-                    paymentStatus = PaymentStatus.PAYMENT_FAILURE_ABORTED;
-                }
+                case PROCESSED:
+                    // Update Payment/PaymentAttempt status
+                    paymentStatus = PaymentStatus.SUCCESS;
+                    paymentDao.updateStatusForPaymentWithAttempt(paymentInput.getId(), paymentStatus, paymentPluginInfo.getGatewayErrorCode(), null, attemptInput.getId(), context);
+
+                    // Fetch latest objects
+                    allAttempts = paymentDao.getAttemptsForPayment(paymentInput.getId(), context);
+
+                    payment = paymentDao.getPayment(paymentInput.getId(), context);
+                    invoiceApi.notifyOfPayment(invoice.getId(),
+                                               payment.getAmount(),
+                                               payment.getCurrency(),
+                                               payment.getId(),
+                                               payment.getEffectiveDate(),
+                                               context);
+
+                    // Create Bus event
+                    event = new DefaultPaymentInfoEvent(account.getId(),
+                                                        invoice.getId(), payment.getId(), payment.getAmount(), payment.getPaymentNumber(), paymentStatus,
+                                                        context.getUserToken(), payment.getEffectiveDate(),
+                                                        context.getAccountRecordId(), context.getTenantRecordId());
+                    break;
+
+                case ERROR:
+                    allAttempts = paymentDao.getAttemptsForPayment(paymentInput.getId(), context);
+                    // Schedule if non instant payment and max attempt for retry not reached yet
+                    if (!isInstantPayment) {
+                        paymentStatus = scheduleRetryOnPaymentFailure(paymentInput.getId(), context);
+                    } else {
+                        paymentStatus = PaymentStatus.PAYMENT_FAILURE_ABORTED;
+                    }
 
-                paymentDao.updateStatusForPaymentWithAttempt(paymentInput.getId(), paymentStatus, paymentPluginInfo.getGatewayErrorCode(),  paymentPluginInfo.getGatewayError(),null, null, attemptInput.getId(), context);
+                    paymentDao.updateStatusForPaymentWithAttempt(paymentInput.getId(), paymentStatus, paymentPluginInfo.getGatewayErrorCode(), paymentPluginInfo.getGatewayError(), attemptInput.getId(), context);
 
-                log.info(String.format("Could not process payment for account %s, invoice %s, error = %s",
-                         account.getId(), invoice.getId(), paymentPluginInfo.getGatewayError()));
+                    log.info(String.format("Could not process payment for account %s, invoice %s, error = %s",
+                                           account.getId(), invoice.getId(), paymentPluginInfo.getGatewayError()));
 
-                event = new DefaultPaymentErrorEvent(account.getId(), invoice.getId(), paymentInput.getId(), paymentPluginInfo.getGatewayError(), context.getUserToken(),
-                        context.getAccountRecordId(), context.getTenantRecordId());
-                throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_PAYMENT, account.getId(), paymentPluginInfo.getGatewayError());
+                    event = new DefaultPaymentErrorEvent(account.getId(), invoice.getId(), paymentInput.getId(), paymentPluginInfo.getGatewayError(), context.getUserToken(),
+                                                         context.getAccountRecordId(), context.getTenantRecordId());
+                    throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_PAYMENT, account.getId(), paymentPluginInfo.getGatewayError());
 
-            default:
-                final String formatError = String.format("Plugin return status %s for payment %s", paymentPluginInfo.getStatus(), paymentInput.getId());
-                // This caught right below as a retryable Plugin failure
-                throw new PaymentPluginApiException("", formatError);
+                default:
+                    final String formatError = String.format("Plugin return status %s for payment %s", paymentPluginInfo.getStatus(), paymentInput.getId());
+                    // This caught right below as a retryable Plugin failure
+                    throw new PaymentPluginApiException("", formatError);
             }
 
         } catch (PaymentPluginApiException e) {
@@ -480,7 +480,7 @@ public class PaymentProcessor extends ProcessorBase {
             paymentStatus = isInstantPayment ? PaymentStatus.PAYMENT_FAILURE_ABORTED : scheduleRetryOnPluginFailure(paymentInput.getId(), context);
             // STEPH message might need truncation to fit??
 
-            paymentDao.updateStatusForPaymentWithAttempt(paymentInput.getId(), paymentStatus, null, e.getMessage(), null, null, attemptInput.getId(), context);
+            paymentDao.updateStatusForPaymentWithAttempt(paymentInput.getId(), paymentStatus, null, e.getMessage(), attemptInput.getId(), context);
 
             throw new PaymentApiException(ErrorCode.PAYMENT_CREATE_PAYMENT, account.getId(), e.toString());
 
@@ -496,15 +496,15 @@ public class PaymentProcessor extends ProcessorBase {
 
     private PaymentStatus scheduleRetryOnPluginFailure(final UUID paymentId, final InternalTenantContext context) {
         final List<PaymentAttemptModelDao> allAttempts = paymentDao.getAttemptsForPayment(paymentId, context);
-        final int retryAttempt = getNumberAttemptsInState(paymentId, allAttempts, PaymentStatus.UNKNOWN, PaymentStatus.PLUGIN_FAILURE);
+        final int retryAttempt = getNumberAttemptsInState(allAttempts, PaymentStatus.UNKNOWN, PaymentStatus.PLUGIN_FAILURE);
         final boolean isScheduledForRetry = pluginFailureRetryService.scheduleRetry(paymentId, retryAttempt);
         return isScheduledForRetry ? PaymentStatus.PLUGIN_FAILURE : PaymentStatus.PLUGIN_FAILURE_ABORTED;
     }
 
     private PaymentStatus scheduleRetryOnPaymentFailure(final UUID paymentId, final InternalTenantContext context) {
         final List<PaymentAttemptModelDao> allAttempts = paymentDao.getAttemptsForPayment(paymentId, context);
-        final int retryAttempt = getNumberAttemptsInState(paymentId, allAttempts,
-                PaymentStatus.UNKNOWN, PaymentStatus.PAYMENT_FAILURE);
+        final int retryAttempt = getNumberAttemptsInState(allAttempts,
+                                                          PaymentStatus.UNKNOWN, PaymentStatus.PAYMENT_FAILURE);
 
 
         final boolean isScheduledForRetry = failedPaymentRetryService.scheduleRetry(paymentId, retryAttempt);
@@ -514,7 +514,7 @@ public class PaymentProcessor extends ProcessorBase {
         return isScheduledForRetry ? PaymentStatus.PAYMENT_FAILURE : PaymentStatus.PAYMENT_FAILURE_ABORTED;
     }
 
-    private int getNumberAttemptsInState(final UUID paymentId, final List<PaymentAttemptModelDao> allAttempts, final PaymentStatus... statuses) {
+    private int getNumberAttemptsInState(final List<PaymentAttemptModelDao> allAttempts, final PaymentStatus... statuses) {
         if (allAttempts == null || allAttempts.size() == 0) {
             return 0;
         }
diff --git a/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java b/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java
index 0665517..e3366aa 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java
@@ -17,7 +17,6 @@
 package com.ning.billing.payment.core;
 
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
@@ -31,11 +30,11 @@ import com.ning.billing.ErrorCode;
 import com.ning.billing.ObjectType;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.osgi.api.OSGIServiceRegistration;
 import com.ning.billing.payment.api.PaymentApiException;
 import com.ning.billing.payment.dao.PaymentDao;
 import com.ning.billing.payment.dao.PaymentMethodModelDao;
 import com.ning.billing.payment.plugin.api.PaymentPluginApi;
-import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
@@ -58,7 +57,7 @@ public abstract class ProcessorBase {
 
     private static final int NB_LOCK_TRY = 5;
 
-    protected final PaymentProviderPluginRegistry pluginRegistry;
+    protected final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry;
     protected final AccountInternalApi accountInternalApi;
     protected final InternalBus eventBus;
     protected final GlobalLocker locker;
@@ -68,7 +67,7 @@ public abstract class ProcessorBase {
 
     private static final Logger log = LoggerFactory.getLogger(ProcessorBase.class);
 
-    public ProcessorBase(final PaymentProviderPluginRegistry pluginRegistry,
+    public ProcessorBase(final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
                          final AccountInternalApi accountInternalApi,
                          final InternalBus eventBus,
                          final PaymentDao paymentDao,
@@ -111,7 +110,7 @@ public abstract class ProcessorBase {
             log.error("PaymentMethod dpes not exist", paymentMethodId);
             throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, paymentMethodId);
         }
-        return pluginRegistry.getPlugin(methodDao.getPluginName());
+        return pluginRegistry.getServiceForName(methodDao.getPluginName());
     }
 
     protected PaymentPluginApi getPaymentProviderPlugin(final String accountKey, final InternalTenantContext context)
@@ -122,7 +121,7 @@ public abstract class ProcessorBase {
             final Account account = accountInternalApi.getAccountByKey(accountKey, context);
             return getPaymentProviderPlugin(account, context);
         }
-        return pluginRegistry.getPlugin(paymentProviderName);
+        return pluginRegistry.getServiceForName(paymentProviderName);
     }
 
     protected PaymentPluginApi getPaymentProviderPlugin(final Account account, final InternalTenantContext context) throws PaymentApiException {
diff --git a/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
index c826777..a1e2783 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
@@ -37,6 +37,7 @@ import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.osgi.api.OSGIServiceRegistration;
 import com.ning.billing.payment.api.DefaultRefund;
 import com.ning.billing.payment.api.PaymentApiException;
 import com.ning.billing.payment.api.Refund;
@@ -46,7 +47,6 @@ import com.ning.billing.payment.dao.RefundModelDao;
 import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
 import com.ning.billing.payment.plugin.api.PaymentPluginApi;
 import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
-import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
 import com.ning.billing.util.callcontext.CallOrigin;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
@@ -75,7 +75,7 @@ public class RefundProcessor extends ProcessorBase {
     private final InternalCallContextFactory internalCallContextFactory;
 
     @Inject
-    public RefundProcessor(final PaymentProviderPluginRegistry pluginRegistry,
+    public RefundProcessor(final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
                            final AccountInternalApi accountApi,
                            final InvoiceInternalApi invoiceApi,
                            final InternalBus eventBus,
@@ -113,58 +113,17 @@ public class RefundProcessor extends ProcessorBase {
                 final BigDecimal refundAmount = computeRefundAmount(paymentId, specifiedRefundAmount, invoiceItemIdsWithAmounts, context);
 
                 try {
-
                     final PaymentModelDao payment = paymentDao.getPayment(paymentId, context);
                     if (payment == null) {
                         throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_SUCCESS_PAYMENT, paymentId);
                     }
 
-                    //
-                    // We are looking for multiple things:
-                    // 1. Compute totalAmountRefunded based on all Refund entries that made it to the plugin.
-                    // 2. If we find a CREATED entry (that did not make it to the plugin) with the same amount, we reuse the entry
-                    // 3. Compute foundPluginCompletedRefunds, number of refund entries for that amount that made it to the plugin
-                    //
-                    int foundPluginCompletedRefunds = 0;
-                    RefundModelDao refundInfo = null;
-                    BigDecimal totalAmountRefunded = BigDecimal.ZERO;
-                    final List<RefundModelDao> existingRefunds = paymentDao.getRefundsForPayment(paymentId, context);
-                    for (final RefundModelDao cur : existingRefunds) {
-
-                        final BigDecimal existingPositiveAmount = cur.getAmount();
-                        if (existingPositiveAmount.compareTo(refundAmount) == 0) {
-                            if (cur.getRefundStatus() == RefundStatus.CREATED) {
-                                if (refundInfo == null) {
-                                    refundInfo = cur;
-                                }
-                            } else {
-                                foundPluginCompletedRefunds++;
-                            }
-                        }
-                        if (cur.getRefundStatus() != RefundStatus.CREATED) {
-                            totalAmountRefunded = totalAmountRefunded.add(existingPositiveAmount);
-                        }
-                    }
-
-                    if (payment.getAmount().subtract(totalAmountRefunded).compareTo(refundAmount) < 0) {
-                        throw new PaymentApiException(ErrorCode.PAYMENT_REFUND_AMOUNT_TOO_LARGE);
-                    }
-
-                    if (refundInfo == null) {
-                        refundInfo = new RefundModelDao(account.getId(), paymentId, refundAmount, account.getCurrency(), isAdjusted);
-                        paymentDao.insertRefund(refundInfo, context);
-                    }
+                    final RefundModelDao refundInfo = new RefundModelDao(account.getId(), paymentId, refundAmount, account.getCurrency(), isAdjusted);
+                    paymentDao.insertRefund(refundInfo, context);
 
                     final PaymentPluginApi plugin = getPaymentProviderPlugin(payment.getPaymentMethodId(), context);
-                    final int nbExistingRefunds = plugin.getNbRefundForPaymentAmount(account, paymentId, refundAmount, context.toCallContext());
-                    log.debug(String.format("found %d pluginRefunds for paymentId %s and amount %s", nbExistingRefunds, paymentId, refundAmount));
-
-                    if (nbExistingRefunds > foundPluginCompletedRefunds) {
-                        log.info("Found existing plugin refund for paymentId {}, skip plugin", paymentId);
-                    } else {
-                        // If there is no such existing refund we create it
-                        plugin.processRefund(account, paymentId, refundAmount, context.toCallContext());
-                    }
+                    plugin.processRefund(paymentId, refundAmount, context.toCallContext());
+
                     paymentDao.updateRefundStatus(refundInfo.getId(), RefundStatus.PLUGIN_COMPLETED, context);
 
                     invoiceApi.createRefund(paymentId, refundAmount, isAdjusted, invoiceItemIdsWithAmounts, refundInfo.getId(), context);
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
index 4ed9c5f..7e81ac1 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/DefaultPaymentDao.java
@@ -96,15 +96,13 @@ public class DefaultPaymentDao implements PaymentDao {
                                                   final PaymentStatus paymentStatus,
                                                   final String gatewayErrorCode,
                                                   final String gatewayErrorMsg,
-                                                  final String extFirstPaymentRefId,
-                                                  final String extSecondPaymentRefId,
                                                   final UUID attemptId,
                                                   final InternalCallContext context) {
         transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
 
             @Override
             public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                entitySqlDaoWrapperFactory.become(PaymentSqlDao.class).updatePaymentStatusAndExtRef(paymentId.toString(), paymentStatus.toString(), extFirstPaymentRefId, extSecondPaymentRefId, context);
+                entitySqlDaoWrapperFactory.become(PaymentSqlDao.class).updatePaymentStatus(paymentId.toString(), paymentStatus.toString(), context);
                 entitySqlDaoWrapperFactory.become(PaymentAttemptSqlDao.class).updatePaymentAttemptStatus(attemptId.toString(), paymentStatus.toString(), gatewayErrorCode, gatewayErrorMsg, context);
                 return null;
             }
@@ -130,52 +128,6 @@ public class DefaultPaymentDao implements PaymentDao {
     }
 
     @Override
-    public List<PaymentMethodModelDao> refreshPaymentMethods(final UUID accountId, final List<PaymentMethodModelDao> paymentMethods, final InternalCallContext context) {
-        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<PaymentMethodModelDao>>() {
-
-            @Override
-            public List<PaymentMethodModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                final PaymentMethodSqlDao transactional = entitySqlDaoWrapperFactory.become(PaymentMethodSqlDao.class);
-                final List<PaymentMethodModelDao> existingPaymentMethods = transactional.getByAccountId(accountId.toString(), context);
-
-                final Set<String> externalPaymentIdProcessed = new HashSet<String>();
-                for (final PaymentMethodModelDao finalPaymentMethod : paymentMethods) {
-                    boolean isExistingPaymentMethod = false;
-
-                    for (final PaymentMethodModelDao existingPaymentMethod : existingPaymentMethods) {
-                        if (existingPaymentMethod.equals(finalPaymentMethod)) {
-                            // We already have it - nothing to do
-                            isExistingPaymentMethod = true;
-                            break;
-                        } else if (existingPaymentMethod.equalsButActive(finalPaymentMethod)) {
-                            // We already have it but its status has changed - update it accordingly
-                            // Note - in the remote system, the payment method will always be active
-                            undeletedPaymentMethodInTransaction(entitySqlDaoWrapperFactory, existingPaymentMethod.getId(), context);
-                            isExistingPaymentMethod = true;
-                            break;
-                        }
-                        // Otherwise, we don't have it
-                    }
-
-                    if (!isExistingPaymentMethod) {
-                        insertPaymentMethodInTransaction(entitySqlDaoWrapperFactory, finalPaymentMethod, context);
-                    }
-
-                    externalPaymentIdProcessed.add(finalPaymentMethod.getExternalId());
-                }
-
-                // Finally, mark as deleted the ones that don't exist in the specified list (remote system)
-                for (final PaymentMethodModelDao existingPaymentMethod : existingPaymentMethods) {
-                    if (!externalPaymentIdProcessed.contains(existingPaymentMethod.getExternalId())) {
-                        deletedPaymentMethodInTransaction(entitySqlDaoWrapperFactory, existingPaymentMethod.getId(), context);
-                    }
-                }
-                return transactional.getByAccountId(accountId.toString(), context);
-            }
-        });
-    }
-
-    @Override
     public RefundModelDao insertRefund(final RefundModelDao refundInfo, final InternalCallContext context) {
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<RefundModelDao>() {
 
@@ -339,4 +291,47 @@ public class DefaultPaymentDao implements PaymentDao {
             }
         });
     }
+
+    @Override
+    public List<PaymentMethodModelDao> refreshPaymentMethods(final UUID accountId, final List<PaymentMethodModelDao> newPaymentMethods, final InternalCallContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<PaymentMethodModelDao>>() {
+
+            @Override
+            public List<PaymentMethodModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final PaymentMethodSqlDao transactional = entitySqlDaoWrapperFactory.become(PaymentMethodSqlDao.class);
+                final List<PaymentMethodModelDao> existingPaymentMethods = transactional.getByAccountId(accountId.toString(), context);
+
+                for (final PaymentMethodModelDao finalPaymentMethod : newPaymentMethods) {
+
+                    PaymentMethodModelDao foundExistingPaymentMethod = null;
+                    for (final PaymentMethodModelDao existingPaymentMethod : existingPaymentMethods) {
+                        if (existingPaymentMethod.equals(finalPaymentMethod)) {
+                            // We already have it - nothing to do
+                            foundExistingPaymentMethod = existingPaymentMethod;
+                            break;
+                        } else if (existingPaymentMethod.equalsButActive(finalPaymentMethod)) {
+                            // We already have it but its status has changed - update it accordingly
+                            undeletedPaymentMethodInTransaction(entitySqlDaoWrapperFactory, existingPaymentMethod.getId(), context);
+                            foundExistingPaymentMethod = existingPaymentMethod;
+                            break;
+                        }
+                        // Otherwise, we don't have it
+                    }
+
+                    if (foundExistingPaymentMethod == null) {
+                        insertPaymentMethodInTransaction(entitySqlDaoWrapperFactory, finalPaymentMethod, context);
+                    } else {
+                        existingPaymentMethods.remove(foundExistingPaymentMethod);
+                    }
+                }
+
+                // Finally, all payment methods left in the existingPaymentMethods should be marked as deleted
+                for (final PaymentMethodModelDao existingPaymentMethod : existingPaymentMethods) {
+                        deletedPaymentMethodInTransaction(entitySqlDaoWrapperFactory, existingPaymentMethod.getId(), context);
+                }
+                return transactional.getByAccountId(accountId.toString(), context);
+            }
+        });
+    }
+
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
index 983aae9..a5eaef5 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentDao.java
@@ -32,7 +32,7 @@ public interface PaymentDao {
     public PaymentAttemptModelDao insertNewAttemptForPayment(UUID paymentId, PaymentAttemptModelDao attempt, InternalCallContext context);
 
     public void updateStatusForPaymentWithAttempt(UUID paymentId, PaymentStatus paymentStatus, String gatewayErrorCode,
-                                                  String gatewayErrorMsg, String extFirstPaymentRefId, String extSecondPaymentRefId,
+                                                  String gatewayErrorMsg,
                                                   UUID attemptId, InternalCallContext context);
 
     public PaymentAttemptModelDao getPaymentAttempt(UUID attemptId, InternalTenantContext context);
@@ -59,8 +59,6 @@ public interface PaymentDao {
 
     public PaymentMethodModelDao insertPaymentMethod(PaymentMethodModelDao paymentMethod, InternalCallContext context);
 
-    public List<PaymentMethodModelDao> refreshPaymentMethods(UUID accountId, List<PaymentMethodModelDao> paymentMethods, InternalCallContext context);
-
     public PaymentMethodModelDao getPaymentMethod(UUID paymentMethodId, InternalTenantContext context);
 
     public PaymentMethodModelDao getPaymentMethodIncludedDeleted(UUID paymentMethodId, InternalTenantContext context);
@@ -69,5 +67,7 @@ public interface PaymentDao {
 
     public void deletedPaymentMethod(UUID paymentMethodId, InternalCallContext context);
 
+    public List<PaymentMethodModelDao> refreshPaymentMethods(final UUID accountId, final List<PaymentMethodModelDao> paymentMethods, final InternalCallContext context);
+
     public void undeletedPaymentMethod(UUID paymentMethodId, InternalCallContext context);
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodModelDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodModelDao.java
index e2d8be2..99ae81d 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodModelDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentMethodModelDao.java
@@ -32,18 +32,16 @@ public class PaymentMethodModelDao extends EntityBase implements EntityModelDao<
     private UUID accountId;
     private String pluginName;
     private Boolean isActive;
-    private String externalId;
 
     public PaymentMethodModelDao() { /* For the DAO mapper */ }
 
     public PaymentMethodModelDao(final UUID id, @Nullable final DateTime createdDate, @Nullable final DateTime updatedDate,
                                  final UUID accountId, final String pluginName,
-                                 final Boolean isActive, final String externalId) {
+                                 final Boolean isActive) {
         super(id, createdDate, updatedDate);
         this.accountId = accountId;
         this.pluginName = pluginName;
         this.isActive = isActive;
-        this.externalId = externalId;
     }
 
     public UUID getAccountId() {
@@ -63,10 +61,6 @@ public class PaymentMethodModelDao extends EntityBase implements EntityModelDao<
         return isActive;
     }
 
-    public String getExternalId() {
-        return externalId;
-    }
-
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
@@ -74,7 +68,6 @@ public class PaymentMethodModelDao extends EntityBase implements EntityModelDao<
         sb.append("{accountId=").append(accountId);
         sb.append(", pluginName='").append(pluginName).append('\'');
         sb.append(", isActive=").append(isActive);
-        sb.append(", externalId='").append(externalId).append('\'');
         sb.append('}');
         return sb.toString();
     }
@@ -102,10 +95,10 @@ public class PaymentMethodModelDao extends EntityBase implements EntityModelDao<
     }
 
     public boolean equalsButActive(final PaymentMethodModelDao that) {
-        if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) {
+        if (id != null ? !id.equals(that.id) : that.id != null) {
             return false;
         }
-        if (externalId != null ? !externalId.equals(that.externalId) : that.externalId != null) {
+        if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) {
             return false;
         }
         if (pluginName != null ? !pluginName.equals(that.pluginName) : that.pluginName != null) {
@@ -120,7 +113,6 @@ public class PaymentMethodModelDao extends EntityBase implements EntityModelDao<
         int result = accountId != null ? accountId.hashCode() : 0;
         result = 31 * result + (pluginName != null ? pluginName.hashCode() : 0);
         result = 31 * result + (isActive != null ? isActive.hashCode() : 0);
-        result = 31 * result + (externalId != null ? externalId.hashCode() : 0);
         return result;
     }
 
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
index 81189e7..5b289ea 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
@@ -37,11 +37,9 @@ public interface PaymentSqlDao extends EntitySqlDao<PaymentModelDao, Payment> {
 
     @SqlUpdate
     @Audited(ChangeType.UPDATE)
-    void updatePaymentStatusAndExtRef(@Bind("id") final String paymentId,
-                                      @Bind("paymentStatus") final String paymentStatus,
-                                      @Bind("extFirstPaymentRefId") final String extFirstPaymentRefId,
-                                      @Bind("extSecondPaymentRefId") final String extSecondPaymentRefId,
-                                      @BindBean final InternalCallContext context);
+    void updatePaymentStatus(@Bind("id") final String paymentId,
+                             @Bind("paymentStatus") final String paymentStatus,
+                             @BindBean final InternalCallContext context);
 
     @SqlUpdate
     @Audited(ChangeType.UPDATE)
diff --git a/payment/src/main/java/com/ning/billing/payment/glue/DefaultPaymentProviderPluginRegistryProvider.java b/payment/src/main/java/com/ning/billing/payment/glue/DefaultPaymentProviderPluginRegistryProvider.java
index 8cbbc5d..ed47264 100644
--- a/payment/src/main/java/com/ning/billing/payment/glue/DefaultPaymentProviderPluginRegistryProvider.java
+++ b/payment/src/main/java/com/ning/billing/payment/glue/DefaultPaymentProviderPluginRegistryProvider.java
@@ -16,6 +16,9 @@
 
 package com.ning.billing.payment.glue;
 
+import com.ning.billing.osgi.api.OSGIServiceDescriptor;
+import com.ning.billing.osgi.api.OSGIServiceRegistration;
+import com.ning.billing.payment.plugin.api.PaymentPluginApi;
 import com.ning.billing.util.config.PaymentConfig;
 import com.ning.billing.payment.provider.DefaultPaymentProviderPluginRegistry;
 import com.ning.billing.payment.provider.ExternalPaymentProviderPlugin;
@@ -23,7 +26,7 @@ import com.ning.billing.payment.provider.ExternalPaymentProviderPlugin;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
-public class DefaultPaymentProviderPluginRegistryProvider implements Provider<DefaultPaymentProviderPluginRegistry> {
+public class DefaultPaymentProviderPluginRegistryProvider implements Provider<OSGIServiceRegistration<PaymentPluginApi>> {
 
     private final PaymentConfig paymentConfig;
     private final ExternalPaymentProviderPlugin externalPaymentProviderPlugin;
@@ -35,11 +38,29 @@ public class DefaultPaymentProviderPluginRegistryProvider implements Provider<De
     }
 
     @Override
-    public DefaultPaymentProviderPluginRegistry get() {
+    public OSGIServiceRegistration<PaymentPluginApi> get() {
         final DefaultPaymentProviderPluginRegistry pluginRegistry = new DefaultPaymentProviderPluginRegistry(paymentConfig);
 
         // Make the external payment provider plugin available by default
-        pluginRegistry.register(externalPaymentProviderPlugin, ExternalPaymentProviderPlugin.PLUGIN_NAME);
+        final OSGIServiceDescriptor desc = new OSGIServiceDescriptor() {
+            @Override
+            public String getPluginSymbolicName() {
+                return null;
+            }
+            @Override
+            public String getServiceName() {
+                return ExternalPaymentProviderPlugin.PLUGIN_NAME;
+            }
+            @Override
+            public String getServiceInfo() {
+                return null;
+            }
+            @Override
+            public String getServiceType() {
+                return null;
+            }
+        };
+        pluginRegistry.registerService(desc, externalPaymentProviderPlugin);
 
         return pluginRegistry;
     }
diff --git a/payment/src/main/java/com/ning/billing/payment/glue/PaymentModule.java b/payment/src/main/java/com/ning/billing/payment/glue/PaymentModule.java
index 19e3f4e..fd45392 100644
--- a/payment/src/main/java/com/ning/billing/payment/glue/PaymentModule.java
+++ b/payment/src/main/java/com/ning/billing/payment/glue/PaymentModule.java
@@ -16,61 +16,52 @@
 
 package com.ning.billing.payment.glue;
 
-import java.util.Properties;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ThreadFactory;
 
 import org.skife.config.ConfigSource;
 import org.skife.config.ConfigurationObjectFactory;
-import org.skife.config.SimplePropertyConfigSource;
 
-import com.ning.billing.payment.bus.PaymentTagHandler;
-import com.ning.billing.payment.dao.DefaultPaymentDao;
-import com.ning.billing.util.config.PaymentConfig;
+import com.ning.billing.osgi.api.OSGIServiceRegistration;
 import com.ning.billing.payment.api.DefaultPaymentApi;
 import com.ning.billing.payment.api.PaymentApi;
 import com.ning.billing.payment.api.PaymentService;
 import com.ning.billing.payment.api.svcs.DefaultPaymentInternalApi;
 import com.ning.billing.payment.bus.InvoiceHandler;
+import com.ning.billing.payment.bus.PaymentTagHandler;
 import com.ning.billing.payment.core.PaymentMethodProcessor;
 import com.ning.billing.payment.core.PaymentProcessor;
 import com.ning.billing.payment.core.RefundProcessor;
+import com.ning.billing.payment.dao.DefaultPaymentDao;
 import com.ning.billing.payment.dao.PaymentDao;
-import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
+import com.ning.billing.payment.plugin.api.PaymentPluginApi;
 import com.ning.billing.payment.retry.AutoPayRetryService;
 import com.ning.billing.payment.retry.AutoPayRetryService.AutoPayRetryServiceScheduler;
 import com.ning.billing.payment.retry.FailedPaymentRetryService;
 import com.ning.billing.payment.retry.FailedPaymentRetryService.FailedPaymentRetryServiceScheduler;
 import com.ning.billing.payment.retry.PluginFailureRetryService;
 import com.ning.billing.payment.retry.PluginFailureRetryService.PluginFailureRetryServiceScheduler;
+import com.ning.billing.util.config.PaymentConfig;
 import com.ning.billing.util.svcapi.payment.PaymentInternalApi;
 
-import com.google.common.annotations.VisibleForTesting;
 import com.google.inject.AbstractModule;
+import com.google.inject.TypeLiteral;
 import com.google.inject.name.Names;
 
 public class PaymentModule extends AbstractModule {
+
     private static final int PLUGIN_NB_THREADS = 3;
     private static final String PLUGIN_THREAD_PREFIX = "Plugin-th-";
 
     public static final String PLUGIN_EXECUTOR_NAMED = "PluginExecutor";
 
-    @VisibleForTesting
     protected ConfigSource configSource;
 
-    public PaymentModule() {
-        this(System.getProperties());
-    }
-
     public PaymentModule(final ConfigSource configSource) {
         this.configSource = configSource;
     }
 
-    public PaymentModule(final Properties properties) {
-        this(new SimplePropertyConfigSource(properties));
-    }
-
     protected void installPaymentDao() {
         bind(PaymentDao.class).to(DefaultPaymentDao.class).asEagerSingleton();
     }
@@ -109,7 +100,7 @@ public class PaymentModule extends AbstractModule {
         final PaymentConfig paymentConfig = factory.build(PaymentConfig.class);
 
         bind(PaymentConfig.class).toInstance(paymentConfig);
-        bind(PaymentProviderPluginRegistry.class).toProvider(DefaultPaymentProviderPluginRegistryProvider.class).asEagerSingleton();
+        bind(new TypeLiteral<OSGIServiceRegistration<PaymentPluginApi>>() {}).toProvider(DefaultPaymentProviderPluginRegistryProvider.class).asEagerSingleton();
 
         bind(PaymentInternalApi.class).to(DefaultPaymentInternalApi.class).asEagerSingleton();
         bind(PaymentApi.class).to(DefaultPaymentApi.class).asEagerSingleton();
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentInfoPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentInfoPlugin.java
index fd588b2..bf10051 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentInfoPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentInfoPlugin.java
@@ -70,16 +70,6 @@ public class DefaultNoOpPaymentInfoPlugin implements PaymentInfoPlugin {
     }
 
     @Override
-    public String getExtFirstReferenceId() {
-        return null;
-    }
-
-    @Override
-    public String getExtSecondReferenceId() {
-        return null;
-    }
-
-    @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
         sb.append("DefaultNoOpPaymentInfoPlugin");
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java
index 75c082c..7a41e27 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java
@@ -24,13 +24,14 @@ import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import com.ning.billing.account.api.Account;
 import com.ning.billing.payment.api.PaymentMethodPlugin;
 import com.ning.billing.payment.plugin.api.NoOpPaymentPluginApi;
 import com.ning.billing.payment.plugin.api.PaymentInfoPlugin;
 import com.ning.billing.payment.plugin.api.PaymentInfoPlugin.PaymentPluginStatus;
+import com.ning.billing.payment.plugin.api.PaymentMethodInfoPlugin;
 import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
-import com.ning.billing.payment.plugin.api.PaymentProviderAccount;
+import com.ning.billing.payment.plugin.api.RefundInfoPlugin;
+import com.ning.billing.payment.plugin.api.RefundInfoPlugin.RefundPluginStatus;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.TenantContext;
 import com.ning.billing.util.clock.Clock;
@@ -47,11 +48,10 @@ public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
     private final AtomicBoolean makeNextInvoiceFailWithException = new AtomicBoolean(false);
     private final AtomicBoolean makeAllInvoicesFailWithError = new AtomicBoolean(false);
 
-    private final Map<UUID, PaymentInfoPlugin> payments = new ConcurrentHashMap<UUID, PaymentInfoPlugin>();
+    private final Map<String, PaymentInfoPlugin> payments = new ConcurrentHashMap<String, PaymentInfoPlugin>();
     // Note: we can't use HashMultiMap as we care about storing duplicate key/value pairs
-    private final Multimap<UUID, BigDecimal> refunds = LinkedListMultimap.<UUID, BigDecimal>create();
+    private final Multimap<String, RefundInfoPlugin> refunds = LinkedListMultimap.<String, RefundInfoPlugin>create();
     private final Map<String, List<PaymentMethodPlugin>> paymentMethods = new ConcurrentHashMap<String, List<PaymentMethodPlugin>>();
-    private final Map<String, PaymentProviderAccount> accounts = new ConcurrentHashMap<String, PaymentProviderAccount>();
 
     private final Clock clock;
 
@@ -89,71 +89,44 @@ public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
     }
 
     @Override
-    public PaymentInfoPlugin processPayment(final String externalKey, final UUID paymentId, final BigDecimal amount, final CallContext context) throws PaymentPluginApiException {
+    public PaymentInfoPlugin processPayment(final UUID kbPaymentId, final UUID kbPaymentMethodId, final BigDecimal amount, final CallContext context) throws PaymentPluginApiException {
         if (makeNextInvoiceFailWithException.getAndSet(false)) {
             throw new PaymentPluginApiException("", "test error");
         }
 
         final PaymentPluginStatus status = (makeAllInvoicesFailWithError.get() || makeNextInvoiceFailWithError.getAndSet(false)) ? PaymentPluginStatus.ERROR : PaymentPluginStatus.PROCESSED;
         final PaymentInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(amount, clock.getUTCNow(), clock.getUTCNow(), status, null);
-        payments.put(paymentId, result);
+        payments.put(kbPaymentId.toString(), result);
         return result;
     }
 
     @Override
-    public PaymentInfoPlugin getPaymentInfo(final UUID paymentId, final TenantContext context) throws PaymentPluginApiException {
-        final PaymentInfoPlugin payment = payments.get(paymentId);
+    public PaymentInfoPlugin getPaymentInfo(final UUID kbPaymentId, final TenantContext context) throws PaymentPluginApiException {
+        final PaymentInfoPlugin payment = payments.get(kbPaymentId.toString());
         if (payment == null) {
-            throw new PaymentPluginApiException("", "No payment found for id " + paymentId);
+            throw new PaymentPluginApiException("", "No payment found for payment id " + kbPaymentId.toString());
         }
         return payment;
     }
 
     @Override
-    public String createPaymentProviderAccount(final Account account, final CallContext context) throws PaymentPluginApiException {
-        if (account != null) {
-            final String id = UUID.randomUUID().toString();
-            final String paymentMethodId = UUID.randomUUID().toString();
-            accounts.put(account.getExternalKey(),
-                         new PaymentProviderAccount.Builder().setAccountKey(account.getExternalKey())
-                                                             .setId(id)
-                                                             .setDefaultPaymentMethod(paymentMethodId)
-                                                             .build());
-            return id;
-        } else {
-            throw new PaymentPluginApiException("", "Did not get account to create payment provider account");
-        }
-    }
-
-    @Override
-    public String addPaymentMethod(final String accountKey, final PaymentMethodPlugin paymentMethodProps, final boolean setDefault, final CallContext context) throws PaymentPluginApiException {
+    public void addPaymentMethod(final UUID kbAccountId, final UUID kbPaymentMethodId, final PaymentMethodPlugin paymentMethodProps, final boolean setDefault, final CallContext context) throws PaymentPluginApiException {
         final PaymentMethodPlugin realWithID = new DefaultNoOpPaymentMethodPlugin(paymentMethodProps);
-        List<PaymentMethodPlugin> pms = paymentMethods.get(accountKey);
+        List<PaymentMethodPlugin> pms = paymentMethods.get(kbPaymentMethodId.toString());
         if (pms == null) {
             pms = new LinkedList<PaymentMethodPlugin>();
-            paymentMethods.put(accountKey, pms);
+            paymentMethods.put(kbPaymentMethodId.toString(), pms);
         }
         pms.add(realWithID);
-
-        return realWithID.getExternalPaymentMethodId();
-    }
-
-    @Override
-    public void updatePaymentMethod(final String accountKey, final PaymentMethodPlugin paymentMethodProps, final CallContext context)
-            throws PaymentPluginApiException {
-        final DefaultNoOpPaymentMethodPlugin e = getPaymentMethod(accountKey, paymentMethodProps.getExternalPaymentMethodId());
-        if (e != null) {
-            e.setProps(paymentMethodProps.getProperties());
-        }
     }
 
     @Override
-    public void deletePaymentMethod(final String accountKey, final String paymentMethodId, final CallContext context) throws PaymentPluginApiException {
+    public void deletePaymentMethod(final UUID kbPaymentMethodId, final CallContext context) throws PaymentPluginApiException {
         PaymentMethodPlugin toBeDeleted = null;
-        final List<PaymentMethodPlugin> pms = paymentMethods.get(accountKey);
+        final List<PaymentMethodPlugin> pms = paymentMethods.get(kbPaymentMethodId.toString());
         if (pms != null) {
             for (final PaymentMethodPlugin cur : pms) {
-                if (cur.getExternalPaymentMethodId().equals(paymentMethodId)) {
+                if (cur.getExternalPaymentMethodId().equals(kbPaymentMethodId.toString())) {
                     toBeDeleted = cur;
                     break;
                 }
@@ -166,64 +139,47 @@ public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
     }
 
     @Override
-    public List<PaymentMethodPlugin> getPaymentMethodDetails(final String accountKey, final TenantContext context)
-            throws PaymentPluginApiException {
-        return paymentMethods.get(accountKey);
+    public PaymentMethodPlugin getPaymentMethodDetail(final UUID kbAccountId, final UUID kbPaymentMethodId, final TenantContext context) throws PaymentPluginApiException {
+        final List<PaymentMethodPlugin> paymentMethodPlugins = paymentMethods.get(kbPaymentMethodId.toString());
+        if (paymentMethodPlugins == null || paymentMethodPlugins.size() == 0) {
+            return null;
+        } else {
+            return paymentMethodPlugins.get(0);
+        }
+    }
+
+    @Override
+    public void setDefaultPaymentMethod(final UUID kbPaymentMethodId, final CallContext context) throws PaymentPluginApiException {
     }
 
     @Override
-    public PaymentMethodPlugin getPaymentMethodDetail(final String accountKey, final String externalPaymentId, final TenantContext context)
-            throws PaymentPluginApiException {
-        return getPaymentMethod(accountKey, externalPaymentId);
+    public List<PaymentMethodInfoPlugin> getPaymentMethods(final UUID kbAccountId, final boolean refreshFromGateway, final CallContext context) {
+        return null;
     }
 
     @Override
-    public void setDefaultPaymentMethod(final String accountKey, final String externalPaymentId, final CallContext context) throws PaymentPluginApiException {
+    public void resetPaymentMethods(final List<PaymentMethodInfoPlugin> paymentMethods) {
     }
 
     @Override
-    public void processRefund(final Account account, final UUID paymentId, final BigDecimal refundAmount, final CallContext context) throws PaymentPluginApiException {
-        final PaymentInfoPlugin paymentInfoPlugin = getPaymentInfo(paymentId, context);
+    public RefundInfoPlugin processRefund(final UUID kbPaymentId, final BigDecimal refundAmount, final CallContext context) throws PaymentPluginApiException {
+        final PaymentInfoPlugin paymentInfoPlugin = getPaymentInfo(kbPaymentId, context);
         if (paymentInfoPlugin == null) {
-            throw new PaymentPluginApiException("", String.format("No payment found for paymentId %s (plugin %s)", paymentId, getName()));
+            throw new PaymentPluginApiException("", String.format("No payment found for payment id %s (plugin %s)", kbPaymentId.toString(), getName()));
         }
 
         BigDecimal maxAmountRefundable = paymentInfoPlugin.getAmount();
-        for (final BigDecimal refund : refunds.get(paymentId)) {
-            maxAmountRefundable = maxAmountRefundable.add(refund.negate());
+        for (final RefundInfoPlugin refund : refunds.get(kbPaymentId.toString())) {
+            maxAmountRefundable = maxAmountRefundable.add(refund.getAmount().negate());
         }
         if (maxAmountRefundable.compareTo(refundAmount) < 0) {
-            throw new PaymentPluginApiException("", String.format("Refund amount of %s for paymentId %s is bigger than the payment amount %s (plugin %s)",
-                                                                  refundAmount, paymentId, paymentInfoPlugin.getAmount(), getName()));
-        }
-
-        refunds.put(paymentId, refundAmount);
-    }
-
-    @Override
-    public int getNbRefundForPaymentAmount(final Account account, final UUID paymentId, final BigDecimal refundAmount, final TenantContext context) throws PaymentPluginApiException {
-        int nbRefunds = 0;
-        for (final BigDecimal amount : refunds.get(paymentId)) {
-            if (amount.compareTo(refundAmount) == 0) {
-                nbRefunds++;
-            }
-        }
-
-        return nbRefunds;
-    }
-
-    private DefaultNoOpPaymentMethodPlugin getPaymentMethod(final String accountKey, final String externalPaymentId) {
-        final List<PaymentMethodPlugin> pms = paymentMethods.get(accountKey);
-        if (pms == null) {
-            return null;
+            throw new PaymentPluginApiException("", String.format("Refund amount of %s for payment id %s is bigger than the payment amount %s (plugin %s)",
+                                                                  refundAmount, kbPaymentId.toString(), paymentInfoPlugin.getAmount(), getName()));
         }
 
-        for (final PaymentMethodPlugin cur : pms) {
-            if (cur.getExternalPaymentMethodId().equals(externalPaymentId)) {
-                return (DefaultNoOpPaymentMethodPlugin) cur;
-            }
-        }
+        final DefaultNoOpRefundInfoPlugin refundInfoPlugin = new DefaultNoOpRefundInfoPlugin(refundAmount, clock.getUTCNow(), clock.getUTCNow(), RefundPluginStatus.PROCESSED, null);
+        refunds.put(kbPaymentId.toString(), refundInfoPlugin);
 
-        return null;
+        return refundInfoPlugin;
     }
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpRefundInfoPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpRefundInfoPlugin.java
new file mode 100644
index 0000000..d9b9805
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpRefundInfoPlugin.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.payment.provider;
+
+import java.math.BigDecimal;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.payment.plugin.api.RefundInfoPlugin;
+
+public class DefaultNoOpRefundInfoPlugin implements RefundInfoPlugin {
+
+    private final BigDecimal amount;
+    private final DateTime effectiveDate;
+    private final DateTime createdDate;
+    private final RefundPluginStatus status;
+    private final String error;
+
+    public DefaultNoOpRefundInfoPlugin(final BigDecimal amount, final DateTime effectiveDate,
+                                       final DateTime createdDate, final RefundPluginStatus status, final String error) {
+        this.amount = amount;
+        this.effectiveDate = effectiveDate;
+        this.createdDate = createdDate;
+        this.status = status;
+        this.error = error;
+    }
+
+    @Override
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    @Override
+    public DateTime getEffectiveDate() {
+        return effectiveDate;
+    }
+
+    @Override
+    public RefundPluginStatus getStatus() {
+        return status;
+    }
+
+    @Override
+    public DateTime getCreatedDate() {
+        return createdDate;
+    }
+
+    @Override
+    public String getGatewayError() {
+        return error;
+    }
+
+    @Override
+    public String getGatewayErrorCode() {
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("DefaultNoOpRefundInfoPlugin");
+        sb.append("{amount=").append(amount);
+        sb.append(", effectiveDate=").append(effectiveDate);
+        sb.append(", createdDate=").append(createdDate);
+        sb.append(", status=").append(status);
+        sb.append(", error='").append(error).append('\'');
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final DefaultNoOpRefundInfoPlugin that = (DefaultNoOpRefundInfoPlugin) o;
+
+        if (amount != null ? !amount.equals(that.amount) : that.amount != null) {
+            return false;
+        }
+        if (createdDate != null ? !createdDate.equals(that.createdDate) : that.createdDate != null) {
+            return false;
+        }
+        if (effectiveDate != null ? !effectiveDate.equals(that.effectiveDate) : that.effectiveDate != null) {
+            return false;
+        }
+        if (error != null ? !error.equals(that.error) : that.error != null) {
+            return false;
+        }
+        if (status != that.status) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = amount != null ? amount.hashCode() : 0;
+        result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
+        result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
+        result = 31 * result + (status != null ? status.hashCode() : 0);
+        result = 31 * result + (error != null ? error.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/DefaultPaymentMethodInfoPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/DefaultPaymentMethodInfoPlugin.java
new file mode 100644
index 0000000..82ace37
--- /dev/null
+++ b/payment/src/main/java/com/ning/billing/payment/provider/DefaultPaymentMethodInfoPlugin.java
@@ -0,0 +1,60 @@
+/*
+ * 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.payment.provider;
+
+import java.util.UUID;
+
+import com.ning.billing.payment.plugin.api.PaymentMethodInfoPlugin;
+
+public class DefaultPaymentMethodInfoPlugin implements PaymentMethodInfoPlugin {
+
+    private final UUID accountId;
+    private final UUID paymentMethodId;
+    private final boolean isDefault;
+    private final String externalPaymentMethodId;
+
+    public DefaultPaymentMethodInfoPlugin(final UUID accountId, final UUID paymentMethodId, final boolean aDefault, final String externalPaymentMethodId) {
+        this.accountId = accountId;
+        this.paymentMethodId = paymentMethodId;
+        isDefault = aDefault;
+        this.externalPaymentMethodId = externalPaymentMethodId;
+    }
+
+    public DefaultPaymentMethodInfoPlugin(PaymentMethodInfoPlugin input, final UUID paymentMethodId) {
+        this(input.getAccountId(), paymentMethodId, input.isDefault(), input.getExternalPaymentMethodId());
+    }
+
+    @Override
+    public UUID getAccountId() {
+        return accountId;
+    }
+
+    @Override
+    public UUID getPaymentMethodId() {
+        return paymentMethodId;
+    }
+
+    @Override
+    public boolean isDefault() {
+        return isDefault;
+    }
+
+    @Override
+    public String getExternalPaymentMethodId() {
+        return externalPaymentMethodId;
+    }
+}
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/DefaultPaymentProviderPluginRegistry.java b/payment/src/main/java/com/ning/billing/payment/provider/DefaultPaymentProviderPluginRegistry.java
index 0a28eaa..6825980 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/DefaultPaymentProviderPluginRegistry.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/DefaultPaymentProviderPluginRegistry.java
@@ -20,13 +20,15 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
-import com.ning.billing.util.config.PaymentConfig;
+import com.ning.billing.osgi.api.OSGIServiceDescriptor;
+import com.ning.billing.osgi.api.OSGIServiceRegistration;
 import com.ning.billing.payment.plugin.api.PaymentPluginApi;
+import com.ning.billing.util.config.PaymentConfig;
 
 import com.google.common.base.Strings;
 import com.google.inject.Inject;
 
-public class DefaultPaymentProviderPluginRegistry implements PaymentProviderPluginRegistry {
+public class DefaultPaymentProviderPluginRegistry implements OSGIServiceRegistration<PaymentPluginApi> {
 
     private final String defaultPlugin;
     private final Map<String, PaymentPluginApi> pluginsByName = new ConcurrentHashMap<String, PaymentPluginApi>();
@@ -36,13 +38,19 @@ public class DefaultPaymentProviderPluginRegistry implements PaymentProviderPlug
         this.defaultPlugin = config.getDefaultPaymentProvider();
     }
 
+
     @Override
-    public void register(final PaymentPluginApi plugin, final String name) {
-        pluginsByName.put(name.toLowerCase(), plugin);
+    public void registerService(final OSGIServiceDescriptor desc, final PaymentPluginApi service) {
+        pluginsByName.put(desc.getServiceName().toLowerCase(), service);
     }
 
     @Override
-    public PaymentPluginApi getPlugin(final String name) {
+    public void unregisterService(final String serviceName) {
+        pluginsByName.remove(serviceName.toLowerCase());
+    }
+
+    @Override
+    public PaymentPluginApi getServiceForName(final String name) {
         final PaymentPluginApi plugin = pluginsByName.get((Strings.emptyToNull(name) == null ? defaultPlugin : name).toLowerCase());
 
         if (plugin == null) {
@@ -53,7 +61,12 @@ public class DefaultPaymentProviderPluginRegistry implements PaymentProviderPlug
     }
 
     @Override
-    public Set<String> getRegisteredPluginNames() {
+    public Set<String> getAllServices() {
         return pluginsByName.keySet();
     }
+
+    @Override
+    public Class<PaymentPluginApi> getServiceType() {
+        return PaymentPluginApi.class;
+    }
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java
index 19a516e..ac621ba 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java
@@ -40,4 +40,5 @@ public class ExternalPaymentProviderPlugin extends DefaultNoOpPaymentProviderPlu
     public String getName() {
         return PLUGIN_NAME;
     }
+
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPluginProvider.java b/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPluginProvider.java
index e392bb2..2a9cf4c 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPluginProvider.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPluginProvider.java
@@ -18,6 +18,10 @@ package com.ning.billing.payment.provider;
 
 import com.google.inject.Inject;
 import com.google.inject.Provider;
+
+import com.ning.billing.osgi.api.OSGIServiceDescriptor;
+import com.ning.billing.osgi.api.OSGIServiceRegistration;
+import com.ning.billing.payment.plugin.api.PaymentPluginApi;
 import com.ning.billing.util.clock.Clock;
 
 public class NoOpPaymentProviderPluginProvider implements Provider<DefaultNoOpPaymentProviderPlugin> {
@@ -25,7 +29,7 @@ public class NoOpPaymentProviderPluginProvider implements Provider<DefaultNoOpPa
     private final String instanceName;
 
     private Clock clock;
-    private PaymentProviderPluginRegistry registry;
+    private OSGIServiceRegistration<PaymentPluginApi> registry;
 
     public NoOpPaymentProviderPluginProvider(final String instanceName) {
         this.instanceName = instanceName;
@@ -33,16 +37,34 @@ public class NoOpPaymentProviderPluginProvider implements Provider<DefaultNoOpPa
     }
 
     @Inject
-    public void setPaymentProviderPluginRegistry(final PaymentProviderPluginRegistry registry, final Clock clock) {
+    public void setPaymentProviderPluginRegistry(final OSGIServiceRegistration<PaymentPluginApi> registry, final Clock clock) {
         this.clock = clock;
         this.registry = registry;
     }
 
     @Override
     public DefaultNoOpPaymentProviderPlugin get() {
-        final DefaultNoOpPaymentProviderPlugin plugin = new DefaultNoOpPaymentProviderPlugin(clock);
 
-        registry.register(plugin, instanceName);
+        final DefaultNoOpPaymentProviderPlugin plugin = new DefaultNoOpPaymentProviderPlugin(clock);
+        final OSGIServiceDescriptor desc = new OSGIServiceDescriptor() {
+            @Override
+            public String getPluginSymbolicName() {
+                return null;
+            }
+            @Override
+            public String getServiceName() {
+                return instanceName;
+            }
+            @Override
+            public String getServiceInfo() {
+                return null;
+            }
+            @Override
+            public String getServiceType() {
+                return null;
+            }
+        };
+        registry.registerService(desc, plugin);
         return plugin;
     }
 }
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentMethodSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentMethodSqlDao.sql.stg
index a242999..0186f2b 100644
--- a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentMethodSqlDao.sql.stg
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentMethodSqlDao.sql.stg
@@ -12,7 +12,6 @@ tableFields(prefix) ::= <<
   <prefix>account_id
 , <prefix>plugin_name
 , <prefix>is_active
-, <prefix>external_id
 , <prefix>created_by
 , <prefix>created_date
 , <prefix>updated_by
@@ -23,7 +22,6 @@ tableValues() ::= <<
   :accountId
 , :pluginName
 , :isActive
-, :externalId
 , :createdBy
 , :createdDate
 , :updatedBy
diff --git a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
index f04f02e..15c9203 100644
--- a/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
+++ b/payment/src/main/resources/com/ning/billing/payment/dao/PaymentSqlDao.sql.stg
@@ -13,8 +13,6 @@ tableFields(prefix) ::= <<
 , <prefix>effective_date
 , <prefix>currency
 , <prefix>payment_status
-, <prefix>ext_first_payment_ref_id
-, <prefix>ext_second_payment_ref_id
 , <prefix>created_by
 , <prefix>created_date
 , <prefix>updated_by
@@ -29,8 +27,6 @@ tableValues() ::= <<
 , :effectiveDate
 , :currency
 , :paymentStatus
-, :extFirstPaymentRefId
-, :extSecondPaymentRefId
 , :createdBy
 , :createdDate
 , :updatedBy
@@ -73,11 +69,9 @@ order by effective_date desc limit 1
 >>
 
 
-updatePaymentStatusAndExtRef() ::= <<
+updatePaymentStatus() ::= <<
 update payments
 set payment_status = :paymentStatus
-, ext_first_payment_ref_id = :extFirstPaymentRefId
-, ext_second_payment_ref_id = :extSecondPaymentRefId
 where id = :id
 <AND_CHECK_TENANT()>
 ;
diff --git a/payment/src/main/resources/com/ning/billing/payment/ddl.sql b/payment/src/main/resources/com/ning/billing/payment/ddl.sql
index 155280b..7d054d5 100644
--- a/payment/src/main/resources/com/ning/billing/payment/ddl.sql
+++ b/payment/src/main/resources/com/ning/billing/payment/ddl.sql
@@ -11,8 +11,6 @@ CREATE TABLE payments (
     currency char(3),    
     effective_date datetime,
     payment_status varchar(50),
-    ext_first_payment_ref_id varchar(128),
-    ext_second_payment_ref_id varchar(128),    
     created_by varchar(50) NOT NULL,
     created_date datetime NOT NULL,
     updated_by varchar(50) NOT NULL,
@@ -102,7 +100,6 @@ CREATE TABLE payment_methods (
     account_id char(36) NOT NULL,
     plugin_name varchar(20) DEFAULT NULL,
     is_active bool DEFAULT true, 
-    external_id varchar(64), 
     created_by varchar(50) NOT NULL,
     created_date datetime NOT NULL,
     updated_by varchar(50) NOT NULL,
@@ -123,7 +120,6 @@ CREATE TABLE payment_method_history (
     account_id char(36) NOT NULL,
     plugin_name varchar(20) DEFAULT NULL, 
     is_active bool DEFAULT true, 
-    external_id varchar(64),                  
     change_type char(6) NOT NULL,
     created_by varchar(50) NOT NULL,
     created_date datetime NOT NULL,
diff --git a/payment/src/test/java/com/ning/billing/payment/api/TestEventJson.java b/payment/src/test/java/com/ning/billing/payment/api/TestEventJson.java
index a7be9cc..84253fd 100644
--- a/payment/src/test/java/com/ning/billing/payment/api/TestEventJson.java
+++ b/payment/src/test/java/com/ning/billing/payment/api/TestEventJson.java
@@ -43,7 +43,7 @@ public class TestEventJson extends PaymentTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testPaymentInfoEvent() throws Exception {
-        final PaymentInfoInternalEvent e = new DefaultPaymentInfoEvent(UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), new BigDecimal(12.9), new Integer(13), PaymentStatus.SUCCESS, "ext-ref1-12345", "ext-ref2-12345",
+        final PaymentInfoInternalEvent e = new DefaultPaymentInfoEvent(UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), new BigDecimal(12.9), new Integer(13), PaymentStatus.SUCCESS,
                 UUID.randomUUID(), new DateTime(), 1L, 1L);
         final String json = mapper.writeValueAsString(e);
 
diff --git a/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
index c62bdda..9026102 100644
--- a/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
+++ b/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
@@ -35,9 +35,9 @@ import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.payment.MockRecurringInvoiceItem;
 import com.ning.billing.payment.PaymentTestSuiteNoDB;
-import com.ning.billing.payment.TestPaymentHelper;
 import com.ning.billing.payment.api.Payment.PaymentAttempt;
 import com.ning.billing.payment.provider.DefaultNoOpPaymentMethodPlugin;
+import com.ning.billing.payment.provider.MockPaymentProviderPlugin;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
@@ -51,14 +51,14 @@ public class TestPaymentApi extends PaymentTestSuiteNoDB {
     private Account account;
 
     @BeforeClass(groups = "fast")
-    public void setup() throws Exception {
-        super.setup();
+    public void beforeClass() throws Exception {
+        super.beforeClass();
         account = testHelper.createTestAccount("yoyo.yahoo.com", false);
     }
 
     @BeforeMethod(groups = "fast")
-    public void setupTest() throws Exception {
-        super.setupTest();
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         final PaymentMethodPlugin paymentMethodInfo = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), true, null);
         testHelper.addTestPaymentMethod(account, paymentMethodInfo);
     }
@@ -144,17 +144,17 @@ public class TestPaymentApi extends PaymentTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testPaymentMethods() throws Exception {
-        List<PaymentMethod> methods = paymentApi.getPaymentMethods(account, false, callContext);
+        List<PaymentMethod> methods = paymentApi.getPaymentMethods(account, callContext);
         assertEquals(methods.size(), 1);
 
         final PaymentMethod initDefaultMethod = methods.get(0);
         assertEquals(initDefaultMethod.getId(), account.getPaymentMethodId());
 
         final PaymentMethodPlugin newPaymenrMethod = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), true, null);
-        final UUID newPaymentMethodId = paymentApi.addPaymentMethod(TestPaymentHelper.PLUGIN_TEST_NAME, account, true, newPaymenrMethod, callContext);
+        final UUID newPaymentMethodId = paymentApi.addPaymentMethod(MockPaymentProviderPlugin.PLUGIN_NAME, account, true, newPaymenrMethod, callContext);
         Mockito.when(account.getPaymentMethodId()).thenReturn(newPaymentMethodId);
 
-        methods = paymentApi.getPaymentMethods(account, false, callContext);
+        methods = paymentApi.getPaymentMethods(account, callContext);
         assertEquals(methods.size(), 2);
 
         assertEquals(newPaymentMethodId, account.getPaymentMethodId());
@@ -168,13 +168,13 @@ public class TestPaymentApi extends PaymentTestSuiteNoDB {
         assertTrue(failed);
 
         paymentApi.deletedPaymentMethod(account, initDefaultMethod.getId(), true,  callContext);
-        methods = paymentApi.getPaymentMethods(account, false, callContext);
+        methods = paymentApi.getPaymentMethods(account, callContext);
         assertEquals(methods.size(), 1);
 
         // NOW retry with default payment method with special flag
         paymentApi.deletedPaymentMethod(account, newPaymentMethodId, true, callContext);
 
-        methods = paymentApi.getPaymentMethods(account, false, callContext);
+        methods = paymentApi.getPaymentMethods(account, callContext);
         assertEquals(methods.size(), 0);
     }
 }
diff --git a/payment/src/test/java/com/ning/billing/payment/core/TestPaymentMethodProcessorRefreshWithDB.java b/payment/src/test/java/com/ning/billing/payment/core/TestPaymentMethodProcessorRefreshWithDB.java
new file mode 100644
index 0000000..69792df
--- /dev/null
+++ b/payment/src/test/java/com/ning/billing/payment/core/TestPaymentMethodProcessorRefreshWithDB.java
@@ -0,0 +1,110 @@
+/*
+ * 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.payment.core;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.payment.PaymentTestSuiteWithEmbeddedDB;
+import com.ning.billing.payment.api.PaymentMethod;
+import com.ning.billing.payment.api.PaymentMethodPlugin.PaymentMethodKVInfo;
+import com.ning.billing.payment.dao.PaymentMethodModelDao;
+import com.ning.billing.payment.plugin.api.PaymentPluginApi;
+import com.ning.billing.payment.provider.DefaultNoOpPaymentMethodPlugin;
+import com.ning.billing.payment.provider.MockPaymentProviderPlugin;
+
+import com.google.common.collect.ImmutableList;
+
+public class TestPaymentMethodProcessorRefreshWithDB extends PaymentTestSuiteWithEmbeddedDB {
+
+
+    @BeforeMethod(groups = "slow")
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
+        getPluginApi().resetPaymentMethods(null);
+    }
+
+    @Test(groups = "slow")
+    public void testRefreshWithNewPaymentMethod() throws Exception {
+
+        final Account account = testHelper.createTestAccount("foo@bar.com", true);
+        Assert.assertEquals(getPluginApi().getPaymentMethods(account.getId(), true, callContext).size(), 1);
+        final UUID existingPMId = account.getPaymentMethodId();
+
+        // Add new payment in plugin directly
+        final UUID newPmId = UUID.randomUUID();
+        getPluginApi().addPaymentMethod(account.getId(), newPmId, new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), false, ImmutableList.<PaymentMethodKVInfo>of()), false, callContext);
+
+        // Verify that the refresh does indeed show 2 PMs
+        final List<PaymentMethod> methods = paymentMethodProcessor.refreshPaymentMethods(MockPaymentProviderPlugin.PLUGIN_NAME, account, internalCallContext);
+        Assert.assertEquals(methods.size(), 2);
+        checkPaymentMethodExistsWithStatus(methods, existingPMId, true);
+        checkPaymentMethodExistsWithStatus(methods, newPmId, true);
+    }
+
+
+    @Test(groups = "slow")
+    public void testRefreshWithDeletedPaymentMethod() throws Exception {
+
+        final Account account = testHelper.createTestAccount("super@bar.com", true);
+        Assert.assertEquals(getPluginApi().getPaymentMethods(account.getId(), true, callContext).size(), 1);
+        final UUID firstPmId = account.getPaymentMethodId();
+
+        final UUID secondPmId = paymentApi.addPaymentMethod(MockPaymentProviderPlugin.PLUGIN_NAME, account, true, new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), false, null), callContext);
+        Assert.assertEquals(getPluginApi().getPaymentMethods(account.getId(), true, callContext).size(), 2);
+        Assert.assertEquals(paymentApi.getPaymentMethods(account, callContext).size(), 2);
+
+        // Remove second PM from plugin
+        getPluginApi().deletePaymentMethod(secondPmId, callContext);
+        Assert.assertEquals(getPluginApi().getPaymentMethods(account.getId(), true, callContext).size(), 1);
+        Assert.assertEquals(paymentApi.getPaymentMethods(account, callContext).size(), 2);
+
+        // Verify that the refresh sees that PM as being deleted now
+        final List<PaymentMethod> methods = paymentMethodProcessor.refreshPaymentMethods(MockPaymentProviderPlugin.PLUGIN_NAME, account, internalCallContext);
+        Assert.assertEquals(methods.size(), 1);
+        checkPaymentMethodExistsWithStatus(methods, firstPmId, true);
+
+        PaymentMethodModelDao deletedPMModel =  paymentDao.getPaymentMethodIncludedDeleted(secondPmId, internalCallContext);
+        Assert.assertNotNull(deletedPMModel);
+        Assert.assertFalse(deletedPMModel.isActive());
+    }
+
+
+    private void checkPaymentMethodExistsWithStatus(final List<PaymentMethod> methods, UUID expectedPaymentMethodId, boolean expectedActive) {
+        PaymentMethod foundPM = null;
+        for (PaymentMethod cur : methods) {
+            if (cur.getId().equals(expectedPaymentMethodId)) {
+                foundPM = cur;
+                break;
+            }
+        }
+        Assert.assertNotNull(foundPM);
+        Assert.assertEquals(foundPM.isActive().booleanValue(), expectedActive);
+    }
+
+
+    private PaymentPluginApi getPluginApi() {
+        final PaymentPluginApi pluginApi = registry.getServiceForName(MockPaymentProviderPlugin.PLUGIN_NAME);
+        Assert.assertNotNull(pluginApi);
+        return pluginApi;
+    }
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java b/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
index a32aed6..7a51f36 100644
--- a/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
+++ b/payment/src/test/java/com/ning/billing/payment/dao/MockPaymentDao.java
@@ -30,6 +30,8 @@ import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 
+import com.google.common.collect.ImmutableList;
+
 public class MockPaymentDao implements PaymentDao {
 
     private final Map<UUID, PaymentModelDao> payments = new HashMap<UUID, PaymentModelDao>();
@@ -55,7 +57,7 @@ public class MockPaymentDao implements PaymentDao {
 
     @Override
     public void updateStatusForPaymentWithAttempt(final UUID paymentId, final PaymentStatus paymentStatus, final String gatewayErrorCode,
-                                                  final String gatewayErrorMsg, final String extFirstPaymentRefId, final String extSecondPaymentRefId,
+                                                  final String gatewayErrorMsg,
                                                   final UUID attemptId, final InternalCallContext context) {
         synchronized (this) {
             final PaymentModelDao entry = payments.remove(paymentId);
@@ -127,11 +129,6 @@ public class MockPaymentDao implements PaymentDao {
     }
 
     @Override
-    public List<PaymentMethodModelDao> refreshPaymentMethods(final UUID accountId, final List<PaymentMethodModelDao> newPaymentMethods, final InternalCallContext context) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
     public PaymentMethodModelDao getPaymentMethod(final UUID paymentMethodId, final InternalTenantContext context) {
         for (final PaymentMethodModelDao cur : paymentMethods) {
             if (cur.getId().equals(paymentMethodId)) {
@@ -165,6 +162,11 @@ public class MockPaymentDao implements PaymentDao {
     }
 
     @Override
+    public List<PaymentMethodModelDao> refreshPaymentMethods(final UUID accountId, final List<PaymentMethodModelDao> paymentMethods, final InternalCallContext context) {
+        return ImmutableList.<PaymentMethodModelDao>of();
+    }
+
+    @Override
     public void undeletedPaymentMethod(final UUID paymentMethodId, final InternalCallContext context) {
         throw new UnsupportedOperationException();
     }
diff --git a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
index 61baab5..a17fcac 100644
--- a/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
+++ b/payment/src/test/java/com/ning/billing/payment/dao/TestPaymentDao.java
@@ -16,30 +16,18 @@
 
 package com.ning.billing.payment.dao;
 
-import java.io.IOException;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
-import org.skife.config.ConfigurationObjectFactory;
-import org.skife.jdbi.v2.IDBI;
-import org.testng.annotations.BeforeSuite;
 import org.testng.annotations.Test;
 
-import com.ning.billing.KillbillTestSuiteWithEmbeddedDB;
 import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.dbi.DBIProvider;
-import com.ning.billing.dbi.DBTestingHelper;
-import com.ning.billing.dbi.DbiConfig;
 import com.ning.billing.payment.PaymentTestSuiteWithEmbeddedDB;
 import com.ning.billing.payment.api.PaymentStatus;
 import com.ning.billing.payment.dao.RefundModelDao.RefundStatus;
-import com.ning.billing.util.cache.CacheControllerDispatcher;
-import com.ning.billing.util.clock.Clock;
-import com.ning.billing.util.clock.DefaultClock;
-import com.ning.billing.util.dao.DefaultNonEntityDao;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
@@ -111,7 +99,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         final PaymentStatus paymentStatus = PaymentStatus.SUCCESS;
         final String gatewayErrorCode = "OK";
 
-        paymentDao.updateStatusForPaymentWithAttempt(payment.getId(), paymentStatus, gatewayErrorCode, null, null, null, attempt.getId(), internalCallContext);
+        paymentDao.updateStatusForPaymentWithAttempt(payment.getId(), paymentStatus, gatewayErrorCode, null, attempt.getId(), internalCallContext);
 
         final List<PaymentModelDao> payments = paymentDao.getPaymentsForInvoice(invoiceId, internalCallContext);
         assertEquals(payments.size(), 1);
@@ -260,7 +248,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         final String externalPaymentId = UUID.randomUUID().toString();
 
         final PaymentMethodModelDao method = new PaymentMethodModelDao(paymentMethodId, null, null,
-                                                                       accountId, pluginName, isActive, externalPaymentId);
+                                                                       accountId, pluginName, isActive);
 
         PaymentMethodModelDao savedMethod = paymentDao.insertPaymentMethod(method, internalCallContext);
         assertEquals(savedMethod.getId(), paymentMethodId);
@@ -275,7 +263,6 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(savedMethod.getAccountId(), accountId);
         assertEquals(savedMethod.getPluginName(), pluginName);
         assertEquals(savedMethod.isActive(), isActive);
-        assertEquals(savedMethod.getExternalId(), externalPaymentId);
 
         paymentDao.deletedPaymentMethod(paymentMethodId, internalCallContext);
 
diff --git a/payment/src/test/java/com/ning/billing/payment/glue/TestPaymentModule.java b/payment/src/test/java/com/ning/billing/payment/glue/TestPaymentModule.java
index 915607b..2357a0d 100644
--- a/payment/src/test/java/com/ning/billing/payment/glue/TestPaymentModule.java
+++ b/payment/src/test/java/com/ning/billing/payment/glue/TestPaymentModule.java
@@ -16,9 +16,15 @@
 
 package com.ning.billing.payment.glue;
 
+import java.io.IOException;
+import java.net.URL;
+import java.util.Properties;
 import java.util.UUID;
 
 import org.mockito.Mockito;
+import org.skife.config.ConfigSource;
+import org.skife.config.SimplePropertyConfigSource;
+import org.testng.Assert;
 
 import com.ning.billing.ObjectType;
 import com.ning.billing.mock.glue.MockAccountModule;
@@ -26,38 +32,52 @@ import com.ning.billing.mock.glue.MockEntitlementModule;
 import com.ning.billing.mock.glue.MockGlobalLockerModule;
 import com.ning.billing.mock.glue.MockInvoiceModule;
 import com.ning.billing.mock.glue.MockNotificationQueueModule;
+import com.ning.billing.payment.PaymentTestSuiteNoDB;
 import com.ning.billing.payment.TestPaymentHelper;
+import com.ning.billing.payment.provider.MockPaymentProviderPlugin;
 import com.ning.billing.payment.provider.MockPaymentProviderPluginModule;
+import com.ning.billing.util.bus.InMemoryBusModule;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.config.PaymentConfig;
-import com.ning.billing.util.email.EmailModule;
-import com.ning.billing.util.email.templates.TemplateModule;
-import com.ning.billing.util.glue.BusModule;
-import com.ning.billing.util.glue.BusModule.BusType;
 import com.ning.billing.util.glue.CacheModule;
-import com.ning.billing.util.glue.CustomFieldModule;
-import com.ning.billing.util.glue.NotificationQueueModule;
-import com.ning.billing.util.glue.TagStoreModule;
 import com.ning.billing.util.svcapi.tag.TagInternalApi;
 import com.ning.billing.util.tag.Tag;
 
 import com.google.common.collect.ImmutableList;
 
-import static org.testng.Assert.assertNotNull;
-
 public class TestPaymentModule extends PaymentModule {
 
+    protected final ConfigSource configSource;
 
     private final Clock clock;
 
-    public TestPaymentModule(final Clock clock) {
+    public TestPaymentModule(final ConfigSource configSource, final Clock clock) {
+        super(configSource);
         this.clock = clock;
+        this.configSource = loadSystemPropertiesFromClasspath("/resource.properties");
+    }
+
+    private ConfigSource loadSystemPropertiesFromClasspath(final String resource) {
+        final URL url = PaymentTestSuiteNoDB.class.getResource(resource);
+        Assert.assertNotNull(url);
+
+        try {
+            final Properties properties = System.getProperties();
+            properties.load(url.openStream());
+
+            properties.setProperty("killbill.payment.provider.default", MockPaymentProviderPlugin.PLUGIN_NAME);
+            properties.setProperty("killbill.payment.engine.events.off", "false");
+
+            return new SimplePropertyConfigSource(properties);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
     }
 
     @Override
     protected void installPaymentProviderPlugins(final PaymentConfig config) {
-        install(new MockPaymentProviderPluginModule(TestPaymentHelper.PLUGIN_TEST_NAME, clock));
+        install(new MockPaymentProviderPluginModule(MockPaymentProviderPlugin.PLUGIN_NAME, clock));
     }
 
     private void installExternalApis() {
@@ -69,13 +89,13 @@ public class TestPaymentModule extends PaymentModule {
     @Override
     protected void configure() {
         super.configure();
-        install(new BusModule(BusType.MEMORY));
-        install(new MockNotificationQueueModule());
+        install(new InMemoryBusModule(configSource));
+        install(new MockNotificationQueueModule(configSource));
         install(new MockInvoiceModule());
         install(new MockAccountModule());
         install(new MockEntitlementModule());
         install(new MockGlobalLockerModule());
-        install(new CacheModule());
+        install(new CacheModule(configSource));
         installExternalApis();
 
         bind(TestPaymentHelper.class).asEagerSingleton();
diff --git a/payment/src/test/java/com/ning/billing/payment/glue/TestPaymentModuleNoDB.java b/payment/src/test/java/com/ning/billing/payment/glue/TestPaymentModuleNoDB.java
index 6ae07e0..31791b3 100644
--- a/payment/src/test/java/com/ning/billing/payment/glue/TestPaymentModuleNoDB.java
+++ b/payment/src/test/java/com/ning/billing/payment/glue/TestPaymentModuleNoDB.java
@@ -16,6 +16,8 @@
 
 package com.ning.billing.payment.glue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.GuicyKillbillTestNoDBModule;
 import com.ning.billing.mock.glue.MockNonEntityDaoModule;
 import com.ning.billing.payment.dao.MockPaymentDao;
@@ -24,8 +26,8 @@ import com.ning.billing.util.clock.Clock;
 
 public class TestPaymentModuleNoDB extends TestPaymentModule {
 
-    public TestPaymentModuleNoDB(final Clock clock) {
-        super(clock);
+    public TestPaymentModuleNoDB(final ConfigSource configSource, final Clock clock) {
+        super(configSource, clock);
     }
 
     @Override
diff --git a/payment/src/test/java/com/ning/billing/payment/glue/TestPaymentModuleWithEmbeddedDB.java b/payment/src/test/java/com/ning/billing/payment/glue/TestPaymentModuleWithEmbeddedDB.java
index a8d8109..1c984d1 100644
--- a/payment/src/test/java/com/ning/billing/payment/glue/TestPaymentModuleWithEmbeddedDB.java
+++ b/payment/src/test/java/com/ning/billing/payment/glue/TestPaymentModuleWithEmbeddedDB.java
@@ -16,14 +16,16 @@
 
 package com.ning.billing.payment.glue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.GuicyKillbillTestWithEmbeddedDBModule;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.glue.NonEntityDaoModule;
 
 public class TestPaymentModuleWithEmbeddedDB extends TestPaymentModule {
 
-    public TestPaymentModuleWithEmbeddedDB(final Clock clock) {
-        super(clock);
+    public TestPaymentModuleWithEmbeddedDB(final ConfigSource configSource, final Clock clock) {
+        super(configSource, clock);
     }
 
     @Override
diff --git a/payment/src/test/java/com/ning/billing/payment/PaymentTestSuiteNoDB.java b/payment/src/test/java/com/ning/billing/payment/PaymentTestSuiteNoDB.java
index c6ac736..0968e0c 100644
--- a/payment/src/test/java/com/ning/billing/payment/PaymentTestSuiteNoDB.java
+++ b/payment/src/test/java/com/ning/billing/payment/PaymentTestSuiteNoDB.java
@@ -16,20 +16,17 @@
 
 package com.ning.billing.payment;
 
-import java.io.IOException;
-import java.net.URL;
-import java.util.Properties;
-
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 
 import com.ning.billing.GuicyKillbillTestSuiteNoDB;
+import com.ning.billing.osgi.api.OSGIServiceRegistration;
 import com.ning.billing.payment.api.PaymentApi;
 import com.ning.billing.payment.core.PaymentMethodProcessor;
 import com.ning.billing.payment.core.PaymentProcessor;
 import com.ning.billing.payment.glue.TestPaymentModuleNoDB;
-import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
+import com.ning.billing.payment.plugin.api.PaymentPluginApi;
 import com.ning.billing.payment.retry.FailedPaymentRetryService;
 import com.ning.billing.payment.retry.PluginFailureRetryService;
 import com.ning.billing.util.config.PaymentConfig;
@@ -41,8 +38,6 @@ import com.google.inject.Guice;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
 
-import static org.testng.Assert.assertNotNull;
-
 public abstract class PaymentTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
 
     @Inject
@@ -54,7 +49,7 @@ public abstract class PaymentTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     @Inject
     protected InvoiceInternalApi invoiceApi;
     @Inject
-    protected PaymentProviderPluginRegistry registry;
+    protected OSGIServiceRegistration<PaymentPluginApi> registry;
     @Inject
     protected FailedPaymentRetryService retryService;
     @Inject
@@ -68,44 +63,19 @@ public abstract class PaymentTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     @Inject
     protected TestPaymentHelper testHelper;
 
-
-
     @BeforeClass(groups = "fast")
-    protected void setup() throws Exception {
-
-        loadSystemPropertiesFromClasspath("/resource.properties");
-
-        final Injector injector = Guice.createInjector(new TestPaymentModuleNoDB(getClock()));
+    protected void beforeClass() throws Exception {
+        final Injector injector = Guice.createInjector(new TestPaymentModuleNoDB(configSource, getClock()));
         injector.injectMembers(this);
     }
 
     @BeforeMethod(groups = "fast")
-    public void setupTest() throws Exception {
+    public void beforeMethod() throws Exception {
         eventBus.start();
     }
 
     @AfterMethod(groups = "fast")
-    public void cleanupTest()throws Exception  {
+    public void afterMethod() throws Exception {
         eventBus.stop();
     }
-
-
-
-    private void loadSystemPropertiesFromClasspath(final String resource) {
-        final URL url = PaymentTestSuiteNoDB.class.getResource(resource);
-        assertNotNull(url);
-
-        try {
-            final Properties properties = System.getProperties();
-            properties.load(url.openStream());
-
-            properties.setProperty("killbill.payment.provider.default", TestPaymentHelper.PLUGIN_TEST_NAME);
-            properties.setProperty("killbill.payment.engine.events.off", "false");
-
-            //configSource = new SimplePropertyConfigSource(properties);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
 }
diff --git a/payment/src/test/java/com/ning/billing/payment/PaymentTestSuiteWithEmbeddedDB.java b/payment/src/test/java/com/ning/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
index 092ba80..a72e118 100644
--- a/payment/src/test/java/com/ning/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
+++ b/payment/src/test/java/com/ning/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
@@ -16,21 +16,18 @@
 
 package com.ning.billing.payment;
 
-import java.io.IOException;
-import java.net.URL;
-import java.util.Properties;
-
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 
 import com.ning.billing.GuicyKillbillTestSuiteWithEmbeddedDB;
+import com.ning.billing.osgi.api.OSGIServiceRegistration;
 import com.ning.billing.payment.api.PaymentApi;
 import com.ning.billing.payment.core.PaymentMethodProcessor;
 import com.ning.billing.payment.core.PaymentProcessor;
 import com.ning.billing.payment.dao.PaymentDao;
 import com.ning.billing.payment.glue.TestPaymentModuleWithEmbeddedDB;
-import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
+import com.ning.billing.payment.plugin.api.PaymentPluginApi;
 import com.ning.billing.payment.retry.FailedPaymentRetryService;
 import com.ning.billing.payment.retry.PluginFailureRetryService;
 import com.ning.billing.util.config.PaymentConfig;
@@ -42,8 +39,6 @@ import com.google.inject.Guice;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
 
-import static org.testng.Assert.assertNotNull;
-
 public abstract class PaymentTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWithEmbeddedDB {
 
     @Inject
@@ -55,7 +50,7 @@ public abstract class PaymentTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
     @Inject
     protected InvoiceInternalApi invoiceApi;
     @Inject
-    protected PaymentProviderPluginRegistry registry;
+    protected OSGIServiceRegistration<PaymentPluginApi> registry;
     @Inject
     protected FailedPaymentRetryService retryService;
     @Inject
@@ -72,35 +67,19 @@ public abstract class PaymentTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
     protected TestPaymentHelper testHelper;
 
     @BeforeClass(groups = "slow")
-    protected void setup() throws Exception {
-
-        loadSystemPropertiesFromClasspath("/resource.properties");
-
-        final Injector injector = Guice.createInjector(new TestPaymentModuleWithEmbeddedDB(getClock()));
+    protected void beforeClass() throws Exception {
+        final Injector injector = Guice.createInjector(new TestPaymentModuleWithEmbeddedDB(configSource, getClock()));
         injector.injectMembers(this);
     }
 
     @BeforeMethod(groups = "slow")
-    public void setupTest() throws Exception {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         eventBus.start();
     }
 
     @AfterMethod(groups = "slow")
-    public void cleanupTest()throws Exception  {
+    public void afterMethod() throws Exception {
         eventBus.stop();
     }
-
-
-
-    private void loadSystemPropertiesFromClasspath(final String resource) {
-        final URL url = PaymentTestSuiteWithEmbeddedDB.class.getResource(resource);
-        assertNotNull(url);
-
-        try {
-            final Properties properties = System.getProperties();
-            properties.load(url.openStream());
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
 }
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
index cbda487..e72c9b3 100644
--- a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
+++ b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
@@ -16,14 +16,165 @@
 
 package com.ning.billing.payment.provider;
 
-import com.google.inject.Inject;
-import com.ning.billing.payment.plugin.api.PaymentPluginApi;
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.ning.billing.payment.api.DefaultPaymentMethodPlugin;
+import com.ning.billing.payment.api.PaymentMethodPlugin;
+import com.ning.billing.payment.plugin.api.NoOpPaymentPluginApi;
+import com.ning.billing.payment.plugin.api.PaymentInfoPlugin;
+import com.ning.billing.payment.plugin.api.PaymentInfoPlugin.PaymentPluginStatus;
+import com.ning.billing.payment.plugin.api.PaymentMethodInfoPlugin;
+import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
+import com.ning.billing.payment.plugin.api.RefundInfoPlugin;
+import com.ning.billing.payment.plugin.api.RefundInfoPlugin.RefundPluginStatus;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.TenantContext;
 import com.ning.billing.util.clock.Clock;
 
-public class MockPaymentProviderPlugin extends DefaultNoOpPaymentProviderPlugin implements PaymentPluginApi {
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.Multimap;
+import com.google.inject.Inject;
+
+/**
+ * This MockPaymentProviderPlugin only works for a single accounts as we don't specify the accountId
+ * for opeartions such as addPaymentMethod.
+ */
+public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
+
+    public static final String PLUGIN_NAME = "__NO_OP__";
+
+    private final AtomicBoolean makeNextInvoiceFailWithError = new AtomicBoolean(false);
+    private final AtomicBoolean makeNextInvoiceFailWithException = new AtomicBoolean(false);
+    private final AtomicBoolean makeAllInvoicesFailWithError = new AtomicBoolean(false);
+
+    private final Map<String, PaymentInfoPlugin> payments = new ConcurrentHashMap<String, PaymentInfoPlugin>();
+    // Note: we can't use HashMultiMap as we care about storing duplicate key/value pairs
+    private final Multimap<String, RefundInfoPlugin> refunds = LinkedListMultimap.<String, RefundInfoPlugin>create();
+    private final Map<String, PaymentMethodPlugin> paymentMethods = new ConcurrentHashMap<String, PaymentMethodPlugin>();
+    private final Map<String, PaymentMethodInfoPlugin> paymentMethodsInfo = new ConcurrentHashMap<String, PaymentMethodInfoPlugin>();
+
+    private final Clock clock;
 
     @Inject
     public MockPaymentProviderPlugin(final Clock clock) {
-        super(clock);
+        this.clock = clock;
+        clear();
+    }
+
+    @Override
+    public void clear() {
+        makeNextInvoiceFailWithException.set(false);
+        makeAllInvoicesFailWithError.set(false);
+        makeNextInvoiceFailWithError.set(false);
+    }
+
+    @Override
+    public void makeNextPaymentFailWithError() {
+        makeNextInvoiceFailWithError.set(true);
+    }
+
+    @Override
+    public void makeNextPaymentFailWithException() {
+        makeNextInvoiceFailWithException.set(true);
+    }
+
+    @Override
+    public void makeAllInvoicesFailWithError(final boolean failure) {
+        makeAllInvoicesFailWithError.set(failure);
+    }
+
+    @Override
+    public String getName() {
+        return PLUGIN_NAME;
+    }
+
+    @Override
+    public PaymentInfoPlugin processPayment(final UUID kbPaymentId, final UUID kbPaymentMethodId, final BigDecimal amount, final CallContext context) throws PaymentPluginApiException {
+        if (makeNextInvoiceFailWithException.getAndSet(false)) {
+            throw new PaymentPluginApiException("", "test error");
+        }
+
+        final PaymentPluginStatus status = (makeAllInvoicesFailWithError.get() || makeNextInvoiceFailWithError.getAndSet(false)) ? PaymentPluginStatus.ERROR : PaymentPluginStatus.PROCESSED;
+        final PaymentInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(amount, clock.getUTCNow(), clock.getUTCNow(), status, null);
+        payments.put(kbPaymentId.toString(), result);
+        return result;
+    }
+
+    @Override
+    public PaymentInfoPlugin getPaymentInfo(final UUID kbPaymentId, final TenantContext context) throws PaymentPluginApiException {
+        final PaymentInfoPlugin payment = payments.get(kbPaymentId.toString());
+        if (payment == null) {
+            throw new PaymentPluginApiException("", "No payment found for payment id " + kbPaymentId.toString());
+        }
+        return payment;
+    }
+
+
+    @Override
+    public void addPaymentMethod(final UUID kbAccountId, final UUID kbPaymentMethodId, final PaymentMethodPlugin paymentMethodProps, final boolean setDefault, final CallContext context) throws PaymentPluginApiException {
+        // externalPaymentMethodId is set to a random value
+        final PaymentMethodPlugin realWithID = new DefaultPaymentMethodPlugin(paymentMethodProps, UUID.randomUUID().toString());
+        paymentMethods.put(kbPaymentMethodId.toString(), realWithID);
+
+        final PaymentMethodInfoPlugin realInfoWithID = new DefaultPaymentMethodInfoPlugin(kbAccountId, kbPaymentMethodId, setDefault, UUID.randomUUID().toString());
+        paymentMethodsInfo.put(kbPaymentMethodId.toString(), realInfoWithID);
+    }
+
+    @Override
+    public void deletePaymentMethod(final UUID kbPaymentMethodId, final CallContext context) throws PaymentPluginApiException {
+        paymentMethods.remove(kbPaymentMethodId.toString());
+        paymentMethodsInfo.remove(kbPaymentMethodId.toString());
+    }
+
+    @Override
+    public PaymentMethodPlugin getPaymentMethodDetail(final UUID kbAccountId, final UUID kbPaymentMethodId, final TenantContext context) throws PaymentPluginApiException {
+        return paymentMethods.get(kbPaymentMethodId.toString());
+    }
+
+    @Override
+    public void setDefaultPaymentMethod(final UUID kbPaymentMethodId, final CallContext context) throws PaymentPluginApiException {
+    }
+
+    @Override
+    public List<PaymentMethodInfoPlugin> getPaymentMethods(final UUID kbAccountId, final boolean refreshFromGateway, final CallContext context) {
+        return ImmutableList.<PaymentMethodInfoPlugin>copyOf(paymentMethodsInfo.values());
+    }
+
+    @Override
+    public void resetPaymentMethods(final List<PaymentMethodInfoPlugin> input) {
+        paymentMethodsInfo.clear();
+        if (input != null) {
+            for (final PaymentMethodInfoPlugin cur : input) {
+                paymentMethodsInfo.put(cur.getPaymentMethodId().toString(), cur);
+            }
+        }
+    }
+
+    @Override
+    public RefundInfoPlugin processRefund(final UUID kbPaymentId, final BigDecimal refundAmount, final CallContext context) throws PaymentPluginApiException {
+        final PaymentInfoPlugin paymentInfoPlugin = getPaymentInfo(kbPaymentId, context);
+        if (paymentInfoPlugin == null) {
+            throw new PaymentPluginApiException("", String.format("No payment found for payment id %s (plugin %s)", kbPaymentId.toString(), getName()));
+        }
+
+        BigDecimal maxAmountRefundable = paymentInfoPlugin.getAmount();
+        for (final RefundInfoPlugin refund : refunds.get(kbPaymentId.toString())) {
+            maxAmountRefundable = maxAmountRefundable.add(refund.getAmount().negate());
+        }
+        if (maxAmountRefundable.compareTo(refundAmount) < 0) {
+            throw new PaymentPluginApiException("", String.format("Refund amount of %s for payment id %s is bigger than the payment amount %s (plugin %s)",
+                                                                  refundAmount, kbPaymentId.toString(), paymentInfoPlugin.getAmount(), getName()));
+        }
+
+        final DefaultNoOpRefundInfoPlugin refundInfoPlugin = new DefaultNoOpRefundInfoPlugin(refundAmount, clock.getUTCNow(), clock.getUTCNow(), RefundPluginStatus.PROCESSED, null);
+        refunds.put(kbPaymentId.toString(), refundInfoPlugin);
+
+        return refundInfoPlugin;
     }
 }
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPluginProvider.java b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPluginProvider.java
index e171e7a..0c4f4ac 100644
--- a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPluginProvider.java
+++ b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPluginProvider.java
@@ -18,11 +18,15 @@ package com.ning.billing.payment.provider;
 
 import com.google.inject.Inject;
 import com.google.inject.Provider;
+
+import com.ning.billing.osgi.api.OSGIServiceDescriptor;
+import com.ning.billing.osgi.api.OSGIServiceRegistration;
+import com.ning.billing.payment.plugin.api.PaymentPluginApi;
 import com.ning.billing.util.clock.Clock;
 
 public class MockPaymentProviderPluginProvider implements Provider<MockPaymentProviderPlugin> {
 
-    private PaymentProviderPluginRegistry registry;
+    private OSGIServiceRegistration<PaymentPluginApi> registry;
     private final String instanceName;
 
     private Clock clock;
@@ -33,7 +37,7 @@ public class MockPaymentProviderPluginProvider implements Provider<MockPaymentPr
     }
 
     @Inject
-    public void setPaymentProviderPluginRegistry(final PaymentProviderPluginRegistry registry) {
+    public void setPaymentProviderPluginRegistry(final OSGIServiceRegistration<PaymentPluginApi> registry) {
         this.registry = registry;
     }
 
@@ -41,7 +45,25 @@ public class MockPaymentProviderPluginProvider implements Provider<MockPaymentPr
     public MockPaymentProviderPlugin get() {
         final MockPaymentProviderPlugin plugin = new MockPaymentProviderPlugin(clock);
 
-        registry.register(plugin, instanceName);
+        final OSGIServiceDescriptor desc =  new OSGIServiceDescriptor() {
+            @Override
+            public String getPluginSymbolicName() {
+                return null;
+            }
+            @Override
+            public String getServiceName() {
+                return instanceName;
+            }
+            @Override
+            public String getServiceInfo() {
+                return null;
+            }
+            @Override
+            public String getServiceType() {
+                return null;
+            }
+        };
+        registry.registerService(desc, plugin);
         return plugin;
     }
 }
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/TestExternalPaymentProviderPlugin.java b/payment/src/test/java/com/ning/billing/payment/provider/TestExternalPaymentProviderPlugin.java
index 5bc8eea..bd2bc91 100644
--- a/payment/src/test/java/com/ning/billing/payment/provider/TestExternalPaymentProviderPlugin.java
+++ b/payment/src/test/java/com/ning/billing/payment/provider/TestExternalPaymentProviderPlugin.java
@@ -38,8 +38,10 @@ public class TestExternalPaymentProviderPlugin extends PaymentTestSuiteNoDB {
     private final Clock clock = new ClockMock();
     private ExternalPaymentProviderPlugin plugin;
 
+    @Override
     @BeforeMethod(groups = "fast")
-    public void setUp() throws Exception {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         plugin = new ExternalPaymentProviderPlugin(clock);
     }
 
@@ -50,16 +52,14 @@ public class TestExternalPaymentProviderPlugin extends PaymentTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testProcessPayment() throws Exception {
-        final String externalKey = UUID.randomUUID().toString();
         final UUID paymentId = UUID.randomUUID();
+        final UUID paymentMethodId = UUID.randomUUID();
         final BigDecimal amount = BigDecimal.TEN;
-        final PaymentInfoPlugin paymentInfoPlugin = plugin.processPayment(externalKey, paymentId, amount, callContext);
+        final PaymentInfoPlugin paymentInfoPlugin = plugin.processPayment(paymentId, paymentMethodId, amount, callContext);
 
         Assert.assertEquals(paymentInfoPlugin.getAmount(), amount);
         Assert.assertEquals(Seconds.secondsBetween(paymentInfoPlugin.getCreatedDate(), clock.getUTCNow()).getSeconds(), 0);
         Assert.assertEquals(Seconds.secondsBetween(paymentInfoPlugin.getEffectiveDate(), clock.getUTCNow()).getSeconds(), 0);
-        Assert.assertNull(paymentInfoPlugin.getExtFirstReferenceId());
-        Assert.assertNull(paymentInfoPlugin.getExtSecondReferenceId());
         Assert.assertNull(paymentInfoPlugin.getGatewayError());
         Assert.assertNull(paymentInfoPlugin.getGatewayErrorCode());
         Assert.assertEquals(paymentInfoPlugin.getStatus(), PaymentPluginStatus.PROCESSED);
@@ -70,67 +70,35 @@ public class TestExternalPaymentProviderPlugin extends PaymentTestSuiteNoDB {
 
     @Test(groups = "fast", expectedExceptions = PaymentPluginApiException.class)
     public void testRefundForNonExistingPayment() throws Exception {
-        plugin.processRefund(Mockito.mock(Account.class), UUID.randomUUID(), BigDecimal.ONE, callContext);
+        plugin.processRefund(UUID.randomUUID(), BigDecimal.ONE, callContext);
     }
 
     @Test(groups = "fast", expectedExceptions = PaymentPluginApiException.class)
     public void testRefundTooLarge() throws Exception {
         final UUID paymentId = UUID.randomUUID();
-        plugin.processPayment(UUID.randomUUID().toString(), paymentId, BigDecimal.ZERO, callContext);
+        final UUID paymentMethodId = UUID.randomUUID();
 
-        plugin.processRefund(Mockito.mock(Account.class), paymentId, BigDecimal.ONE, callContext);
+        plugin.processPayment(paymentId, paymentMethodId, BigDecimal.ZERO, callContext);
+        plugin.processRefund(paymentId, BigDecimal.ONE, callContext);
     }
 
     @Test(groups = "fast")
     public void testRefundTooLargeMultipleTimes() throws Exception {
         final UUID paymentId = UUID.randomUUID();
-        plugin.processPayment(UUID.randomUUID().toString(), paymentId, BigDecimal.TEN, callContext);
+        final UUID paymentMethodId = UUID.randomUUID();
+
+        plugin.processPayment(paymentId, paymentMethodId, BigDecimal.TEN, callContext);
 
         final Account account = Mockito.mock(Account.class);
         for (int i = 0; i < 10; i++) {
-            plugin.processRefund(account, paymentId, BigDecimal.ONE, callContext);
+            plugin.processRefund(paymentId, BigDecimal.ONE, callContext);
         }
 
         try {
-            plugin.processRefund(account, paymentId, BigDecimal.ONE, callContext);
+            plugin.processRefund(paymentId, BigDecimal.ONE, callContext);
             Assert.fail("Shouldn't have been able to refund");
         } catch (PaymentPluginApiException e) {
             Assert.assertTrue(true);
         }
     }
-
-    @Test(groups = "fast")
-    public void testRefund() throws Exception {
-        // An external payment refund would be e.g. a check that we trash
-        final String externalKey = UUID.randomUUID().toString();
-        final UUID paymentId = UUID.randomUUID();
-        final BigDecimal amount = BigDecimal.TEN;
-        plugin.processPayment(externalKey, paymentId, amount, callContext);
-
-        plugin.processRefund(Mockito.mock(Account.class), paymentId, BigDecimal.ONE, callContext);
-        Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), UUID.randomUUID(), BigDecimal.ONE, callContext), 0);
-        Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, BigDecimal.TEN, callContext), 0);
-        Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, BigDecimal.ONE, callContext), 1);
-        Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, new BigDecimal("5"), callContext), 0);
-
-        // Try multiple refunds
-
-        plugin.processRefund(Mockito.mock(Account.class), paymentId, BigDecimal.ONE, callContext);
-        Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), UUID.randomUUID(), BigDecimal.ONE, callContext), 0);
-        Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, BigDecimal.TEN, callContext), 0);
-        Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, BigDecimal.ONE, callContext), 2);
-        Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, new BigDecimal("5"), callContext), 0);
-
-        plugin.processRefund(Mockito.mock(Account.class), paymentId, BigDecimal.ONE, callContext);
-        Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), UUID.randomUUID(), BigDecimal.ONE, callContext), 0);
-        Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, BigDecimal.TEN, callContext), 0);
-        Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, BigDecimal.ONE, callContext), 3);
-        Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, new BigDecimal("5"), callContext), 0);
-
-        plugin.processRefund(Mockito.mock(Account.class), paymentId, new BigDecimal("5"), callContext);
-        Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), UUID.randomUUID(), BigDecimal.ONE, callContext), 0);
-        Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, BigDecimal.TEN, callContext), 0);
-        Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, BigDecimal.ONE, callContext), 3);
-        Assert.assertEquals(plugin.getNbRefundForPaymentAmount(Mockito.mock(Account.class), paymentId, new BigDecimal("5"), callContext), 1);
-    }
 }
diff --git a/payment/src/test/java/com/ning/billing/payment/TestPaymentHelper.java b/payment/src/test/java/com/ning/billing/payment/TestPaymentHelper.java
index 5455ca8..4263a5e 100644
--- a/payment/src/test/java/com/ning/billing/payment/TestPaymentHelper.java
+++ b/payment/src/test/java/com/ning/billing/payment/TestPaymentHelper.java
@@ -29,6 +29,7 @@ import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.payment.api.PaymentApi;
 import com.ning.billing.payment.api.PaymentMethodPlugin;
 import com.ning.billing.payment.provider.DefaultNoOpPaymentMethodPlugin;
+import com.ning.billing.payment.provider.MockPaymentProviderPlugin;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
@@ -43,8 +44,6 @@ import com.google.inject.Inject;
 
 public class TestPaymentHelper {
 
-    public static final String PLUGIN_TEST_NAME = "my-mock";
-
     protected final AccountInternalApi AccountApi;
     protected final InvoiceInternalApi invoiceApi;
     protected PaymentApi paymentApi;
@@ -128,7 +127,7 @@ public class TestPaymentHelper {
     }
 
     public void addTestPaymentMethod(final Account account, final PaymentMethodPlugin paymentMethodInfo) throws Exception {
-        final UUID paymentMethodId = paymentApi.addPaymentMethod(PLUGIN_TEST_NAME, account, true, paymentMethodInfo, context);
+        final UUID paymentMethodId = paymentApi.addPaymentMethod(MockPaymentProviderPlugin.PLUGIN_NAME, account, true, paymentMethodInfo, context);
         Mockito.when(account.getPaymentMethodId()).thenReturn(paymentMethodId);
     }
 }
diff --git a/payment/src/test/java/com/ning/billing/payment/TestRetryService.java b/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
index 162f007..281f0e9 100644
--- a/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
+++ b/payment/src/test/java/com/ning/billing/payment/TestRetryService.java
@@ -51,22 +51,22 @@ public class TestRetryService extends PaymentTestSuiteNoDB {
 
     @Override
     @BeforeMethod(groups = "fast")
-    public void setupTest() throws Exception {
-        super.setupTest();
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         pluginRetryService.initialize(DefaultPaymentService.SERVICE_NAME);
         pluginRetryService.start();
 
         retryService.initialize(DefaultPaymentService.SERVICE_NAME);
         retryService.start();
 
-        mockPaymentProviderPlugin = (MockPaymentProviderPlugin) registry.getPlugin(null);
+        mockPaymentProviderPlugin = (MockPaymentProviderPlugin) registry.getServiceForName(null);
         mockPaymentProviderPlugin.clear();
     }
 
     @Override
     @AfterMethod(groups = "fast")
-    public void cleanupTest() throws Exception {
-        super.cleanupTest();
+    public void afterMethod() throws Exception {
+        super.afterMethod();
         retryService.stop();
         pluginRetryService.stop();
     }

pom.xml 560(+390 -170)

diff --git a/pom.xml b/pom.xml
index 6a8061d..b15d93e 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>
@@ -91,6 +107,7 @@
                 <groupId>javax.servlet</groupId>
                 <artifactId>javax.servlet-api</artifactId>
                 <version>3.0.1</version>
+                <scope>provided</scope>
             </dependency>
             <dependency>
                 <groupId>com.ning.billing</groupId>
@@ -244,12 +261,27 @@
             </dependency>
             <dependency>
                 <groupId>com.ning.billing</groupId>
-                <artifactId>killbill-osgi-bundles</artifactId>
+                <artifactId>killbill-osgi-all-bundles</artifactId>
                 <version>${project.version}</version>
             </dependency>
             <dependency>
                 <groupId>com.ning.billing</groupId>
-                <artifactId>killbill-osgi-bundles-hello</artifactId>
+                <artifactId>killbill-osgi-bundles-lib-killbill</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.ning.billing</groupId>
+                <artifactId>killbill-osgi-bundles-lib-slf4j-osgi</artifactId>
+                <version>${project.version}</version>
+            </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>
@@ -259,6 +291,21 @@
             </dependency>
             <dependency>
                 <groupId>com.ning.billing</groupId>
+                <artifactId>killbill-osgi-bundles-logger</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.ning.billing</groupId>
+                <artifactId>killbill-osgi-bundles-test-beatrix</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.ning.billing</groupId>
+                <artifactId>killbill-osgi-bundles-test-payment</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.ning.billing</groupId>
                 <artifactId>killbill-osgi-bundles-meter</artifactId>
                 <version>${project.version}</version>
             </dependency>
@@ -281,12 +328,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>
@@ -301,7 +348,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>
@@ -320,6 +367,12 @@
                 <scope>provided</scope>
             </dependency>
             <dependency>
+                <groupId>com.google.code.findbugs</groupId>
+                <artifactId>jsr305</artifactId>
+                <version>1.3.9</version>
+                <scope>provided</scope>
+            </dependency>
+            <dependency>
                 <groupId>com.google.inject</groupId>
                 <artifactId>guice</artifactId>
                 <version>3.0</version>
@@ -360,6 +413,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>
@@ -459,6 +517,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>
@@ -466,63 +575,185 @@
                 <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.8</version>
+                    <executions>
+                        <execution>
+                            <phase>verify</phase>
+                            <goals>
+                                <goal>check</goal>
+                            </goals>
+                            <configuration>
+                                <licenseFamilies>
+                                    <licenseFamily implementation="org.apache.rat.license.Apache20LicenseFamily"></licenseFamily>
+                                    <!-- Hack, rat 0.9 will have org.apache.rat.analysis.license.MITLicense -->
+                                    <licenseFamily implementation="org.apache.rat.license.SimpleLicenseFamily">
+                                        <familyName>The MIT License</familyName>
+                                    </licenseFamily>
+                                </licenseFamilies>
+                                <useEclipseDefaultExcludes>true</useEclipseDefaultExcludes>
+                                <useIdeaDefaultExcludes>true</useIdeaDefaultExcludes>
+                                <useMavenDefaultExcludes>true</useMavenDefaultExcludes>
+                                <licenses>
+                                    <!-- For server/src/main/webapp/javascripts/html5.js -->
+                                    <license implementation="org.apache.rat.analysis.license.SimplePatternBasedLicense">
+                                        <licenseFamilyCategory>MIT</licenseFamilyCategory>
+                                        <licenseFamilyName>The MIT License</licenseFamilyName>
+                                        <patterns>
+                                            <pattern>MIT/GPL2 Licensed</pattern>
+                                        </patterns>
+                                    </license>
+                                    <!-- For server/src/main/webapp/javascripts/jquery.min.js -->
+                                    <license implementation="org.apache.rat.analysis.license.SimplePatternBasedLicense">
+                                        <licenseFamilyCategory>MIT</licenseFamilyCategory>
+                                        <licenseFamilyName>The MIT License</licenseFamilyName>
+                                        <patterns>
+                                            <pattern>jquery.org/license</pattern>
+                                        </patterns>
+                                    </license>
+                                </licenses>
+                                <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>
@@ -530,81 +761,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>
@@ -632,13 +788,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>
-                        <log4j.configuration>file:${project.basedir}/src/test/resources/log4j.xml</log4j.configuration>
-                    </systemPropertyVariables>
                 </configuration>
             </plugin>
             <plugin>
@@ -649,21 +800,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>
@@ -674,14 +810,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>
@@ -695,18 +828,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>
                             </systemPropertyVariables>
                         </configuration>
                     </plugin>
@@ -719,16 +846,37 @@
                 <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>
+                    <!-- Comment out the plugin for now. Logs are getting too big on Travis which fails the build,
+                         see for example https://api.travis-ci.org/jobs/5172875/log.txt?deansi=true -->
+                    <!--<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>
@@ -739,7 +887,6 @@
                     <plugin>
                         <groupId>org.apache.maven.plugins</groupId>
                         <artifactId>maven-surefire-plugin</artifactId>
-                        <version>2.11</version>
                         <configuration>
                             <groups>stress</groups>
                         </configuration>
@@ -753,6 +900,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>
@@ -768,13 +919,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>
@@ -791,7 +1011,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>
@@ -822,22 +1042,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>
@@ -871,6 +1091,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 126(+25 -101)

diff --git a/server/pom.xml b/server/pom.xml
index 4f388a4..e446957 100644
--- a/server/pom.xml
+++ b/server/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>
@@ -117,7 +118,6 @@
         <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>
-            <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>com.ning.jetty</groupId>
@@ -217,7 +217,6 @@
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
-            <version>11.0.2</version>
             <scope>compile</scope>
         </dependency>
         <dependency>
@@ -316,13 +315,15 @@
                         <goals>
                             <goal>createconsole</goal>
                         </goals>
+                        <configuration>
+                            <backgroundImage>${basedir}/src/main/jettyconsole/killbill.png</backgroundImage>
+                        </configuration>
                     </execution>
                 </executions>
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-shade-plugin</artifactId>
-                <version>1.4</version>
                 <executions>
                     <execution>
                         <id>assemble-killbill</id>
@@ -344,98 +345,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>
@@ -450,13 +362,6 @@
                 <artifactId>jetty-maven-plugin</artifactId>
                 <version>${jetty.version}</version>
                 <dependencies>
-                    <dependency><!-- For LogLevelCounterAppender -->
-                        <groupId>com.ning.jetty</groupId>
-                        <artifactId>ning-service-skeleton-log4j</artifactId>
-                        <version>${skeleton.version}</version>
-                        <classifier>selfcontained</classifier>
-                        <scope>runtime</scope>
-                    </dependency>
                     <!-- Needed to redirect Jetty logs to slf4j -->
                     <dependency>
                         <groupId>org.slf4j</groupId>
@@ -473,15 +378,34 @@
                         <artifactId>logback-classic</artifactId>
                         <version>${logback.version}</version>
                     </dependency>
+                    <dependency>
+                        <groupId>org.eclipse.jetty</groupId>
+                        <artifactId>jetty-deploy</artifactId>
+                        <version>${jetty.version}</version>
+                    </dependency>
+                    <dependency>
+                        <groupId>org.eclipse.jetty</groupId>
+                        <artifactId>jetty-jmx</artifactId>
+                        <version>${jetty.version}</version>
+                    </dependency>
                 </dependencies>
                 <configuration>
-                    <scanIntervalSeconds>60</scanIntervalSeconds>
+                    <jettyXml>${basedir}/src/main/jetty-config/ning-jetty-conf.xml</jettyXml>
+                    <contextXml>${basedir}/src/main/jetty-config/contexts/root.xml</contextXml>
                     <systemProperties>
                         <systemProperty>
+                            <!-- See root.xml -->
+                            <name>xn.jetty.webapps.defaultsDescriptor</name>
+                            <value>${basedir}/src/main/jetty-config/etc/webdefault.xml</value>
+                        </systemProperty>
+                        <systemProperty>
                             <name>logback.configurationFile</name>
                             <value>file:${basedir}/src/main/resources/logback.xml</value>
                         </systemProperty>
                     </systemProperties>
+                    <scanIntervalSeconds>0</scanIntervalSeconds>
+                    <stopPort>9966</stopPort>
+                    <stopKey>foo</stopKey>
                 </configuration>
             </plugin>
         </plugins>
diff --git a/server/src/main/java/com/ning/billing/server/listeners/KillbillGuiceListener.java b/server/src/main/java/com/ning/billing/server/listeners/KillbillGuiceListener.java
index aaf22b7..d42045d 100644
--- a/server/src/main/java/com/ning/billing/server/listeners/KillbillGuiceListener.java
+++ b/server/src/main/java/com/ning/billing/server/listeners/KillbillGuiceListener.java
@@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory;
 
 import com.ning.billing.beatrix.bus.api.ExternalBus;
 import com.ning.billing.beatrix.lifecycle.DefaultLifecycle;
+import com.ning.billing.jaxrs.resources.JaxRsResourceBase;
 import com.ning.billing.jaxrs.util.KillbillEventHandler;
 import com.ning.billing.server.config.KillbillServerConfig;
 import com.ning.billing.server.healthchecks.KillbillHealthcheck;
@@ -76,6 +77,9 @@ public class KillbillGuiceListener extends SetupServer {
                 .addJMXExport(InternalBus.class)
                 .addJMXExport(ExternalBus.class)
                 .addModule(getModule())
+                // Don't filter all requests through Jersey, only the JAX-RS APIs (otherwise,
+                // things like static resources, favicon, etc. are 404'ed)
+                .setJerseyUriPattern("(" + JaxRsResourceBase.PREFIX + "|" + JaxRsResourceBase.PLUGINS_PATH + ")" + "/.*")
                 .addJerseyResource("com.ning.billing.jaxrs.mappers")
                 .addJerseyResource("com.ning.billing.jaxrs.resources");
 
diff --git a/server/src/main/java/com/ning/billing/server/modules/DBIProvider.java b/server/src/main/java/com/ning/billing/server/modules/DBIProvider.java
index 5d86c2b..66c5cdf 100644
--- a/server/src/main/java/com/ning/billing/server/modules/DBIProvider.java
+++ b/server/src/main/java/com/ning/billing/server/modules/DBIProvider.java
@@ -20,6 +20,7 @@ import java.util.concurrent.TimeUnit;
 
 import javax.sql.DataSource;
 
+import org.skife.config.TimeSpan;
 import org.skife.jdbi.v2.DBI;
 import org.skife.jdbi.v2.TimingCollector;
 import org.skife.jdbi.v2.tweak.SQLLog;
@@ -65,8 +66,8 @@ public class DBIProvider implements Provider<DBI> {
 
     @Override
     public DBI get() {
+        final DataSource ds = getDataSource();
 
-        final DataSource ds = getC3P0DataSource();
         final DBI dbi = new DBI(ds);
         dbi.registerArgumentFactory(new UUIDArgumentFactory());
         dbi.registerArgumentFactory(new DateTimeZoneArgumentFactory());
@@ -95,6 +96,21 @@ public class DBIProvider implements Provider<DBI> {
         return dbi;
     }
 
+    private DataSource getDataSource() {
+        final DataSource ds;
+
+        // TODO PIERRE DaoConfig is in the skeleton
+        final String dataSource = System.getProperty("com.ning.jetty.jdbi.datasource", "c3p0");
+        if (dataSource.equals("c3p0")) {
+            ds = getC3P0DataSource();
+        } else if (dataSource.equals("bonecp")) {
+            ds = getBoneCPDatSource();
+        } else {
+            throw new IllegalArgumentException("DataSource " + dataSource + " unsupported");
+        }
+
+        return ds;
+    }
 
     private DataSource getBoneCPDatSource() {
         final BoneCPConfig dbConfig = new BoneCPConfig();
@@ -110,17 +126,53 @@ public class DBIProvider implements Provider<DBI> {
         dbConfig.setPartitionCount(1);
         dbConfig.setDisableJMX(false);
 
-        final BoneCPDataSource ds = new BoneCPDataSource(dbConfig);
-        return ds;
+        return new BoneCPDataSource(dbConfig);
     }
 
     private DataSource getC3P0DataSource() {
-        ComboPooledDataSource cpds = new ComboPooledDataSource();
+        final ComboPooledDataSource cpds = new ComboPooledDataSource();
         cpds.setJdbcUrl(config.getJdbcUrl());
         cpds.setUser(config.getUsername());
         cpds.setPassword(config.getPassword());
-        cpds.setMinPoolSize(1);
-        cpds.setMaxPoolSize(10);
+        // http://www.mchange.com/projects/c3p0/#minPoolSize
+        // Minimum number of Connections a pool will maintain at any given time.
+        cpds.setMinPoolSize(config.getMinIdle());
+        // http://www.mchange.com/projects/c3p0/#maxPoolSize
+        // Maximum number of Connections a pool will maintain at any given time.
+        cpds.setMaxPoolSize(config.getMaxActive());
+        // http://www.mchange.com/projects/c3p0/#checkoutTimeout
+        // The number of milliseconds a client calling getConnection() will wait for a Connection to be checked-in or
+        // acquired when the pool is exhausted. Zero means wait indefinitely. Setting any positive value will cause the getConnection()
+        // call to time-out and break with an SQLException after the specified number of milliseconds.
+        cpds.setCheckoutTimeout(toMilliSeconds(config.getConnectionTimeout()));
+        // http://www.mchange.com/projects/c3p0/#maxIdleTime
+        // Seconds a Connection can remain pooled but unused before being discarded. Zero means idle connections never expire.
+        cpds.setMaxIdleTime(toSeconds(config.getIdleMaxAge()));
+        // http://www.mchange.com/projects/c3p0/#maxConnectionAge
+        // Seconds, effectively a time to live. A Connection older than maxConnectionAge will be destroyed and purged from the pool.
+        // This differs from maxIdleTime in that it refers to absolute age. Even a Connection which has not been much idle will be purged
+        // from the pool if it exceeds maxConnectionAge. Zero means no maximum absolute age is enforced.
+        cpds.setMaxConnectionAge(toSeconds(config.getMaxConnectionAge()));
+        // http://www.mchange.com/projects/c3p0/#idleConnectionTestPeriod
+        // If this is a number greater than 0, c3p0 will test all idle, pooled but unchecked-out connections, every this number of seconds.
+        cpds.setIdleConnectionTestPeriod(toSeconds(config.getIdleConnectionTestPeriod()));
+
         return cpds;
     }
+
+    private int toSeconds(final TimeSpan timeSpan) {
+        return toSeconds(timeSpan.getPeriod(), timeSpan.getUnit());
+    }
+
+    private int toSeconds(final long period, final TimeUnit timeUnit) {
+        return (int) TimeUnit.SECONDS.convert(period, timeUnit);
+    }
+
+    private int toMilliSeconds(final TimeSpan timeSpan) {
+        return toMilliSeconds(timeSpan.getPeriod(), timeSpan.getUnit());
+    }
+
+    private int toMilliSeconds(final long period, final TimeUnit timeUnit) {
+        return (int) TimeUnit.MILLISECONDS.convert(period, timeUnit);
+    }
 }
diff --git a/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java b/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
index 1640670..e6ff204 100644
--- a/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
+++ b/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
@@ -16,6 +16,8 @@
 
 package com.ning.billing.server.modules;
 
+import org.skife.config.ConfigSource;
+import org.skife.config.SimplePropertyConfigSource;
 import org.skife.jdbi.v2.DBI;
 import org.skife.jdbi.v2.IDBI;
 
@@ -31,18 +33,21 @@ import com.ning.billing.jaxrs.resources.CatalogResource;
 import com.ning.billing.jaxrs.resources.InvoiceResource;
 import com.ning.billing.jaxrs.resources.PaymentMethodResource;
 import com.ning.billing.jaxrs.resources.PaymentResource;
+import com.ning.billing.jaxrs.resources.PluginResource;
 import com.ning.billing.jaxrs.resources.RefundResource;
 import com.ning.billing.jaxrs.resources.SubscriptionResource;
 import com.ning.billing.jaxrs.resources.TagResource;
 import com.ning.billing.jaxrs.resources.TenantResource;
 import com.ning.billing.jaxrs.util.KillbillEventHandler;
 import com.ning.billing.junction.glue.DefaultJunctionModule;
+import com.ning.billing.osgi.glue.DefaultOSGIModule;
 import com.ning.billing.overdue.glue.DefaultOverdueModule;
 import com.ning.billing.payment.glue.PaymentModule;
 import com.ning.billing.server.DefaultServerService;
 import com.ning.billing.server.ServerService;
 import com.ning.billing.server.notifications.PushNotificationListener;
 import com.ning.billing.tenant.glue.TenantModule;
+import com.ning.billing.usage.glue.UsageModule;
 import com.ning.billing.util.email.EmailModule;
 import com.ning.billing.util.email.templates.TemplateModule;
 import com.ning.billing.util.glue.AuditModule;
@@ -93,6 +98,7 @@ public class KillbillServerModule extends AbstractModule {
         bind(CatalogResource.class).asEagerSingleton();
         bind(PaymentMethodResource.class).asEagerSingleton();
         bind(PaymentResource.class).asEagerSingleton();
+        bind(PluginResource.class).asEagerSingleton();
         bind(RefundResource.class).asEagerSingleton();
         bind(TenantResource.class).asEagerSingleton();
         bind(KillbillEventHandler.class).asEagerSingleton();
@@ -103,28 +109,33 @@ public class KillbillServerModule extends AbstractModule {
     }
 
     protected void installKillbillModules() {
-        install(new EmailModule());
-        install(new CacheModule());
+        final ConfigSource configSource = new SimplePropertyConfigSource(System.getProperties());
+
+        install(new EmailModule(configSource));
+        install(new CacheModule(configSource));
         install(new GlobalLockerModule());
         install(new CustomFieldModule());
         install(new AuditModule());
-        install(new CatalogModule());
-        install(new BusModule());
-        install(new NotificationQueueModule());
+        install(new CatalogModule(configSource));
+        install(new BusModule(configSource));
+        install(new NotificationQueueModule(configSource));
         install(new CallContextModule());
-        install(new DefaultAccountModule());
-        install(new DefaultInvoiceModule());
+        install(new DefaultAccountModule(configSource));
+        install(new DefaultInvoiceModule(configSource));
         install(new TemplateModule());
-        install(new DefaultEntitlementModule());
-        install(new AnalyticsModule());
-        install(new PaymentModule());
+        install(new DefaultEntitlementModule(configSource));
+        install(new AnalyticsModule(configSource));
+        install(new PaymentModule(configSource));
         install(new BeatrixModule());
-        install(new DefaultJunctionModule());
-        install(new DefaultOverdueModule());
-        install(new TenantModule());
+        install(new DefaultJunctionModule(configSource));
+        install(new DefaultOverdueModule(configSource));
+        install(new TenantModule(configSource));
         install(new ExportModule());
         install(new TagStoreModule());
         install(new NonEntityDaoModule());
+        install(new DefaultOSGIModule(configSource));
+        install(new UsageModule(configSource));
+
         installClock();
     }
 }
diff --git a/server/src/main/jetty-config/contexts/root.xml b/server/src/main/jetty-config/contexts/root.xml
index cdf71e1..52c9c0f 100755
--- a/server/src/main/jetty-config/contexts/root.xml
+++ b/server/src/main/jetty-config/contexts/root.xml
@@ -1,4 +1,20 @@
 <?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.
+  -->
+
 <!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
 <Configure class="org.eclipse.jetty.webapp.WebAppContext">
     <Set name="contextPath">/</Set>
diff --git a/server/src/main/jetty-config/etc/webdefault.xml b/server/src/main/jetty-config/etc/webdefault.xml
index 19ae6ff..1eebc9b 100755
--- a/server/src/main/jetty-config/etc/webdefault.xml
+++ b/server/src/main/jetty-config/etc/webdefault.xml
@@ -1,4 +1,20 @@
 <?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  ~ 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.
+  -->
+
 <web-app
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -9,6 +25,57 @@
         Default web.xml file.
         This file is applied to a Web application before it's own WEB_INF/web.xml file
     </description>
+
+    <servlet>
+        <servlet-name>default</servlet-name>
+        <servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class>
+        <init-param>
+            <param-name>aliases</param-name>
+            <param-value>false</param-value>
+        </init-param>
+        <init-param>
+            <param-name>acceptRanges</param-name>
+            <param-value>true</param-value>
+        </init-param>
+        <init-param>
+            <param-name>dirAllowed</param-name>
+            <param-value>false</param-value>
+        </init-param>
+        <init-param>
+            <param-name>welcomeServlets</param-name>
+            <param-value>false</param-value>
+        </init-param>
+        <init-param>
+            <param-name>redirectWelcome</param-name>
+            <param-value>false</param-value>
+        </init-param>
+        <init-param>
+            <param-name>maxCacheSize</param-name>
+            <param-value>256000000</param-value>
+        </init-param>
+        <init-param>
+            <param-name>maxCachedFileSize</param-name>
+            <param-value>200000000</param-value>
+        </init-param>
+        <init-param>
+            <param-name>maxCachedFiles</param-name>
+            <param-value>2048</param-value>
+        </init-param>
+        <init-param>
+            <param-name>gzip</param-name>
+            <param-value>true</param-value>
+        </init-param>
+        <init-param>
+            <param-name>etags</param-name>
+            <param-value>true</param-value>
+        </init-param>
+        <init-param>
+            <param-name>useFileMappedBuffer</param-name>
+            <param-value>true</param-value>
+        </init-param>
+        <load-on-startup>0</load-on-startup>
+    </servlet>
+
     <context-param>
         <param-name>org.mortbay.jetty.webapp.NoTLDJarPattern</param-name>
         <param-value>
diff --git a/server/src/main/jetty-config/ning-jetty-conf.xml b/server/src/main/jetty-config/ning-jetty-conf.xml
index 60764c5..3423b27 100644
--- a/server/src/main/jetty-config/ning-jetty-conf.xml
+++ b/server/src/main/jetty-config/ning-jetty-conf.xml
@@ -1,4 +1,20 @@
 <?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.
+  -->
+
 <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
 
 <Configure id="Server" class="org.eclipse.jetty.server.Server">
diff --git a/server/src/main/jettyconsole/killbill.png b/server/src/main/jettyconsole/killbill.png
new file mode 100644
index 0000000..610f58a
Binary files /dev/null and b/server/src/main/jettyconsole/killbill.png differ
diff --git a/server/src/main/resources/killbill-server.properties b/server/src/main/resources/killbill-server.properties
index daaa110..7401da5 100644
--- a/server/src/main/resources/killbill-server.properties
+++ b/server/src/main/resources/killbill-server.properties
@@ -1,12 +1,24 @@
+#
+# 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.
+#
+
 # Use skeleton properties for server and configure killbill database
 com.ning.jetty.jdbi.url=jdbc:mysql://127.0.0.1:3306/killbill
 com.ning.jetty.jdbi.user=root
 com.ning.jetty.jdbi.password=root
 
-killbill.catalog.uri=file:src/main/resources/catalog-demo.xml
-
-killbill.entitlement.dao.claim.time=60000
-killbill.entitlement.dao.ready.max=1
-killbill.entitlement.engine.notifications.sleep=500
 user.timezone=UTC
 
+ANTLR_USE_DIRECT_CLASS_LOADING=true
diff --git a/server/src/main/resources/logback.xml b/server/src/main/resources/logback.xml
index 3087885..0cb11ef 100644
--- a/server/src/main/resources/logback.xml
+++ b/server/src/main/resources/logback.xml
@@ -1,3 +1,19 @@
+<!--
+  ~ 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.
+  -->
+
 <configuration>
   <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
     <!-- encoders are assigned the type
diff --git a/server/src/main/resources/NoOverdueConfig.xml b/server/src/main/resources/NoOverdueConfig.xml
new file mode 100644
index 0000000..955873e
--- /dev/null
+++ b/server/src/main/resources/NoOverdueConfig.xml
@@ -0,0 +1,26 @@
+<?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.
+  -->
+
+<overdueConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
+	<bundleOverdueStates>
+		<state name="Clear">
+			<isClearState>true</isClearState>
+		</state>
+	</bundleOverdueStates>
+</overdueConfig>
+
diff --git a/server/src/main/resources/SpyCarBasic.xml b/server/src/main/resources/SpyCarBasic.xml
new file mode 100644
index 0000000..7db7465
--- /dev/null
+++ b/server/src/main/resources/SpyCarBasic.xml
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+  ~ 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.
+  -->
+
+<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
+
+    <effectiveDate>2013-02-08T00:00:00+00:00</effectiveDate>
+    <catalogName>SpyCarBasic</catalogName>
+
+    <currencies>
+        <currency>USD</currency>
+        <currency>GBP</currency>
+    </currencies>
+
+    <products>
+        <product name="Standard">
+            <category>BASE</category>
+        </product>
+        <product name="Sports">
+            <category>BASE</category>
+        </product>
+        <product name="Super">
+            <category>BASE</category>
+        </product>
+    </products>
+
+    <rules>
+        <changePolicy>
+            <changePolicyCase>
+                <policy>IMMEDIATE</policy>
+            </changePolicyCase>
+        </changePolicy>
+        <changeAlignment>
+           <changeAlignmentCase>
+                <alignment>START_OF_BUNDLE</alignment>
+            </changeAlignmentCase>
+        </changeAlignment>
+        <cancelPolicy>
+            <cancelPolicyCase>
+                <policy>IMMEDIATE</policy>
+            </cancelPolicyCase>
+        </cancelPolicy>
+        <createAlignment>
+            <createAlignmentCase>
+                <alignment>START_OF_BUNDLE</alignment>
+            </createAlignmentCase>
+        </createAlignment>
+        <billingAlignment>
+            <billingAlignmentCase>
+                <alignment>ACCOUNT</alignment>
+            </billingAlignmentCase>
+        </billingAlignment>
+        <priceList>
+           <priceListCase>
+                <toPriceList>DEFAULT</toPriceList>
+           </priceListCase>
+        </priceList>
+    </rules>
+
+    <plans>
+        <plan name="standard-monthly">
+            <product>Standard</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+                    <fixedPrice> <!-- empty price implies $0 -->
+                    </fixedPrice>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <billingPeriod>MONTHLY</billingPeriod>
+                <recurringPrice>
+                    <price>
+                        <currency>GBP</currency>
+                        <value>75.00</value>
+                    </price>
+                    <price>
+                        <currency>USD</currency>
+                        <value>100.00</value>
+                    </price>
+                </recurringPrice>
+            </finalPhase>
+        </plan>
+        <plan name="sports-monthly">
+            <product>Sports</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+                    <fixedPrice> <!-- empty price implies $0 -->
+                    </fixedPrice>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <billingPeriod>MONTHLY</billingPeriod>
+                <recurringPrice>
+                    <price>
+                        <currency>GBP</currency>
+                        <value>375.00</value>
+                    </price>
+                    <price>
+                        <currency>USD</currency>
+                        <value>500.00</value>
+                    </price>
+                </recurringPrice>
+            </finalPhase>
+        </plan>
+        <plan name="super-monthly">
+            <product>Super</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+                    <fixedPrice> <!-- empty price implies $0 -->
+                    </fixedPrice>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <billingPeriod>MONTHLY</billingPeriod>
+                <recurringPrice>
+                    <price>
+                        <currency>GBP</currency>
+                        <value>750.00</value>
+                    </price>
+                    <price>
+                        <currency>USD</currency>
+                        <value>1000.00</value>
+                    </price>
+                </recurringPrice>
+            </finalPhase>
+        </plan>
+    </plans>
+    <priceLists>
+        <defaultPriceList name="DEFAULT">
+            <plans>
+                <plan>standard-monthly</plan>
+                <plan>sports-monthly</plan>
+                <plan>super-monthly</plan>
+            </plans>
+        </defaultPriceList>
+    </priceLists>
+</catalog>
diff --git a/server/src/main/webapp/favicon.ico b/server/src/main/webapp/favicon.ico
new file mode 100644
index 0000000..675f906
Binary files /dev/null and b/server/src/main/webapp/favicon.ico differ
diff --git a/server/src/main/webapp/img/glyphicons-halflings.png b/server/src/main/webapp/img/glyphicons-halflings.png
new file mode 100644
index 0000000..a996999
Binary files /dev/null and b/server/src/main/webapp/img/glyphicons-halflings.png differ
diff --git a/server/src/main/webapp/img/glyphicons-halflings-white.png b/server/src/main/webapp/img/glyphicons-halflings-white.png
new file mode 100644
index 0000000..3bf6484
Binary files /dev/null and b/server/src/main/webapp/img/glyphicons-halflings-white.png differ
diff --git a/server/src/main/webapp/img/KillBillLogo400x400.png b/server/src/main/webapp/img/KillBillLogo400x400.png
new file mode 100644
index 0000000..610f58a
Binary files /dev/null and b/server/src/main/webapp/img/KillBillLogo400x400.png differ
diff --git a/server/src/main/webapp/index.html b/server/src/main/webapp/index.html
new file mode 100644
index 0000000..bc17c34
--- /dev/null
+++ b/server/src/main/webapp/index.html
@@ -0,0 +1,63 @@
+<!--
+  ~ 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.
+  -->
+
+  <!DOCTYPE html>
+  <html lang="en">
+      <head>
+          <meta charset="utf-8">
+          <title>Kill Bill, the Open-Source billing platform</title>
+
+          <!--[if lt IE 9]>
+          <script src=javascripts/html5.js" type="text/javascript"></script>
+          <![endif]-->
+          <script src="javascripts/jquery.min.js"></script>
+          <script src="javascripts/bootstrap.min.js"></script>
+
+          <link rel=stylesheet type="text/css" href="stylesheets/bootstrap.min.css">
+          <link rel=stylesheet type="text/css" href="stylesheets/killbill.css">
+      </head>
+
+      <body data-spy="scroll" data-target=".bs-docs-sidebar">
+          <div class="navbar navbar-inverse navbar-fixed-top">
+              <div class="navbar-inner">
+                  <div class="container">
+                      <div class="nav-collapse collapse">
+                          <ul class="nav pull-right">
+                              <li><a href="http://groups.google.com/group/killbilling-users" target="_blank">User Mailing-List</a></li>
+                              <li><a href="http://groups.google.com/group/killbilling-dev" target="_blank">Dev Mailing-List</a></li>
+                          </ul>
+                      </div>
+                  </div>
+              </div>
+          </div>
+          <div class="container">
+              <div class="jumbotron">
+                  <h1>Kill Bill</h1>
+                  <h2>The Open-Source Billing Platform</h2>
+                  <img src="img/KillBillLogo400x400.png" style="height: 200px; margin-bottom: 20px;" />
+              </div>
+
+              <div class="marketing">
+                  <h1>Congratulations!</h1>
+                  <p class="lead">Kill Bill is up and running.</p>
+                  <ul class="inline">
+                      <li><a class="btn btn-primary btn-large" href="/1.0/metrics?pretty=true">Metrics</a></li>
+                      <li><a class="btn btn-primary btn-large" href="/1.0/threads">Threads</a></li>
+                  </ul>
+              </div>
+          </div>
+    </body>
+</html>
diff --git a/server/src/main/webapp/javascripts/bootstrap.min.js b/server/src/main/webapp/javascripts/bootstrap.min.js
new file mode 100644
index 0000000..e05923d
--- /dev/null
+++ b/server/src/main/webapp/javascripts/bootstrap.min.js
@@ -0,0 +1,6 @@
+/*!
+* Bootstrap.js by @fat & @mdo
+* Copyright 2012 Twitter, Inc.
+* http://www.apache.org/licenses/LICENSE-2.0.txt
+*/
+!function(e){"use strict";e(function(){e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()};var r=e.fn.alert;e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e.fn.alert.noConflict=function(){return e.fn.alert=r,this},e(document).on("click.alert.data-api",t,n.prototype.close)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")};var n=e.fn.button;e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e.fn.button.noConflict=function(){return e.fn.button=n,this},e(document).on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.$indicators=this.$element.find(".carousel-indicators"),this.options=n,this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},getActiveIndex:function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},to:function(t){var n=this.getActiveIndex(),r=this;if(t>this.$items.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){r.to(t)}):n==t?this.pause().cycle():this.slide(t>n?"next":"prev",e(this.$items[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle()),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f;this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u](),f=e.Event("slide",{relatedTarget:i[0],direction:o});if(i.hasClass("active"))return;this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var t=e(a.$indicators.children()[a.getActiveIndex()]);t&&t.addClass("active")}));if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}};var n=e.fn.carousel;e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.pause().cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e.fn.carousel.noConflict=function(){return e.fn.carousel=n,this},e(document).on("click.carousel.data-api","[data-slide], [data-slide-to]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=e.extend({},i.data(),n.data()),o;i.carousel(s),(o=n.attr("data-slide-to"))&&i.data("carousel").pause().to(o).cycle(),t.preventDefault()})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning||this.$element.hasClass("in"))return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning||!this.$element.hasClass("in"))return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var n=e.fn.collapse;e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=e.extend({},e.fn.collapse.defaults,r.data(),typeof n=="object"&&n);i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e.fn.collapse.noConflict=function(){return e.fn.collapse=n,this},e(document).on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})}(window.jQuery),!function(e){"use strict";function r(){e(t).each(function(){i(e(this)).removeClass("open")})}function i(t){var n=t.attr("data-target"),r;n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=n&&e(n);if(!r||!r.length)r=t.parent();return r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||s.toggleClass("open"),n.focus(),!1},keydown:function(n){var r,s,o,u,a,f;if(!/(38|40|27)/.test(n.keyCode))return;r=e(this),n.preventDefault(),n.stopPropagation();if(r.is(".disabled, :disabled"))return;u=i(r),a=u.hasClass("open");if(!a||a&&n.keyCode==27)return n.which==27&&u.find(t).focus(),r.click();s=e("[role=menu] li:not(.divider):visible a",u);if(!s.length)return;f=s.index(s.filter(":focus")),n.keyCode==38&&f>0&&f--,n.keyCode==40&&f<s.length-1&&f++,~f||(f=0),s.eq(f).focus()}};var s=e.fn.dropdown;e.fn.dropdown=function(t){return this.each(function(){var r=e(this),i=r.data("dropdown");i||r.data("dropdown",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.dropdown.Constructor=n,e.fn.dropdown.noConflict=function(){return e.fn.dropdown=s,this},e(document).on("click.dropdown.data-api",r).on("click.dropdown.data-api",".dropdown form",function(e){e.stopPropagation()}).on(".dropdown-menu",function(e){e.stopPropagation()}).on("click.dropdown.data-api",t,n.prototype.toggle).on("keydown.dropdown.data-api",t+", [role=menu]",n.prototype.keydown)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=n,this.$element=e(t).delegate('[data-dismiss="modal"]',"click.dismiss.modal",e.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};t.prototype={constructor:t,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var t=this,n=e.Event("show");this.$element.trigger(n);if(this.isShown||n.isDefaultPrevented())return;this.isShown=!0,this.escape(),this.backdrop(function(){var n=e.support.transition&&t.$element.hasClass("fade");t.$element.parent().length||t.$element.appendTo(document.body),t.$element.show(),n&&t.$element[0].offsetWidth,t.$element.addClass("in").attr("aria-hidden",!1),t.enforceFocus(),n?t.$element.one(e.support.transition.end,function(){t.$element.focus().trigger("shown")}):t.$element.focus().trigger("shown")})},hide:function(t){t&&t.preventDefault();var n=this;t=e.Event("hide"),this.$element.trigger(t);if(!this.isShown||t.isDefaultPrevented())return;this.isShown=!1,this.escape(),e(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),e.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal()},enforceFocus:function(){var t=this;e(document).on("focusin.modal",function(e){t.$element[0]!==e.target&&!t.$element.has(e.target).length&&t.$element.focus()})},escape:function(){var e=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(t){t.which==27&&e.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var t=this,n=setTimeout(function(){t.$element.off(e.support.transition.end),t.hideModal()},500);this.$element.one(e.support.transition.end,function(){clearTimeout(n),t.hideModal()})},hideModal:function(){var e=this;this.$element.hide(),this.backdrop(function(){e.removeBackdrop(),e.$element.trigger("hidden")})},removeBackdrop:function(){this.$backdrop.remove(),this.$backdrop=null},backdrop:function(t){var n=this,r=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var i=e.support.transition&&r;this.$backdrop=e('<div class="modal-backdrop '+r+'" />').appendTo(document.body),this.$backdrop.click(this.options.backdrop=="static"?e.proxy(this.$element[0].focus,this.$element[0]):e.proxy(this.hide,this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in");if(!t)return;i?this.$backdrop.one(e.support.transition.end,t):t()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),e.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(e.support.transition.end,t):t()):t&&t()}};var n=e.fn.modal;e.fn.modal=function(n){return this.each(function(){var r=e(this),i=r.data("modal"),s=e.extend({},e.fn.modal.defaults,r.data(),typeof n=="object"&&n);i||r.data("modal",i=new t(this,s)),typeof n=="string"?i[n]():s.show&&i.show()})},e.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},e.fn.modal.Constructor=t,e.fn.modal.noConflict=function(){return e.fn.modal=n,this},e(document).on("click.modal.data-api",'[data-toggle="modal"]',function(t){var n=e(this),r=n.attr("href"),i=e(n.attr("data-target")||r&&r.replace(/.*(?=#[^\s]+$)/,"")),s=i.data("modal")?"toggle":e.extend({remote:!/#/.test(r)&&r},i.data(),n.data());t.preventDefault(),i.modal(s).one("hide",function(){n.focus()})})}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("tooltip",e,t)};t.prototype={constructor:t,init:function(t,n,r){var i,s,o,u,a;this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.enabled=!0,o=this.options.trigger.split(" ");for(a=o.length;a--;)u=o[a],u=="click"?this.$element.on("click."+this.type,this.options.selector,e.proxy(this.toggle,this)):u!="manual"&&(i=u=="hover"?"mouseenter":"focus",s=u=="hover"?"mouseleave":"blur",this.$element.on(i+"."+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(s+"."+this.type,this.options.selector,e.proxy(this.leave,this)));this.options.selector?this._options=e.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(t){return t=e.extend({},e.fn[this.type].defaults,this.$element.data(),t),t.delay&&typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),t},enter:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);if(!n.options.delay||!n.options.delay.show)return n.show();clearTimeout(this.timeout),n.hoverState="in",this.timeout=setTimeout(function(){n.hoverState=="in"&&n.show()},n.options.delay.show)},leave:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!n.options.delay||!n.options.delay.hide)return n.hide();n.hoverState="out",this.timeout=setTimeout(function(){n.hoverState=="out"&&n.hide()},n.options.delay.hide)},show:function(){var t,n,r,i,s,o,u=e.Event("show");if(this.hasContent()&&this.enabled){this.$element.trigger(u);if(u.isDefaultPrevented())return;t=this.tip(),this.setContent(),this.options.animation&&t.addClass("fade"),s=typeof this.options.placement=="function"?this.options.placement.call(this,t[0],this.$element[0]):this.options.placement,t.detach().css({top:0,left:0,display:"block"}),this.options.container?t.appendTo(this.options.container):t.insertAfter(this.$element),n=this.getPosition(),r=t[0].offsetWidth,i=t[0].offsetHeight;switch(s){case"bottom":o={top:n.top+n.height,left:n.left+n.width/2-r/2};break;case"top":o={top:n.top-i,left:n.left+n.width/2-r/2};break;case"left":o={top:n.top+n.height/2-i/2,left:n.left-r};break;case"right":o={top:n.top+n.height/2-i/2,left:n.left+n.width}}this.applyPlacement(o,s),this.$element.trigger("shown")}},applyPlacement:function(e,t){var n=this.tip(),r=n[0].offsetWidth,i=n[0].offsetHeight,s,o,u,a;n.offset(e).addClass(t).addClass("in"),s=n[0].offsetWidth,o=n[0].offsetHeight,t=="top"&&o!=i&&(e.top=e.top+i-o,a=!0),t=="bottom"||t=="top"?(u=0,e.left<0&&(u=e.left*-2,e.left=0,n.offset(e),s=n[0].offsetWidth,o=n[0].offsetHeight),this.replaceArrow(u-r+s,s,"left")):this.replaceArrow(o-i,o,"top"),a&&n.offset(e)},replaceArrow:function(e,t,n){this.arrow().css(n,e?50*(1-e/t)+"%":"")},setContent:function(){var e=this.tip(),t=this.getTitle();e.find(".tooltip-inner")[this.options.html?"html":"text"](t),e.removeClass("fade in top bottom left right")},hide:function(){function i(){var t=setTimeout(function(){n.off(e.support.transition.end).detach()},500);n.one(e.support.transition.end,function(){clearTimeout(t),n.detach()})}var t=this,n=this.tip(),r=e.Event("hide");this.$element.trigger(r);if(r.isDefaultPrevented())return;return n.removeClass("in"),e.support.transition&&this.$tip.hasClass("fade")?i():n.detach(),this.$element.trigger("hidden"),this},fixTitle:function(){var e=this.$element;(e.attr("title")||typeof e.attr("data-original-title")!="string")&&e.attr("data-original-title",e.attr("title")||"").attr("title","")},hasContent:function(){return this.getTitle()},getPosition:function(){var t=this.$element[0];return e.extend({},typeof t.getBoundingClientRect=="function"?t.getBoundingClientRect():{width:t.offsetWidth,height:t.offsetHeight},this.$element.offset())},getTitle:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-original-title")||(typeof n.title=="function"?n.title.call(t[0]):n.title),e},tip:function(){return this.$tip=this.$tip||e(this.options.template)},arrow:function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(t){var n=t?e(t.currentTarget)[this.type](this._options).data(this.type):this;n.tip().hasClass("in")?n.hide():n.show()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}};var n=e.fn.tooltip;e.fn.tooltip=function(n){return this.each(function(){var r=e(this),i=r.data("tooltip"),s=typeof n=="object"&&n;i||r.data("tooltip",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.tooltip.Constructor=t,e.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},e.fn.tooltip.noConflict=function(){return e.fn.tooltip=n,this}}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("popover",e,t)};t.prototype=e.extend({},e.fn.tooltip.Constructor.prototype,{constructor:t,setContent:function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(".popover-title")[this.options.html?"html":"text"](t),e.find(".popover-content")[this.options.html?"html":"text"](n),e.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var e,t=this.$element,n=this.options;return e=(typeof n.content=="function"?n.content.call(t[0]):n.content)||t.attr("data-content"),e},tip:function(){return this.$tip||(this.$tip=e(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}});var n=e.fn.popover;e.fn.popover=function(n){return this.each(function(){var r=e(this),i=r.data("popover"),s=typeof n=="object"&&n;i||r.data("popover",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.popover.Constructor=t,e.fn.popover.defaults=e.extend({},e.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:'<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),e.fn.popover.noConflict=function(){return e.fn.popover=n,this}}(window.jQuery),!function(e){"use strict";function t(t,n){var r=e.proxy(this.process,this),i=e(t).is("body")?e(window):e(t),s;this.options=e.extend({},e.fn.scrollspy.defaults,n),this.$scrollElement=i.on("scroll.scroll-spy.data-api",r),this.selector=(this.options.target||(s=e(t).attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=e("body"),this.refresh(),this.process()}t.prototype={constructor:t,refresh:function(){var t=this,n;this.offsets=e([]),this.targets=e([]),n=this.$body.find(this.selector).map(function(){var n=e(this),r=n.data("target")||n.attr("href"),i=/^#\w/.test(r)&&e(r);return i&&i.length&&[[i.position().top+(!e.isWindow(t.$scrollElement.get(0))&&t.$scrollElement.scrollTop()),r]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},process:function(){var e=this.$scrollElement.scrollTop()+this.options.offset,t=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=t-this.$scrollElement.height(),r=this.offsets,i=this.targets,s=this.activeTarget,o;if(e>=n)return s!=(o=i.last()[0])&&this.activate(o);for(o=r.length;o--;)s!=i[o]&&e>=r[o]&&(!r[o+1]||e<=r[o+1])&&this.activate(i[o])},activate:function(t){var n,r;this.activeTarget=t,e(this.selector).parent(".active").removeClass("active"),r=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=e(r).parent("li").addClass("active"),n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate")}};var n=e.fn.scrollspy;e.fn.scrollspy=function(n){return this.each(function(){var r=e(this),i=r.data("scrollspy"),s=typeof n=="object"&&n;i||r.data("scrollspy",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.scrollspy.Constructor=t,e.fn.scrollspy.defaults={offset:10},e.fn.scrollspy.noConflict=function(){return e.fn.scrollspy=n,this},e(window).on("load",function(){e('[data-spy="scroll"]').each(function(){var t=e(this);t.scrollspy(t.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t){this.element=e(t)};t.prototype={constructor:t,show:function(){var t=this.element,n=t.closest("ul:not(.dropdown-menu)"),r=t.attr("data-target"),i,s,o;r||(r=t.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,""));if(t.parent("li").hasClass("active"))return;i=n.find(".active:last a")[0],o=e.Event("show",{relatedTarget:i}),t.trigger(o);if(o.isDefaultPrevented())return;s=e(r),this.activate(t.parent("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"shown",relatedTarget:i})})},activate:function(t,n,r){function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),r&&r()}var i=n.find("> .active"),s=r&&e.support.transition&&i.hasClass("fade");s?i.one(e.support.transition.end,o):o(),i.removeClass("in")}};var n=e.fn.tab;e.fn.tab=function(n){return this.each(function(){var r=e(this),i=r.data("tab");i||r.data("tab",i=new t(this)),typeof n=="string"&&i[n]()})},e.fn.tab.Constructor=t,e.fn.tab.noConflict=function(){return e.fn.tab=n,this},e(document).on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(t){t.preventDefault(),e(this).tab("show")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.typeahead.defaults,n),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.source=this.options.source,this.$menu=e(this.options.menu),this.shown=!1,this.listen()};t.prototype={constructor:t,select:function(){var e=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(e)).change(),this.hide()},updater:function(e){return e},show:function(){var t=e.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});return this.$menu.insertAfter(this.$element).css({top:t.top+t.height,left:t.left}).show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(t){var n;return this.query=this.$element.val(),!this.query||this.query.length<this.options.minLength?this.shown?this.hide():this:(n=e.isFunction(this.source)?this.source(this.query,e.proxy(this.process,this)):this.source,n?this.process(n):this)},process:function(t){var n=this;return t=e.grep(t,function(e){return n.matcher(e)}),t=this.sorter(t),t.length?this.render(t.slice(0,this.options.items)).show():this.shown?this.hide():this},matcher:function(e){return~e.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(e){var t=[],n=[],r=[],i;while(i=e.shift())i.toLowerCase().indexOf(this.query.toLowerCase())?~i.indexOf(this.query)?n.push(i):r.push(i):t.push(i);return t.concat(n,r)},highlighter:function(e){var t=this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&");return e.replace(new RegExp("("+t+")","ig"),function(e,t){return"<strong>"+t+"</strong>"})},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),t.first().addClass("active"),this.$menu.html(t),this},next:function(t){var n=this.$menu.find(".active").removeClass("active"),r=n.next();r.length||(r=e(this.$menu.find("li")[0])),r.addClass("active")},prev:function(e){var t=this.$menu.find(".active").removeClass("active"),n=t.prev();n.length||(n=this.$menu.find("li").last()),n.addClass("active")},listen:function(){this.$element.on("focus",e.proxy(this.focus,this)).on("blur",e.proxy(this.blur,this)).on("keypress",e.proxy(this.keypress,this)).on("keyup",e.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown",e.proxy(this.keydown,this)),this.$menu.on("click",e.proxy(this.click,this)).on("mouseenter","li",e.proxy(this.mouseenter,this)).on("mouseleave","li",e.proxy(this.mouseleave,this))},eventSupported:function(e){var t=e in this.$element;return t||(this.$element.setAttribute(e,"return;"),t=typeof this.$element[e]=="function"),t},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault(),this.prev();break;case 40:e.preventDefault(),this.next()}e.stopPropagation()},keydown:function(t){this.suppressKeyPressRepeat=~e.inArray(t.keyCode,[40,38,9,13,27]),this.move(t)},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},focus:function(e){this.focused=!0},blur:function(e){this.focused=!1,!this.mousedover&&this.shown&&this.hide()},click:function(e){e.stopPropagation(),e.preventDefault(),this.select(),this.$element.focus()},mouseenter:function(t){this.mousedover=!0,this.$menu.find(".active").removeClass("active"),e(t.currentTarget).addClass("active")},mouseleave:function(e){this.mousedover=!1,!this.focused&&this.shown&&this.hide()}};var n=e.fn.typeahead;e.fn.typeahead=function(n){return this.each(function(){var r=e(this),i=r.data("typeahead"),s=typeof n=="object"&&n;i||r.data("typeahead",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu"></ul>',item:'<li><a href="#"></a></li>',minLength:1},e.fn.typeahead.Constructor=t,e.fn.typeahead.noConflict=function(){return e.fn.typeahead=n,this},e(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(t){var n=e(this);if(n.data("typeahead"))return;n.typeahead(n.data())})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.affix.defaults,n),this.$window=e(window).on("scroll.affix.data-api",e.proxy(this.checkPosition,this)).on("click.affix.data-api",e.proxy(function(){setTimeout(e.proxy(this.checkPosition,this),1)},this)),this.$element=e(t),this.checkPosition()};t.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var t=e(document).height(),n=this.$window.scrollTop(),r=this.$element.offset(),i=this.options.offset,s=i.bottom,o=i.top,u="affix affix-top affix-bottom",a;typeof i!="object"&&(s=o=i),typeof o=="function"&&(o=i.top()),typeof s=="function"&&(s=i.bottom()),a=this.unpin!=null&&n+this.unpin<=r.top?!1:s!=null&&r.top+this.$element.height()>=t-s?"bottom":o!=null&&n<=o?"top":!1;if(this.affixed===a)return;this.affixed=a,this.unpin=a=="bottom"?r.top-n:null,this.$element.removeClass(u).addClass("affix"+(a?"-"+a:""))};var n=e.fn.affix;e.fn.affix=function(n){return this.each(function(){var r=e(this),i=r.data("affix"),s=typeof n=="object"&&n;i||r.data("affix",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.affix.Constructor=t,e.fn.affix.defaults={offset:0},e.fn.affix.noConflict=function(){return e.fn.affix=n,this},e(window).on("load",function(){e('[data-spy="affix"]').each(function(){var t=e(this),n=t.data();n.offset=n.offset||{},n.offsetBottom&&(n.offset.bottom=n.offsetBottom),n.offsetTop&&(n.offset.top=n.offsetTop),t.affix(n)})})}(window.jQuery);
\ No newline at end of file
diff --git a/server/src/main/webapp/javascripts/html5.js b/server/src/main/webapp/javascripts/html5.js
new file mode 100644
index 0000000..087417a
--- /dev/null
+++ b/server/src/main/webapp/javascripts/html5.js
@@ -0,0 +1,9 @@
+/*
+ HTML5 Shiv v3.6.2pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
+ Uncompressed source: https://github.com/aFarkas/html5shiv
+*/
+(function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag();
+a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/\w+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x<style>article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}</style>";
+c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="<xyz></xyz>";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode||
+"undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup main mark meter nav output progress section summary time video",version:"3.6.2pre",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);if(g)return a.createDocumentFragment();
+for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d<h;d++)c.createElement(e[d]);return c}};l.html5=e;q(f)})(this,document);
\ No newline at end of file
diff --git a/server/src/main/webapp/javascripts/jquery.min.js b/server/src/main/webapp/javascripts/jquery.min.js
new file mode 100644
index 0000000..198b3ff
--- /dev/null
+++ b/server/src/main/webapp/javascripts/jquery.min.js
@@ -0,0 +1,4 @@
+/*! jQuery v1.7.1 jquery.com | jquery.org/license */
+(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!ck[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cl||(cl=c.createElement("iframe"),cl.frameBorder=cl.width=cl.height=0),b.appendChild(cl);if(!cm||!cl.createElement)cm=(cl.contentWindow||cl.contentDocument).document,cm.write((c.compatMode==="CSS1Compat"?"<!doctype html>":"")+"<html><body>"),cm.close();d=cm.createElement(a),cm.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cl)}ck[a]=e}return ck[a]}function cu(a,b){var c={};f.each(cq.concat.apply([],cq.slice(0,b)),function(){c[this]=a});return c}function ct(){cr=b}function cs(){setTimeout(ct,0);return cr=f.now()}function cj(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ci(){try{return new a.XMLHttpRequest}catch(b){}}function cc(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function cb(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function ca(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bE.test(a)?d(a,e):ca(a+"["+(typeof e=="object"||f.isArray(e)?b:"")+"]",e,c,d)});else if(!c&&b!=null&&typeof b=="object")for(var e in b)ca(a+"["+e+"]",b[e],c,d);else d(a,b)}function b_(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function b$(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bT,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=b$(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=b$(a,c,d,e,"*",g));return l}function bZ(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bP),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bC(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?bx:by,g=0,h=e.length;if(d>0){if(c!=="border")for(;g<h;g++)c||(d-=parseFloat(f.css(a,"padding"+e[g]))||0),c==="margin"?d+=parseFloat(f.css(a,c+e[g]))||0:d-=parseFloat(f.css(a,"border"+e[g]+"Width"))||0;return d+"px"}d=bz(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0;if(c)for(;g<h;g++)d+=parseFloat(f.css(a,"padding"+e[g]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+e[g]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+e[g]))||0);return d+"px"}function bp(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c+(i[c][d].namespace?".":"")+i[c][d].namespace,i[c][d],i[c][d].data)}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h){var i=a.length;if(typeof c=="object"){for(var j in c)e.access(a,j,c[j],f,g,d);return a}if(d!==b){f=!h&&f&&e.isFunction(d);for(var k=0;k<i;k++)g(a[k],c,f?d.call(a[k],k,g(a[k],c)):d,h);return a}return i?g(a[0],c):b},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?m(g):h==="function"&&(!a.unique||!o.has(g))&&c.push(g)},n=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,l=j||0,j=0,k=c.length;for(;c&&l<k;l++)if(c[l].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}i=!1,c&&(a.once?e===!0?o.disable():c=[]:d&&d.length&&(e=d.shift(),o.fireWith(e[0],e[1])))},o={add:function(){if(c){var a=c.length;m(arguments),i?k=c.length:e&&e!==!0&&(j=a,n(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){i&&f<=k&&(k--,f<=l&&l--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&o.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(i?a.once||d.push([b,c]):(!a.once||!e)&&n(b,c));return this},fire:function(){o.fireWith(this,arguments);return this},fired:function(){return!!e}};return o};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p,q=c.createElement("div"),r=c.documentElement;q.setAttribute("className","t"),q.innerHTML="   <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=q.getElementsByTagName("*"),e=q.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=q.getElementsByTagName("input")[0],b={leadingWhitespace:q.firstChild.nodeType===3,tbody:!q.getElementsByTagName("tbody").length,htmlSerialize:!!q.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:q.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete q.test}catch(s){b.deleteExpando=!1}!q.addEventListener&&q.attachEvent&&q.fireEvent&&(q.attachEvent("onclick",function(){b.noCloneEvent=!1}),q.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),q.appendChild(i),k=c.createDocumentFragment(),k.appendChild(q.lastChild),b.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,k.removeChild(i),k.appendChild(q),q.innerHTML="",a.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",q.style.width="2px",q.appendChild(j),b.reliableMarginRight=(parseInt((a.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(q.attachEvent)for(o in{submit:1,change:1,focusin:1})n="on"+o,p=n in q,p||(q.setAttribute(n,"return;"),p=typeof q[n]=="function"),b[o+"Bubbles"]=p;k.removeChild(q),k=g=h=j=q=i=null,f(function(){var a,d,e,g,h,i,j,k,m,n,o,r=c.getElementsByTagName("body")[0];!r||(j=1,k="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",m="visibility:hidden;border:0;",n="style='"+k+"border:5px solid #000;padding:0;'",o="<div "+n+"><div></div></div>"+"<table "+n+" cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",a=c.createElement("div"),a.style.cssText=m+"width:0;height:0;position:static;top:0;margin-top:"+j+"px",r.insertBefore(a,r.firstChild),q=c.createElement("div"),a.appendChild(q),q.innerHTML="<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>",l=q.getElementsByTagName("td"),p=l[0].offsetHeight===0,l[0].style.display="",l[1].style.display="none",b.reliableHiddenOffsets=p&&l[0].offsetHeight===0,q.innerHTML="",q.style.width=q.style.paddingLeft="1px",f.boxModel=b.boxModel=q.offsetWidth===2,typeof q.style.zoom!="undefined"&&(q.style.display="inline",q.style.zoom=1,b.inlineBlockNeedsLayout=q.offsetWidth===2,q.style.display="",q.innerHTML="<div style='width:4px;'></div>",b.shrinkWrapBlocks=q.offsetWidth!==2),q.style.cssText=k+m,q.innerHTML=o,d=q.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,i={doesNotAddBorder:e.offsetTop!==5,doesAddBorderForTableAndCells:h.offsetTop===5},e.style.position="fixed",e.style.top="20px",i.fixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",i.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,i.doesNotIncludeMarginInBodyOffset=r.offsetTop!==j,r.removeChild(a),q=a=null,f.extend(b,i))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h=null;if(typeof a=="undefined"){if(this.length){h=f.data(this[0]);if(this[0].nodeType===1&&!f._data(this[0],"parsedAttrs")){e=this[0].attributes;for(var i=0,j=e.length;i<j;i++)g=e[i].name,g.indexOf("data-")===0&&(g=f.camelCase(g.substring(5)),l(this[0],g,h[g]));f._data(this[0],"parsedAttrs",!0)}}return h}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split("."),d[1]=d[1]?"."+d[1]:"";if(c===b){h=this.triggerHandler("getData"+d[1]+"!",[d[0]]),h===b&&this.length&&(h=f.data(this[0],a),h=l(this[0],a,h));return h===b&&d[1]?this.data(d[0]):h}return this.each(function(){var b=f(this),e=[d[0],c];b.triggerHandler("setData"+d[1]+"!",e),f.data(this,a,c),b.triggerHandler("changeData"+d[1]+"!",e)})},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){typeof a!="string"&&(c=a,a="fx");if(c===b)return f.queue(this[0],a);return this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise()}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,a,b,!0,f.attr)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,a,b,!0,f.prop)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;h<g;h++)e=d[h],e&&(c=f.propFix[e]||e,f.attr(a,e,""),a.removeAttribute(v?e:c),u.test(e)&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/\bhover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};
+f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=[],j,k,l,m,n,o,p,q,r,s,t;g[0]=c,c.delegateTarget=this;if(e&&!c.target.disabled&&(!c.button||c.type!=="click")){m=f(this),m.context=this.ownerDocument||this;for(l=c.target;l!=this;l=l.parentNode||this){o={},q=[],m[0]=l;for(j=0;j<e;j++)r=d[j],s=r.selector,o[s]===b&&(o[s]=r.quick?H(l,r.quick):m.is(s)),o[s]&&q.push(r);q.length&&i.push({elem:l,matches:q})}}d.length>e&&i.push({elem:this,matches:d.slice(e)});for(j=0;j<i.length&&!c.isPropagationStopped();j++){p=i[j],c.currentTarget=p.elem;for(k=0;k<p.matches.length&&!c.isImmediatePropagationStopped();k++){r=p.matches[k];if(h||!c.namespace&&!r.namespace||c.namespace_re&&c.namespace_re.test(r.namespace))c.data=r.data,c.handleObj=r,n=((f.event.special[r.origType]||{}).handle||r.handler).apply(p.elem,g),n!==b&&(c.result=n,n===!1&&(c.preventDefault(),c.stopPropagation()))}}return c.result},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0)}),d._submit_attached=!0)})},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on.call(this,a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.type+"."+e.namespace:e.type,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.POS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function()
+{for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(var c=0,d=this.length;c<d;c++)this[c].nodeType===1&&(f.cleanData(this[c].getElementsByTagName("*")),this[c].innerHTML=a)}catch(e){this.empty().append(a)}}else f.isFunction(a)?this.each(function(b){var c=f(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,bp)}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||!bc.test("<"+a.nodeName)?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1></$2>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");b===c?bh.appendChild(o):U(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]==="<table>"&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i<r;i++)bn(k[i]);else bn(k);k.nodeType?h.push(k):h=f.merge(h,k)}if(d){g=function(a){return!a.type||be.test(a.type)};for(j=0;h[j];j++)if(e&&f.nodeName(h[j],"script")&&(!h[j].type||h[j].type.toLowerCase()==="text/javascript"))e.push(h[j].parentNode?h[j].parentNode.removeChild(h[j]):h[j]);else{if(h[j].nodeType===1){var s=f.grep(h[j].getElementsByTagName("script"),g);h.splice.apply(h,[j+1,0].concat(s))}d.appendChild(h[j])}}return h},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bq=/alpha\([^)]*\)/i,br=/opacity=([^)]*)/,bs=/([A-Z]|^ms)/g,bt=/^-?\d+(?:px)?$/i,bu=/^-?\d/,bv=/^([\-+])=([\-+.\de]+)/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Left","Right"],by=["Top","Bottom"],bz,bA,bB;f.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return f.access(this,a,c,!0,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)})},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bz(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bv.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(bz)return bz(a,c)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]}}),f.curCSS=f.css,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){var e;if(c){if(a.offsetWidth!==0)return bC(a,b,d);f.swap(a,bw,function(){e=bC(a,b,d)});return e}},set:function(a,b){if(!bt.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return br.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bq,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bq.test(g)?g.replace(bq,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,b){var c,d,e;b=b.replace(bs,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b)));return c}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bt.test(f)&&bu.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bD=/%20/g,bE=/\[\]$/,bF=/\r?\n/g,bG=/#.*$/,bH=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bI=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bJ=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bK=/^(?:GET|HEAD)$/,bL=/^\/\//,bM=/\?/,bN=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bO=/^(?:select|textarea)/i,bP=/\s+/,bQ=/([?&])_=[^&]*/,bR=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bS=f.fn.load,bT={},bU={},bV,bW,bX=["*/"]+["*"];try{bV=e.href}catch(bY){bV=c.createElement("a"),bV.href="",bV=bV.href}bW=bR.exec(bV.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bS)return bS.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bN,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bO.test(this.nodeName)||bI.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bF,"\r\n")}}):{name:b.name,value:c.replace(bF,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b_(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b_(a,b);return a},ajaxSettings:{url:bV,isLocal:bJ.test(bW[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bX},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bZ(bT),ajaxTransport:bZ(bU),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cb(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cc(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bH.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bG,"").replace(bL,bW[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bP),d.crossDomain==null&&(r=bR.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bW[1]&&r[2]==bW[2]&&(r[3]||(r[1]==="http:"?80:443))==(bW[3]||(bW[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bT,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bK.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bM.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bQ,"$1_="+x);d.url=y+(y===d.url?(bM.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bX+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bU,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)ca(g,a[g],c,e);return d.join("&").replace(bD,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cd=f.now(),ce=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cd++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ce.test(b.url)||e&&ce.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ce,l),b.url===j&&(e&&(k=k.replace(ce,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cf=a.ActiveXObject?function(){for(var a in ch)ch[a](0,1)}:!1,cg=0,ch;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ci()||cj()}:ci,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cf&&delete ch[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cg,cf&&(ch||(ch={},f(a).unload(cf)),ch[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var ck={},cl,cm,cn=/^(?:toggle|show|hide)$/,co=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cp,cq=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cr;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),e===""&&f.css(d,"display")==="none"&&f._data(d,"olddisplay",cv(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(cu("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(cu("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]),h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cv(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cn.test(h)?(o=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),o?(f._data(this,"toggle"+i,o==="show"?"hide":"show"),j[o]()):j[h]()):(k=co.exec(h),l=j.cur(),k?(m=parseFloat(k[2]),n=k[3]||(f.cssNumber[i]?"":"px"),n!=="px"&&(f.style(this,i,(m||1)+n),l=(m||1)/j.cur()*l,f.style(this,i,l+n)),k[1]&&(m=(k[1]==="-="?-1:1)*m+l),j.custom(l,m,n)):j.custom(l,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:cu("show",1),slideUp:cu("hide",1),slideToggle:cu("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cr||cs(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){e.options.hide&&f._data(e.elem,"fxshow"+e.prop)===b&&f._data(e.elem,"fxshow"+e.prop,e.start)},h()&&f.timers.push(h)&&!cp&&(cp=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cr||cs(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(cp),cp=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(["width","height"],function(a,b){f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)}}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?f.fn.offset=function(a){var b=this[0],c;if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);try{c=b.getBoundingClientRect()}catch(d){}var e=b.ownerDocument,g=e.documentElement;if(!c||!f.contains(g,b))return c?{top:c.top,left:c.left}:{top:0,left:0};var h=e.body,i=cy(e),j=g.clientTop||h.clientTop||0,k=g.clientLeft||h.clientLeft||0,l=i.pageYOffset||f.support.boxModel&&g.scrollTop||h.scrollTop,m=i.pageXOffset||f.support.boxModel&&g.scrollLeft||h.scrollLeft,n=c.top+l-j,o=c.left+m-k;return{top:n,left:o}}:f.fn.offset=function(a){var b=this[0];if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);var c,d=b.offsetParent,e=b,g=b.ownerDocument,h=g.documentElement,i=g.body,j=g.defaultView,k=j?j.getComputedStyle(b,null):b.currentStyle,l=b.offsetTop,m=b.offsetLeft;while((b=b.parentNode)&&b!==i&&b!==h){if(f.support.fixedPosition&&k.position==="fixed")break;c=j?j.getComputedStyle(b,null):b.currentStyle,l-=b.scrollTop,m-=b.scrollLeft,b===d&&(l+=b.offsetTop,m+=b.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(b.nodeName))&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),e=d,d=b.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&c.overflow!=="visible"&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),k=c}if(k.position==="relative"||k.position==="static")l+=i.offsetTop,m+=i.offsetLeft;f.support.fixedPosition&&k.position==="fixed"&&(l+=Math.max(h.scrollTop,i.scrollTop),m+=Math.max(h.scrollLeft,i.scrollLeft));return{top:l,left:m}},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window);
\ No newline at end of file
diff --git a/server/src/main/webapp/stylesheets/bootstrap.min.css b/server/src/main/webapp/stylesheets/bootstrap.min.css
new file mode 100644
index 0000000..fd5ed73
--- /dev/null
+++ b/server/src/main/webapp/stylesheets/bootstrap.min.css
@@ -0,0 +1,9 @@
+/*!
+ * Bootstrap v2.3.0
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{width:auto\9;height:auto;max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img,.google-maps img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}label,select,button,input[type="button"],input[type="reset"],input[type="submit"],input[type="radio"],input[type="checkbox"]{cursor:pointer}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}@media print{*{color:#000!important;text-shadow:none!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#333;background-color:#fff}a{color:#08c;text-decoration:none}a:hover,a:focus{color:#005580;text-decoration:underline}.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px}.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.span12{width:940px}.span11{width:860px}.span10{width:780px}.span9{width:700px}.span8{width:620px}.span7{width:540px}.span6{width:460px}.span5{width:380px}.span4{width:300px}.span3{width:220px}.span2{width:140px}.span1{width:60px}.offset12{margin-left:980px}.offset11{margin-left:900px}.offset10{margin-left:820px}.offset9{margin-left:740px}.offset8{margin-left:660px}.offset7{margin-left:580px}.offset6{margin-left:500px}.offset5{margin-left:420px}.offset4{margin-left:340px}.offset3{margin-left:260px}.offset2{margin-left:180px}.offset1{margin-left:100px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.127659574468085%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%}.row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%}.row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%}.row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%}.row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%}.row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%}.row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%}.row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%}.row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%}.row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%}.row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%}.row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%}.row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%}.row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%}.row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%}.row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%}.row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%}.row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%}.row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%}.row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%}.row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%}.row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%}.row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%}.row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%}.row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%}.row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%}.row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%}.row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%}.row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%}.row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%}.row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%}.row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%}.row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%}.row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%}.row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%}[class*="span"].hide,.row-fluid [class*="span"].hide{display:none}[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;line-height:0;content:""}.container:after{clear:both}.container-fluid{padding-right:20px;padding-left:20px;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;line-height:0;content:""}.container-fluid:after{clear:both}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:21px;font-weight:200;line-height:30px}small{font-size:85%}strong{font-weight:bold}em{font-style:italic}cite{font-style:normal}.muted{color:#999}a.muted:hover,a.muted:focus{color:#808080}.text-warning{color:#c09853}a.text-warning:hover,a.text-warning:focus{color:#a47e3c}.text-error{color:#b94a48}a.text-error:hover,a.text-error:focus{color:#953b39}.text-info{color:#3a87ad}a.text-info:hover,a.text-info:focus{color:#2d6987}.text-success{color:#468847}a.text-success:hover,a.text-success:focus{color:#356635}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}h1,h2,h3,h4,h5,h6{margin:10px 0;font-family:inherit;font-weight:bold;line-height:20px;color:inherit;text-rendering:optimizelegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#999}h1,h2,h3{line-height:40px}h1{font-size:38.5px}h2{font-size:31.5px}h3{font-size:24.5px}h4{font-size:17.5px}h5{font-size:14px}h6{font-size:11.9px}h1 small{font-size:24.5px}h2 small{font-size:17.5px}h3 small{font-size:14px}h4 small{font-size:14px}.page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #eee}ul,ol{padding:0;margin:0 0 10px 25px}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}li{line-height:20px}ul.unstyled,ol.unstyled{margin-left:0;list-style:none}ul.inline,ol.inline{margin-left:0;list-style:none}ul.inline>li,ol.inline>li{display:inline-block;*display:inline;padding-right:5px;padding-left:5px;*zoom:1}dl{margin-bottom:20px}dt,dd{line-height:20px}dt{font-weight:bold}dd{margin-left:10px}.dl-horizontal{*zoom:1}.dl-horizontal:before,.dl-horizontal:after{display:table;line-height:0;content:""}.dl-horizontal:after{clear:both}.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}hr{margin:20px 0;border:0;border-top:1px solid #eee;border-bottom:1px solid #fff}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #eee}blockquote p{margin-bottom:0;font-size:17.5px;font-weight:300;line-height:1.25}blockquote small{display:block;line-height:20px;color:#999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}blockquote.pull-right small:before{content:''}blockquote.pull-right small:after{content:'\00A0 \2014'}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:20px}code,pre{padding:0 3px 2px;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:12px;color:#333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}code{padding:2px 4px;color:#d14;white-space:nowrap;background-color:#f7f7f9;border:1px solid #e1e1e8}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}pre.prettyprint{margin-bottom:20px}pre code{padding:0;color:inherit;white-space:pre;white-space:pre-wrap;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}form{margin:0 0 20px}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:15px;color:#999}label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px}input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}label{display:block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:10px;font-size:14px;line-height:20px;color:#555;vertical-align:middle;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}input,textarea,.uneditable-input{width:206px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#fff;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82,168,236,0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;*margin-top:0;line-height:normal}input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px}select{width:220px;background-color:#fff;border:1px solid #ccc}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.uneditable-input,.uneditable-textarea{color:#999;cursor:not-allowed;background-color:#fcfcfc;border-color:#ccc;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}.uneditable-input{overflow:hidden;white-space:nowrap}.uneditable-textarea{width:auto;height:auto}input:-moz-placeholder,textarea:-moz-placeholder{color:#999}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#999}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#999}.radio,.checkbox{min-height:20px;padding-left:20px}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-20px}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:926px}input.span11,textarea.span11,.uneditable-input.span11{width:846px}input.span10,textarea.span10,.uneditable-input.span10{width:766px}input.span9,textarea.span9,.uneditable-input.span9{width:686px}input.span8,textarea.span8,.uneditable-input.span8{width:606px}input.span7,textarea.span7,.uneditable-input.span7{width:526px}input.span6,textarea.span6,.uneditable-input.span6{width:446px}input.span5,textarea.span5,.uneditable-input.span5{width:366px}input.span4,textarea.span4,.uneditable-input.span4{width:286px}input.span3,textarea.span3,.uneditable-input.span3{width:206px}input.span2,textarea.span2,.uneditable-input.span2{width:126px}input.span1,textarea.span1,.uneditable-input.span1{width:46px}.controls-row{*zoom:1}.controls-row:before,.controls-row:after{display:table;line-height:0;content:""}.controls-row:after{clear:both}.controls-row [class*="span"],.row-fluid .controls-row [class*="span"]{float:left}.controls-row .checkbox[class*="span"],.controls-row .radio[class*="span"]{padding-top:5px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eee}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.control-group.warning .control-label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853}.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853}.control-group.warning input,.control-group.warning select,.control-group.warning textarea{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.control-group.error .control-label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48}.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48}.control-group.error input,.control-group.error select,.control-group.error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.control-group.success .control-label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847}.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847}.control-group.success input,.control-group.success select,.control-group.success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}.control-group.info .control-label,.control-group.info .help-block,.control-group.info .help-inline{color:#3a87ad}.control-group.info .checkbox,.control-group.info .radio,.control-group.info input,.control-group.info select,.control-group.info textarea{color:#3a87ad}.control-group.info input,.control-group.info select,.control-group.info textarea{border-color:#3a87ad;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.info input:focus,.control-group.info select:focus,.control-group.info textarea:focus{border-color:#2d6987;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3}.control-group.info .input-prepend .add-on,.control-group.info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#b94a48;border-color:#ee5f5b}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;line-height:0;content:""}.form-actions:after{clear:both}.help-block,.help-inline{color:#595959}.help-block{display:block;margin-bottom:10px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-append,.input-prepend{display:inline-block;margin-bottom:10px;font-size:0;white-space:nowrap;vertical-align:middle}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input,.input-append .dropdown-menu,.input-prepend .dropdown-menu,.input-append .popover,.input-prepend .popover{font-size:14px}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:top;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2}.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #fff;background-color:#eee;border:1px solid #ccc}.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn,.input-append .btn-group>.dropdown-toggle,.input-prepend .btn-group>.dropdown-toggle{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input+.btn-group .btn:last-child,.input-append select+.btn-group .btn:last-child,.input-append .uneditable-input+.btn-group .btn:last-child{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append .add-on,.input-append .btn,.input-append .btn-group{margin-left:-1px}.input-append .add-on:last-child,.input-append .btn:last-child,.input-append .btn-group:last-child>.dropdown-toggle{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append input+.btn-group .btn,.input-prepend.input-append select+.btn-group .btn,.input-prepend.input-append .uneditable-input+.btn-group .btn{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .btn-group:first-child{margin-left:0}input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;vertical-align:middle;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:10px}legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:20px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;line-height:0;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:180px}.form-horizontal .help-block{margin-bottom:0}.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block,.form-horizontal .uneditable-input+.help-block,.form-horizontal .input-prepend+.help-block,.form-horizontal .input-append+.help-block{margin-top:10px}.form-horizontal .form-actions{padding-left:180px}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:20px}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #ddd}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #ddd}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child>th:first-child,.table-bordered tbody:first-child tr:first-child>td:first-child,.table-bordered tbody:first-child tr:first-child>th:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered thead:first-child tr:first-child>th:last-child,.table-bordered tbody:first-child tr:first-child>td:last-child,.table-bordered tbody:first-child tr:first-child>th:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-bordered thead:last-child tr:last-child>th:first-child,.table-bordered tbody:last-child tr:last-child>td:first-child,.table-bordered tbody:last-child tr:last-child>th:first-child,.table-bordered tfoot:last-child tr:last-child>td:first-child,.table-bordered tfoot:last-child tr:last-child>th:first-child{-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.table-bordered thead:last-child tr:last-child>th:last-child,.table-bordered tbody:last-child tr:last-child>td:last-child,.table-bordered tbody:last-child tr:last-child>th:last-child,.table-bordered tfoot:last-child tr:last-child>td:last-child,.table-bordered tfoot:last-child tr:last-child>th:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.table-bordered tfoot+tbody:last-child tr:last-child td:first-child{-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomleft:0}.table-bordered tfoot+tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomright:0}.table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-striped tbody>tr:nth-child(odd)>td,.table-striped tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover tbody tr:hover>td,.table-hover tbody tr:hover>th{background-color:#f5f5f5}table td[class*="span"],table th[class*="span"],.row-fluid table td[class*="span"],.row-fluid table th[class*="span"]{display:table-cell;float:none;margin-left:0}.table td.span1,.table th.span1{float:none;width:44px;margin-left:0}.table td.span2,.table th.span2{float:none;width:124px;margin-left:0}.table td.span3,.table th.span3{float:none;width:204px;margin-left:0}.table td.span4,.table th.span4{float:none;width:284px;margin-left:0}.table td.span5,.table th.span5{float:none;width:364px;margin-left:0}.table td.span6,.table th.span6{float:none;width:444px;margin-left:0}.table td.span7,.table th.span7{float:none;width:524px;margin-left:0}.table td.span8,.table th.span8{float:none;width:604px;margin-left:0}.table td.span9,.table th.span9{float:none;width:684px;margin-left:0}.table td.span10,.table th.span10{float:none;width:764px;margin-left:0}.table td.span11,.table th.span11{float:none;width:844px;margin-left:0}.table td.span12,.table th.span12{float:none;width:924px;margin-left:0}.table tbody tr.success>td{background-color:#dff0d8}.table tbody tr.error>td{background-color:#f2dede}.table tbody tr.warning>td{background-color:#fcf8e3}.table tbody tr.info>td{background-color:#d9edf7}.table-hover tbody tr.success:hover>td{background-color:#d0e9c6}.table-hover tbody tr.error:hover>td{background-color:#ebcccc}.table-hover tbody tr.warning:hover>td{background-color:#faf2cc}.table-hover tbody tr.info:hover>td{background-color:#c4e3f3}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;margin-top:1px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat}.icon-white,.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:focus>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>li>a:focus>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:focus>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"],.dropdown-submenu:focus>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png")}.icon-glass{background-position:0 0}.icon-music{background-position:-24px 0}.icon-search{background-position:-48px 0}.icon-envelope{background-position:-72px 0}.icon-heart{background-position:-96px 0}.icon-star{background-position:-120px 0}.icon-star-empty{background-position:-144px 0}.icon-user{background-position:-168px 0}.icon-film{background-position:-192px 0}.icon-th-large{background-position:-216px 0}.icon-th{background-position:-240px 0}.icon-th-list{background-position:-264px 0}.icon-ok{background-position:-288px 0}.icon-remove{background-position:-312px 0}.icon-zoom-in{background-position:-336px 0}.icon-zoom-out{background-position:-360px 0}.icon-off{background-position:-384px 0}.icon-signal{background-position:-408px 0}.icon-cog{background-position:-432px 0}.icon-trash{background-position:-456px 0}.icon-home{background-position:0 -24px}.icon-file{background-position:-24px -24px}.icon-time{background-position:-48px -24px}.icon-road{background-position:-72px -24px}.icon-download-alt{background-position:-96px -24px}.icon-download{background-position:-120px -24px}.icon-upload{background-position:-144px -24px}.icon-inbox{background-position:-168px -24px}.icon-play-circle{background-position:-192px -24px}.icon-repeat{background-position:-216px -24px}.icon-refresh{background-position:-240px -24px}.icon-list-alt{background-position:-264px -24px}.icon-lock{background-position:-287px -24px}.icon-flag{background-position:-312px -24px}.icon-headphones{background-position:-336px -24px}.icon-volume-off{background-position:-360px -24px}.icon-volume-down{background-position:-384px -24px}.icon-volume-up{background-position:-408px -24px}.icon-qrcode{background-position:-432px -24px}.icon-barcode{background-position:-456px -24px}.icon-tag{background-position:0 -48px}.icon-tags{background-position:-25px -48px}.icon-book{background-position:-48px -48px}.icon-bookmark{background-position:-72px -48px}.icon-print{background-position:-96px -48px}.icon-camera{background-position:-120px -48px}.icon-font{background-position:-144px -48px}.icon-bold{background-position:-167px -48px}.icon-italic{background-position:-192px -48px}.icon-text-height{background-position:-216px -48px}.icon-text-width{background-position:-240px -48px}.icon-align-left{background-position:-264px -48px}.icon-align-center{background-position:-288px -48px}.icon-align-right{background-position:-312px -48px}.icon-align-justify{background-position:-336px -48px}.icon-list{background-position:-360px -48px}.icon-indent-left{background-position:-384px -48px}.icon-indent-right{background-position:-408px -48px}.icon-facetime-video{background-position:-432px -48px}.icon-picture{background-position:-456px -48px}.icon-pencil{background-position:0 -72px}.icon-map-marker{background-position:-24px -72px}.icon-adjust{background-position:-48px -72px}.icon-tint{background-position:-72px -72px}.icon-edit{background-position:-96px -72px}.icon-share{background-position:-120px -72px}.icon-check{background-position:-144px -72px}.icon-move{background-position:-168px -72px}.icon-step-backward{background-position:-192px -72px}.icon-fast-backward{background-position:-216px -72px}.icon-backward{background-position:-240px -72px}.icon-play{background-position:-264px -72px}.icon-pause{background-position:-288px -72px}.icon-stop{background-position:-312px -72px}.icon-forward{background-position:-336px -72px}.icon-fast-forward{background-position:-360px -72px}.icon-step-forward{background-position:-384px -72px}.icon-eject{background-position:-408px -72px}.icon-chevron-left{background-position:-432px -72px}.icon-chevron-right{background-position:-456px -72px}.icon-plus-sign{background-position:0 -96px}.icon-minus-sign{background-position:-24px -96px}.icon-remove-sign{background-position:-48px -96px}.icon-ok-sign{background-position:-72px -96px}.icon-question-sign{background-position:-96px -96px}.icon-info-sign{background-position:-120px -96px}.icon-screenshot{background-position:-144px -96px}.icon-remove-circle{background-position:-168px -96px}.icon-ok-circle{background-position:-192px -96px}.icon-ban-circle{background-position:-216px -96px}.icon-arrow-left{background-position:-240px -96px}.icon-arrow-right{background-position:-264px -96px}.icon-arrow-up{background-position:-289px -96px}.icon-arrow-down{background-position:-312px -96px}.icon-share-alt{background-position:-336px -96px}.icon-resize-full{background-position:-360px -96px}.icon-resize-small{background-position:-384px -96px}.icon-plus{background-position:-408px -96px}.icon-minus{background-position:-433px -96px}.icon-asterisk{background-position:-456px -96px}.icon-exclamation-sign{background-position:0 -120px}.icon-gift{background-position:-24px -120px}.icon-leaf{background-position:-48px -120px}.icon-fire{background-position:-72px -120px}.icon-eye-open{background-position:-96px -120px}.icon-eye-close{background-position:-120px -120px}.icon-warning-sign{background-position:-144px -120px}.icon-plane{background-position:-168px -120px}.icon-calendar{background-position:-192px -120px}.icon-random{width:16px;background-position:-216px -120px}.icon-comment{background-position:-240px -120px}.icon-magnet{background-position:-264px -120px}.icon-chevron-up{background-position:-288px -120px}.icon-chevron-down{background-position:-313px -119px}.icon-retweet{background-position:-336px -120px}.icon-shopping-cart{background-position:-360px -120px}.icon-folder-close{width:16px;background-position:-384px -120px}.icon-folder-open{width:16px;background-position:-408px -120px}.icon-resize-vertical{background-position:-432px -119px}.icon-resize-horizontal{background-position:-456px -118px}.icon-hdd{background-position:0 -144px}.icon-bullhorn{background-position:-24px -144px}.icon-bell{background-position:-48px -144px}.icon-certificate{background-position:-72px -144px}.icon-thumbs-up{background-position:-96px -144px}.icon-thumbs-down{background-position:-120px -144px}.icon-hand-right{background-position:-144px -144px}.icon-hand-left{background-position:-168px -144px}.icon-hand-up{background-position:-192px -144px}.icon-hand-down{background-position:-216px -144px}.icon-circle-arrow-right{background-position:-240px -144px}.icon-circle-arrow-left{background-position:-264px -144px}.icon-circle-arrow-up{background-position:-288px -144px}.icon-circle-arrow-down{background-position:-312px -144px}.icon-globe{background-position:-336px -144px}.icon-wrench{background-position:-360px -144px}.icon-tasks{background-position:-384px -144px}.icon-filter{background-position:-408px -144px}.icon-briefcase{background-position:-432px -144px}.icon-fullscreen{background-position:-456px -144px}.dropup,.dropdown{position:relative}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.dropdown .caret{margin-top:8px;margin-left:2px}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus,.dropdown-submenu:hover>a,.dropdown-submenu:focus>a{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;outline:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:default;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open{*z-index:1000}.open>.dropdown-menu{display:block}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropup .dropdown-submenu>.dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-2px;-webkit-border-radius:5px 5px 5px 0;-moz-border-radius:5px 5px 5px 0;border-radius:5px 5px 5px 0}.dropdown-submenu>a:after{display:block;float:right;width:0;height:0;margin-top:5px;margin-right:-10px;border-color:transparent;border-left-color:#ccc;border-style:solid;border-width:5px 0 5px 5px;content:" "}.dropdown-submenu:hover>a:after{border-left-color:#fff}.dropdown-submenu.pull-left{float:none}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.dropdown .dropdown-menu .nav-header{padding-right:20px;padding-left:20px}.typeahead{z-index:1051;margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.btn{display:inline-block;*display:inline;padding:4px 12px;margin-bottom:0;*margin-left:.3em;font-size:14px;line-height:20px;color:#333;text-align:center;text-shadow:0 1px 1px rgba(255,255,255,0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border:1px solid #ccc;*border:0;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn:hover,.btn:focus,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn:active,.btn.active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:hover,.btn:focus{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class^="icon-"],.btn-large [class*=" icon-"]{margin-top:4px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-small [class^="icon-"],.btn-small [class*=" icon-"]{margin-top:0}.btn-mini [class^="icon-"],.btn-mini [class*=" icon-"]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-block{display:block;width:100%;padding-right:0;padding-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255,255,255,0.75)}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#006dcc;*background-color:#04c;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0044cc',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary:active,.btn-primary.active{background-color:#039 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#faa732;*background-color:#f89406;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning:active,.btn-warning.active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#da4f49;*background-color:#bd362f;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffbd362f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger:active,.btn-danger.active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#5bb75b;*background-color:#51a351;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff51a351',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success:active,.btn-success.active{background-color:#408140 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#49afcd;*background-color:#2f96b4;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2f96b4',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#363636;*background-color:#222;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444',endColorstr='#ff222222',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:focus,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{color:#08c;cursor:pointer;border-color:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:hover,.btn-link:focus{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,.btn-link[disabled]:focus{color:#333;text-decoration:none}.btn-group{position:relative;display:inline-block;*display:inline;*margin-left:.3em;font-size:0;white-space:nowrap;vertical-align:middle;*zoom:1}.btn-group:first-child{*margin-left:0}.btn-group+.btn-group{margin-left:5px}.btn-toolbar{margin-top:10px;margin-bottom:10px;font-size:0}.btn-toolbar>.btn+.btn,.btn-toolbar>.btn-group+.btn,.btn-toolbar>.btn+.btn-group{margin-left:5px}.btn-group>.btn{position:relative;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group>.btn+.btn{margin-left:-1px}.btn-group>.btn,.btn-group>.dropdown-menu,.btn-group>.popover{font-size:14px}.btn-group>.btn-mini{font-size:10.5px}.btn-group>.btn-small{font-size:11.9px}.btn-group>.btn-large{font-size:17.5px}.btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{*padding-top:5px;padding-right:8px;*padding-bottom:5px;padding-left:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn-group>.btn-mini+.dropdown-toggle{*padding-top:2px;padding-right:5px;*padding-bottom:2px;padding-left:5px}.btn-group>.btn-small+.dropdown-toggle{*padding-top:5px;*padding-bottom:4px}.btn-group>.btn-large+.dropdown-toggle{*padding-top:7px;padding-right:12px;*padding-bottom:7px;padding-left:12px}.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6}.btn-group.open .btn-primary.dropdown-toggle{background-color:#04c}.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406}.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f}.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351}.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}.btn .caret{margin-top:8px;margin-left:0}.btn-large .caret{margin-top:6px}.btn-large .caret{border-top-width:5px;border-right-width:5px;border-left-width:5px}.btn-mini .caret,.btn-small .caret{margin-top:8px}.dropup .btn-large .caret{border-bottom-width:5px}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff}.btn-group-vertical{display:inline-block;*display:inline;*zoom:1}.btn-group-vertical>.btn{display:block;float:none;max-width:100%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group-vertical>.btn+.btn{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.btn-group-vertical>.btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.btn-group-vertical>.btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0}.btn-group-vertical>.btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.alert{padding:8px 35px 8px 14px;margin-bottom:20px;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.alert,.alert h4{color:#c09853}.alert h4{margin:0}.alert .close{position:relative;top:-2px;right:-21px;line-height:20px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-success h4{color:#468847}.alert-danger,.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-danger h4,.alert-error h4{color:#b94a48}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-info h4{color:#3a87ad}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.nav{margin-bottom:20px;margin-left:0;list-style:none}.nav>li>a{display:block}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li>a>img{max-width:none}.nav>.pull-right{float:right}.nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#999;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.nav li+.nav-header{margin-top:9px}.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list>li>a{padding:3px 15px}.nav-list>.active>a,.nav-list>.active>a:hover,.nav-list>.active>a:focus{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#08c}.nav-list [class^="icon-"],.nav-list [class*=" icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;line-height:0;content:""}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs>li,.nav-pills>li{float:left}.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{margin-bottom:-1px}.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:20px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover,.nav-tabs>li>a:focus{border-color:#eee #eee #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover,.nav-tabs>.active>a:focus{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover,.nav-pills>.active>a:focus{color:#fff;background-color:#08c}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-topleft:4px}.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px}.nav-tabs.nav-stacked>li>a:hover,.nav-tabs.nav-stacked>li>a:focus{z-index:2;border-color:#ddd}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.nav .dropdown-toggle .caret{margin-top:6px;border-top-color:#08c;border-bottom-color:#08c}.nav .dropdown-toggle:hover .caret,.nav .dropdown-toggle:focus .caret{border-top-color:#005580;border-bottom-color:#005580}.nav-tabs .dropdown-toggle .caret{margin-top:8px}.nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.nav-tabs .active .dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.nav>.dropdown.active>a:hover,.nav>.dropdown.active>a:focus{cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover,.nav>li.dropdown.open.active>a:focus{color:#fff;background-color:#999;border-color:#999}.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret,.nav li.dropdown.open a:focus .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.tabs-stacked .open>a:hover,.tabs-stacked .open>a:focus{border-color:#999}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;line-height:0;content:""}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #ddd}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.tabs-below>.nav-tabs>li>a:hover,.tabs-below>.nav-tabs>li>a:focus{border-top-color:#ddd;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover,.tabs-below>.nav-tabs>.active>a:focus{border-color:transparent #ddd #ddd #ddd}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover,.tabs-left>.nav-tabs>li>a:focus{border-color:#eee #ddd #eee #eee}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover,.tabs-left>.nav-tabs .active>a:focus{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover,.tabs-right>.nav-tabs>li>a:focus{border-color:#eee #eee #eee #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover,.tabs-right>.nav-tabs .active>a:focus{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.nav>.disabled>a{color:#999}.nav>.disabled>a:hover,.nav>.disabled>a:focus{text-decoration:none;cursor:default;background-color:transparent}.navbar{*position:relative;*z-index:2;margin-bottom:20px;overflow:visible}.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#fafafa;background-image:-moz-linear-gradient(top,#fff,#f2f2f2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f2f2f2));background-image:-webkit-linear-gradient(top,#fff,#f2f2f2);background-image:-o-linear-gradient(top,#fff,#f2f2f2);background-image:linear-gradient(to bottom,#fff,#f2f2f2);background-repeat:repeat-x;border:1px solid #d4d4d4;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff2f2f2',GradientType=0);*zoom:1;-webkit-box-shadow:0 1px 4px rgba(0,0,0,0.065);-moz-box-shadow:0 1px 4px rgba(0,0,0,0.065);box-shadow:0 1px 4px rgba(0,0,0,0.065)}.navbar-inner:before,.navbar-inner:after{display:table;line-height:0;content:""}.navbar-inner:after{clear:both}.navbar .container{width:auto}.nav-collapse.collapse{height:auto;overflow:visible}.navbar .brand{display:block;float:left;padding:10px 20px 10px;margin-left:-20px;font-size:20px;font-weight:200;color:#777;text-shadow:0 1px 0 #fff}.navbar .brand:hover,.navbar .brand:focus{text-decoration:none}.navbar-text{margin-bottom:0;line-height:40px;color:#777}.navbar-link{color:#777}.navbar-link:hover,.navbar-link:focus{color:#333}.navbar .divider-vertical{height:40px;margin:0 9px;border-right:1px solid #fff;border-left:1px solid #f2f2f2}.navbar .btn,.navbar .btn-group{margin-top:5px}.navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn,.navbar .input-prepend .btn-group,.navbar .input-append .btn-group{margin-top:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;line-height:0;content:""}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px}.navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:5px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:5px;margin-bottom:0}.navbar-search .search-query{padding:4px 14px;margin-bottom:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.navbar-static-top{position:static;margin-bottom:0}.navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px}.navbar-fixed-bottom .navbar-inner{border-width:1px 0 0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.navbar-fixed-top{top:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{-webkit-box-shadow:0 1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 10px rgba(0,0,0,0.1);box-shadow:0 1px 10px rgba(0,0,0,0.1)}.navbar-fixed-bottom{bottom:0}.navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:0 -1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 -1px 10px rgba(0,0,0,0.1);box-shadow:0 -1px 10px rgba(0,0,0,0.1)}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right;margin-right:0}.navbar .nav>li{float:left}.navbar .nav>li>a{float:none;padding:10px 15px 10px;color:#777;text-decoration:none;text-shadow:0 1px 0 #fff}.navbar .nav .dropdown-toggle .caret{margin-top:8px}.navbar .nav>li>a:focus,.navbar .nav>li>a:hover{color:#333;text-decoration:none;background-color:transparent}.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#555;text-decoration:none;background-color:#e5e5e5;-webkit-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);box-shadow:inset 0 3px 8px rgba(0,0,0,0.125)}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ededed;*background-color:#e5e5e5;background-image:-moz-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#e5e5e5));background-image:-webkit-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-o-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:linear-gradient(to bottom,#f2f2f2,#e5e5e5);background-repeat:repeat-x;border-color:#e5e5e5 #e5e5e5 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffe5e5e5',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:focus,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#fff;background-color:#e5e5e5;*background-color:#d9d9d9}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#ccc \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .nav>li>.dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.navbar .nav>li>.dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.navbar-fixed-bottom .nav>li>.dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.navbar-fixed-bottom .nav>li>.dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #fff;border-bottom:0}.navbar .nav li.dropdown>a:hover .caret,.navbar .nav li.dropdown>a:focus .caret{border-top-color:#333;border-bottom-color:#333}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{color:#555;background-color:#e5e5e5}.navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#777;border-bottom-color:#777}.navbar .nav li.dropdown.open>.dropdown-toggle .caret,.navbar .nav li.dropdown.active>.dropdown-toggle .caret,.navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.navbar .pull-right>li>.dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right{right:0;left:auto}.navbar .pull-right>li>.dropdown-menu:before,.navbar .nav>li>.dropdown-menu.pull-right:before{right:12px;left:auto}.navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{right:13px;left:auto}.navbar .pull-right>li>.dropdown-menu .dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{right:100%;left:auto;margin-right:-1px;margin-left:0;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.navbar-inverse .navbar-inner{background-color:#1b1b1b;background-image:-moz-linear-gradient(top,#222,#111);background-image:-webkit-gradient(linear,0 0,0 100%,from(#222),to(#111));background-image:-webkit-linear-gradient(top,#222,#111);background-image:-o-linear-gradient(top,#222,#111);background-image:linear-gradient(to bottom,#222,#111);background-repeat:repeat-x;border-color:#252525;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff111111',GradientType=0)}.navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#999;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover,.navbar-inverse .brand:focus,.navbar-inverse .nav>li>a:focus{color:#fff}.navbar-inverse .brand{color:#999}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#fff;background-color:#111}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover,.navbar-inverse .navbar-link:focus{color:#fff}.navbar-inverse .divider-vertical{border-right-color:#222;border-left-color:#111}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{color:#fff;background-color:#111}.navbar-inverse .nav li.dropdown>a:hover .caret,.navbar-inverse .nav li.dropdown>a:focus .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#999;border-bottom-color:#999}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .navbar-search .search-query{color:#fff;background-color:#515151;border-color:#111;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#333;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-inverse .btn-navbar{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e0e0e;*background-color:#040404;background-image:-moz-linear-gradient(top,#151515,#040404);background-image:-webkit-gradient(linear,0 0,0 100%,from(#151515),to(#040404));background-image:-webkit-linear-gradient(top,#151515,#040404);background-image:-o-linear-gradient(top,#151515,#040404);background-image:linear-gradient(to bottom,#151515,#040404);background-repeat:repeat-x;border-color:#040404 #040404 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515',endColorstr='#ff040404',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:focus,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#fff;background-color:#040404;*background-color:#000}.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#000 \9}.breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.breadcrumb>li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.breadcrumb>li>.divider{padding:0 5px;color:#ccc}.breadcrumb>.active{color:#999}.pagination{margin:20px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination ul>li{display:inline}.pagination ul>li>a,.pagination ul>li>span{float:left;padding:4px 12px;line-height:20px;text-decoration:none;background-color:#fff;border:1px solid #ddd;border-left-width:0}.pagination ul>li>a:hover,.pagination ul>li>a:focus,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#f5f5f5}.pagination ul>.active>a,.pagination ul>.active>span{color:#999;cursor:default}.pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover,.pagination ul>.disabled>a:focus{color:#999;cursor:default;background-color:transparent}.pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.pagination ul>li:last-child>a,.pagination ul>li:last-child>span{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pagination-large ul>li>a,.pagination-large ul>li>span{padding:11px 19px;font-size:17.5px}.pagination-large ul>li:first-child>a,.pagination-large ul>li:first-child>span{-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.pagination-large ul>li:last-child>a,.pagination-large ul>li:last-child>span{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.pagination-mini ul>li:first-child>a,.pagination-small ul>li:first-child>a,.pagination-mini ul>li:first-child>span,.pagination-small ul>li:first-child>span{-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px}.pagination-mini ul>li:last-child>a,.pagination-small ul>li:last-child>a,.pagination-mini ul>li:last-child>span,.pagination-small ul>li:last-child>span{-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px;-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px}.pagination-small ul>li>a,.pagination-small ul>li>span{padding:2px 10px;font-size:11.9px}.pagination-mini ul>li>a,.pagination-mini ul>li>span{padding:0 6px;font-size:10.5px}.pager{margin:20px 0;text-align:center;list-style:none;*zoom:1}.pager:before,.pager:after{display:table;line-height:0;content:""}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#f5f5f5}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999;cursor:default;background-color:#fff}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.modal{position:fixed;top:10%;left:50%;z-index:1050;width:560px;margin-left:-280px;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;outline:0;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.modal.fade{top:-25%;-webkit-transition:opacity .3s linear,top .3s ease-out;-moz-transition:opacity .3s linear,top .3s ease-out;-o-transition:opacity .3s linear,top .3s ease-out;transition:opacity .3s linear,top .3s ease-out}.modal.fade.in{top:10%}.modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}.modal-header h3{margin:0;line-height:30px}.modal-body{position:relative;max-height:400px;padding:15px;overflow-y:auto}.modal-form{margin-bottom:0}.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.modal-footer:before,.modal-footer:after{display:table;line-height:0;content:""}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.tooltip{position:absolute;z-index:1030;display:block;font-size:11px;line-height:1.4;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:#000;border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#000;border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:#000;border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:#000;border-width:0 5px 5px}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;white-space:normal;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.popover-title:empty{display:none}.popover-content{padding:9px 14px}.popover .arrow,.popover .arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover .arrow{border-width:11px}.popover .arrow:after{border-width:10px;content:""}.popover.top .arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top .arrow:after{bottom:1px;margin-left:-10px;border-top-color:#fff;border-bottom-width:0}.popover.right .arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right .arrow:after{bottom:-10px;left:1px;border-right-color:#fff;border-left-width:0}.popover.bottom .arrow{top:-11px;left:50%;margin-left:-11px;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);border-top-width:0}.popover.bottom .arrow:after{top:1px;margin-left:-10px;border-bottom-color:#fff;border-top-width:0}.popover.left .arrow{top:50%;right:-11px;margin-top:-11px;border-left-color:#999;border-left-color:rgba(0,0,0,0.25);border-right-width:0}.popover.left .arrow:after{right:1px;bottom:-10px;border-left-color:#fff;border-right-width:0}.thumbnails{margin-left:-20px;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;line-height:0;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:20px;margin-left:20px}.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.055);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.055);box-shadow:0 1px 3px rgba(0,0,0,0.055);-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}a.thumbnail:hover,a.thumbnail:focus{border-color:#08c;-webkit-box-shadow:0 1px 4px rgba(0,105,214,0.25);-moz-box-shadow:0 1px 4px rgba(0,105,214,0.25);box-shadow:0 1px 4px rgba(0,105,214,0.25)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#555}.media,.media-body{overflow:hidden;*overflow:visible;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{margin-left:0;list-style:none}.label,.badge{display:inline-block;padding:2px 4px;font-size:11.844px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#999}.label{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.badge{padding-right:9px;padding-left:9px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px}.label:empty,.badge:empty{display:none}a.label:hover,a.label:focus,a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.label-important,.badge-important{background-color:#b94a48}.label-important[href],.badge-important[href]{background-color:#953b39}.label-warning,.badge-warning{background-color:#f89406}.label-warning[href],.badge-warning[href]{background-color:#c67605}.label-success,.badge-success{background-color:#468847}.label-success[href],.badge-success[href]{background-color:#356635}.label-info,.badge-info{background-color:#3a87ad}.label-info[href],.badge-info[href]{background-color:#2d6987}.label-inverse,.badge-inverse{background-color:#333}.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a}.btn .label,.btn .badge{position:relative;top:-1px}.btn-mini .label,.btn-mini .badge{top:0}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(to bottom,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15)}.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);background-image:linear-gradient(to bottom,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffc43c35',GradientType=0)}.progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);background-image:linear-gradient(to bottom,#62c462,#57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff57a957',GradientType=0)}.progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);background-image:linear-gradient(to bottom,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff339bb9',GradientType=0)}.progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar,.progress .bar-warning{background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0)}.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:20px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px}.accordion-toggle{cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative;margin-bottom:20px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{right:15px;left:auto}.carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-indicators{position:absolute;top:15px;right:15px;z-index:5;margin:0;list-style:none}.carousel-indicators li{display:block;float:left;width:10px;height:10px;margin-left:5px;text-indent:-999px;background-color:#ccc;background-color:rgba(255,255,255,0.25);border-radius:5px}.carousel-indicators .active{background-color:#fff}.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:15px;background:#333;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{line-height:20px;color:#fff}.carousel-caption h4{margin:0 0 5px}.carousel-caption p{margin-bottom:0}.hero-unit{padding:60px;margin-bottom:30px;font-size:18px;font-weight:200;line-height:30px;color:inherit;background-color:#eee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.hero-unit li{line-height:30px}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}.affix{position:fixed}
diff --git a/server/src/main/webapp/stylesheets/killbill.css b/server/src/main/webapp/stylesheets/killbill.css
new file mode 100644
index 0000000..a5f7cca
--- /dev/null
+++ b/server/src/main/webapp/stylesheets/killbill.css
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+body {
+    padding-top: 60px;
+}
+
+section {
+    padding-top: 30px;
+}
+
+.api_table {
+    margin-left: 15px;
+}
+
+.jumbotron {
+  margin: 80px 0;
+  text-align: center;
+}
+.jumbotron h1 {
+  font-size: 100px;
+  line-height: 1;
+}
+.jumbotron .lead {
+  font-size: 24px;
+  line-height: 1.25;
+}
+.jumbotron .btn {
+  font-size: 21px;
+  padding: 14px 24px;
+}
+.jumbotron ul {
+  list-style: none;
+}
+
+.marketing {
+  text-align: center;
+}
+
+.center {
+  float: none;
+  margin-left: auto;
+  margin-right: auto;
+  text-align: center;
+}
diff --git a/server/src/main/webapp/WEB-INF/web.xml b/server/src/main/webapp/WEB-INF/web.xml
index 23a2813..13d7694 100644
--- a/server/src/main/webapp/WEB-INF/web.xml
+++ b/server/src/main/webapp/WEB-INF/web.xml
@@ -1,11 +1,25 @@
 <?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.
+  -->
+
 <web-app
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
-        xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
-    <display-name>irs</display-name>
     <listener>
         <!-- Initialize Shiro WebEnvironment and put it into the ServletContext -->
         <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
@@ -43,13 +57,10 @@
         <listener-class>com.ning.billing.server.listeners.KillbillGuiceListener</listener-class>
     </listener>
 
-    <!-- ServletHandler#handle requires a backend servlet, it won't be used though (handled by Guice) -->
-    <servlet>
-        <servlet-name>log-invalid-resources</servlet-name>
-        <servlet-class>com.ning.jetty.core.servlets.LogInvalidResourcesServlet</servlet-class>
-    </servlet>
+    <!-- ServletHandler#handle requires a backend servlet. Besides, this will also be used to serve static resources,
+         such as the favicon or the welcome page -->
     <servlet-mapping>
-        <servlet-name>log-invalid-resources</servlet-name>
+        <servlet-name>default</servlet-name>
         <url-pattern>/*</url-pattern>
     </servlet-mapping>
 </web-app>
diff --git a/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java b/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
index 35c7509..5a65520 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
@@ -77,7 +77,6 @@ import com.google.common.collect.ImmutableMap;
 import static com.ning.billing.jaxrs.resources.JaxrsResource.ACCOUNTS;
 import static com.ning.billing.jaxrs.resources.JaxrsResource.BUNDLES;
 import static com.ning.billing.jaxrs.resources.JaxrsResource.QUERY_DELETE_DEFAULT_PM_WITH_AUTO_PAY_OFF;
-import static com.ning.billing.jaxrs.resources.JaxrsResource.QUERY_PAYMENT_METHOD_PLUGIN_INFO;
 import static com.ning.billing.jaxrs.resources.JaxrsResource.SUBSCRIPTIONS;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
@@ -567,7 +566,6 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
         final String paymentMethodURI = JaxrsResource.PAYMENT_METHODS_PATH + "/" + paymentMethodId;
 
         final Map<String, String> queryPaymentMethods = new HashMap<String, String>();
-        queryPaymentMethods.put(QUERY_PAYMENT_METHOD_PLUGIN_INFO, "true");
         final Response paymentMethodResponse = doGet(paymentMethodURI, queryPaymentMethods, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(paymentMethodResponse.getStatusCode(), Status.OK.getStatusCode());
 
@@ -810,6 +808,58 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
     }
 
     //
+    // PLUGINS
+    //
+
+    protected Response pluginGET(final String uri) throws Exception {
+        return pluginGET(uri, DEFAULT_EMPTY_QUERY);
+    }
+
+    protected Response pluginGET(final String uri, final Map<String, String> queryParams) throws Exception {
+        return doGet(JaxrsResource.PLUGINS_PATH + "/" + uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+    }
+
+    protected Response pluginHEAD(final String uri) throws Exception {
+        return pluginHEAD(uri, DEFAULT_EMPTY_QUERY);
+    }
+
+    protected Response pluginHEAD(final String uri, final Map<String, String> queryParams) throws Exception {
+        return doHead(JaxrsResource.PLUGINS_PATH + "/" + uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+    }
+
+    protected Response pluginPOST(final String uri, @Nullable final String body) throws Exception {
+        return pluginPOST(uri, body, DEFAULT_EMPTY_QUERY);
+    }
+
+    protected Response pluginPOST(final String uri, @Nullable final String body, final Map<String, String> queryParams) throws Exception {
+        return doPost(JaxrsResource.PLUGINS_PATH + "/" + uri, body, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+    }
+
+    protected Response pluginPUT(final String uri, @Nullable final String body) throws Exception {
+        return pluginPUT(uri, body, DEFAULT_EMPTY_QUERY);
+    }
+
+    protected Response pluginPUT(final String uri, @Nullable final String body, final Map<String, String> queryParams) throws Exception {
+        return doPut(JaxrsResource.PLUGINS_PATH + "/" + uri, body, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+    }
+
+    protected Response pluginDELETE(final String uri) throws Exception {
+        return pluginDELETE(uri, DEFAULT_EMPTY_QUERY);
+    }
+
+    protected Response pluginDELETE(final String uri, final Map<String, String> queryParams) throws Exception {
+        return doDelete(JaxrsResource.PLUGINS_PATH + "/" + uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+    }
+
+    protected Response pluginOPTIONS(final String uri) throws Exception {
+        return pluginOPTIONS(uri, DEFAULT_EMPTY_QUERY);
+    }
+
+    protected Response pluginOPTIONS(final String uri, final Map<String, String> queryParams) throws Exception {
+        return doOptions(JaxrsResource.PLUGINS_PATH + "/" + uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+    }
+
+    //
     // HTTP CLIENT HELPERS
     //
     protected Response doPost(final String uri, @Nullable final String body, final Map<String, String> queryParams, final int timeoutSec) {
@@ -844,11 +894,31 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
         return doGetWithUrl(url, queryParams, timeoutSec);
     }
 
+    protected Response doHead(final String uri, final Map<String, String> queryParams, final int timeoutSec) {
+        final String url = String.format("http://%s:%d%s", config.getServerHost(), config.getServerPort(), uri);
+        return doHeadWithUrl(url, queryParams, timeoutSec);
+    }
+
+    protected Response doOptions(final String uri, final Map<String, String> queryParams, final int timeoutSec) {
+        final String url = String.format("http://%s:%d%s", config.getServerHost(), config.getServerPort(), uri);
+        return doOptionsWithUrl(url, queryParams, timeoutSec);
+    }
+
     protected Response doGetWithUrl(final String url, final Map<String, String> queryParams, final int timeoutSec) {
         final BoundRequestBuilder builder = getBuilderWithHeaderAndQuery("GET", url, queryParams);
         return executeAndWait(builder, timeoutSec, false);
     }
 
+    protected Response doHeadWithUrl(final String url, final Map<String, String> queryParams, final int timeoutSec) {
+        final BoundRequestBuilder builder = getBuilderWithHeaderAndQuery("HEAD", url, queryParams);
+        return executeAndWait(builder, timeoutSec, false);
+    }
+
+    protected Response doOptionsWithUrl(final String url, final Map<String, String> queryParams, final int timeoutSec) {
+        final BoundRequestBuilder builder = getBuilderWithHeaderAndQuery("OPTIONS", url, queryParams);
+        return executeAndWait(builder, timeoutSec, false);
+    }
+
     private Response executeAndWait(final BoundRequestBuilder builder, final int timeoutSec, final boolean addContextHeader) {
 
         if (addContextHeader) {
@@ -888,6 +958,10 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
             builder = httpClient.preparePut(url);
         } else if (verb.equals("DELETE")) {
             builder = httpClient.prepareDelete(url);
+        } else if (verb.equals("HEAD")) {
+            builder = httpClient.prepareHead(url);
+        } else if (verb.equals("OPTIONS")) {
+            builder = httpClient.prepareOptions(url);
         } else {
             Assert.fail("Unknown verb " + verb);
         }
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java b/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
index c1d87c6..f469837 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
@@ -151,7 +151,6 @@ public class TestAccount extends TestJaxrsBase {
         // FETCH ALL PAYMENT METHODS
         //
         queryParams = new HashMap<String, String>();
-        queryParams.put(JaxrsResource.QUERY_PAYMENT_METHOD_PLUGIN_INFO, "true");
         response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
         baseJson = response.getResponseBody();
@@ -183,7 +182,6 @@ public class TestAccount extends TestJaxrsBase {
         //
         uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.PAYMENT_METHODS;
         queryParams = new HashMap<String, String>();
-        queryParams.put(JaxrsResource.QUERY_PAYMENT_METHOD_PLUGIN_INFO, "true");
         response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
         baseJson = response.getResponseBody();
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 64d73fa..421caef 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
@@ -23,9 +23,14 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 
+import javax.inject.Inject;
+import javax.servlet.Servlet;
+
 import org.eclipse.jetty.servlet.FilterHolder;
 import org.joda.time.LocalDate;
+import org.skife.config.ConfigSource;
 import org.skife.config.ConfigurationObjectFactory;
+import org.skife.config.SimplePropertyConfigSource;
 import org.testng.annotations.AfterSuite;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
@@ -44,12 +49,15 @@ import com.ning.billing.invoice.api.InvoiceNotifier;
 import com.ning.billing.invoice.glue.DefaultInvoiceModule;
 import com.ning.billing.invoice.notification.NullInvoiceNotifier;
 import com.ning.billing.junction.glue.DefaultJunctionModule;
+import com.ning.billing.osgi.api.OSGIServiceRegistration;
+import com.ning.billing.osgi.glue.DefaultOSGIModule;
 import com.ning.billing.overdue.glue.DefaultOverdueModule;
 import com.ning.billing.payment.glue.PaymentModule;
 import com.ning.billing.payment.provider.MockPaymentProviderPluginModule;
 import com.ning.billing.server.listeners.KillbillGuiceListener;
 import com.ning.billing.server.modules.KillbillServerModule;
 import com.ning.billing.tenant.glue.TenantModule;
+import com.ning.billing.usage.glue.UsageModule;
 import com.ning.billing.util.config.PaymentConfig;
 import com.ning.billing.util.email.EmailModule;
 import com.ning.billing.util.email.templates.TemplateModule;
@@ -82,6 +90,9 @@ public class TestJaxrsBase extends KillbillClient {
 
     protected static final int DEFAULT_HTTP_TIMEOUT_SEC = 6000; // 5;
 
+    @Inject
+    protected OSGIServiceRegistration<Servlet> servletRouter;
+
     protected static TestKillbillGuiceListener listener;
 
     private HttpServer server;
@@ -116,6 +127,10 @@ public class TestJaxrsBase extends KillbillClient {
 
     public static class InvoiceModuleWithMockSender extends DefaultInvoiceModule {
 
+        public InvoiceModuleWithMockSender(final ConfigSource configSource) {
+            super(configSource);
+        }
+
         @Override
         protected void installInvoiceNotifier() {
             bind(InvoiceNotifier.class).to(NullInvoiceNotifier.class).asEagerSingleton();
@@ -143,6 +158,10 @@ public class TestJaxrsBase extends KillbillClient {
 
         private static final class PaymentMockModule extends PaymentModule {
 
+            public PaymentMockModule(final ConfigSource configSource) {
+                super(configSource);
+            }
+
             @Override
             protected void installPaymentProviderPlugins(final PaymentConfig config) {
                 install(new MockPaymentProviderPluginModule(PLUGIN_NAME, getClock()));
@@ -151,6 +170,7 @@ public class TestJaxrsBase extends KillbillClient {
 
         @Override
         protected void installKillbillModules() {
+            final ConfigSource configSource = new SimplePropertyConfigSource(System.getProperties());
 
             /*
              * For a lack of getting module override working, copy all install modules from parent class...
@@ -162,41 +182,44 @@ public class TestJaxrsBase extends KillbillClient {
             install(new GuicyKillbillTestWithEmbeddedDBModule());
 
 
-            install(new EmailModule());
-            install(new CacheModule());
+            install(new EmailModule(configSource));
+            install(new CacheModule(configSource));
             install(new NonEntityDaoModule());
             install(new TestGlobalLockerModule(helper));
             install(new CustomFieldModule());
             install(new TagStoreModule());
             install(new AuditModule());
-            install(new CatalogModule());
-            install(new BusModule());
-            install(new NotificationQueueModule());
+            install(new CatalogModule(configSource));
+            install(new BusModule(configSource));
+            install(new NotificationQueueModule(configSource));
             install(new CallContextModule());
-            install(new DefaultAccountModule());
-            install(new InvoiceModuleWithMockSender());
+            install(new DefaultAccountModule(configSource));
+            install(new InvoiceModuleWithMockSender(configSource));
             install(new TemplateModule());
-            install(new DefaultEntitlementModule());
-            install(new AnalyticsModule());
-            install(new PaymentMockModule());
+            install(new DefaultEntitlementModule(configSource));
+            install(new AnalyticsModule(configSource));
+            install(new PaymentMockModule(configSource));
             install(new BeatrixModule());
-            install(new DefaultJunctionModule());
-            install(new DefaultOverdueModule());
-            install(new TenantModule());
+            install(new DefaultJunctionModule(configSource));
+            install(new DefaultOverdueModule(configSource));
+            install(new TenantModule(configSource));
             install(new ExportModule());
+            install(new DefaultOSGIModule(configSource));
+            install(new UsageModule(configSource));
             installClock();
         }
     }
 
     @BeforeMethod(groups = "slow")
-    public void cleanupBeforeMethod() throws Exception {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         busHandler.reset();
         clock.reset();
         clock.setDay(new LocalDate(2012, 8, 25));
     }
 
     @BeforeClass(groups = "slow")
-    public void setupClass() throws Exception {
+    public void beforeClass() throws Exception {
         loadConfig();
 
 
@@ -224,7 +247,8 @@ public class TestJaxrsBase extends KillbillClient {
     }
 
     @BeforeSuite(groups = "slow")
-    public void setup() throws Exception {
+    public void beforeSuite() throws Exception {
+        super.beforeSuite();
         loadSystemPropertiesFromClasspath("/killbill.properties");
         loadConfig();
 
@@ -234,6 +258,8 @@ public class TestJaxrsBase extends KillbillClient {
         server.configure(config, getListeners(), getFilters());
 
         server.start();
+
+        listener.getInstantiatedInjector().injectMembers(this);
     }
 
     protected Iterable<EventListener> getListeners() {
@@ -250,7 +276,7 @@ public class TestJaxrsBase extends KillbillClient {
     }
 
     @AfterSuite(groups = "slow")
-    public void tearDown() {
+    public void afterSuite() {
         try {
             server.stop();
         } catch (final Exception ignored) {
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestPayment.java b/server/src/test/java/com/ning/billing/jaxrs/TestPayment.java
index c4c882c..15247e5 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestPayment.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestPayment.java
@@ -168,7 +168,6 @@ public class TestPayment extends TestJaxrsBase {
         final PaymentMethodJson paymentMethodJson = getPaymentMethodWithPluginInfo(paymentMethodId);
         Assert.assertEquals(paymentMethodJson.getPaymentMethodId(), paymentMethodId);
         Assert.assertEquals(paymentMethodJson.getAccountId(), accountJson.getAccountId());
-        Assert.assertNotNull(paymentMethodJson.getPluginInfo().getExternalPaymentId());
 
         // Verify the refunds
         final List<RefundJson> objRefundFromJson = getRefundsForPayment(paymentJsonSimple.getPaymentId());
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestPlugin.java b/server/src/test/java/com/ning/billing/jaxrs/TestPlugin.java
new file mode 100644
index 0000000..7d4ec76
--- /dev/null
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestPlugin.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.jaxrs;
+
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.annotation.Nullable;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.ning.billing.osgi.http.DefaultServletRouter;
+import com.ning.http.client.Response;
+
+public class TestPlugin extends TestJaxrsBase {
+
+    private static final String TEST_PLUGIN_NAME = "test-osgi";
+
+    private static final byte[] TEST_PLUGIN_RESPONSE_BYTES = new byte[]{0xC, 0x0, 0xF, 0xF, 0xE, 0xE};
+
+    private static final String TEST_PLUGIN_VALID_GET_PATH = "setGETMarkerToTrue";
+    private static final String TEST_PLUGIN_VALID_HEAD_PATH = "setHEADMarkerToTrue";
+    private static final String TEST_PLUGIN_VALID_POST_PATH = "setPOSTMarkerToTrue";
+    private static final String TEST_PLUGIN_VALID_PUT_PATH = "setPUTMarkerToTrue";
+    private static final String TEST_PLUGIN_VALID_DELETE_PATH = "setDELETEMarkerToTrue";
+    private static final String TEST_PLUGIN_VALID_OPTIONS_PATH = "setOPTIONSMarkerToTrue";
+
+    private final AtomicBoolean requestGETMarker = new AtomicBoolean(false);
+    private final AtomicBoolean requestHEADMarker = new AtomicBoolean(false);
+    private final AtomicBoolean requestPOSTMarker = new AtomicBoolean(false);
+    private final AtomicBoolean requestPUTMarker = new AtomicBoolean(false);
+    private final AtomicBoolean requestDELETEMarker = new AtomicBoolean(false);
+    private final AtomicBoolean requestOPTIONSMarker = new AtomicBoolean(false);
+
+    @Override
+    @BeforeMethod(groups = "slow")
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
+        setupOSGIPlugin();
+        resetAllMarkers();
+    }
+
+    @Test(groups = "slow")
+    public void testPassRequestsToUnknownPlugin() throws Exception {
+        final String uri = "pluginDoesNotExist/something";
+        Response response;
+
+        // We don't test the output here as it is some Jetty specific HTML blurb
+
+        response = pluginGET(uri);
+        testAndResetAllMarkers(response, 404, null, false, false, false, false, false, false);
+
+        response = pluginHEAD(uri);
+        testAndResetAllMarkers(response, 404, null, false, false, false, false, false, false);
+
+        response = pluginPOST(uri, null);
+        testAndResetAllMarkers(response, 404, null, false, false, false, false, false, false);
+
+        response = pluginPUT(uri, null);
+        testAndResetAllMarkers(response, 404, null, false, false, false, false, false, false);
+
+        response = pluginDELETE(uri);
+        testAndResetAllMarkers(response, 404, null, false, false, false, false, false, false);
+
+        response = pluginOPTIONS(uri);
+        testAndResetAllMarkers(response, 404, null, false, false, false, false, false, false);
+    }
+
+    @Test(groups = "slow")
+    public void testPassRequestsToKnownPluginButWrongPath() throws Exception {
+        final String uri = TEST_PLUGIN_NAME + "/somethingSomething";
+        Response response;
+
+        response = pluginGET(uri);
+        testAndResetAllMarkers(response, 200, new byte[]{}, false, false, false, false, false, false);
+
+        response = pluginHEAD(uri);
+        testAndResetAllMarkers(response, 204, new byte[]{}, false, false, false, false, false, false);
+
+        response = pluginPOST(uri, null);
+        testAndResetAllMarkers(response, 200, new byte[]{}, false, false, false, false, false, false);
+
+        response = pluginPUT(uri, null);
+        testAndResetAllMarkers(response, 200, new byte[]{}, false, false, false, false, false, false);
+
+        response = pluginDELETE(uri);
+        testAndResetAllMarkers(response, 200, new byte[]{}, false, false, false, false, false, false);
+
+        response = pluginOPTIONS(uri);
+        testAndResetAllMarkers(response, 200, new byte[]{}, false, false, false, false, false, false);
+    }
+
+    @Test(groups = "slow")
+    public void testPassRequestsToKnownPluginAndKnownPath() throws Exception {
+        Response response;
+
+        response = pluginGET(TEST_PLUGIN_NAME + "/" + TEST_PLUGIN_VALID_GET_PATH);
+        testAndResetAllMarkers(response, 230, TEST_PLUGIN_RESPONSE_BYTES, true, false, false, false, false, false);
+
+        response = pluginHEAD(TEST_PLUGIN_NAME + "/" + TEST_PLUGIN_VALID_HEAD_PATH);
+        testAndResetAllMarkers(response, 204, new byte[]{}, false, true, false, false, false, false);
+
+        response = pluginPOST(TEST_PLUGIN_NAME + "/" + TEST_PLUGIN_VALID_POST_PATH, null);
+        testAndResetAllMarkers(response, 230, TEST_PLUGIN_RESPONSE_BYTES, false, false, true, false, false, false);
+
+        response = pluginPUT(TEST_PLUGIN_NAME + "/" + TEST_PLUGIN_VALID_PUT_PATH, null);
+        testAndResetAllMarkers(response, 230, TEST_PLUGIN_RESPONSE_BYTES, false, false, false, true, false, false);
+
+        response = pluginDELETE(TEST_PLUGIN_NAME + "/" + TEST_PLUGIN_VALID_DELETE_PATH);
+        testAndResetAllMarkers(response, 230, TEST_PLUGIN_RESPONSE_BYTES, false, false, false, false, true, false);
+
+        response = pluginOPTIONS(TEST_PLUGIN_NAME + "/" + TEST_PLUGIN_VALID_OPTIONS_PATH);
+        testAndResetAllMarkers(response, 230, TEST_PLUGIN_RESPONSE_BYTES, false, false, false, false, false, true);
+    }
+
+    private void testAndResetAllMarkers(final Response response, final int responseCode, @Nullable final byte[] responseBytes, final boolean get, final boolean head,
+                                        final boolean post, final boolean put, final boolean delete, final boolean options) throws IOException {
+        Assert.assertEquals(response.getStatusCode(), responseCode);
+        if (responseBytes != null) {
+            Assert.assertEquals(response.getResponseBodyAsBytes(), responseBytes);
+        }
+
+        Assert.assertEquals(requestGETMarker.get(), get);
+        Assert.assertEquals(requestHEADMarker.get(), head);
+        Assert.assertEquals(requestPOSTMarker.get(), post);
+        Assert.assertEquals(requestPUTMarker.get(), put);
+        Assert.assertEquals(requestDELETEMarker.get(), delete);
+        Assert.assertEquals(requestOPTIONSMarker.get(), options);
+
+        resetAllMarkers();
+    }
+
+    private void resetAllMarkers() {
+        requestGETMarker.set(false);
+        requestHEADMarker.set(false);
+        requestPOSTMarker.set(false);
+        requestPUTMarker.set(false);
+        requestDELETEMarker.set(false);
+        requestOPTIONSMarker.set(false);
+    }
+
+    private void setupOSGIPlugin() {
+        ((DefaultServletRouter) servletRouter).registerServiceFromPath(TEST_PLUGIN_NAME, new HttpServlet() {
+            @Override
+            protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+                if (("/" + TEST_PLUGIN_VALID_GET_PATH).equals(req.getPathInfo())) {
+                    requestGETMarker.set(true);
+                    resp.getOutputStream().write(TEST_PLUGIN_RESPONSE_BYTES);
+                    resp.setStatus(230);
+                }
+            }
+
+            @Override
+            protected void doHead(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+                if (("/" + 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 (("/" + TEST_PLUGIN_VALID_POST_PATH).equals(req.getPathInfo())) {
+                    requestPOSTMarker.set(true);
+                    resp.getOutputStream().write(TEST_PLUGIN_RESPONSE_BYTES);
+                    resp.setStatus(230);
+                }
+            }
+
+            @Override
+            protected void doPut(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+                if (("/" + TEST_PLUGIN_VALID_PUT_PATH).equals(req.getPathInfo())) {
+                    requestPUTMarker.set(true);
+                    resp.getOutputStream().write(TEST_PLUGIN_RESPONSE_BYTES);
+                    resp.setStatus(230);
+                }
+            }
+
+            @Override
+            protected void doDelete(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+                if (("/" + TEST_PLUGIN_VALID_DELETE_PATH).equals(req.getPathInfo())) {
+                    requestDELETEMarker.set(true);
+                    resp.getOutputStream().write(TEST_PLUGIN_RESPONSE_BYTES);
+                    resp.setStatus(230);
+                }
+            }
+
+            @Override
+            protected void doOptions(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+                if (("/" + TEST_PLUGIN_VALID_OPTIONS_PATH).equals(req.getPathInfo())) {
+                    requestOPTIONSMarker.set(true);
+                    resp.getOutputStream().write(TEST_PLUGIN_RESPONSE_BYTES);
+                    resp.setStatus(230);
+                }
+            }
+        });
+    }
+}
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestPushNotification.java b/server/src/test/java/com/ning/billing/jaxrs/TestPushNotification.java
index eacaf5e..83ab8cf 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestPushNotification.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestPushNotification.java
@@ -51,8 +51,10 @@ public class TestPushNotification extends TestJaxrsBase {
     private volatile boolean callbackCompletedWithError;
 
 
+    @Override
     @BeforeMethod(groups = "slow")
-    public void startServer() throws Exception {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         callbackServer = new CallbackServer(this, SERVER_PORT, CALLBACK_ENDPPOINT);
         callbackCompleted = false;
         callbackCompletedWithError = false;
@@ -60,7 +62,7 @@ public class TestPushNotification extends TestJaxrsBase {
     }
 
     @AfterMethod(groups = "slow")
-    public void stopServer() throws Exception {
+    public void afterMethod() throws Exception {
         callbackServer.stopServer();
     }
 
diff --git a/server/src/test/java/com/ning/billing/server/security/TestKillbillJdbcRealm.java b/server/src/test/java/com/ning/billing/server/security/TestKillbillJdbcRealm.java
index f89cf16..8f353f8 100644
--- a/server/src/test/java/com/ning/billing/server/security/TestKillbillJdbcRealm.java
+++ b/server/src/test/java/com/ning/billing/server/security/TestKillbillJdbcRealm.java
@@ -46,9 +46,9 @@ public class TestKillbillJdbcRealm extends TestJaxrsBase {
 
     @Override
     @BeforeMethod(groups = "slow")
-    public void cleanupBeforeMethod() throws Exception {
+    public void beforeMethod() throws Exception {
 
-        super.cleanupBeforeMethod();
+        super.beforeMethod();
 
         // Create the tenant
         final CacheControllerDispatcher controllerDispatcher = new CacheControllerDispatcher();
diff --git a/server/src/test/resources/killbill.properties b/server/src/test/resources/killbill.properties
index 1148c07..bbbb8c1 100644
--- a/server/src/test/resources/killbill.properties
+++ b/server/src/test/resources/killbill.properties
@@ -1,3 +1,19 @@
+#
+# 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.
+#
+
 # Use killbill util test properties (DbiProvider/MysqltestingHelper) on the test side configured with killbill
 com.ning.billing.dbi.jdbc.url=jdbc:mysql://127.0.0.1:3306/killbill
 
diff --git a/server/src/test/resources/overdue.xml b/server/src/test/resources/overdue.xml
index bd13ebe..3e24e14 100644
--- a/server/src/test/resources/overdue.xml
+++ b/server/src/test/resources/overdue.xml
@@ -1,3 +1,19 @@
+<!--
+  ~ 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.
+  -->
+
 <overdueConfig>
    <bundleOverdueStates>
        <state name="OD3">

tenant/pom.xml 15(+0 -15)

diff --git a/tenant/pom.xml b/tenant/pom.xml
index 16825fe..f594159 100644
--- a/tenant/pom.xml
+++ b/tenant/pom.xml
@@ -106,19 +106,4 @@
             <scope>test</scope>
         </dependency>
     </dependencies>
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-jar-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>test-jar</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-        </plugins>
-    </build>
 </project>
diff --git a/tenant/src/main/java/com/ning/billing/tenant/glue/TenantModule.java b/tenant/src/main/java/com/ning/billing/tenant/glue/TenantModule.java
index 17bdf5a..ddcc847 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/glue/TenantModule.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/glue/TenantModule.java
@@ -16,6 +16,8 @@
 
 package com.ning.billing.tenant.glue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.tenant.api.DefaultTenantService;
 import com.ning.billing.tenant.api.TenantService;
 import com.ning.billing.tenant.api.TenantUserApi;
@@ -27,6 +29,12 @@ import com.google.inject.AbstractModule;
 
 public class TenantModule extends AbstractModule {
 
+    protected final ConfigSource configSource;
+
+    public TenantModule(final ConfigSource configSource) {
+        this.configSource = configSource;
+    }
+
     private void installConfig() {
     }
 
diff --git a/tenant/src/test/java/com/ning/billing/tenant/dao/TestDefaultTenantDao.java b/tenant/src/test/java/com/ning/billing/tenant/dao/TestDefaultTenantDao.java
index e10b69e..26dd469 100644
--- a/tenant/src/test/java/com/ning/billing/tenant/dao/TestDefaultTenantDao.java
+++ b/tenant/src/test/java/com/ning/billing/tenant/dao/TestDefaultTenantDao.java
@@ -23,6 +23,7 @@ import org.apache.shiro.authc.AuthenticationInfo;
 import org.apache.shiro.authc.AuthenticationToken;
 import org.apache.shiro.authc.UsernamePasswordToken;
 import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 import com.ning.billing.tenant.TenantTestSuiteWithEmbeddedDb;
diff --git a/tenant/src/test/java/com/ning/billing/tenant/glue/TestTenantModule.java b/tenant/src/test/java/com/ning/billing/tenant/glue/TestTenantModule.java
index b59207d..62a715c 100644
--- a/tenant/src/test/java/com/ning/billing/tenant/glue/TestTenantModule.java
+++ b/tenant/src/test/java/com/ning/billing/tenant/glue/TestTenantModule.java
@@ -16,34 +16,22 @@
 
 package com.ning.billing.tenant.glue;
 
-import java.util.Properties;
-
 import org.skife.config.ConfigSource;
-import org.skife.config.SimplePropertyConfigSource;
 
 import com.ning.billing.util.glue.CacheModule;
 import com.ning.billing.util.glue.CallContextModule;
 
 public class TestTenantModule extends TenantModule {
 
-    protected final ConfigSource configSource;
-
-    public TestTenantModule() {
-        final Properties properties = new Properties(System.getProperties());
-        // Speed up the bus
-        properties.put("killbill.billing.util.persistent.bus.sleep", "10");
-        properties.put("killbill.billing.util.persistent.bus.nbThreads", "1");
-        configSource = new SimplePropertyConfigSource(properties);
-
-        // Ignore ehcache checks. Unfortunately, ehcache looks at system properties directly...
-        System.setProperty("net.sf.ehcache.skipUpdateCheck", "true");
+    public TestTenantModule(final ConfigSource configSource) {
+        super(configSource);
     }
 
     @Override
     protected void configure() {
         super.configure();
 
-        install(new CacheModule());
+        install(new CacheModule(configSource));
         install(new CallContextModule());
     }
 }
diff --git a/tenant/src/test/java/com/ning/billing/tenant/glue/TestTenantModuleNoDB.java b/tenant/src/test/java/com/ning/billing/tenant/glue/TestTenantModuleNoDB.java
index 91d463a..f369f2e 100644
--- a/tenant/src/test/java/com/ning/billing/tenant/glue/TestTenantModuleNoDB.java
+++ b/tenant/src/test/java/com/ning/billing/tenant/glue/TestTenantModuleNoDB.java
@@ -16,11 +16,17 @@
 
 package com.ning.billing.tenant.glue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.GuicyKillbillTestNoDBModule;
 import com.ning.billing.mock.glue.MockNonEntityDaoModule;
 
 public class TestTenantModuleNoDB extends TestTenantModule {
 
+    public TestTenantModuleNoDB(final ConfigSource configSource) {
+        super(configSource);
+    }
+
     @Override
     public void configure() {
         super.configure();
diff --git a/tenant/src/test/java/com/ning/billing/tenant/glue/TestTenantModuleWithEmbeddedDB.java b/tenant/src/test/java/com/ning/billing/tenant/glue/TestTenantModuleWithEmbeddedDB.java
index 8a3821b..b05792a 100644
--- a/tenant/src/test/java/com/ning/billing/tenant/glue/TestTenantModuleWithEmbeddedDB.java
+++ b/tenant/src/test/java/com/ning/billing/tenant/glue/TestTenantModuleWithEmbeddedDB.java
@@ -16,11 +16,17 @@
 
 package com.ning.billing.tenant.glue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.GuicyKillbillTestWithEmbeddedDBModule;
 import com.ning.billing.util.glue.NonEntityDaoModule;
 
 public class TestTenantModuleWithEmbeddedDB extends TestTenantModule {
 
+    public TestTenantModuleWithEmbeddedDB(final ConfigSource configSource) {
+        super(configSource);
+    }
+
     @Override
     public void configure() {
         super.configure();
diff --git a/tenant/src/test/java/com/ning/billing/tenant/TenantTestSuiteNoDB.java b/tenant/src/test/java/com/ning/billing/tenant/TenantTestSuiteNoDB.java
index 0e12ad0..57272d2 100644
--- a/tenant/src/test/java/com/ning/billing/tenant/TenantTestSuiteNoDB.java
+++ b/tenant/src/test/java/com/ning/billing/tenant/TenantTestSuiteNoDB.java
@@ -16,9 +16,7 @@
 
 package com.ning.billing.tenant;
 
-import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeClass;
-import org.testng.annotations.BeforeMethod;
 
 import com.ning.billing.GuicyKillbillTestSuiteNoDB;
 import com.ning.billing.tenant.glue.TestTenantModuleNoDB;
@@ -29,16 +27,8 @@ import com.google.inject.Injector;
 public class TenantTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
 
     @BeforeClass(groups = "fast")
-    protected void setup() throws Exception {
-        final Injector injector = Guice.createInjector(new TestTenantModuleNoDB());
+    protected void beforeClass() throws Exception {
+        final Injector injector = Guice.createInjector(new TestTenantModuleNoDB(configSource));
         injector.injectMembers(this);
     }
-
-    @BeforeMethod(groups = "fast")
-    public void setupTest() {
-    }
-
-    @AfterMethod(groups = "fast")
-    public void cleanupTest() {
-    }
 }
diff --git a/tenant/src/test/java/com/ning/billing/tenant/TenantTestSuiteWithEmbeddedDb.java b/tenant/src/test/java/com/ning/billing/tenant/TenantTestSuiteWithEmbeddedDb.java
index 18f36a1..6296380 100644
--- a/tenant/src/test/java/com/ning/billing/tenant/TenantTestSuiteWithEmbeddedDb.java
+++ b/tenant/src/test/java/com/ning/billing/tenant/TenantTestSuiteWithEmbeddedDb.java
@@ -34,16 +34,17 @@ public class TenantTestSuiteWithEmbeddedDb extends GuicyKillbillTestSuiteWithEmb
     protected DefaultTenantDao tenantDao;
 
     @BeforeClass(groups = "slow")
-    protected void setup() throws Exception {
-        final Injector injector = Guice.createInjector(new TestTenantModuleWithEmbeddedDB());
+    protected void beforeClass() throws Exception {
+        final Injector injector = Guice.createInjector(new TestTenantModuleWithEmbeddedDB(configSource));
         injector.injectMembers(this);
     }
 
     @BeforeMethod(groups = "slow")
-    public void setupTest() {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
     }
 
     @AfterMethod(groups = "slow")
-    public void cleanupTest() {
+    public void afterMethod() {
     }
 }
diff --git a/usage/src/main/java/com/ning/billing/usage/glue/UsageModule.java b/usage/src/main/java/com/ning/billing/usage/glue/UsageModule.java
index 38f92ae..29af1c6 100644
--- a/usage/src/main/java/com/ning/billing/usage/glue/UsageModule.java
+++ b/usage/src/main/java/com/ning/billing/usage/glue/UsageModule.java
@@ -16,6 +16,8 @@
 
 package com.ning.billing.usage.glue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.usage.api.UsageUserApi;
 import com.ning.billing.usage.api.user.DefaultUsageUserApi;
 import com.ning.billing.usage.dao.DefaultRolledUpUsageDao;
@@ -25,6 +27,12 @@ import com.google.inject.AbstractModule;
 
 public class UsageModule extends AbstractModule {
 
+    protected final ConfigSource configSource;
+
+    public UsageModule(final ConfigSource configSource) {
+        this.configSource = configSource;
+    }
+
     protected void installRolledUpUsageDao() {
         bind(RolledUpUsageDao.class).to(DefaultRolledUpUsageDao.class).asEagerSingleton();
     }
diff --git a/usage/src/test/java/com/ning/billing/usage/glue/TestUsageModule.java b/usage/src/test/java/com/ning/billing/usage/glue/TestUsageModule.java
index 2456757..2ba70bc 100644
--- a/usage/src/test/java/com/ning/billing/usage/glue/TestUsageModule.java
+++ b/usage/src/test/java/com/ning/billing/usage/glue/TestUsageModule.java
@@ -16,8 +16,14 @@
 
 package com.ning.billing.usage.glue;
 
+import org.skife.config.ConfigSource;
+
 public class TestUsageModule extends UsageModule {
 
+    public TestUsageModule(final ConfigSource configSource) {
+        super(configSource);
+    }
+
     @Override
     protected void configure() {
         super.configure();
diff --git a/usage/src/test/java/com/ning/billing/usage/glue/TestUsageModuleNoDB.java b/usage/src/test/java/com/ning/billing/usage/glue/TestUsageModuleNoDB.java
index 57ee1bf..4015808 100644
--- a/usage/src/test/java/com/ning/billing/usage/glue/TestUsageModuleNoDB.java
+++ b/usage/src/test/java/com/ning/billing/usage/glue/TestUsageModuleNoDB.java
@@ -16,10 +16,16 @@
 
 package com.ning.billing.usage.glue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.GuicyKillbillTestNoDBModule;
 
 public class TestUsageModuleNoDB extends TestUsageModule {
 
+    public TestUsageModuleNoDB(final ConfigSource configSource) {
+        super(configSource);
+    }
+
     @Override
     public void configure() {
         super.configure();
diff --git a/usage/src/test/java/com/ning/billing/usage/glue/TestUsageModuleWithEmbeddedDB.java b/usage/src/test/java/com/ning/billing/usage/glue/TestUsageModuleWithEmbeddedDB.java
index 8a3b6ac..6ed8a2e 100644
--- a/usage/src/test/java/com/ning/billing/usage/glue/TestUsageModuleWithEmbeddedDB.java
+++ b/usage/src/test/java/com/ning/billing/usage/glue/TestUsageModuleWithEmbeddedDB.java
@@ -16,10 +16,16 @@
 
 package com.ning.billing.usage.glue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.GuicyKillbillTestWithEmbeddedDBModule;
 
 public class TestUsageModuleWithEmbeddedDB extends TestUsageModule {
 
+    public TestUsageModuleWithEmbeddedDB(final ConfigSource configSource) {
+        super(configSource);
+    }
+
     @Override
     public void configure() {
         super.configure();
diff --git a/usage/src/test/java/com/ning/billing/usage/UsageTestSuiteNoDB.java b/usage/src/test/java/com/ning/billing/usage/UsageTestSuiteNoDB.java
index 0721f7a..39126c9 100644
--- a/usage/src/test/java/com/ning/billing/usage/UsageTestSuiteNoDB.java
+++ b/usage/src/test/java/com/ning/billing/usage/UsageTestSuiteNoDB.java
@@ -29,16 +29,16 @@ import com.google.inject.Injector;
 public class UsageTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
 
     @BeforeClass(groups = "fast")
-    protected void setup() throws Exception {
-        final Injector injector = Guice.createInjector(new TestUsageModuleNoDB());
+    protected void beforeClass() throws Exception {
+        final Injector injector = Guice.createInjector(new TestUsageModuleNoDB(configSource));
         injector.injectMembers(this);
     }
 
     @BeforeMethod(groups = "fast")
-    public void setupTest() {
+    public void beforeMethod() {
     }
 
     @AfterMethod(groups = "fast")
-    public void cleanupTest() {
+    public void afterMethod() {
     }
 }
diff --git a/usage/src/test/java/com/ning/billing/usage/UsageTestSuiteWithEmbeddedDB.java b/usage/src/test/java/com/ning/billing/usage/UsageTestSuiteWithEmbeddedDB.java
index 28ee828..cf3a123 100644
--- a/usage/src/test/java/com/ning/billing/usage/UsageTestSuiteWithEmbeddedDB.java
+++ b/usage/src/test/java/com/ning/billing/usage/UsageTestSuiteWithEmbeddedDB.java
@@ -29,16 +29,16 @@ import com.google.inject.Injector;
 public class UsageTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWithEmbeddedDB {
 
     @BeforeClass(groups = "slow")
-    protected void setup() throws Exception {
-        final Injector injector = Guice.createInjector(new TestUsageModuleWithEmbeddedDB());
+    protected void beforeClass() throws Exception {
+        final Injector injector = Guice.createInjector(new TestUsageModuleWithEmbeddedDB(configSource));
         injector.injectMembers(this);
     }
 
     @BeforeMethod(groups = "fast")
-    public void setupTest() {
+    public void beforeMethod() throws Exception {
     }
 
     @AfterMethod(groups = "fast")
-    public void cleanupTest() {
+    public void afterMethod() {
     }
 }

util/pom.xml 15(+0 -15)

diff --git a/util/pom.xml b/util/pom.xml
index e0a65a8..a9f388c 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -134,19 +134,4 @@
             <artifactId>jackson-dataformat-csv</artifactId>
         </dependency>
     </dependencies>
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-jar-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>test-jar</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-        </plugins>
-    </build>
 </project>
diff --git a/util/src/main/java/com/ning/billing/util/bus/PersistentBusConfig.java b/util/src/main/java/com/ning/billing/util/bus/PersistentBusConfig.java
index cf27ca6..2a3822b 100644
--- a/util/src/main/java/com/ning/billing/util/bus/PersistentBusConfig.java
+++ b/util/src/main/java/com/ning/billing/util/bus/PersistentBusConfig.java
@@ -13,10 +13,12 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
+
 package com.ning.billing.util.bus;
 
 import org.skife.config.Config;
 import org.skife.config.Default;
+import org.skife.config.Description;
 
 import com.ning.billing.util.config.PersistentQueueConfig;
 
@@ -24,15 +26,18 @@ public interface PersistentBusConfig extends PersistentQueueConfig {
 
     @Override
     @Config("killbill.billing.util.persistent.bus.sleep")
-    @Default("500")
+    @Default("3000")
+    @Description("Time in milliseconds to sleep between runs")
     public long getSleepTimeMs();
 
+    @Override
     @Config("killbill.billing.util.persistent.bus.off")
     @Default("false")
-    @Override
+    @Description("Whether to turn off the persistent bus")
     public boolean isProcessingOff();
 
     @Config("killbill.billing.util.persistent.bus.nbThreads")
     @Default("3")
+    @Description("Number of threads to use")
     public int getNbThreads();
 }
diff --git a/util/src/main/java/com/ning/billing/util/config/CacheConfig.java b/util/src/main/java/com/ning/billing/util/config/CacheConfig.java
index b10edb9..d5785e0 100644
--- a/util/src/main/java/com/ning/billing/util/config/CacheConfig.java
+++ b/util/src/main/java/com/ning/billing/util/config/CacheConfig.java
@@ -18,11 +18,13 @@ package com.ning.billing.util.config;
 
 import org.skife.config.Config;
 import org.skife.config.Default;
+import org.skife.config.Description;
 
 public interface CacheConfig extends KillbillConfig {
 
     @Config("killbill.cache.config.location")
     @Default("/ehcache.xml")
+    @Description("Path to Ehcache XML configuration")
     public String getCacheConfigLocation();
 
 }
diff --git a/util/src/main/java/com/ning/billing/util/config/CatalogConfig.java b/util/src/main/java/com/ning/billing/util/config/CatalogConfig.java
index a9779b8..3a6cc3e 100644
--- a/util/src/main/java/com/ning/billing/util/config/CatalogConfig.java
+++ b/util/src/main/java/com/ning/billing/util/config/CatalogConfig.java
@@ -18,10 +18,12 @@ package com.ning.billing.util.config;
 
 import org.skife.config.Config;
 import org.skife.config.Default;
+import org.skife.config.Description;
 
 public interface CatalogConfig extends KillbillConfig {
+
     @Config("killbill.catalog.uri")
-    @Default("jar:///com/ning/billing/irs/catalog/Catalog.xml")
+    @Default("SpyCarBasic.xml")
+    @Description("Catalog location. Either in the classpath or in the filesystem")
     String getCatalogURI();
-
 }
diff --git a/util/src/main/java/com/ning/billing/util/config/InvoiceConfig.java b/util/src/main/java/com/ning/billing/util/config/InvoiceConfig.java
index 73db4fc..0a16915 100644
--- a/util/src/main/java/com/ning/billing/util/config/InvoiceConfig.java
+++ b/util/src/main/java/com/ning/billing/util/config/InvoiceConfig.java
@@ -18,13 +18,17 @@ package com.ning.billing.util.config;
 
 import org.skife.config.Config;
 import org.skife.config.Default;
+import org.skife.config.Description;
 
 public interface InvoiceConfig extends KillbillConfig {
+
     @Config("killbill.invoice.maxNumberOfMonthsInFuture")
     @Default("36")
+    @Description("Maximum target date to consider when generating an invoice")
     public int getNumberOfMonthsInFuture();
 
     @Config("killbill.invoice.emailNotificationsEnabled")
     @Default("false")
+    @Description("Whether to send email notifications on invoice creation (for configured accounts)")
     public boolean isEmailNotificationsEnabled();
 }
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 6e929cf..f4e289b 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
@@ -18,29 +18,65 @@ package com.ning.billing.util.config;
 
 import org.skife.config.Config;
 import org.skife.config.Default;
+import org.skife.config.Description;
 
 public interface OSGIConfig extends KillbillConfig {
 
+    @Config("killbill.osgi.bundle.property.name")
+    @Default("killbill.properties")
+    @Description("Name of the properties file for OSGI plugins")
+    public String getOSGIKillbillPropertyName();
+
     @Config("killbill.osgi.root.dir")
     @Default("/var/tmp/felix")
+    @Description("Bundles cache area for the OSGI framework")
     public String getOSGIBundleRootDir();
 
     @Config("killbill.osgi.bundle.cache.name")
     @Default("osgi-cache")
+    @Description("Bundles cache name")
     public String getOSGIBundleCacheName();
 
-
     @Config("killbill.osgi.bundle.install.dir")
     @Default("/var/tmp/bundles")
+    @Description("Bundles install directory")
     public String getRootInstallationDir();
 
     @Config("killbill.osgi.system.bundle.export.packages")
-    @Default("com.ning.billing.account.api,com.ning.billing.beatrix.bus.api,com.ning.billing.payment.plugin.api,com.ning.billing.osgi.api.config,com.ning.billing.util.callcontext,com.google.common.eventbus")
+    @Default("com.ning.billing.account.api," +
+             "com.ning.billing.analytics.api.sanity," +
+             "com.ning.billing.analytics.api.user," +
+             "com.ning.billing.beatrix.bus.api," +
+             "com.ning.billing.catalog.api," +
+             "com.ning.billing.entitlement.api.migration," +
+             "com.ning.billing.entitlement.api.timeline," +
+             "com.ning.billing.entitlement.api.transfer," +
+             "com.ning.billing.entitlement.api.user," +
+             "com.ning.billing.invoice.api," +
+             "com.ning.billing," +
+             "com.ning.billing.osgi.api," +
+             "com.ning.billing.osgi.api.config," +
+             "com.ning.billing.overdue," +
+             "com.ning.billing.payment.api," +
+             "com.ning.billing.payment.plugin.api," +
+             "com.ning.billing.tenant.api," +
+             "com.ning.billing.usage.api," +
+             "com.ning.billing.util.api," +
+             "com.ning.billing.util.callcontext," +
+             // javax.servlet and javax.servlet.http are not exported by default - we
+             // need the bundles to see them for them to be able to register their servlets.
+             // Note: bundles should mark javax.servlet:servlet-api as provided
+             "javax.servlet;version=3.0," +
+             "javax.servlet.http;version=3.0," +
+             "org.osgi.service.log;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")
+    @Description("Packages to export from the system bundle")
     public String getSystemBundleExportPackages();
-
-    // TODO FIXME OSGI
-    @Config("killbill.osgi.jruby.bundle.path")
-    @Default("file:/var/tmp/killbill-osgi-jruby-bundle-0.1.51-SNAPSHOT-jar-with-dependencies.jar")
-    public String getJrubyBundlePath();
-
 }
diff --git a/util/src/main/java/com/ning/billing/util/config/PaymentConfig.java b/util/src/main/java/com/ning/billing/util/config/PaymentConfig.java
index 64ae12d..d4d7ad0 100644
--- a/util/src/main/java/com/ning/billing/util/config/PaymentConfig.java
+++ b/util/src/main/java/com/ning/billing/util/config/PaymentConfig.java
@@ -20,15 +20,19 @@ import java.util.List;
 
 import org.skife.config.Config;
 import org.skife.config.Default;
-
+import org.skife.config.Description;
 
 public interface PaymentConfig extends KillbillConfig {
+
     @Config("killbill.payment.provider.default")
-    @Default("noop")
+    // See ExternalPaymentProviderPlugin.PLUGIN_NAME
+    @Default("__external_payment__")
+    @Description("Default payment provider to use")
     public String getDefaultPaymentProvider();
 
     @Config("killbill.payment.retry.days")
     @Default("8,8,8")
+    @Description("Interval in days between payment retries")
     public List<Integer> getPaymentRetryDays();
 
     @Config("killbill.payment.failure.retry.start.sec")
@@ -41,9 +45,11 @@ public interface PaymentConfig extends KillbillConfig {
 
     @Config("killbill.payment.failure.retry.max.attempts")
     @Default("8")
+    @Description("Maximum number of retries for failed payments")
     public int getPluginFailureRetryMaxAttempts();
 
     @Config("killbill.payment.off")
     @Default("false")
+    @Description("Whether the payment subsystem is off")
     public boolean isPaymentOff();
 }
diff --git a/util/src/main/java/com/ning/billing/util/email/EmailConfig.java b/util/src/main/java/com/ning/billing/util/email/EmailConfig.java
index 60513d9..175f89e 100644
--- a/util/src/main/java/com/ning/billing/util/email/EmailConfig.java
+++ b/util/src/main/java/com/ning/billing/util/email/EmailConfig.java
@@ -19,39 +19,49 @@ package com.ning.billing.util.email;
 import org.skife.config.Config;
 import org.skife.config.Default;
 import org.skife.config.DefaultNull;
+import org.skife.config.Description;
 
 import com.ning.billing.util.config.KillbillConfig;
 
 public interface EmailConfig extends KillbillConfig {
+
     @Config("killbill.mail.smtp.host")
     @DefaultNull
+    @Description("MTA host used for email notifications")
     public String getSmtpServerName();
 
     @Config("killbill.mail.smtp.port")
     @DefaultNull
+    @Description("MTA port used for email notifications")
     public int getSmtpPort();
 
     @Config("killbill.mail.smtp.auth")
     @Default("false")
+    @Description("Whether to authenticate against the MTA")
     public boolean useSmtpAuth();
 
     @Config("killbill.mail.smtp.user")
     @DefaultNull
+    @Description("Username to use to authenticate against the MTA")
     public String getSmtpUserName();
 
     @Config("killbill.mail.smtp.password")
     @DefaultNull
+    @Description("Password to use to authenticate against the MTA")
     public String getSmtpPassword();
 
     @Config("killbill.mail.from")
     @Default("support@example.com")
+    @Description("Default From: field for email notifications")
     String getDefaultFrom();
 
     @Config("killbill.mail.useSSL")
     @Default("false")
+    @Description("Whether to use secure SMTP")
     boolean useSSL();
 
     @Config("killbill.mail.invoiceEmailSubject")
     @Default("Your invoice")
+    @Description("Default Subject: field for invoice notifications")
     String getInvoiceEmailSubject();
 }
diff --git a/util/src/main/java/com/ning/billing/util/email/EmailModule.java b/util/src/main/java/com/ning/billing/util/email/EmailModule.java
index e8574cd..55d9cb4 100644
--- a/util/src/main/java/com/ning/billing/util/email/EmailModule.java
+++ b/util/src/main/java/com/ning/billing/util/email/EmailModule.java
@@ -16,13 +16,21 @@
 
 package com.ning.billing.util.email;
 
+import org.skife.config.ConfigSource;
 import org.skife.config.ConfigurationObjectFactory;
 
 import com.google.inject.AbstractModule;
 
 public class EmailModule extends AbstractModule {
+
+    protected final ConfigSource configSource;
+
+    public EmailModule(final ConfigSource configSource) {
+        this.configSource = configSource;
+    }
+
     protected void installEmailConfig() {
-        final EmailConfig config = new ConfigurationObjectFactory(System.getProperties()).build(EmailConfig.class);
+        final EmailConfig config = new ConfigurationObjectFactory(configSource).build(EmailConfig.class);
         bind(EmailConfig.class).toInstance(config);
     }
 
diff --git a/util/src/main/java/com/ning/billing/util/events/PaymentInfoInternalEvent.java b/util/src/main/java/com/ning/billing/util/events/PaymentInfoInternalEvent.java
index 33f4f6f..956718c 100644
--- a/util/src/main/java/com/ning/billing/util/events/PaymentInfoInternalEvent.java
+++ b/util/src/main/java/com/ning/billing/util/events/PaymentInfoInternalEvent.java
@@ -13,6 +13,7 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
+
 package com.ning.billing.util.events;
 
 import java.math.BigDecimal;
@@ -37,8 +38,4 @@ public interface PaymentInfoInternalEvent extends BusInternalEvent {
     public Integer getPaymentNumber();
 
     public PaymentStatus getStatus();
-
-    public String getExtFirstPaymentRefId();
-
-    public String getExtSecondPaymentRefId();
 }
diff --git a/util/src/main/java/com/ning/billing/util/glue/BusModule.java b/util/src/main/java/com/ning/billing/util/glue/BusModule.java
index b736ea4..7712d6c 100644
--- a/util/src/main/java/com/ning/billing/util/glue/BusModule.java
+++ b/util/src/main/java/com/ning/billing/util/glue/BusModule.java
@@ -18,7 +18,6 @@ package com.ning.billing.util.glue;
 
 import org.skife.config.ConfigSource;
 import org.skife.config.ConfigurationObjectFactory;
-import org.skife.config.SimplePropertyConfigSource;
 
 import com.ning.billing.util.bus.DefaultBusService;
 import com.ning.billing.util.bus.InMemoryInternalBus;
@@ -34,19 +33,11 @@ public class BusModule extends AbstractModule {
     private final BusType type;
     private final ConfigSource configSource;
 
-    public BusModule() {
-        this(BusType.PERSISTENT);
-    }
-
     public BusModule(final ConfigSource configSource) {
         this(BusType.PERSISTENT, configSource);
     }
 
-    public BusModule(final BusType type) {
-        this(type, new SimplePropertyConfigSource(System.getProperties()));
-    }
-
-    public BusModule(final BusType type, final ConfigSource configSource) {
+    protected BusModule(final BusType type, final ConfigSource configSource) {
         this.type = type;
         this.configSource = configSource;
     }
diff --git a/util/src/main/java/com/ning/billing/util/glue/CacheModule.java b/util/src/main/java/com/ning/billing/util/glue/CacheModule.java
index 827367c..df3c014 100644
--- a/util/src/main/java/com/ning/billing/util/glue/CacheModule.java
+++ b/util/src/main/java/com/ning/billing/util/glue/CacheModule.java
@@ -20,7 +20,6 @@ import java.util.UUID;
 
 import org.skife.config.ConfigSource;
 import org.skife.config.ConfigurationObjectFactory;
-import org.skife.config.SimplePropertyConfigSource;
 
 import com.ning.billing.util.cache.AccountRecordIdCacheLoader;
 import com.ning.billing.util.cache.Cachable;
@@ -47,10 +46,6 @@ public class CacheModule extends AbstractModule {
 
     private final ConfigSource configSource;
 
-    public CacheModule() {
-        this(new SimplePropertyConfigSource(System.getProperties()));
-    }
-
     public CacheModule(final ConfigSource configSource) {
         this.configSource = configSource;
     }
diff --git a/util/src/main/java/com/ning/billing/util/glue/NotificationQueueModule.java b/util/src/main/java/com/ning/billing/util/glue/NotificationQueueModule.java
index a5140e8..97f2a13 100644
--- a/util/src/main/java/com/ning/billing/util/glue/NotificationQueueModule.java
+++ b/util/src/main/java/com/ning/billing/util/glue/NotificationQueueModule.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.util.glue;
 
+import org.skife.config.ConfigSource;
 import org.skife.config.ConfigurationObjectFactory;
 
 import com.ning.billing.util.notificationq.DefaultNotificationQueueService;
@@ -26,9 +27,14 @@ import com.google.inject.AbstractModule;
 
 public class NotificationQueueModule extends AbstractModule {
 
+    protected final ConfigSource configSource;
+
+    public NotificationQueueModule(final ConfigSource configSource) {
+        this.configSource = configSource;
+    }
 
     protected void configureNotificationQueueConfig() {
-        final NotificationQueueConfig config = new ConfigurationObjectFactory(System.getProperties()).build(NotificationQueueConfig.class);
+        final NotificationQueueConfig config = new ConfigurationObjectFactory(configSource).build(NotificationQueueConfig.class);
         bind(NotificationQueueConfig.class).toInstance(config);
     }
     @Override
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueueConfig.java b/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueueConfig.java
index 7c7bab1..dfc8678 100644
--- a/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueueConfig.java
+++ b/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueueConfig.java
@@ -18,6 +18,7 @@ package com.ning.billing.util.notificationq;
 
 import org.skife.config.Config;
 import org.skife.config.Default;
+import org.skife.config.Description;
 
 import com.ning.billing.util.config.PersistentQueueConfig;
 
@@ -25,16 +26,18 @@ public interface NotificationQueueConfig extends PersistentQueueConfig {
 
     @Config("killbill.billing.util.notificationq.prefetch")
     @Default("7")
+    @Description("Number of notifications to fetch from the database at once")
     public int getPrefetchAmount();
 
     @Override
     @Config("killbill.billing.util.notificationq.sleep")
-    @Default("500")
+    @Default("3000")
+    @Description("Time in milliseconds to sleep between runs")
     public long getSleepTimeMs();
 
+    @Override
     @Config("killbill.billing.util.notificationq.notification.off")
     @Default("false")
-    @Override
+    @Description("Whether to turn off the notification queue")
     public boolean isProcessingOff();
-
 }
diff --git a/util/src/main/java/com/ning/billing/util/svcapi/account/AccountInternalApi.java b/util/src/main/java/com/ning/billing/util/svcapi/account/AccountInternalApi.java
index b86da74..4d6127a 100644
--- a/util/src/main/java/com/ning/billing/util/svcapi/account/AccountInternalApi.java
+++ b/util/src/main/java/com/ning/billing/util/svcapi/account/AccountInternalApi.java
@@ -41,5 +41,5 @@ public interface AccountInternalApi {
 
     public void updatePaymentMethod(UUID accountId, UUID paymentMethodId, InternalCallContext context) throws AccountApiException;
 
-    public UUID getByRecordId(Long recordId, InternalCallContext context) throws AccountApiException;
+    public UUID getByRecordId(Long recordId, InternalTenantContext context) throws AccountApiException;
 }
diff --git a/util/src/main/java/com/ning/billing/util/svcapi/payment/PaymentInternalApi.java b/util/src/main/java/com/ning/billing/util/svcapi/payment/PaymentInternalApi.java
index f0d144a..79937ae 100644
--- a/util/src/main/java/com/ning/billing/util/svcapi/payment/PaymentInternalApi.java
+++ b/util/src/main/java/com/ning/billing/util/svcapi/payment/PaymentInternalApi.java
@@ -16,7 +16,6 @@
 
 package com.ning.billing.util.svcapi.payment;
 
-
 import java.util.List;
 import java.util.UUID;
 
@@ -31,12 +30,12 @@ public interface PaymentInternalApi {
     public Payment getPayment(UUID paymentId, InternalTenantContext context)
             throws PaymentApiException;
 
-    public PaymentMethod getPaymentMethod(Account account, UUID paymentMethodId, boolean withPluginDetail, InternalTenantContext context)
+    public PaymentMethod getPaymentMethodById(UUID paymentMethodId, InternalTenantContext context)
             throws PaymentApiException;
 
     public List<Payment> getAccountPayments(UUID accountId, InternalTenantContext context)
             throws PaymentApiException;
 
-    public List<PaymentMethod> getPaymentMethods(Account account, boolean withPluginDetail, InternalTenantContext context)
+    public List<PaymentMethod> getPaymentMethods(Account account, InternalTenantContext context)
             throws PaymentApiException;
 }
diff --git a/util/src/test/java/com/ning/billing/GuicyKillbillTestSuite.java b/util/src/test/java/com/ning/billing/GuicyKillbillTestSuite.java
index 86ab3ce..4c0617f 100644
--- a/util/src/test/java/com/ning/billing/GuicyKillbillTestSuite.java
+++ b/util/src/test/java/com/ning/billing/GuicyKillbillTestSuite.java
@@ -17,9 +17,12 @@
 package com.ning.billing;
 
 import java.lang.reflect.Method;
+import java.util.Properties;
 
 import javax.inject.Inject;
 
+import org.skife.config.ConfigSource;
+import org.skife.config.SimplePropertyConfigSource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.ITestResult;
@@ -48,21 +51,39 @@ public class GuicyKillbillTestSuite {
     protected ClockMock clock;
 
 
-    private final static ClockMock theStaticClock = new ClockMock();
+    private static final ClockMock theStaticClock = new ClockMock();
+
+    protected final ConfigSource configSource;
+
+    public GuicyKillbillTestSuite() {
+        final Properties properties = new Properties(System.getProperties());
+        properties.put("user.timezone", "UTC");
+
+        // Speed up the notification queue
+        properties.put("killbill.billing.util.notificationq.sleep", "100");
+        // Speed up the bus
+        properties.put("killbill.billing.util.persistent.bus.sleep", "100");
+        properties.put("killbill.billing.util.persistent.bus.nbThreads", "1");
+
+        configSource = new SimplePropertyConfigSource(properties);
+
+        // Ignore ehcache checks. Unfortunately, ehcache looks at system properties directly...
+        System.setProperty("net.sf.ehcache.skipUpdateCheck", "true");
+    }
 
     public static ClockMock getClock() {
         return theStaticClock;
     }
 
     @BeforeMethod(alwaysRun = true)
-    public void startTestSuite(final Method method) throws Exception {
+    public void beforeMethodAlwaysRun(final Method method) throws Exception {
         log.info("***************************************************************************************************");
         log.info("*** Starting test {}:{}", method.getDeclaringClass().getName(), method.getName());
         log.info("***************************************************************************************************");
     }
 
     @AfterMethod(alwaysRun = true)
-    public void endTestSuite(final Method method, final ITestResult result) throws Exception {
+    public void afterMethodAlwaysRun(final Method method, final ITestResult result) throws Exception {
         log.info("***************************************************************************************************");
         log.info("***   Ending test {}:{} {} ({} s.)", new Object[]{method.getDeclaringClass().getName(), method.getName(),
                 result.isSuccess() ? "SUCCESS" : "!!! FAILURE !!!",
diff --git a/util/src/test/java/com/ning/billing/GuicyKillbillTestSuiteWithEmbeddedDB.java b/util/src/test/java/com/ning/billing/GuicyKillbillTestSuiteWithEmbeddedDB.java
index 8ec407e..cfc6f22 100644
--- a/util/src/test/java/com/ning/billing/GuicyKillbillTestSuiteWithEmbeddedDB.java
+++ b/util/src/test/java/com/ning/billing/GuicyKillbillTestSuiteWithEmbeddedDB.java
@@ -16,10 +16,6 @@
 
 package com.ning.billing;
 
-import java.io.IOException;
-import java.net.URISyntaxException;
-import java.sql.SQLException;
-
 import javax.inject.Inject;
 
 import org.skife.jdbi.v2.IDBI;
@@ -30,8 +26,6 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.BeforeSuite;
 
 import com.ning.billing.dbi.DBTestingHelper;
-import com.ning.billing.dbi.H2TestingHelper;
-import com.ning.billing.dbi.MysqlTestingHelper;
 
 public class GuicyKillbillTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuite {
 
@@ -49,15 +43,14 @@ public class GuicyKillbillTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuite
     }
 
     @BeforeSuite(groups = {"slow", "mysql"})
-    public void startMysqlBeforeTestSuite() throws IOException, ClassNotFoundException, SQLException, URISyntaxException {
-
+    public void beforeSuite() throws Exception {
         GuicyKillbillTestWithEmbeddedDBModule.getDBTestingHelper().start();
         GuicyKillbillTestWithEmbeddedDBModule.getDBTestingHelper().initDb();
         GuicyKillbillTestWithEmbeddedDBModule.getDBTestingHelper().cleanupAllTables();
     }
 
     @BeforeMethod(groups = {"slow", "mysql"})
-    public void cleanupTablesBetweenMethods() {
+    public void beforeMethod() throws Exception {
         try {
             GuicyKillbillTestWithEmbeddedDBModule.getDBTestingHelper().cleanupAllTables();
         } catch (Exception ignored) {
@@ -65,7 +58,7 @@ public class GuicyKillbillTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuite
     }
 
     @AfterSuite(groups = {"slow", "mysql"})
-    public void shutdownMysqlAfterTestSuite() throws IOException, ClassNotFoundException, SQLException, URISyntaxException {
+    public void afterSuite() throws Exception {
         if (hasFailed()) {
             log.error("**********************************************************************************************");
             log.error("*** TESTS HAVE FAILED - LEAVING DB RUNNING FOR DEBUGGING - MAKE SURE TO KILL IT ONCE DONE ****");
diff --git a/util/src/test/java/com/ning/billing/mock/glue/MockNotificationQueueModule.java b/util/src/test/java/com/ning/billing/mock/glue/MockNotificationQueueModule.java
index 93ef001..46fb7d9 100644
--- a/util/src/test/java/com/ning/billing/mock/glue/MockNotificationQueueModule.java
+++ b/util/src/test/java/com/ning/billing/mock/glue/MockNotificationQueueModule.java
@@ -16,18 +16,23 @@
 
 package com.ning.billing.mock.glue;
 
+import org.skife.config.ConfigSource;
 import org.skife.config.ConfigurationObjectFactory;
 
+import com.ning.billing.util.glue.NotificationQueueModule;
 import com.ning.billing.util.notificationq.MockNotificationQueueService;
 import com.ning.billing.util.notificationq.NotificationQueueConfig;
 import com.ning.billing.util.notificationq.NotificationQueueService;
 
-import com.google.inject.AbstractModule;
+public class MockNotificationQueueModule extends NotificationQueueModule {
 
-public class MockNotificationQueueModule extends AbstractModule {
+    public MockNotificationQueueModule(final ConfigSource configSource) {
+        super(configSource);
+    }
 
+    @Override
     protected void configureNotificationQueueConfig() {
-        final NotificationQueueConfig config = new ConfigurationObjectFactory(System.getProperties()).build(NotificationQueueConfig.class);
+        final NotificationQueueConfig config = new ConfigurationObjectFactory(configSource).build(NotificationQueueConfig.class);
         bind(NotificationQueueConfig.class).toInstance(config);
     }
     @Override
diff --git a/util/src/test/java/com/ning/billing/payment/plugin/api/PaymentPluginApiWithTestControl.java b/util/src/test/java/com/ning/billing/payment/plugin/api/PaymentPluginApiWithTestControl.java
new file mode 100644
index 0000000..9336aab
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/payment/plugin/api/PaymentPluginApiWithTestControl.java
@@ -0,0 +1,26 @@
+/*
+ * 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.payment.plugin.api;
+
+public interface PaymentPluginApiWithTestControl extends PaymentPluginApi {
+
+    public void setPaymentPluginApiExceptionOnNextCalls(PaymentPluginApiException e);
+
+    public void setPaymentRuntimeExceptionOnNextCalls(RuntimeException e);
+
+    public void resetToNormalbehavior();
+}
diff --git a/util/src/test/java/com/ning/billing/util/audit/api/TestDefaultAuditUserApi.java b/util/src/test/java/com/ning/billing/util/audit/api/TestDefaultAuditUserApi.java
index 955653f..9f14e5b 100644
--- a/util/src/test/java/com/ning/billing/util/audit/api/TestDefaultAuditUserApi.java
+++ b/util/src/test/java/com/ning/billing/util/audit/api/TestDefaultAuditUserApi.java
@@ -58,11 +58,13 @@ public class TestDefaultAuditUserApi extends AuditLogsTestBase {
 
     @Override
     @BeforeClass(groups = "fast")
-    public void setup() throws Exception {
-        super.setup();
+    public void beforeClass() throws Exception {
+        super.beforeClass();
         auditLogs = ImmutableList.<AuditLog>of(createAuditLog(), createAuditLog(), createAuditLog(), createAuditLog());
         objectIds = ImmutableList.<UUID>of(UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID());
-        super.setup();
+
+//        super.setup();
+
         for (final TableName tableName : TableName.values()) {
             for (final UUID objectId : objectIds) {
                 for (final AuditLog auditLog : auditLogs) {
diff --git a/util/src/test/java/com/ning/billing/util/bus/InMemoryBusModule.java b/util/src/test/java/com/ning/billing/util/bus/InMemoryBusModule.java
index e178bad..21034e5 100644
--- a/util/src/test/java/com/ning/billing/util/bus/InMemoryBusModule.java
+++ b/util/src/test/java/com/ning/billing/util/bus/InMemoryBusModule.java
@@ -22,10 +22,6 @@ import com.ning.billing.util.glue.BusModule;
 
 public class InMemoryBusModule extends BusModule {
 
-    public InMemoryBusModule() {
-        super(BusType.MEMORY);
-    }
-
     public InMemoryBusModule(final ConfigSource configSource) {
         super(BusType.MEMORY, configSource);
     }
diff --git a/util/src/test/java/com/ning/billing/util/bus/TestEventBus.java b/util/src/test/java/com/ning/billing/util/bus/TestEventBus.java
index 79533d8..d411843 100644
--- a/util/src/test/java/com/ning/billing/util/bus/TestEventBus.java
+++ b/util/src/test/java/com/ning/billing/util/bus/TestEventBus.java
@@ -27,8 +27,8 @@ public class TestEventBus extends UtilTestSuiteNoDB {
 
     @Override
     @BeforeClass(groups = "fast")
-    public void setup() throws Exception {
-        super.setup();
+    public void beforeClass() throws Exception {
+        super.beforeClass();
         testEventBusBase = new TestEventBusBase(eventBus, internalCallContext);
     }
 
diff --git a/util/src/test/java/com/ning/billing/util/bus/TestPersistentEventBus.java b/util/src/test/java/com/ning/billing/util/bus/TestPersistentEventBus.java
index 6e339f5..c9a6c60 100644
--- a/util/src/test/java/com/ning/billing/util/bus/TestPersistentEventBus.java
+++ b/util/src/test/java/com/ning/billing/util/bus/TestPersistentEventBus.java
@@ -28,8 +28,8 @@ public class TestPersistentEventBus extends UtilTestSuiteWithEmbeddedDB {
 
     @Override
     @BeforeClass(groups = "slow")
-    public void setup() throws Exception {
-        super.setup();
+    public void beforeClass() throws Exception {
+        super.beforeClass();
         testEventBusBase = new TestEventBusBase(eventBus, internalCallContext);
     }
 
diff --git a/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritance.java b/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritance.java
index 17399c3..4daf73a 100644
--- a/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritance.java
+++ b/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritance.java
@@ -36,16 +36,16 @@ public class TestStringTemplateInheritance extends UtilTestSuiteNoDB {
 
     @Override
     @BeforeMethod(groups = "fast")
-    public void setupTest() throws Exception {
-        super.setupTest();
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         entityStream = this.getClass().getResourceAsStream("/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg");
         kombuchaStream = this.getClass().getResourceAsStream("/com/ning/billing/util/dao/Kombucha.sql.stg");
     }
 
     @Override
     @AfterMethod(groups = "fast")
-    public void cleanupTest() throws Exception {
-        super.cleanupTest();
+    public void afterMethod() throws Exception {
+        super.afterMethod();
         if (entityStream != null) {
             entityStream.close();
         }
diff --git a/util/src/test/java/com/ning/billing/util/email/DefaultCatalogTranslationTest.java b/util/src/test/java/com/ning/billing/util/email/DefaultCatalogTranslationTest.java
index a5e0d08..d7f3ed4 100644
--- a/util/src/test/java/com/ning/billing/util/email/DefaultCatalogTranslationTest.java
+++ b/util/src/test/java/com/ning/billing/util/email/DefaultCatalogTranslationTest.java
@@ -37,8 +37,8 @@ public class DefaultCatalogTranslationTest extends UtilTestSuiteNoDB {
 
     @Override
     @BeforeClass(groups = "fast")
-    public void setup() throws Exception {
-        super.setup();
+    public void beforeClass() throws Exception {
+        super.beforeClass();
         final ConfigSource configSource = new ConfigSource() {
             private final Map<String, String> properties = ImmutableMap.<String, String>of("killbill.template.invoiceFormatterFactoryClass",
                                                                                            "com.ning.billing.mock.MockInvoiceFormatterFactory");
diff --git a/util/src/test/java/com/ning/billing/util/email/EmailSenderTest.java b/util/src/test/java/com/ning/billing/util/email/EmailSenderTest.java
index 367ffd1..2ac6509 100644
--- a/util/src/test/java/com/ning/billing/util/email/EmailSenderTest.java
+++ b/util/src/test/java/com/ning/billing/util/email/EmailSenderTest.java
@@ -29,7 +29,8 @@ public class EmailSenderTest extends UtilTestSuiteNoDB {
     private EmailConfig config;
 
     @BeforeClass
-    public void setup() {
+    public void beforeClass() throws Exception {
+        super.beforeClass();
         config = new ConfigurationObjectFactory(System.getProperties()).build(EmailConfig.class);
     }
 
diff --git a/util/src/test/java/com/ning/billing/util/glue/TestUtilModule.java b/util/src/test/java/com/ning/billing/util/glue/TestUtilModule.java
index f45f2e6..d2932fa 100644
--- a/util/src/test/java/com/ning/billing/util/glue/TestUtilModule.java
+++ b/util/src/test/java/com/ning/billing/util/glue/TestUtilModule.java
@@ -17,6 +17,7 @@
 package com.ning.billing.util.glue;
 
 import org.mockito.Mockito;
+import org.skife.config.ConfigSource;
 
 import com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi;
 
@@ -24,6 +25,12 @@ import com.google.inject.AbstractModule;
 
 public class TestUtilModule extends AbstractModule {
 
+    protected final ConfigSource configSource;
+
+    public TestUtilModule(final ConfigSource configSource) {
+        this.configSource = configSource;
+    }
+
     // TODO STEPH this is bad-- because DefaultAuditUserApi is using entitlementTimeline API
     public void installHack() {
         bind(EntitlementTimelineApi.class).toInstance(Mockito.mock(EntitlementTimelineApi.class));
@@ -32,7 +39,7 @@ public class TestUtilModule extends AbstractModule {
     @Override
     protected void configure() {
         //install(new CallContextModule());
-        install(new CacheModule());
+        install(new CacheModule(configSource));
 
         installHack();
     }
diff --git a/util/src/test/java/com/ning/billing/util/glue/TestUtilModuleNoDB.java b/util/src/test/java/com/ning/billing/util/glue/TestUtilModuleNoDB.java
index f7a7af6..ec302c8 100644
--- a/util/src/test/java/com/ning/billing/util/glue/TestUtilModuleNoDB.java
+++ b/util/src/test/java/com/ning/billing/util/glue/TestUtilModuleNoDB.java
@@ -16,6 +16,8 @@
 
 package com.ning.billing.util.glue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.GuicyKillbillTestNoDBModule;
 import com.ning.billing.mock.glue.MockGlobalLockerModule;
 import com.ning.billing.mock.glue.MockNonEntityDaoModule;
@@ -28,6 +30,9 @@ import com.ning.billing.util.bus.InMemoryBusModule;
 
 public class TestUtilModuleNoDB extends TestUtilModule {
 
+    public TestUtilModuleNoDB(final ConfigSource configSource) {
+        super(configSource);
+    }
 
     private void installAuditMock() {
         bind(AuditDao.class).toInstance(new MockAuditDao());
@@ -41,8 +46,8 @@ public class TestUtilModuleNoDB extends TestUtilModule {
 
         install(new MockNonEntityDaoModule());
         install(new MockGlobalLockerModule());
-        install(new InMemoryBusModule());
-        install(new MockNotificationQueueModule());
+        install(new InMemoryBusModule(configSource));
+        install(new MockNotificationQueueModule(configSource));
 
         installAuditMock();
     }
diff --git a/util/src/test/java/com/ning/billing/util/glue/TestUtilModuleWithEmbeddedDB.java b/util/src/test/java/com/ning/billing/util/glue/TestUtilModuleWithEmbeddedDB.java
index 5db3bf1..cdf1b98 100644
--- a/util/src/test/java/com/ning/billing/util/glue/TestUtilModuleWithEmbeddedDB.java
+++ b/util/src/test/java/com/ning/billing/util/glue/TestUtilModuleWithEmbeddedDB.java
@@ -16,10 +16,16 @@
 
 package com.ning.billing.util.glue;
 
+import org.skife.config.ConfigSource;
+
 import com.ning.billing.GuicyKillbillTestWithEmbeddedDBModule;
 
 public class TestUtilModuleWithEmbeddedDB extends TestUtilModule {
 
+    public TestUtilModuleWithEmbeddedDB(final ConfigSource configSource) {
+        super(configSource);
+    }
+
     @Override
     protected void configure() {
         super.configure();
@@ -28,8 +34,8 @@ public class TestUtilModuleWithEmbeddedDB extends TestUtilModule {
         install(new AuditModule());
         install(new TagStoreModule());
         install(new CustomFieldModule());
-        install(new BusModule());
-        install(new NotificationQueueModule());
+        install(new BusModule(configSource));
+        install(new NotificationQueueModule(configSource));
         install(new NonEntityDaoModule());
         install(new GlobalLockerModule());
     }
diff --git a/util/src/test/java/com/ning/billing/util/notificationq/dao/TestNotificationSqlDao.java b/util/src/test/java/com/ning/billing/util/notificationq/dao/TestNotificationSqlDao.java
index 7bab177..866b711 100644
--- a/util/src/test/java/com/ning/billing/util/notificationq/dao/TestNotificationSqlDao.java
+++ b/util/src/test/java/com/ning/billing/util/notificationq/dao/TestNotificationSqlDao.java
@@ -44,8 +44,8 @@ public class TestNotificationSqlDao extends UtilTestSuiteWithEmbeddedDB {
 
     @Override
     @BeforeClass(groups = "slow")
-    public void setup() throws Exception {
-        super.setup();
+    public void beforeClass() throws Exception {
+        super.beforeClass();
         dao = getDBI().onDemand(NotificationSqlDao.class);
     }
 
diff --git a/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueueService.java b/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueueService.java
index 1ae3a5c..6550c4c 100644
--- a/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueueService.java
+++ b/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueueService.java
@@ -43,9 +43,10 @@ public class MockNotificationQueueService extends NotificationQueueServiceBase {
     public int doProcessEvents() {
 
         int result = 0;
-
-        for (NotificationQueue cur : queues.values()) {
-            result += doProcessEventsForQueue((MockNotificationQueue) cur);
+        synchronized (queues) {
+            for (NotificationQueue cur : queues.values()) {
+                result += doProcessEventsForQueue((MockNotificationQueue) cur);
+            }
         }
         return result;
     }
diff --git a/util/src/test/java/com/ning/billing/util/notificationq/TestNotificationQueue.java b/util/src/test/java/com/ning/billing/util/notificationq/TestNotificationQueue.java
index 3018299..71a03f4 100644
--- a/util/src/test/java/com/ning/billing/util/notificationq/TestNotificationQueue.java
+++ b/util/src/test/java/com/ning/billing/util/notificationq/TestNotificationQueue.java
@@ -85,15 +85,15 @@ public class TestNotificationQueue extends UtilTestSuiteWithEmbeddedDB {
 
     @Override
     @BeforeClass(groups = "slow")
-    public void setup() throws Exception {
-        super.setup();
+    public void beforeClass() throws Exception {
+        super.beforeClass();
         entitySqlDaoTransactionalJdbiWrapper = new EntitySqlDaoTransactionalJdbiWrapper(getDBI(), clock, cacheControllerDispatcher, nonEntityDao);
     }
 
     @Override
     @BeforeMethod(groups = "slow")
-    public void setupTest() throws Exception {
-        super.setupTest();
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
 
         // Reset time to real value
         clock.resetDeltaFromReality();
diff --git a/util/src/test/java/com/ning/billing/util/tag/dao/TestDefaultTagDao.java b/util/src/test/java/com/ning/billing/util/tag/dao/TestDefaultTagDao.java
index 2d238ea..e1c95b0 100644
--- a/util/src/test/java/com/ning/billing/util/tag/dao/TestDefaultTagDao.java
+++ b/util/src/test/java/com/ning/billing/util/tag/dao/TestDefaultTagDao.java
@@ -60,17 +60,17 @@ public class TestDefaultTagDao extends UtilTestSuiteWithEmbeddedDB {
 
     @Override
     @BeforeMethod(groups = "slow")
-    public void setupTest() throws Exception {
-        super.setupTest();
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         eventsListener = new TestApiListener(null);
         eventBus.register(eventsListener);
     }
 
     @Override
     @AfterMethod(groups = "slow")
-    public void cleanupTest() throws Exception {
+    public void afterMethod() throws Exception {
         eventBus.unregister(eventsListener);
-        super.cleanupTest();
+        super.afterMethod();
     }
 
     @Test(groups = "slow")
diff --git a/util/src/test/java/com/ning/billing/util/tag/dao/TestDefaultTagDefinitionDao.java b/util/src/test/java/com/ning/billing/util/tag/dao/TestDefaultTagDefinitionDao.java
index b63e883..6ea6e0c 100644
--- a/util/src/test/java/com/ning/billing/util/tag/dao/TestDefaultTagDefinitionDao.java
+++ b/util/src/test/java/com/ning/billing/util/tag/dao/TestDefaultTagDefinitionDao.java
@@ -40,17 +40,17 @@ public class TestDefaultTagDefinitionDao extends UtilTestSuiteWithEmbeddedDB {
 
     @Override
     @BeforeMethod(groups = "slow")
-    public void setupTest() throws Exception {
-        super.setupTest();
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         eventsListener = new TestApiListener(null);
         eventBus.register(eventsListener);
     }
 
     @Override
     @AfterMethod(groups = "slow")
-    public void cleanupTest() throws Exception {
+    public void afterMethod() throws Exception {
         eventBus.unregister(eventsListener);
-        super.cleanupTest();
+        super.afterMethod();
     }
 
 
diff --git a/util/src/test/java/com/ning/billing/util/UtilTestSuiteNoDB.java b/util/src/test/java/com/ning/billing/util/UtilTestSuiteNoDB.java
index 64fa9df..2022493 100644
--- a/util/src/test/java/com/ning/billing/util/UtilTestSuiteNoDB.java
+++ b/util/src/test/java/com/ning/billing/util/UtilTestSuiteNoDB.java
@@ -54,18 +54,18 @@ public class UtilTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     protected AuditUserApi auditUserApi;
 
     @BeforeClass(groups = "fast")
-    public void setup() throws Exception {
-        final Injector g = Guice.createInjector(Stage.PRODUCTION, new TestUtilModuleNoDB());
+    public void beforeClass() throws Exception {
+        final Injector g = Guice.createInjector(Stage.PRODUCTION, new TestUtilModuleNoDB(configSource));
         g.injectMembers(this);
     }
 
     @BeforeMethod(groups = "fast")
-    public void setupTest() throws Exception {
+    public void beforeMethod() throws Exception {
         eventBus.start();
     }
 
     @AfterMethod(groups = "fast")
-    public void cleanupTest() throws Exception {
+    public void afterMethod() throws Exception {
         eventBus.stop();
     }
 
diff --git a/util/src/test/java/com/ning/billing/util/UtilTestSuiteWithEmbeddedDB.java b/util/src/test/java/com/ning/billing/util/UtilTestSuiteWithEmbeddedDB.java
index 617eaef..410b21a 100644
--- a/util/src/test/java/com/ning/billing/util/UtilTestSuiteWithEmbeddedDB.java
+++ b/util/src/test/java/com/ning/billing/util/UtilTestSuiteWithEmbeddedDB.java
@@ -19,10 +19,8 @@ package com.ning.billing.util;
 import javax.inject.Inject;
 
 import org.testng.annotations.AfterMethod;
-import org.testng.annotations.AfterTest;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.BeforeTest;
 
 import com.ning.billing.GuicyKillbillTestSuiteWithEmbeddedDB;
 import com.ning.billing.util.audit.dao.AuditDao;
@@ -71,18 +69,20 @@ public abstract class UtilTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuite
     protected AuditDao auditDao;
 
     @BeforeClass(groups = "slow")
-    public void setup() throws Exception {
-        final Injector g = Guice.createInjector(Stage.PRODUCTION, new TestUtilModuleWithEmbeddedDB());
+    public void beforeClass() throws Exception {
+        final Injector g = Guice.createInjector(Stage.PRODUCTION, new TestUtilModuleWithEmbeddedDB(configSource));
         g.injectMembers(this);
     }
 
+    @Override
     @BeforeMethod(groups = "slow")
-    public void setupTest() throws Exception  {
+    public void beforeMethod() throws Exception  {
+        super.beforeMethod();
         eventBus.start();
     }
 
     @AfterMethod(groups = "slow")
-    public void cleanupTest() throws Exception {
+    public void afterMethod() throws Exception {
         eventBus.stop();
     }
 
diff --git a/util/src/test/java/com/ning/billing/util/validation/TestValidationManager.java b/util/src/test/java/com/ning/billing/util/validation/TestValidationManager.java
index 0faa814..c7c8803 100644
--- a/util/src/test/java/com/ning/billing/util/validation/TestValidationManager.java
+++ b/util/src/test/java/com/ning/billing/util/validation/TestValidationManager.java
@@ -39,8 +39,8 @@ public class TestValidationManager extends UtilTestSuiteWithEmbeddedDB {
 
     @Override
     @BeforeClass(groups = "slow")
-    public void setup() throws Exception {
-        super.setup();
+    public void beforeClass() throws Exception {
+        super.beforeClass();
         final DatabaseSchemaDao dao = new DatabaseSchemaDao(getDBI());
         vm = new ValidationManager(dao);
         vm.loadSchemaInformation(helper.getDbName());