killbill-memoizeit

Merge remote-tracking branch 'origin/work-for-release-0.19.x'

3/22/2018 7:17:04 AM

Details

diff --git a/.circleci/config.yml b/.circleci/config.yml
index ccde348..ada2982 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -8,7 +8,7 @@ jobs:
   build:
     <<: *defaults
     docker:
-      - image: killbill/kbbuild:0.1.0
+      - image: killbill/kbbuild:0.4.0
     steps:
       - checkout
       - restore_cache:
@@ -38,7 +38,7 @@ jobs:
   test-h2:
     <<: *defaults
     docker:
-      - image: killbill/kbbuild:0.1.0
+      - image: killbill/kbbuild:0.4.0
     steps:
       - checkout
       - restore_cache:
@@ -57,8 +57,8 @@ jobs:
   test-mysql:
     <<: *defaults
     docker:
-      - image: killbill/kbbuild:0.1.0
-      - image: killbill/mariadb:0.18
+      - image: killbill/kbbuild:0.4.0
+      - image: killbill/mariadb:0.19
         environment:
         - MYSQL_ROOT_PASSWORD=root
     steps:
@@ -96,8 +96,8 @@ jobs:
   test-postgresql:
     <<: *defaults
     docker:
-      - image: killbill/kbbuild:0.1.0
-      - image: killbill/postgresql:0.18
+      - image: killbill/kbbuild:0.4.0
+      - image: killbill/postgresql:0.19
         environment:
         - POSTGRES_PASSWORD=postgres
     steps:
@@ -122,8 +122,8 @@ jobs:
   integration-tests:
     <<: *defaults
     docker:
-      - image: killbill/kbbuild:0.1.0
-      - image: killbill/mariadb:0.18
+      - image: killbill/kbbuild:0.4.0
+      - image: killbill/mariadb:0.19
         environment:
         - MYSQL_ROOT_PASSWORD=root
     steps:
@@ -149,7 +149,9 @@ jobs:
                 git checkout -b ${CIRCLE_BRANCH} origin/${CIRCLE_BRANCH}
               fi
             fi
