killbill-aplcache

Details

.travis.yml 10(+1 -9)

diff --git a/.travis.yml b/.travis.yml
index bdf121e..be5eefd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -34,19 +34,11 @@ matrix:
     - env: PHASE="-Ptravis,jdk17" JDK=openjdk8
     - env: PHASE="-Ptravis,jdk18" JDK=oraclejdk8
     - env: PHASE="-Ptravis,jdk18" JDK=openjdk8
-    - env: PHASE="-Pmysql,jdk16" JDK=oraclejdk8
-    - env: PHASE="-Pmysql,jdk16" JDK=openjdk8
-    - env: PHASE="-Pmysql,jdk17" JDK=oraclejdk8
-    - env: PHASE="-Pmysql,jdk17" JDK=openjdk8
     - env: PHASE="-Pmysql,jdk18" JDK=oraclejdk8
     - env: PHASE="-Pmysql,jdk18" JDK=openjdk8
-    - env: PHASE="-Ppostgresql,jdk16" JDK=oraclejdk8
-    - env: PHASE="-Ppostgresql,jdk16" JDK=openjdk8
-    - env: PHASE="-Ppostgresql,jdk17" JDK=oraclejdk8
-    - env: PHASE="-Ppostgresql,jdk17" JDK=openjdk8
     - env: PHASE="-Ppostgresql,jdk18" JDK=oraclejdk8
     - env: PHASE="-Ppostgresql,jdk18" JDK=openjdk8
   fast_finish: true
 
 after_success:
-  - '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && echo "<settings><servers><server><id>sonatype-nexus-snapshots</id><username>\${env.OSSRH_USER}</username><password>\${env.OSSRH_PASS}</password></server></servers></settings>" > ~/settings.xml && mvn deploy -DskipTests=true --settings ~/settings.xml | egrep "WARN|ERR|\[INFO\]\ ---|Upload" | egrep -v "[0-9]+/[0-9]+ KB" ; rm -f ~/settings.xml'
+  - '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && echo "<settings><servers><server><id>sonatype-nexus-snapshots</id><username>\${env.OSSRH_USER}</username><password>\${env.OSSRH_PASS}</password></server></servers></settings>" > ~/settings.xml && mvn deploy -DskipTests=true --settings ~/settings.xml | egrep "WARN|ERR|\[INFO\]\ ---|Upload" | egrep -v "[0-9]+/[0-9]+ [kKmM]B" ; rm -f ~/settings.xml'
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
index f41b399..ac018a3 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
@@ -259,16 +259,17 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
     @Override
     public SubscriptionBaseBundle createSubscriptionBundle(final DefaultSubscriptionBaseBundle bundle, final Catalog catalog, final InternalCallContext context) throws SubscriptionBaseApiException {
 
+
         return transactionalSqlDao.execute(SubscriptionBaseApiException.class, new EntitySqlDaoTransactionWrapper<SubscriptionBaseBundle>() {
-            @Override
-            public SubscriptionBaseBundle inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
-                final List<SubscriptionBundleModelDao> existingBundles = entitySqlDaoWrapperFactory.become(BundleSqlDao.class).getBundlesForKey(bundle.getExternalKey(), context);
-                //
-                // Because the creation of the SubscriptionBundle is not atomic (with creation of Subscription/SubscriptionEvent), we verify if we were left
-                // with an empty SubscriptionBaseBundle form a past failing operation (See #684). We only allow reuse if such SubscriptionBaseBundle is fully
-                // empty (and don't allow use case where all Subscription are cancelled, which is the condition for that key to be re-used)
-                // Such condition should have been checked upstream (to decide whether that key is valid or not)
-                //
+
+            //
+            // Because the creation of the SubscriptionBundle is not atomic (with creation of Subscription/SubscriptionEvent), we verify if we were left
+            // with an empty SubscriptionBaseBundle form a past failing operation (See #684). We only allow reuse if such SubscriptionBaseBundle is fully
+            // empty (and don't allow use case where all Subscription are cancelled, which is the condition for that key to be re-used)
+            // Such condition should have been checked upstream (to decide whether that key is valid or not)
+            //
+
+            private SubscriptionBaseBundle findExistingUnusedBundleForExternalKeyAndAccount(final List<SubscriptionBundleModelDao> existingBundles, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) {
                 final SubscriptionBundleModelDao existingBundleForAccount = Iterables.tryFind(existingBundles, new Predicate<SubscriptionBundleModelDao>() {
                     @Override
                     public boolean apply(final SubscriptionBundleModelDao input) {
@@ -276,19 +277,37 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
                     }
                 }).orNull();
 
-                // If Bundle already exists, and there is 0 Subscription, we reuse
+                // If Bundle already exists, and there is 0 Subscription associated with this bundle, we reuse
                 if (existingBundleForAccount != null) {
                     final List<SubscriptionModelDao> accountSubscriptions = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class).getByAccountRecordId(context);
-                    if (accountSubscriptions == null || accountSubscriptions.size() == 0) {
+                    if (accountSubscriptions == null ||
+                        ! Iterables.any(accountSubscriptions, new Predicate<SubscriptionModelDao>() {
+                        @Override
+                        public boolean apply(final SubscriptionModelDao input) {
+                            return input.getBundleId().equals(existingBundleForAccount.getId());
+                        }
+                    })) {
                         return SubscriptionBundleModelDao.toSubscriptionbundle(existingBundleForAccount);
                     }
                 }
+                return null;
+            }
+
+
+            @Override
+            public SubscriptionBaseBundle inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+                final List<SubscriptionBundleModelDao> existingBundles = entitySqlDaoWrapperFactory.become(BundleSqlDao.class).getBundlesForKey(bundle.getExternalKey(), context);
+
+                final SubscriptionBaseBundle unusedBundle = findExistingUnusedBundleForExternalKeyAndAccount(existingBundles, entitySqlDaoWrapperFactory);
+                if (unusedBundle != null) {
+                    return unusedBundle;
+                }
 
                 for (SubscriptionBundleModelDao cur : existingBundles) {
                     final List<SubscriptionModelDao> subscriptions = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class).getSubscriptionsFromBundleId(cur.getId().toString(), context);
                     final Iterable<SubscriptionModelDao> filtered = subscriptions != null ? Iterables.filter(subscriptions, new Predicate<SubscriptionModelDao>() {
                         @Override
-                        public boolean apply(@Nullable final SubscriptionModelDao input) {
+                        public boolean apply(final SubscriptionModelDao input) {
                             return input.getCategory() != ProductCategory.ADD_ON;
                         }
                     }) : ImmutableList.<SubscriptionModelDao>of();