killbill-memoizeit
Changes
profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationKey.java 10(+9 -1)
Details
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/NotificationJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/NotificationJson.java
index 22fe656..2f5fbbe 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/NotificationJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/NotificationJson.java
@@ -33,23 +33,27 @@ public class NotificationJson {
private final String objectType;
@ApiModelProperty(dataType = "java.util.UUID")
private final String objectId;
+ private String metaData;
@JsonCreator
public NotificationJson(@JsonProperty("eventType") final String eventType,
@JsonProperty("accountId") final String accountId,
@JsonProperty("objectType") final String objectType,
- @JsonProperty("objectId") final String objectId) {
+ @JsonProperty("objectId") final String objectId,
+ @JsonProperty("metaData") final String metaData) {
this.eventType = eventType;
this.accountId = accountId;
this.objectType = objectType;
this.objectId = objectId;
+ this.metaData = metaData;
}
public NotificationJson(final ExtBusEvent event) {
this(event.getEventType().toString(),
event.getAccountId() != null ? event.getAccountId().toString() : null,
event.getObjectType().toString(),
- event.getObjectId() != null ? event.getObjectId().toString() : null);
+ event.getObjectId() != null ? event.getObjectId().toString() : null,
+ event.getMetaData());
}
public String getEventType() {
@@ -67,4 +71,8 @@ public class NotificationJson {
public String getObjectId() {
return objectId;
}
+
+ public String getMetaData() {
+ return metaData;
+ }
}
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationKey.java b/profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationKey.java
index e72dec3..eb25be3 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationKey.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationKey.java
@@ -33,6 +33,7 @@ public class PushNotificationKey implements NotificationEvent {
private final UUID objectId;
private final int attemptNumber;
private final String url;
+ private final String metaData;
@JsonCreator
public PushNotificationKey(@JsonProperty("tenantId") final UUID tenantId,
@@ -41,6 +42,7 @@ public class PushNotificationKey implements NotificationEvent {
@JsonProperty("objectType") final String objectType,
@JsonProperty("objectId") final UUID objectId,
@JsonProperty("attemptNumber") final int attemptNumber,
+ @JsonProperty("metaData") final String metaData,
@JsonProperty("url") final String url) {
this.tenantId = tenantId;
this.accountId = accountId;
@@ -48,12 +50,13 @@ public class PushNotificationKey implements NotificationEvent {
this.objectType = objectType;
this.objectId = objectId;
this.attemptNumber = attemptNumber;
+ this.metaData = metaData;
this.url = url;
}
public PushNotificationKey(final PushNotificationKey key, final int attemptNumber) {
this(key.getTenantId(), key.getAccountId(), key.getEventType(), key.getObjectType(), key.getObjectId(),
- attemptNumber, key.getUrl());
+ attemptNumber, key.getMetaData(), key.getUrl());
}
public UUID getTenantId() {
@@ -84,6 +87,10 @@ public class PushNotificationKey implements NotificationEvent {
return url;
}
+ public String getMetaData() {
+ return metaData;
+ }
+
@Override
public String toString() {
return "PushNotificationKey{" +
@@ -92,6 +99,7 @@ public class PushNotificationKey implements NotificationEvent {
", eventType='" + eventType + '\'' +
", objectType='" + objectType + '\'' +
", objectId=" + objectId +
+ ", metaData=" + metaData +
", attemptNumber=" + attemptNumber +
", url='" + url + '\'' +
'}';
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationListener.java b/profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationListener.java
index 0f35812..01875b1 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationListener.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationListener.java
@@ -162,7 +162,8 @@ public class PushNotificationListener {
final NotificationJson notification = new NotificationJson(key.getEventType() != null ? key.getEventType().toString() : null,
key.getAccountId() != null ? key.getAccountId().toString() : null,
key.getObjectType() != null ? key.getObjectType().toString() : null,
- key.getObjectId() != null ? key.getObjectId().toString() : null);
+ key.getObjectId() != null ? key.getObjectId().toString() : null,
+ key.getMetaData());
final String body = mapper.writeValueAsString(notification);
doPost(key.getTenantId(), key.getUrl(), body, notification, TIMEOUT_NOTIFICATION, key.getAttemptNumber());
}
@@ -173,7 +174,9 @@ public class PushNotificationListener {
notificationJson.getEventType(),
notificationJson.getObjectType(),
notificationJson.getObjectId() != null ? UUID.fromString(notificationJson.getObjectId()) : null,
- attemptRetryNumber + 1, url);
+ attemptRetryNumber + 1,
+ notificationJson.getMetaData(),
+ url);
final TenantContext tenantContext = contextFactory.createTenantContext(tenantId);
final DateTime nextNotificationTime = getNextNotificationTime(key.getAttemptNumber(), internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(tenantContext));
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestJaxrsBase.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestJaxrsBase.java
index 333cb3e..86ed2cf 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestJaxrsBase.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestJaxrsBase.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2017 Groupon, Inc
- * Copyright 2014-2017 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
*
* The Billing Project 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
@@ -62,6 +62,7 @@ import org.killbill.billing.util.config.definition.PaymentConfig;
import org.killbill.billing.util.config.definition.SecurityConfig;
import org.killbill.bus.api.PersistentBus;
import org.killbill.commons.jdbi.guice.DaoConfig;
+import org.killbill.notificationq.api.NotificationQueueService;
import org.skife.config.ConfigurationObjectFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -107,6 +108,9 @@ public class TestJaxrsBase extends KillbillClient {
protected TestApiListener busHandler;
@Inject
+ protected NotificationQueueService notificationQueueService;
+
+ @Inject
@Named(KillbillServerModule.SHIRO_DATA_SOURCE_ID)
protected DataSource shiroDataSource;
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPushNotification.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPushNotification.java
index 8c790d7..b8aebb8 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPushNotification.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPushNotification.java
@@ -21,22 +21,39 @@ package org.killbill.billing.jaxrs;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.UUID;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.awaitility.Awaitility;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
+import org.joda.time.DateTime;
+import org.killbill.CreatorName;
import org.killbill.billing.api.FlakyRetryAnalyzer;
import org.killbill.billing.client.KillBillClientException;
import org.killbill.billing.client.model.TenantKey;
import org.killbill.billing.jaxrs.json.NotificationJson;
+import org.killbill.billing.notification.plugin.api.ExtBusEventType;
+import org.killbill.billing.server.DefaultServerService;
+import org.killbill.billing.server.notifications.PushNotificationKey;
import org.killbill.billing.server.notifications.PushNotificationListener;
+import org.killbill.billing.server.notifications.PushNotificationRetryService;
import org.killbill.billing.tenant.api.TenantKV;
+import org.killbill.notificationq.DefaultNotificationQueueService;
+import org.killbill.notificationq.NotificationQueueDispatcher;
+import org.killbill.notificationq.api.NotificationEvent;
+import org.killbill.notificationq.api.NotificationQueue;
+import org.killbill.notificationq.api.NotificationQueueService.NotificationQueueHandler;
+import org.killbill.notificationq.dao.NotificationEventModelDao;
+import org.killbill.queue.QueueObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
@@ -44,7 +61,11 @@ import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.MoreObjects;
import com.google.common.io.CharStreams;
public class TestPushNotification extends TestJaxrsBase {
@@ -125,6 +146,72 @@ public class TestPushNotification extends TestJaxrsBase {
unregisterTenantForCallback(callback);
}
+ @Test(groups = "slow", description = "https://github.com/killbill/killbill/issues/726")
+ public void testVerify726Backport() throws Exception {
+ // Record an event without the metadata field
+ final PushNotificationKeyPre726 key = new PushNotificationKeyPre726(UUID.randomUUID(),
+ UUID.randomUUID(),
+ UUID.randomUUID().toString(),
+ UUID.randomUUID().toString(),
+ UUID.randomUUID(),
+ 1,
+ UUID.randomUUID().toString());
+ final String eventJson = QueueObjectMapper.get().writeValueAsString(key);
+ // Need to serialize it manually, to reflect the correct class name
+ final NotificationEventModelDao notificationEventModelDao = new NotificationEventModelDao(CreatorName.get(),
+ clock.getUTCNow(),
+ PushNotificationKey.class.getName(),
+ eventJson,
+ UUID.randomUUID(),
+ internalCallContext.getAccountRecordId(),
+ internalCallContext.getTenantRecordId(),
+ UUID.randomUUID(),
+ clock.getUTCNow(),
+ DefaultServerService.SERVER_SERVICE + ":testVerify726Backport");
+
+ final AtomicReference<PushNotificationKey> notification = new AtomicReference<PushNotificationKey>();
+ // Need to create a custom queue to extract the deserialized event
+ final NotificationQueue notificationQueue = notificationQueueService.createNotificationQueue(DefaultServerService.SERVER_SERVICE,
+ "testVerify726Backport",
+ new NotificationQueueHandler() {
+ @Override
+ public void handleReadyNotification(final NotificationEvent notificationKey, final DateTime eventDateTime, final UUID userToken, final Long accountRecordId, final Long tenantRecordId) {
+ if (!(notificationKey instanceof PushNotificationKey)) {
+ Assert.fail();
+ return;
+ }
+ final PushNotificationKey key = (PushNotificationKey) notificationKey;
+ notification.set(key);
+ }
+ }
+ );
+ try {
+ notificationQueue.startQueue();
+ ((NotificationQueueDispatcher) notificationQueueService).getDao().insertEntry(notificationEventModelDao);
+ Awaitility.await()
+ .atMost(10, TimeUnit.SECONDS)
+ .until(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return notification.get() != null;
+ }
+ });
+ } finally {
+ notificationQueue.stopQueue();
+ }
+
+ final PushNotificationKey retrievedKey = notification.get();
+ Assert.assertEquals(retrievedKey.getTenantId(), key.tenantId);
+ Assert.assertEquals(retrievedKey.getAccountId(), key.accountId);
+ Assert.assertEquals(retrievedKey.getEventType(), key.eventType);
+ Assert.assertEquals(retrievedKey.getObjectType(), key.objectType);
+ Assert.assertEquals(retrievedKey.getObjectId(), key.objectId);
+ Assert.assertEquals(retrievedKey.getAttemptNumber(), (Integer) key.attemptNumber);
+ Assert.assertEquals(retrievedKey.getUrl(), key.url);
+ // New NULL field
+ Assert.assertNull(retrievedKey.getMetaData());
+ }
+
private void unregisterTenantForCallback(final String callback) throws KillBillClientException {
final TenantKey result = killBillClient.getCallbackNotificationForTenant(requestOptions);
Assert.assertEquals(result.getKey(), TenantKV.TenantKey.PUSH_NOTIFICATION_CB.toString());
@@ -141,8 +228,7 @@ public class TestPushNotification extends TestJaxrsBase {
final String callback = "http://127.0.0.1:" + SERVER_PORT + CALLBACK_ENDPOINT;
final TenantKey result0 = killBillClient.registerCallbackNotificationForTenant(callback, requestOptions);
- assertAllCallbacksCompleted();
- Assert.assertTrue(callbackCompletedWithError); // expected true because is not an ACCOUNT_CREATION event
+ Assert.assertTrue(waitForCallbacksToComplete());
Assert.assertEquals(result0.getKey(), TenantKV.TenantKey.PUSH_NOTIFICATION_CB.toString());
Assert.assertEquals(result0.getValues().size(), 1);
@@ -324,14 +410,12 @@ public class TestPushNotification extends TestJaxrsBase {
public CallmebackServlet(final TestPushNotification test) {
this.test = test;
this.receivedCalls = new AtomicInteger(0);
- this.withError = false;
}
@Override
protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
final int current = receivedCalls.incrementAndGet();
final String body = CharStreams.toString(new InputStreamReader(request.getInputStream(), "UTF-8"));
- withError = false;
log.info("CallmebackServlet received {} calls , current = {} at {}", current, body, getClock().getUTCNow());
@@ -346,22 +430,31 @@ public class TestPushNotification extends TestJaxrsBase {
log.info("Got body {}", body);
- try {
- final NotificationJson notification = objectMapper.readValue(body, NotificationJson.class);
- Assert.assertEquals(notification.getEventType(), "ACCOUNT_CREATION");
- Assert.assertEquals(notification.getObjectType(), "ACCOUNT");
- Assert.assertNotNull(notification.getObjectId());
- Assert.assertNotNull(notification.getAccountId());
- Assert.assertEquals(notification.getObjectId(), notification.getAccountId());
-
- test.retrieveAccountWithAsserts(notification.getObjectId());
-
- Assert.assertEquals(request.getHeader(PushNotificationListener.HTTP_HEADER_CONTENT_TYPE), PushNotificationListener.CONTENT_TYPE_JSON);
- } catch (final AssertionError e) {
- withError = true;
+ final NotificationJson notification = objectMapper.readValue(body, NotificationJson.class);
+
+ final ExtBusEventType type = ExtBusEventType.valueOf(notification.getEventType());
+ switch (type) {
+ case TENANT_CONFIG_CHANGE:
+ Assert.assertEquals(notification.getEventType(), "TENANT_CONFIG_CHANGE");
+ Assert.assertEquals(notification.getObjectType(), "TENANT_KVS");
+ Assert.assertNotNull(notification.getObjectId());
+ Assert.assertNull(notification.getAccountId());
+ Assert.assertNotNull(notification.getMetaData());
+ Assert.assertEquals(notification.getMetaData(), "PUSH_NOTIFICATION_CB");
+ break;
+ case ACCOUNT_CREATION:
+ Assert.assertEquals(notification.getEventType(), "ACCOUNT_CREATION");
+ Assert.assertEquals(notification.getObjectType(), "ACCOUNT");
+ Assert.assertNotNull(notification.getObjectId());
+ Assert.assertNotNull(notification.getAccountId());
+ Assert.assertEquals(notification.getObjectId(), notification.getAccountId());
+ break;
}
- stopServerWhenComplete(current, withError);
+ test.retrieveAccountWithAsserts(notification.getObjectId());
+
+ Assert.assertEquals(request.getHeader(PushNotificationListener.HTTP_HEADER_CONTENT_TYPE), PushNotificationListener.CONTENT_TYPE_JSON);
+ stopServerWhenComplete(current, false);
}
private void stopServerWhenComplete(final int current, final boolean withError) {
@@ -370,6 +463,33 @@ public class TestPushNotification extends TestJaxrsBase {
test.setCompleted(withError);
}
}
+ }
+ public static final class PushNotificationKeyPre726 implements NotificationEvent {
+
+ public UUID tenantId;
+ public UUID accountId;
+ public String eventType;
+ public String objectType;
+ public UUID objectId;
+ public int attemptNumber;
+ public String url;
+
+ @JsonCreator
+ public PushNotificationKeyPre726(@JsonProperty("tenantId") final UUID tenantId,
+ @JsonProperty("accountId") final UUID accountId,
+ @JsonProperty("eventType") final String eventType,
+ @JsonProperty("objectType") final String objectType,
+ @JsonProperty("objectId") final UUID objectId,
+ @JsonProperty("attemptNumber") final int attemptNumber,
+ @JsonProperty("url") final String url) {
+ this.tenantId = tenantId;
+ this.accountId = accountId;
+ this.eventType = eventType;
+ this.objectType = objectType;
+ this.objectId = objectId;
+ this.attemptNumber = attemptNumber;
+ this.url = url;
+ }
}
}