killbill-uncached

util: add notifications for tag definition changes Signed-off-by:

6/12/2012 11:22:00 PM

Details

diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java
index 049c42d..f7f25b7 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java
@@ -23,6 +23,8 @@ import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
 import org.skife.jdbi.v2.exceptions.TransactionFailedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.inject.Inject;
 import com.ning.billing.ErrorCode;
@@ -32,9 +34,12 @@ import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.tag.ControlTagType;
 import com.ning.billing.util.tag.DefaultTagDefinition;
 import com.ning.billing.util.tag.TagDefinition;
+import com.ning.billing.util.tag.api.TagDefinitionEvent;
 import com.ning.billing.util.tag.api.user.TagEventBuilder;
 
 public class DefaultTagDefinitionDao implements TagDefinitionDao {
+    private static final Logger log = LoggerFactory.getLogger(DefaultTagDefinitionDao.class);
+
     private final TagDefinitionSqlDao tagDefinitionSqlDao;
     private final TagEventBuilder tagEventBuilder;
     private final Bus bus;
@@ -83,17 +88,31 @@ public class DefaultTagDefinitionDao implements TagDefinitionDao {
         try {
             return tagDefinitionSqlDao.inTransaction(new Transaction<TagDefinition, TagDefinitionSqlDao>() {
                 @Override
-                public TagDefinition inTransaction(final TagDefinitionSqlDao transactional, final TransactionStatus status) throws Exception {
+                public TagDefinition inTransaction(final TagDefinitionSqlDao tagDefinitionSqlDao, final TransactionStatus status) throws Exception {
                     // Make sure the tag definition doesn't exist already
                     final TagDefinition existingDefinition = tagDefinitionSqlDao.getByName(definitionName);
                     if (existingDefinition != null) {
                         throw new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_ALREADY_EXISTS, definitionName);
                     }
 
-                    final TagDefinition definition = new DefaultTagDefinition(definitionName, description, false);
-                    tagDefinitionSqlDao.create(definition, context);
+                    // Create it
+                    final TagDefinition tagDefinition = new DefaultTagDefinition(definitionName, description, false);
+                    tagDefinitionSqlDao.create(tagDefinition, context);
+
+                    // Post an event to the bus
+                    final TagDefinitionEvent tagDefinitionEvent;
+                    if (tagDefinition.isControlTag()) {
+                        tagDefinitionEvent = tagEventBuilder.newControlTagDefinitionCreationEvent(tagDefinition.getId(), tagDefinition, context.getUserToken());
+                    } else {
+                        tagDefinitionEvent = tagEventBuilder.newUserTagDefinitionCreationEvent(tagDefinition.getId(), tagDefinition, context.getUserToken());
+                    }
+                    try {
+                        bus.postFromTransaction(tagDefinitionEvent, tagDefinitionSqlDao);
+                    } catch (Bus.EventBusException e) {
+                        log.warn("Failed to post tag definition creation event for tag " + tagDefinition.getId(), e);
+                    }
 
-                    return definition;
+                    return tagDefinition;
                 }
             });
         } catch (TransactionFailedException exception) {
@@ -120,10 +139,10 @@ public class DefaultTagDefinitionDao implements TagDefinitionDao {
         try {
             tagDefinitionSqlDao.inTransaction(new Transaction<Void, TagDefinitionSqlDao>() {
                 @Override
-                public Void inTransaction(final TagDefinitionSqlDao transactional, final TransactionStatus status) throws Exception {
+                public Void inTransaction(final TagDefinitionSqlDao tagDefinitionSqlDao, final TransactionStatus status) throws Exception {
                     // Make sure the tag definition exists
-                    final TagDefinition existingDefinition = tagDefinitionSqlDao.getByName(definitionName);
-                    if (existingDefinition == null) {
+                    final TagDefinition tagDefinition = tagDefinitionSqlDao.getByName(definitionName);
+                    if (tagDefinition == null) {
                         throw new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_DOES_NOT_EXIST, definitionName);
                     }
 
@@ -132,8 +151,22 @@ public class DefaultTagDefinitionDao implements TagDefinitionDao {
                         throw new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_IN_USE, definitionName);
                     }
 
+                    // Delete it
                     tagDefinitionSqlDao.deleteTagDefinition(definitionName, context);
 
+                    // Post an event to the Bus
+                    final TagDefinitionEvent tagDefinitionEvent;
+                    if (tagDefinition.isControlTag()) {
+                        tagDefinitionEvent = tagEventBuilder.newControlTagDefinitionDeletionEvent(tagDefinition.getId(), tagDefinition, context.getUserToken());
+                    } else {
+                        tagDefinitionEvent = tagEventBuilder.newUserTagDefinitionDeletionEvent(tagDefinition.getId(), tagDefinition, context.getUserToken());
+                    }
+                    try {
+                        bus.postFromTransaction(tagDefinitionEvent, tagDefinitionSqlDao);
+                    } catch (Bus.EventBusException e) {
+                        log.warn("Failed to post tag definition deletion event for tag " + tagDefinition.getId(), e);
+                    }
+
                     return null;
                 }
             });
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.java
index 4a7dd1c..6dee4b8 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionSqlDao.java
@@ -35,6 +35,7 @@ import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
 import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
@@ -46,7 +47,7 @@ import com.ning.billing.util.tag.TagDefinition;
 
 @ExternalizedSqlViaStringTemplate3
 @RegisterMapper(TagDefinitionSqlDao.TagDefinitionMapper.class)
-public interface TagDefinitionSqlDao extends EntitySqlDao<TagDefinition>, Transactional<TagDefinitionSqlDao> {
+public interface TagDefinitionSqlDao extends EntitySqlDao<TagDefinition>, Transactional<TagDefinitionSqlDao>, Transmogrifier {
     @Override
     @SqlUpdate
     public void create(@TagDefinitionBinder final TagDefinition entity, @CallContextBinder final CallContext context);
diff --git a/util/src/main/java/com/ning/billing/util/tag/DefaultTagDefinition.java b/util/src/main/java/com/ning/billing/util/tag/DefaultTagDefinition.java
index 139a66c..d9f714d 100644
--- a/util/src/main/java/com/ning/billing/util/tag/DefaultTagDefinition.java
+++ b/util/src/main/java/com/ning/billing/util/tag/DefaultTagDefinition.java
@@ -50,4 +50,47 @@ public class DefaultTagDefinition extends EntityBase implements TagDefinition {
     public Boolean isControlTag() {
         return isControlTag;
     }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("DefaultTagDefinition");
+        sb.append("{description='").append(description).append('\'');
+        sb.append(", name='").append(name).append('\'');
+        sb.append(", isControlTag=").append(isControlTag);
+        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 DefaultTagDefinition that = (DefaultTagDefinition) o;
+
+        if (description != null ? !description.equals(that.description) : that.description != null) {
+            return false;
+        }
+        if (isControlTag != null ? !isControlTag.equals(that.isControlTag) : that.isControlTag != null) {
+            return false;
+        }
+        if (name != null ? !name.equals(that.name) : that.name != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = name != null ? name.hashCode() : 0;
+        result = 31 * result + (description != null ? description.hashCode() : 0);
+        result = 31 * result + (isControlTag != null ? isControlTag.hashCode() : 0);
+        return result;
+    }
 }
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
new file mode 100644
index 0000000..3a44325
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/tag/dao/TestDefaultTagDefinitionDao.java
@@ -0,0 +1,154 @@
+/*
+ * 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.util.tag.dao;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.commons.io.IOUtils;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import com.google.common.eventbus.Subscribe;
+import com.google.inject.Inject;
+import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.util.bus.Bus;
+import com.ning.billing.util.bus.BusEvent;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.DefaultCallContextFactory;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.tag.MockTagStoreModuleSql;
+import com.ning.billing.util.tag.TagDefinition;
+import com.ning.billing.util.tag.TestTagStore;
+import com.ning.billing.util.tag.api.TagDefinitionEvent;
+
+@Guice(modules = MockTagStoreModuleSql.class)
+public class TestDefaultTagDefinitionDao {
+    @Inject
+    private MysqlTestingHelper helper;
+
+    @Inject
+    private TagDefinitionDao tagDefinitionDao;
+
+    @Inject
+    private Clock clock;
+
+    @Inject
+    private Bus bus;
+
+    private CallContext context;
+    private EventsListener eventsListener;
+
+    @BeforeClass(groups = "slow")
+    public void setup() throws IOException {
+        final String utilDdl = IOUtils.toString(TestTagStore.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
+
+        helper.startMysql();
+        helper.initDb(utilDdl);
+
+        context = new DefaultCallContextFactory(clock).createCallContext("TagDefinition DAO test", CallOrigin.TEST, UserType.TEST, UUID.randomUUID());
+        bus.start();
+    }
+
+    @BeforeMethod(groups = "slow")
+    public void cleanup() throws Bus.EventBusException {
+        eventsListener = new EventsListener();
+        bus.register(eventsListener);
+    }
+
+    @AfterClass(groups = "slow")
+    public void stopMysql() {
+        bus.stop();
+        helper.stopMysql();
+    }
+
+    @Test(groups = "slow")
+    public void testCatchEventsOnCreateAndDelete() throws Exception {
+        final String definitionName = UUID.randomUUID().toString().substring(0, 5);
+        final String description = UUID.randomUUID().toString().substring(0, 5);
+
+        // Verify the initial state
+        Assert.assertEquals(eventsListener.getEvents().size(), 0);
+        Assert.assertEquals(eventsListener.getTagDefinitionEvents().size(), 0);
+
+        // Make sure we can create a tag definition
+        final TagDefinition createdTagDefinition = tagDefinitionDao.create(definitionName, description, context);
+        Assert.assertEquals(createdTagDefinition.getName(), definitionName);
+        Assert.assertEquals(createdTagDefinition.getDescription(), description);
+
+        // Make sure we can retrieve it via the DAO
+        final TagDefinition foundTagDefinition = tagDefinitionDao.getByName(definitionName);
+        Assert.assertEquals(foundTagDefinition, createdTagDefinition);
+
+        // Verify we caught an event on the bus
+        Assert.assertEquals(eventsListener.getEvents().size(), 1);
+        Assert.assertEquals(eventsListener.getTagDefinitionEvents().size(), 1);
+        final TagDefinitionEvent tagDefinitionFirstEventReceived = eventsListener.getTagDefinitionEvents().get(0);
+        Assert.assertEquals(eventsListener.getEvents().get(0), tagDefinitionFirstEventReceived);
+        Assert.assertEquals(tagDefinitionFirstEventReceived.getTagDefinitionId(), createdTagDefinition.getId());
+        Assert.assertEquals(tagDefinitionFirstEventReceived.getTagDefinition(), createdTagDefinition);
+        Assert.assertEquals(tagDefinitionFirstEventReceived.getBusEventType(), BusEvent.BusEventType.USER_TAGDEFINITION_CREATION);
+        Assert.assertEquals(tagDefinitionFirstEventReceived.getUserToken(), context.getUserToken());
+
+        // Delete the tag
+        tagDefinitionDao.deleteTagDefinition(definitionName, context);
+
+        // Make sure the tag is deleted
+        Assert.assertNull(tagDefinitionDao.getByName(definitionName));
+
+        // Verify we caught an event on the bus
+        Assert.assertEquals(eventsListener.getEvents().size(), 2);
+        Assert.assertEquals(eventsListener.getTagDefinitionEvents().size(), 2);
+        final TagDefinitionEvent tagDefinitionSecondEventReceived = eventsListener.getTagDefinitionEvents().get(1);
+        Assert.assertEquals(eventsListener.getEvents().get(1), tagDefinitionSecondEventReceived);
+        Assert.assertEquals(tagDefinitionSecondEventReceived.getTagDefinitionId(), createdTagDefinition.getId());
+        Assert.assertEquals(tagDefinitionSecondEventReceived.getTagDefinition(), createdTagDefinition);
+        Assert.assertEquals(tagDefinitionSecondEventReceived.getBusEventType(), BusEvent.BusEventType.USER_TAGDEFINITION_DELETION);
+        Assert.assertEquals(tagDefinitionSecondEventReceived.getUserToken(), context.getUserToken());
+    }
+
+    private static final class EventsListener {
+        private final List<BusEvent> events = new ArrayList<BusEvent>();
+        private final List<TagDefinitionEvent> tagDefinitionEvents = new ArrayList<TagDefinitionEvent>();
+
+        @Subscribe
+        public synchronized void processEvent(final BusEvent event) {
+            events.add(event);
+        }
+
+        @Subscribe
+        public synchronized void processTagDefinitionEvent(final TagDefinitionEvent tagDefinitionEvent) {
+            tagDefinitionEvents.add(tagDefinitionEvent);
+        }
+
+        public List<BusEvent> getEvents() {
+            return events;
+        }
+
+        public List<TagDefinitionEvent> getTagDefinitionEvents() {
+            return tagDefinitionEvents;
+        }
+    }
+}