-            bundle install
+            source /usr/share/rvm/scripts/rvm
+            rvm use ruby-2.4.2
+            bundle install --jobs=4 --retry=3 --path=vendor/bundle
 
             count=0
             until $(curl --output /dev/null --silent --fail http://127.0.0.1:8080/1.0/healthcheck); do
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
index 01a673e..2de2814 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
@@ -113,6 +113,7 @@ import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldApiException;
 import org.killbill.billing.util.api.CustomFieldUserApi;
+import org.killbill.billing.util.api.RecordIdApi;
 import org.killbill.billing.util.api.TagApiException;
 import org.killbill.billing.util.api.TagDefinitionApiException;
 import org.killbill.billing.util.api.TagUserApi;
@@ -128,6 +129,8 @@ import org.killbill.billing.util.tag.Tag;
 import org.killbill.clock.Clock;
 import org.killbill.commons.metrics.MetricTag;
 import org.killbill.commons.metrics.TimedResource;
+import org.killbill.notificationq.api.NotificationQueue;
+import org.killbill.notificationq.api.NotificationQueueService;
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
@@ -142,8 +145,6 @@ import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
-import io.swagger.annotations.Authorization;
-import io.swagger.annotations.BasicAuthDefinition;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
@@ -161,6 +162,8 @@ public class AccountResource extends JaxRsResourceBase {
     private final PaymentConfig paymentConfig;
     private final JaxrsExecutors jaxrsExecutors;
     private final JaxrsConfig jaxrsConfig;
+    private final RecordIdApi recordIdApi;
+    private final NotificationQueueService notificationQueueService;
 
     @Inject
     public AccountResource(final JaxrsUriBuilder uriBuilder,
@@ -177,7 +180,9 @@ public class AccountResource extends JaxRsResourceBase {
                            final PaymentConfig paymentConfig,
                            final JaxrsExecutors jaxrsExecutors,
                            final JaxrsConfig jaxrsConfig,
-                           final Context context) {
+                           final Context context,
+                           final RecordIdApi recordIdApi,
+                           final NotificationQueueService notificationQueueService) {
         super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountApi, paymentApi, subscriptionApi, clock, context);
         this.subscriptionApi = subscriptionApi;
         this.invoiceApi = invoiceApi;
@@ -186,6 +191,8 @@ public class AccountResource extends JaxRsResourceBase {
         this.paymentConfig = paymentConfig;
         this.jaxrsExecutors = jaxrsExecutors;
         this.jaxrsConfig = jaxrsConfig;
+        this.recordIdApi = recordIdApi;
+        this.notificationQueueService = notificationQueueService;
     }
 
     @TimedResource
@@ -405,11 +412,11 @@ public class AccountResource extends JaxRsResourceBase {
                                  @QueryParam(QUERY_CANCEL_ALL_SUBSCRIPTIONS) @DefaultValue("false") final Boolean cancelAllSubscriptions,
                                  @QueryParam(QUERY_WRITE_OFF_UNPAID_INVOICES) @DefaultValue("false") final Boolean writeOffUnpaidInvoices,
                                  @QueryParam(QUERY_ITEM_ADJUST_UNPAID_INVOICES) @DefaultValue("false") final Boolean itemAdjustUnpaidInvoices,
+                                 @QueryParam(QUERY_REMOVE_FUTURE_NOTIFICATIONS) @DefaultValue("true") final Boolean removeFutureNotifications,
                                  @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 SubscriptionApiException, AccountApiException, EntitlementApiException, InvoiceApiException, TagApiException {
-
         final CallContext callContext = context.createCallContextWithAccountId(accountId, createdBy, reason, comment, request);
 
         if (cancelAllSubscriptions) {
@@ -458,6 +465,15 @@ public class AccountResource extends JaxRsResourceBase {
         final BlockingStateJson blockingState = new BlockingStateJson(accountId, "CLOSE_ACCOUNT", "account-service", true, false, false, null, BlockingStateType.ACCOUNT, null);
         addBlockingState(blockingState, accountId, BlockingStateType.ACCOUNT, null, ImmutableList.<String>of(), createdBy, reason, comment, request);
 
+        if (removeFutureNotifications) {
+            final Long tenantRecordId = recordIdApi.getRecordId(callContext.getTenantId(), ObjectType.TENANT, callContext);
+            final Long accountRecordId = accountId == null ? null : recordIdApi.getRecordId(accountId, ObjectType.ACCOUNT, callContext);
+            for (final NotificationQueue notificationQueue : notificationQueueService.getNotificationQueues()) {
+                log.debug("Removing future notifications for queueName={}", notificationQueue.getFullQName());
+                notificationQueue.removeFutureNotificationsForSearchKeys(accountRecordId, tenantRecordId);
+            }
+        }
+
         return Response.status(Status.OK).build();
     }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java
index 70752e0..259ff88 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java
@@ -118,9 +118,29 @@ public class CatalogResource extends JaxRsResourceBase {
     @Produces(APPLICATION_XML)
     @ApiOperation(value = "Retrieve the full catalog as XML", response = String.class, hidden = true)
     @ApiResponses(value = {})
-    public Response getCatalogXml(@javax.ws.rs.core.Context final HttpServletRequest request) throws Exception {
+    public Response getCatalogXml(@QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
+                                  @javax.ws.rs.core.Context final HttpServletRequest request) throws Exception {
         final TenantContext tenantContext = context.createTenantContextNoAccountId(request);
-        return Response.status(Status.OK).entity(XMLWriter.writeXML((VersionedCatalog) catalogUserApi.getCatalog(catalogName, tenantContext), VersionedCatalog.class)).build();
+        final DateTime catalogDateVersion = requestedDate != null ?
+                                            DATE_TIME_FORMATTER.parseDateTime(requestedDate).toDateTime(DateTimeZone.UTC) :
+                                            null;
+
+        final VersionedCatalog catalog = (VersionedCatalog) catalogUserApi.getCatalog(catalogName, tenantContext);
+        final String result;
+        if (catalogDateVersion != null) {
+            final VersionedCatalog oneVersionCatalog = new VersionedCatalog();
+            for (final StandaloneCatalog v : catalog.getVersions()) {
+                if (v.getEffectiveDate().compareTo(catalogDateVersion.toDate()) >= 0) {
+                    oneVersionCatalog.add(v);
+                    break;
+                }
+            }
+            result = XMLWriter.writeXML(oneVersionCatalog, VersionedCatalog.class);
+        } else {
+            result = XMLWriter.writeXML(catalog, VersionedCatalog.class);
+        }
+
+        return Response.status(Status.OK).entity(result).build();
     }
 
     @TimedResource
@@ -166,6 +186,25 @@ public class CatalogResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(result).build();
     }
 
+    @TimedResource
+    @GET
+    @Path("/versions")
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Retrieve a list of catalog versions", response = DateTime.class, responseContainer = "List")
+    @ApiResponses(value = {})
+    public Response getCatalogVersionJson(@javax.ws.rs.core.Context final HttpServletRequest request) throws Exception {
+
+        final TenantContext tenantContext = context.createTenantContextNoAccountId(request);
+        final VersionedCatalog catalog = (VersionedCatalog) catalogUserApi.getCatalog(catalogName, tenantContext);
+
+        final List<DateTime> result = new ArrayList<DateTime>();
+        for (final StandaloneCatalog v : catalog.getVersions()) {
+            result.add(new DateTime(v.getEffectiveDate()));
+        }
+
+        return Response.status(Status.OK).entity(result).build();
+    }
+
     // Need to figure out dependency on StandaloneCatalog
     //    @GET
     //    @Path("/xsd")
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
index 6264557..444f06f 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 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
@@ -99,6 +99,7 @@ public interface JaxrsResource {
     public static final String QUERY_CANCEL_ALL_SUBSCRIPTIONS = "cancelAllSubscriptions";
     public static final String QUERY_WRITE_OFF_UNPAID_INVOICES = "writeOffUnpaidInvoices";
     public static final String QUERY_ITEM_ADJUST_UNPAID_INVOICES = "itemAdjustUnpaidInvoices";
+    public static final String QUERY_REMOVE_FUTURE_NOTIFICATIONS = "removeFutureNotifications";
 
     public static final String QUERY_BLOCKING_STATE_TYPES = "blockingStateTypes";
     public static final String QUERY_BLOCKING_STATE_SVCS = "blockingStateSvcs";

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index 33d1295..66e3e55 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.141.45-SNAPSHOT</version>
+        <version>0.141.47-SNAPSHOT</version>
     </parent>
     <artifactId>killbill</artifactId>
     <version>0.19.7-SNAPSHOT</version>
diff --git a/profiles/killbill/src/main/resources/logback.xml b/profiles/killbill/src/main/resources/logback.xml
index 1d267cb..fb1c70b 100644
--- a/profiles/killbill/src/main/resources/logback.xml
+++ b/profiles/killbill/src/main/resources/logback.xml
@@ -185,10 +185,12 @@
 
     <!-- Silence verbose loggers in DEBUG mode -->
     <logger name="com.dmurph" level="OFF"/>
-    <logger name="org.killbill.billing.notificationq" level="INFO"/>
-    <logger name="org.killbill.billing.queue" level="INFO"/>
-    <logger name="org.killbill.billing.server.updatechecker" level="INFO"/>
     <logger name="org.eclipse" level="INFO"/>
+    <logger name="org.killbill.billing.server.updatechecker" level="INFO"/>
+    <!-- Useful loggers for debugging -->
+    <logger name="org.killbill.billing.jaxrs.resources" level="INFO"/>
+    <logger name="org.killbill.notificationq" level="INFO"/>
+    <logger name="org.killbill.queue" level="INFO"/>
 
     <root level="INFO">
         <appender-ref ref="STDOUT"/>
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java
index c5f263b..cfe419e 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java
@@ -237,4 +237,14 @@ public class TestCatalog extends TestJaxrsBase {
         Assert.assertEquals(catalogsJson.size(), 0);
 
     }
+
+    @Test(groups = "slow")
+    public  void testGetCatalogVersions() throws Exception {
+        final String versionPath1 = Resources.getResource("SpyCarBasic.xml").getPath();
+        killBillClient.uploadXMLCatalog(versionPath1, requestOptions);
+
+        List<DateTime> versions = killBillClient.getCatalogVersions(requestOptions);
+        Assert.assertEquals(1, versions.size());
+        Assert.assertEquals(versions.get(0).compareTo(DateTime.parse("2013-02-08T00:00:00+00:00")), 0);
+    }
 }
diff --git a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java
index 60934fb..7a01782 100644
--- a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java
+++ b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java
@@ -72,7 +72,7 @@ public class EntitySqlDaoTransactionalJdbiWrapper {
 
         @Override
         public ReturnType inTransaction(final EntitySqlDao<M, E> transactionalSqlDao, final TransactionStatus status) throws Exception {
-            final EntitySqlDaoWrapperFactory factoryEntitySqlDao = new EntitySqlDaoWrapperFactory(h, clock, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory);
+            final EntitySqlDaoWrapperFactory factoryEntitySqlDao = new EntitySqlDaoWrapperFactory(h, clock, cacheControllerDispatcher, internalCallContextFactory);
             return entitySqlDaoTransactionWrapper.inTransaction(factoryEntitySqlDao);
         }
     }
diff --git a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperFactory.java b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperFactory.java
index 2d29ee7..12aab8d 100644
--- a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperFactory.java
+++ b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperFactory.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2012 Ning, Inc.
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 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
@@ -22,7 +22,6 @@ import java.lang.reflect.Proxy;
 
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.billing.util.entity.Entity;
 import org.killbill.clock.Clock;
 import org.skife.jdbi.v2.Handle;
@@ -40,14 +39,12 @@ public class EntitySqlDaoWrapperFactory {
     private final Clock clock;
     private final CacheControllerDispatcher cacheControllerDispatcher;
 
-    private final NonEntityDao nonEntityDao;
     private final InternalCallContextFactory internalCallContextFactory;
 
-    public EntitySqlDaoWrapperFactory(final Handle handle, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory) {
+    public EntitySqlDaoWrapperFactory(final Handle handle, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher, final InternalCallContextFactory internalCallContextFactory) {
         this.handle = handle;
         this.clock = clock;
         this.cacheControllerDispatcher = cacheControllerDispatcher;
-        this.nonEntityDao = nonEntityDao;
         this.internalCallContextFactory = internalCallContextFactory;
     }
 
@@ -76,7 +73,7 @@ public class EntitySqlDaoWrapperFactory {
         final ClassLoader classLoader = newSqlDao.getClass().getClassLoader();
         final Class[] interfacesToImplement = {newSqlDaoClass};
         final EntitySqlDaoWrapperInvocationHandler<NewSqlDao, NewEntityModelDao, NewEntity> wrapperInvocationHandler =
-                new EntitySqlDaoWrapperInvocationHandler<NewSqlDao, NewEntityModelDao, NewEntity>(newSqlDaoClass, newSqlDao, handle, clock, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory);
+                new EntitySqlDaoWrapperInvocationHandler<NewSqlDao, NewEntityModelDao, NewEntity>(newSqlDaoClass, newSqlDao, handle, clock, cacheControllerDispatcher, internalCallContextFactory);
 
         final Object newSqlDaoObject = Proxy.newProxyInstance(classLoader, interfacesToImplement, wrapperInvocationHandler);
         return newSqlDaoClass.cast(newSqlDaoObject);
diff --git a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
index f6ba247..907af4b 100644
--- a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
+++ b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2012 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
@@ -20,11 +20,13 @@ package org.killbill.billing.util.entity.dao;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
+import java.sql.SQLWarning;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -49,7 +51,6 @@ import org.killbill.billing.util.cache.CacheLoaderArgument;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.dao.EntityAudit;
 import org.killbill.billing.util.dao.EntityHistoryModelDao;
-import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.entity.Entity;
 import org.killbill.clock.Clock;
@@ -93,7 +94,6 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>, 
 
     private final CacheControllerDispatcher cacheControllerDispatcher;
     private final Clock clock;
-    private final NonEntityDao nonEntityDao;
     private final InternalCallContextFactory internalCallContextFactory;
     private final Profiling<Object, Throwable> prof;
 
@@ -103,14 +103,12 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>, 
                                                 final Clock clock,
                                                 // Special DAO that don't require caching can invoke EntitySqlDaoWrapperInvocationHandler with no caching (e.g NoCachingTenantDao)
                                                 @Nullable final CacheControllerDispatcher cacheControllerDispatcher,
-                                                @Nullable final NonEntityDao nonEntityDao,
                                                 final InternalCallContextFactory internalCallContextFactory) {
         this.sqlDaoClass = sqlDaoClass;
         this.sqlDao = sqlDao;
         this.handle = handle;
         this.clock = clock;
         this.cacheControllerDispatcher = cacheControllerDispatcher;
-        this.nonEntityDao = nonEntityDao;
         this.internalCallContextFactory = internalCallContextFactory;
         this.prof = new Profiling<Object, Throwable>();
     }
@@ -124,7 +122,7 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>, 
                     return invokeSafely(proxy, method, args);
                 }
             });
-        } catch (Throwable t) {
+        } catch (final Throwable t) {
             if (t.getCause() != null && t.getCause().getCause() != null && DBIException.class.isAssignableFrom(t.getCause().getClass())) {
                 // Likely a JDBC error, try to extract the SQL statement and JDBI bindings
                 if (t.getCause() instanceof StatementException) {
@@ -208,7 +206,8 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>, 
         return prof.executeWithProfiling(ProfilingFeatureType.DAO_DETAILS, getProfilingId("raw", method), new WithProfilingCallback<Object, Throwable>() {
             @Override
             public Object execute() throws Throwable {
-                Object result = method.invoke(sqlDao, args);
+                // Real jdbc call
+                final Object result = executeJDBCCall(method, args);
                 // This is *almost* the default invocation except that we want to intercept getById calls to populate the caches; the pattern is to always fetch
                 // the object after it was created, which means this method is (by pattern) first called right after object creation and contains all the goodies we care
                 // about (record_id, account_record_id, object_id, tenant_record_id)
@@ -324,6 +323,7 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>, 
         if (changeType == ChangeType.UPDATE || changeType == ChangeType.DELETE) {
             for (final String entityId : entityIds) {
                 deletedEntities.put(entityId, sqlDao.getById(entityId, context));
+                printSQLWarnings();
             }
         }
 
@@ -331,7 +331,7 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>, 
         final Object obj = prof.executeWithProfiling(ProfilingFeatureType.DAO_DETAILS, getProfilingId("raw", method), new WithProfilingCallback<Object, Throwable>() {
             @Override
             public Object execute() throws Throwable {
-                return method.invoke(sqlDao, args);
+                return executeJDBCCall(method, args);
             }
         });
 
@@ -350,6 +350,27 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>, 
         }
     }
 
+    private Object executeJDBCCall(final Method method, final Object[] args) throws IllegalAccessException, InvocationTargetException {
+        final Object invoke = method.invoke(sqlDao, args);
+        printSQLWarnings();
+        return invoke;
+    }
+
+    private void printSQLWarnings() {
+        if (logger.isDebugEnabled()) {
+            try {
+                SQLWarning warning = handle.getConnection().getWarnings();
+                while (warning != null) {
+                    logger.debug("[SQL WARNING] {}", warning);
+                    warning = warning.getNextWarning();
+                }
+                handle.getConnection().clearWarnings();
+            } catch (final SQLException e) {
+                logger.debug("Error whilst retrieving SQL warnings", e);
+            }
+        }
+    }
+
     private void populateCacheOnGetByIdInvocation(final M model) {
         populateCaches(cacheControllerDispatcher, model);
     }
@@ -388,6 +409,7 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>, 
                 } else {
                     // See note above regarding "markAsInactive" operations
                     reHydratedEntity = MoreObjects.firstNonNull(sqlDao.getById(entityId, context), deletedEntity);
+                    printSQLWarnings();
                 }
                 Preconditions.checkNotNull(reHydratedEntity, "reHydratedEntity cannot be null");
                 final Long entityRecordId = reHydratedEntity.getRecordId();
@@ -483,7 +505,9 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>, 
 
     private Long insertHistory(final Long entityRecordId, final M entityModelDao, final ChangeType changeType, final InternalCallContext context) {
         final EntityHistoryModelDao<M, E> history = new EntityHistoryModelDao<M, E>(entityModelDao, entityRecordId, changeType, clock.getUTCNow());
-        return sqlDao.addHistoryFromTransaction(history, context);
+        final Long recordId = sqlDao.addHistoryFromTransaction(history, context);
+        printSQLWarnings();
+        return recordId;
     }
 
     private void insertAudits(final TableName tableName, final M entityModelDao, final Long entityRecordId, final Long historyRecordId, final ChangeType changeType, final InternalCallContext contextMaybeWithoutAccountRecordId) {
@@ -500,6 +524,7 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>, 
             context = contextMaybeWithoutAccountRecordId;
         }
         sqlDao.insertAuditFromTransaction(audit, context);
+        printSQLWarnings();
 
         // We need to invalidate the caches. There is a small window of doom here where caches will be stale.
         // TODO Knowledge on how the key is constructed is also in AuditSqlDao
diff --git a/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDefinitionDao.java b/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDefinitionDao.java
index c31ad6c..6a42f6f 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDefinitionDao.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDefinitionDao.java
@@ -105,8 +105,12 @@ public class DefaultTagDefinitionDao extends EntityDaoBase<TagDefinitionModelDao
         return transactionalSqlDao.execute(true, new EntitySqlDaoTransactionWrapper<TagDefinitionModelDao>() {
             @Override
             public TagDefinitionModelDao inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
-                final TagDefinitionModelDao tagDefinitionModelDao = SystemTags.lookup(definitionId);
-                return tagDefinitionModelDao != null ? tagDefinitionModelDao : entitySqlDaoWrapperFactory.become(TagDefinitionSqlDao.class).getById(definitionId.toString(), context);
+                final TagDefinitionModelDao systemTag = SystemTags.lookup(definitionId);
+                final TagDefinitionModelDao tag = systemTag != null ? systemTag : entitySqlDaoWrapperFactory.become(TagDefinitionSqlDao.class).getById(definitionId.toString(), context);
+                if(tag == null) {
+                    throw new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_DOES_NOT_EXIST, definitionId);
+                }
+                return tag;
             }
         });
     }