killbill-aplcache

Details

diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/inv_ent/MockModule.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/inv_ent/MockModule.java
index 8816190..28294f9 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/inv_ent/MockModule.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/inv_ent/MockModule.java
@@ -45,6 +45,7 @@ import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
 import com.ning.billing.util.bus.BusService;
 import com.ning.billing.util.glue.BusModule;
+import com.ning.billing.util.glue.GlobalLockerModule;
 import com.ning.billing.util.glue.NotificationQueueModule;
 
 
@@ -67,6 +68,7 @@ public class MockModule extends AbstractModule {
         install(new CatalogModule());
         install(new EntitlementModule());
         install(new InvoiceModule());
+        install(new GlobalLockerModule());
     }
 
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
index de8132e..22849d1 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
@@ -214,33 +214,6 @@ public class DefaultInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public boolean lockAccount(final UUID accountId) {
-        /*
-        try {
-            invoiceSqlDao.lockAccount(accountId.toString());
-            return true;
-        } catch (Exception e) {
-            log.error("Ouch! I broke", e);
-            return false;
-        }
-        */
-        return true;
-    }
-
-    @Override
-    public boolean releaseAccount(final UUID accountId) {
-        /*
-        try {
-            invoiceSqlDao.releaseAccount(accountId.toString());
-            return true;
-        } catch (Exception e) {
-            return false;
-        }
-        */
-        return true;
-    }
-
-    @Override
     public UUID getInvoiceIdByPaymentAttemptId(UUID paymentAttemptId) {
         return invoiceSqlDao.getInvoiceIdByPaymentAttemptId(paymentAttemptId.toString());
     }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
index a86cde7..7a7c280 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
@@ -53,9 +53,5 @@ public interface InvoiceDao {
 
     List<Invoice> getUnpaidInvoicesByAccountId(final UUID accountId, final DateTime upToDate);
 
-    boolean lockAccount(final UUID accountId);
-
-    boolean releaseAccount(final UUID accountId);
-
     void test();
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
index e580d05..05be556 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
@@ -78,7 +78,7 @@ public interface InvoiceSqlDao extends EntityDao<Invoice>, Transactional<Invoice
     @RegisterMapper(UuidMapper.class)
     List<UUID> getInvoicesForPayment(@Bind("targetDate") final Date targetDate,
                                     @Bind("numberOfDays") final int numberOfDays);
-    
+
     @SqlQuery
     @RegisterMapper(BalanceMapper.class)
     BigDecimal getAccountBalance(@Bind("accountId") final String accountId);
@@ -86,13 +86,6 @@ public interface InvoiceSqlDao extends EntityDao<Invoice>, Transactional<Invoice
     @SqlQuery
     List<Invoice> getUnpaidInvoicesByAccountId(@Bind("accountId") final String accountId,
                                                @Bind("upToDate") final Date upToDate);
-
-    @SqlUpdate
-    void lockAccount(@Bind("accountId") final String accountId);
-
-    @SqlUpdate
-    void releaseAccount(@Bind("accountId") final String accountId);
-    
     @BindingAnnotation(InvoiceBinder.InvoiceBinderFactory.class)
     @Retention(RetentionPolicy.RUNTIME)
     @Target({ElementType.PARAMETER})
@@ -130,7 +123,7 @@ public interface InvoiceSqlDao extends EntityDao<Invoice>, Transactional<Invoice
             return new DefaultInvoice(id, accountId, invoiceDate, targetDate, currency);
         }
     }
