Skip to content

Commit

Permalink
entitlement: simplify ordering building logic of SubscriptionEvent
Browse files Browse the repository at this point in the history
Signed-off-by: Pierre-Alexandre Meyer <pierre@mouraf.org>
  • Loading branch information
pierre committed May 1, 2017
1 parent 9d1f705 commit 70d257d
Show file tree
Hide file tree
Showing 4 changed files with 480 additions and 361 deletions.
@@ -1,6 +1,6 @@
/*
* Copyright 2014-2016 Groupon, Inc
* Copyright 2014-2016 The Billing Project, LLC
* Copyright 2014-2017 Groupon, Inc
* Copyright 2014-2017 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
Expand All @@ -19,6 +19,7 @@

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
Expand All @@ -42,6 +43,7 @@
import org.killbill.billing.entitlement.block.DefaultBlockingChecker.DefaultBlockingAggregator;
import org.killbill.billing.junction.DefaultBlockingState;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
Expand All @@ -53,13 +55,13 @@
// Given an event stream (across one or multiple entitlements), insert the blocking events at the right place
public class BlockingStateOrdering extends EntitlementOrderingBase {

private static final BlockingStateOrdering INSTANCE = new BlockingStateOrdering();
@VisibleForTesting
static final BlockingStateOrdering INSTANCE = new BlockingStateOrdering();

private BlockingStateOrdering() {}

public static void insertSorted(final Iterable<Entitlement> entitlements, final InternalTenantContext internalTenantContext, final LinkedList<SubscriptionEvent> inputAndOutputResult) {
INSTANCE.computeEvents(entitlements, internalTenantContext, inputAndOutputResult);

}

private void computeEvents(final Iterable<Entitlement> entitlements, final InternalTenantContext internalTenantContext, final LinkedList<SubscriptionEvent> inputAndOutputResult) {
Expand All @@ -71,6 +73,14 @@ private void computeEvents(final Iterable<Entitlement> entitlements, final Inter
blockingStates.addAll(((DefaultEntitlement) entitlement).getEventsStream().getBlockingStates());
}

computeEvents(new LinkedList<UUID>(allEntitlementUUIDs), blockingStates, internalTenantContext, inputAndOutputResult);
}

@VisibleForTesting
void computeEvents(final LinkedList<UUID> allEntitlementUUIDs, final Collection<BlockingState> blockingStates, final InternalTenantContext internalTenantContext, final LinkedList<SubscriptionEvent> inputAndOutputResult) {
// Make sure the ordering is stable
Collections.sort(allEntitlementUUIDs);

final SupportForOlderVersionThan_0_17_X backwardCompatibleContext = new SupportForOlderVersionThan_0_17_X(inputAndOutputResult, blockingStates);

// Trust the incoming ordering here: blocking states were sorted using ProxyBlockingStateDao#sortedCopy
Expand Down Expand Up @@ -107,12 +117,7 @@ private int insertFromBlockingEvent(final Collection<UUID> allEntitlementUUIDs,
shouldContinue = false;
break;
case 0:
// In case of exact same date, we want to make sure that a START_ENTITLEMENT event gets correctly populated when the STOP_BILLING is also on the same date
if (currentBlockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_START) && cur.getSubscriptionEventType() != SubscriptionEventType.STOP_BILLING) {
shouldContinue = false;
} else {
shouldContinue = true;
}
shouldContinue = compareBlockingStateWithNextSubscriptionEvent(currentBlockingState, cur) > 0;
break;
case 1:
shouldContinue = true;
Expand Down Expand Up @@ -169,9 +174,75 @@ private int insertFromBlockingEvent(final Collection<UUID> allEntitlementUUIDs,
outputNewEvents.add(toSubscriptionEvent(prevNext[0], prevNext[1], targetEntitlementId, currentBlockingState, t, internalTenantContext));
}
}

return index;
}

private int compareBlockingStateWithNextSubscriptionEvent(final BlockingState blockingState, final SubscriptionEvent next) {
final String serviceName = blockingState.getService();

// For consistency, make sure entitlement-service and billing-service events always happen in a
// deterministic order (e.g. after other services for STOP events and before for START events)
if ((DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME.equals(serviceName) ||
BILLING_SERVICE_NAME.equals(serviceName) ||
ENT_BILLING_SERVICE_NAME.equals(serviceName)) &&
!(DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME.equals(next.getServiceName()) ||
BILLING_SERVICE_NAME.equals(next.getServiceName()) ||
ENT_BILLING_SERVICE_NAME.equals(next.getServiceName()))) {
// first is an entitlement-service or billing-service event, but not second
if (blockingState.isBlockBilling() || blockingState.isBlockEntitlement()) {
// PAUSE_ and STOP_ events go last
return 1;
} else {
return -1;
}
} else if ((DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME.equals(next.getServiceName()) ||
BILLING_SERVICE_NAME.equals(next.getServiceName()) ||
ENT_BILLING_SERVICE_NAME.equals(next.getServiceName())) &&
!(DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME.equals(serviceName) ||
BILLING_SERVICE_NAME.equals(serviceName) ||
ENT_BILLING_SERVICE_NAME.equals(serviceName))) {
// second is an entitlement-service or billing-service event, but not first
if (next.getSubscriptionEventType().equals(SubscriptionEventType.START_ENTITLEMENT) ||
next.getSubscriptionEventType().equals(SubscriptionEventType.START_BILLING) ||
next.getSubscriptionEventType().equals(SubscriptionEventType.RESUME_ENTITLEMENT) ||
next.getSubscriptionEventType().equals(SubscriptionEventType.RESUME_BILLING) ||
next.getSubscriptionEventType().equals(SubscriptionEventType.PHASE) ||
next.getSubscriptionEventType().equals(SubscriptionEventType.CHANGE)) {
return 1;
} else if (next.getSubscriptionEventType().equals(SubscriptionEventType.PAUSE_ENTITLEMENT) ||
next.getSubscriptionEventType().equals(SubscriptionEventType.PAUSE_BILLING) ||
next.getSubscriptionEventType().equals(SubscriptionEventType.STOP_ENTITLEMENT) ||
next.getSubscriptionEventType().equals(SubscriptionEventType.STOP_BILLING)) {
return -1;
} else {
// Default behavior
return 1;
}
} else if (isStartEntitlement(blockingState)) {
// START_ENTITLEMENT is always first
return -1;
} else if (next.getSubscriptionEventType().equals(SubscriptionEventType.START_ENTITLEMENT)) {
// START_ENTITLEMENT is always first
return 1;
} else if (next.getSubscriptionEventType().equals(SubscriptionEventType.STOP_BILLING)) {
// STOP_BILLING is always last
return -1;
} else if (next.getSubscriptionEventType().equals(SubscriptionEventType.START_BILLING)) {
// START_BILLING is first after START_ENTITLEMENT
return 1;
} else if (isStopEntitlement(blockingState)) {
// STOP_ENTITLEMENT is last after STOP_BILLING
return 1;
} else if (next.getSubscriptionEventType().equals(SubscriptionEventType.STOP_ENTITLEMENT)) {
// STOP_ENTITLEMENT is last after STOP_BILLING
return -1;
} else {
// Trust the current ordering
return 1;
}
}

// Extract prev and next events in the stream events for that particular target subscription from the insertionEvent
private SubscriptionEvent[] findPrevNext(final List<SubscriptionEvent> events, final UUID targetEntitlementId, final SubscriptionEvent insertionEvent) {
// Find prev/next event for the same entitlement
Expand Down Expand Up @@ -390,11 +461,11 @@ private List<SubscriptionEventType> addStateAndReturnEventTypes(final BlockingSt
bs.getEffectiveDate());

final List<SubscriptionEventType> result = new ArrayList<SubscriptionEventType>(4);
if (fixedBlockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_START)) {
if (isStartEntitlement(fixedBlockingState)) {
isEntitlementStarted = true;
result.add(SubscriptionEventType.START_ENTITLEMENT);
return result;
} else if (fixedBlockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CANCELLED)) {
} else if (isStopEntitlement(fixedBlockingState)) {
isEntitlementStopped = true;
result.add(SubscriptionEventType.STOP_ENTITLEMENT);
return result;
Expand Down Expand Up @@ -450,6 +521,16 @@ private BlockingAggregator getState() {
}
}

private static boolean isStartEntitlement(final BlockingState blockingState) {
return DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME.equals(blockingState.getService()) &&
DefaultEntitlementApi.ENT_STATE_START.equals(blockingState.getStateName());
}

private static boolean isStopEntitlement(final BlockingState blockingState) {
return DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME.equals(blockingState.getService()) &&
DefaultEntitlementApi.ENT_STATE_CANCELLED.equals(blockingState.getStateName());
}

//
// The logic to add the missing START_ENTITLEMENT for older subscriptions is contained in this class. When we want/need to drop backward compatibility we can
// simply drop this class and where it is called.
Expand Down

0 comments on commit 70d257d

Please sign in to comment.