-      
+
     public static class BalanceMapper implements ResultSetMapper<BigDecimal> {
         @Override
         public BigDecimal map(final int index, final ResultSet result, final StatementContext context) throws SQLException {
diff --git a/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java b/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
index 174b44d..a86e063 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
@@ -43,15 +43,23 @@ import com.ning.billing.invoice.model.InvoiceGenerator;
 import com.ning.billing.invoice.model.InvoiceItemList;
 import com.ning.billing.invoice.notification.NextBillingDateEvent;
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.globalLocker.GlobalLock;
+import com.ning.billing.util.globalLocker.GlobalLocker;
+import com.ning.billing.util.globalLocker.GlobalLocker.LockerService;
+import com.ning.billing.util.globalLocker.LockFailedException;
 
 public class InvoiceListener {
+
+
     private final static Logger log = LoggerFactory.getLogger(InvoiceListener.class);
+    private final static int NB_LOCK_TRY = 5;
 
     private final InvoiceGenerator generator;
     private final EntitlementBillingApi entitlementBillingApi;
     private final AccountUserApi accountUserApi;
     private final InvoiceDao invoiceDao;
     private final Clock clock;
+    private final  GlobalLocker locker;
 
     private final static boolean VERBOSE_OUTPUT = false;
 
@@ -59,11 +67,13 @@ public class InvoiceListener {
     public InvoiceListener(final InvoiceGenerator generator, final AccountUserApi accountUserApi,
                            final EntitlementBillingApi entitlementBillingApi,
                            final InvoiceDao invoiceDao,
+                           final GlobalLocker locker,
                            final Clock clock) {
         this.generator = generator;
         this.entitlementBillingApi = entitlementBillingApi;
         this.accountUserApi = accountUserApi;
         this.invoiceDao = invoiceDao;
+        this.locker = locker;
         this.clock = clock;
     }
 
@@ -87,6 +97,7 @@ public class InvoiceListener {
     }
 
     private void processSubscription(final UUID subscriptionId, final DateTime targetDate) {
+
         if (subscriptionId == null) {
             log.error("Failed handling entitlement change.", new InvoiceApiException(ErrorCode.INVOICE_INVALID_TRANSITION));
             return;
@@ -95,52 +106,62 @@ public class InvoiceListener {
         UUID accountId = entitlementBillingApi.getAccountIdFromSubscriptionId(subscriptionId);
         if (accountId == null) {
             log.error("Failed handling entitlement change.",
-                      new InvoiceApiException(ErrorCode.INVOICE_NO_ACCOUNT_ID_FOR_SUBSCRIPTION_ID, subscriptionId.toString()));
+                    new InvoiceApiException(ErrorCode.INVOICE_NO_ACCOUNT_ID_FOR_SUBSCRIPTION_ID, subscriptionId.toString()));
             return;
         }
 
-        if (!invoiceDao.lockAccount(accountId)) {
-            log.warn("Conflicting lock detected from InvoiceListener on account "  + accountId.toString());
-        } else {
-            log.info("Locked " + accountId.toString());
+        GlobalLock lock = null;
+        try {
+            lock = locker.lockWithNumberOfTries(LockerService.INVOICE, accountId.toString(), NB_LOCK_TRY);
 
-            Account account = accountUserApi.getAccountById(accountId);
-            if (account == null) {
-                log.error("Failed handling entitlement change.",
-                          new InvoiceApiException(ErrorCode.INVOICE_ACCOUNT_ID_INVALID, accountId.toString()));
-                return;
+            processAccountWithLock(accountId, targetDate);
+
+        } catch (LockFailedException e) {
+            // Not good!
+            log.error(String.format("Failed to process invoice for account %s, subscription %s, targetDate %s",
+                    accountId.toString(), subscriptionId.toString(), targetDate), e);
+        } finally {
+            if (lock != null) {
+                lock.release();
             }
+        }
+    }
 
-            SortedSet<BillingEvent> events = entitlementBillingApi.getBillingEventsForAccount(accountId);
-            BillingEventSet billingEvents = new BillingEventSet(events);
+    private void processAccountWithLock(final UUID accountId, final DateTime targetDate) {
 
-            Currency targetCurrency = account.getCurrency();
+        Account account = accountUserApi.getAccountById(accountId);
+        if (account == null) {
+            log.error("Failed handling entitlement change.",
+                    new InvoiceApiException(ErrorCode.INVOICE_ACCOUNT_ID_INVALID, accountId.toString()));
+            return;
+        }
 
-            List<InvoiceItem> items = invoiceDao.getInvoiceItemsByAccount(accountId);
-            InvoiceItemList invoiceItemList = new InvoiceItemList(items);
-            Invoice invoice = generator.generateInvoice(accountId, billingEvents, invoiceItemList, targetDate, targetCurrency);
+        SortedSet<BillingEvent> events = entitlementBillingApi.getBillingEventsForAccount(accountId);
+        BillingEventSet billingEvents = new BillingEventSet(events);
 
-            if (invoice == null) {
-                log.info("Generated null invoice.");
-                outputDebugData(events, invoiceItemList);
-            } else {
-                log.info("Generated invoice {} with {} items.", invoice.getId().toString(), invoice.getNumberOfItems());
+        Currency targetCurrency = account.getCurrency();
 
-                if (VERBOSE_OUTPUT) {
-                    log.info("New items");
-                    for (InvoiceItem item : invoice.getInvoiceItems()) {
-                        log.info(item.toString());
-                    }
-                }
+        List<InvoiceItem> items = invoiceDao.getInvoiceItemsByAccount(accountId);
+        InvoiceItemList invoiceItemList = new InvoiceItemList(items);
+        Invoice invoice = generator.generateInvoice(accountId, billingEvents, invoiceItemList, targetDate, targetCurrency);
 
-                outputDebugData(events, invoiceItemList);
+        if (invoice == null) {
+            log.info("Generated null invoice.");
+            outputDebugData(events, invoiceItemList);
+        } else {
+            log.info("Generated invoice {} with {} items.", invoice.getId().toString(), invoice.getNumberOfItems());
 
-                if (invoice.getNumberOfItems() > 0) {
-                    invoiceDao.create(invoice);
+            if (VERBOSE_OUTPUT) {
+                log.info("New items");
+                for (InvoiceItem item : invoice.getInvoiceItems()) {
+                    log.info(item.toString());
                 }
             }
+            outputDebugData(events, invoiceItemList);
 
-            invoiceDao.releaseAccount(accountId);
+            if (invoice.getNumberOfItems() > 0) {
+                invoiceDao.create(invoice);
+            }
         }
     }
 
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
index 8a6fe38..10beb12 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
@@ -92,14 +92,6 @@ getUnpaidInvoicesByAccountId() ::= <<
   ORDER BY i.target_date ASC;
 >>
 
-lockAccount() ::= <<
-  INSERT INTO invoice_locking(account_id) VALUES (:accountId);
->>
-
-releaseAccount() ::= <<
-  DELETE FROM invoice_locking
-  WHERE account_id = :accountId;
->>
 
 test() ::= <<
   SELECT 1
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java b/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java
index 8332e8c..cf6c112 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java
@@ -215,14 +215,4 @@ public class MockInvoiceDao implements InvoiceDao {
 
         return unpaidInvoices;
     }
-
-    @Override
-    public boolean lockAccount(UUID accountId) {
-        return true;
-    }
-
-    @Override
-    public boolean releaseAccount(UUID accountId) {
-        return true;
-    }
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithEmbeddedDb.java b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithEmbeddedDb.java
index 53eca25..b252531 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithEmbeddedDb.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithEmbeddedDb.java
@@ -72,6 +72,7 @@ public class InvoiceModuleWithEmbeddedDb extends InvoiceModule {
         install(new AccountModule());
         install(new CatalogModule());
         install(new EntitlementModule());
+        install(new GlobalLockerModule());
 
         super.configure();
 
diff --git a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java
index 60015c1..ec36375 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/glue/InvoiceModuleWithMocks.java
@@ -18,12 +18,15 @@ package com.ning.billing.invoice.glue;
 
 import com.ning.billing.invoice.dao.InvoiceDao;
 import com.ning.billing.invoice.dao.MockInvoiceDao;
+import com.ning.billing.util.globalLocker.GlobalLocker;
+import com.ning.billing.util.globalLocker.MockGlobalLocker;
 
 public class InvoiceModuleWithMocks extends InvoiceModule {
     @Override
     protected void installInvoiceDao() {
         bind(MockInvoiceDao.class).asEagerSingleton();
         bind(InvoiceDao.class).to(MockInvoiceDao.class);
+        bind(GlobalLocker.class).to(MockGlobalLocker.class).asEagerSingleton();
     }
 
     @Override
diff --git a/util/src/main/java/com/ning/billing/util/globalLocker/GlobalLocker.java b/util/src/main/java/com/ning/billing/util/globalLocker/GlobalLocker.java
index 59adb44..d6ce0af 100644
--- a/util/src/main/java/com/ning/billing/util/globalLocker/GlobalLocker.java
+++ b/util/src/main/java/com/ning/billing/util/globalLocker/GlobalLocker.java
@@ -17,5 +17,24 @@
 package com.ning.billing.util.globalLocker;
 
 public interface GlobalLocker {
-    GlobalLock lockWithNumberOfTries(String lockName, int i);
+
+    GlobalLock lockWithNumberOfTries(final LockerService service, final String lockKey, final int retry);
+    Boolean isFree(final LockerService service, final String lockKey);
+
+    public enum LockerService {
+
+        // Only service needing global lock
+        INVOICE("invoice");
+
+        private final String svcName;
+
+        LockerService(String svcName) {
+            this.svcName = svcName;
+        }
+
+        @Override
+        public String toString() {
+            return svcName;
+        }
+    }
 }
\ No newline at end of file
diff --git a/util/src/main/java/com/ning/billing/util/globalLocker/MySqlGlobalLocker.java b/util/src/main/java/com/ning/billing/util/globalLocker/MySqlGlobalLocker.java
index 43a1993..762ddc7 100644
--- a/util/src/main/java/com/ning/billing/util/globalLocker/MySqlGlobalLocker.java
+++ b/util/src/main/java/com/ning/billing/util/globalLocker/MySqlGlobalLocker.java
@@ -19,16 +19,22 @@ package com.ning.billing.util.globalLocker;
 import com.google.inject.Inject;
 import org.skife.jdbi.v2.Handle;
 import org.skife.jdbi.v2.IDBI;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class MySqlGlobalLocker implements GlobalLocker {
+
+    private final static Logger logger = LoggerFactory.getLogger(MySqlGlobalLocker.class);
+
+    private final static long DEFAULT_TIMEOUT = 3L; // 3 seconds
+
     private final IDBI dbi;
     private long timeout;
 
     @Inject
-    public MySqlGlobalLocker(IDBI dbi)
-    {
+    public MySqlGlobalLocker(IDBI dbi) {
         this.dbi = dbi;
-        this.timeout = 1000L;
+        this.timeout = DEFAULT_TIMEOUT;
     }
 
     public void setTimeout(final long timeout) {
@@ -36,39 +42,65 @@ public class MySqlGlobalLocker implements GlobalLocker {
     }
 
     @Override
-    public GlobalLock lockWithNumberOfTries(final String lockName, final int i)
-    {
-        int tries_left = i;
+    public GlobalLock lockWithNumberOfTries(final LockerService service, final String lockKey, final int retry) {
+
+        final String lockName = getLockName(service, lockKey);
+        int tries_left = retry;
         while (tries_left-- > 0) {
             GlobalLock lock = lock(lockName);
             if (lock != null) {
                 return lock;
             }
         }
+        logger.error(String.format("Failed to acquire lock %s for service %s after %d retry", lockKey, service, retry));
         throw new LockFailedException();
     }
 
-    private GlobalLock lock(final String lockName) throws LockFailedException
-    {
+    private GlobalLock lock(final String lockName) throws LockFailedException {
+
         final Handle h = dbi.open();
         final MySqlGlobalLockerDao dao = h.attach(MySqlGlobalLockerDao.class);
 
         final boolean obtained = dao.lock(lockName, timeout);
         if (obtained) {
             return new GlobalLock() {
-                public void release()
-                {
+                @Override
+                public void release() {
                     try {
                         dao.releaseLock(lockName);
                     }
                     finally {
-                        h.close();
+                        if (h != null) {
+                            h.close();
+                        }
                     }
                 }
             };
-        }
-        else {
+        } else {
             return null;
         }
     }
+
+    @Override
+    public Boolean isFree(final LockerService service, final String lockKey) {
+
+        final String lockName = getLockName(service, lockKey);
+        final Handle h = dbi.open();
+        try {
+            final MySqlGlobalLockerDao dao = h.attach(MySqlGlobalLockerDao.class);
+            return dao.isFree(lockName);
+        } finally {
+            if (h != null) {
+                h.close();
+            }
+        }
+    }
+
+    private String getLockName(final LockerService service, final String lockKey) {
+        StringBuilder tmp = new StringBuilder()
+            .append(service.toString())
+            .append("-")
+            .append(lockKey);
+        return tmp.toString();
+    }
 }
diff --git a/util/src/main/java/com/ning/billing/util/globalLocker/MySqlGlobalLockerDao.java b/util/src/main/java/com/ning/billing/util/globalLocker/MySqlGlobalLockerDao.java
index d9ff452..14a02d4 100644
--- a/util/src/main/java/com/ning/billing/util/globalLocker/MySqlGlobalLockerDao.java
+++ b/util/src/main/java/com/ning/billing/util/globalLocker/MySqlGlobalLockerDao.java
@@ -27,12 +27,16 @@ import java.sql.SQLException;
 
 @RegisterMapper(MySqlGlobalLockerDao.LockMapper.class)
 public interface MySqlGlobalLockerDao {
+
     @SqlQuery("Select GET_LOCK(:lockName, :timeout);")
     public Boolean lock(@Bind("lockName") final String lockName, @Bind("timeout") final long timeout);
 
     @SqlQuery("Select RELEASE_LOCK(:lockName);")
     public Boolean releaseLock(@Bind("lockName") final String lockName);
 
+    @SqlQuery("Select IS_FREE_LOCK(:lockName);")
+    public Boolean isFree(@Bind("lockName") final String lockName);
+
     class LockMapper implements ResultSetMapper<Boolean> {
          @Override
          public Boolean map(int index, ResultSet r, StatementContext ctx) throws SQLException {
diff --git a/util/src/test/java/com/ning/billing/util/globalLocker/MockGlobalLocker.java b/util/src/test/java/com/ning/billing/util/globalLocker/MockGlobalLocker.java
new file mode 100644
index 0000000..18336df
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/globalLocker/MockGlobalLocker.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2010-2011 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.util.globalLocker;
+
+public class MockGlobalLocker implements GlobalLocker {
+
+    @Override
+    public GlobalLock lockWithNumberOfTries(LockerService service,
+            String lockKey, int retry) {
+        return new GlobalLock() {
+            @Override
+            public void release() {
+            }
+        };
+    }
+
+    @Override
+    public Boolean isFree(LockerService service, String lockKey) {
+        return Boolean.TRUE;
+    }
+}
diff --git a/util/src/test/java/com/ning/billing/util/globalLocker/TestMysqlGlobalLocker.java b/util/src/test/java/com/ning/billing/util/globalLocker/TestMysqlGlobalLocker.java
index 674efa3..b797522 100644
--- a/util/src/test/java/com/ning/billing/util/globalLocker/TestMysqlGlobalLocker.java
+++ b/util/src/test/java/com/ning/billing/util/globalLocker/TestMysqlGlobalLocker.java
@@ -16,18 +16,100 @@
 
 package com.ning.billing.util.globalLocker;
 
-import org.skife.jdbi.v2.DBI;
+import java.io.IOException;
+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;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Guice;
 import org.testng.annotations.Test;
 
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.util.globalLocker.GlobalLocker.LockerService;
+
+@Guice(modules=TestMysqlGlobalLocker.TestMysqlGlobalLockerModule.class)
 public class TestMysqlGlobalLocker {
 
+    @Inject
+    private IDBI dbi;
+
+    @Inject
+    private MysqlTestingHelper helper;
+
+    @BeforeClass(alwaysRun=true)
+    public void setup() throws IOException  {
+        helper.startMysql();
+        createSimpleTable(dbi);
+    }
+
+    @AfterClass(alwaysRun=true)
+    public void tearDown() {
+        helper.stopMysql();
+    }
+
     // Used as a manual test to validate the simple DAO by stepping through that locking is done and release correctly
-    @Test(enabled=false)
+    @Test(groups= "slow", enabled = true)
     public void testSimpleLocking() {
-        IDBI dbi = new DBI("jdbc:mysql://localhost:3306/killbill?createDatabaseIfNotExist=true", "root", "root");
-        GlobalLocker lock = new  MySqlGlobalLocker(dbi);
-        lock.lockWithNumberOfTries("test-lock", 3);
-        System.out.println("youpihh!");
+
+        final String lockName = UUID.randomUUID().toString();
+
+        GlobalLocker locker = new  MySqlGlobalLocker(dbi);
+        GlobalLock lock = locker.lockWithNumberOfTries(LockerService.INVOICE, lockName, 3);
+
+        dbi.inTransaction(new TransactionCallback<Void>() {
+            @Override
+            public Void inTransaction(Handle conn, TransactionStatus status)
+                    throws Exception {
+                conn.execute("insert into dummy (dummy_id) values ('" + UUID.randomUUID().toString()  + "')");
+                return null;
+            }
+        });
+        Assert.assertEquals(locker.isFree(LockerService.INVOICE, lockName), Boolean.FALSE);
+
+        boolean gotException = false;
+        try {
+            locker.lockWithNumberOfTries(LockerService.INVOICE, lockName, 1);
+        } catch (LockFailedException e) {
+            gotException = true;
+        }
+        Assert.assertTrue(gotException);
+
+        lock.release();
+
+        Assert.assertEquals(locker.isFree(LockerService.INVOICE, lockName), Boolean.TRUE);
+    }
+
+    private void createSimpleTable(IDBI dbi) {
+        dbi.inTransaction(new TransactionCallback<Void>() {
+
+            @Override
+            public Void inTransaction(Handle h, TransactionStatus status)
+                    throws Exception {
+                h.execute("create table dummy " +
+                        "(id int(11) unsigned NOT NULL AUTO_INCREMENT, " +
+                        "dummy_id char(36) NOT NULL, " +
+                        "PRIMARY KEY(id)" +
+                		") ENGINE=innodb;");
+                return null;
+            }
+        });
+    }
+
+    public final static class TestMysqlGlobalLockerModule extends AbstractModule {
+
+        @Override
+        protected void configure() {
+            MysqlTestingHelper helper = new MysqlTestingHelper();
+            bind(MysqlTestingHelper.class).toInstance(helper);
+            final IDBI dbi = helper.getDBI();
+            bind(IDBI.class).toInstance(dbi);
+        }
     }
 }