package org.broadleafcommerce.inventory.service.workflow;
import java.util.Map;
import java.util.Set;
import javax.annotation.Resource;
import org.broadleafcommerce.common.logging.SupportLogManager;
import org.broadleafcommerce.common.logging.SupportLogger;
import org.broadleafcommerce.core.workflow.Activity;
import org.broadleafcommerce.core.workflow.ProcessContext;
import org.broadleafcommerce.core.workflow.state.RollbackFailureException;
import org.broadleafcommerce.core.workflow.state.RollbackHandler;
import org.broadleafcommerce.inventory.domain.FulfillmentLocation;
import org.broadleafcommerce.inventory.exception.ConcurrentInventoryModificationException;
import org.broadleafcommerce.inventory.exception.InventoryUnavailableException;
import org.broadleafcommerce.inventory.service.InventoryService;
/**
* Provides a standard rollback handler to ensure that in the event of something going wrong in a workflow, that
*
* @author Kelly Tisdell
*
*/
public class InventoryRollbackHandler implements RollbackHandler {
private static final SupportLogger LOG = SupportLogManager.getLogger("broadleaf-oms", InventoryRollbackHandler.class);
public static final String ROLLBACK_BLC_INVENTORY_DECREMENTED = "ROLLBACK_BLC_INVENTORY_DECREMENTED";
public static final String ROLLBACK_BLC_INVENTORY_INCREMENTED = "ROLLBACK_BLC_INVENTORY_INCREMENTED";
public static final String ROLLBACK_BLC_ORDER_ID = "ROLLBACK_BLC_ORDER_ID";
@Resource(name = "blInventoryService")
protected InventoryService inventoryService;
protected int maxRetries = 5;
@Override
public void rollbackState(Activity<? extends ProcessContext> activity,
ProcessContext processContext, Map<String, Object> stateConfiguration) throws RollbackFailureException {
if (stateConfiguration == null ||
(stateConfiguration.get(ROLLBACK_BLC_INVENTORY_DECREMENTED) == null &&
stateConfiguration.get(ROLLBACK_BLC_INVENTORY_INCREMENTED) == null)) {
return;
}
String orderId = "(Not Known)";
if (stateConfiguration.get(ROLLBACK_BLC_ORDER_ID) != null) {
orderId = String.valueOf(stateConfiguration.get(ROLLBACK_BLC_ORDER_ID));
}
@SuppressWarnings("unchecked")
Map<FulfillmentLocation, InventoryState> inventoryToIncrement = (Map<FulfillmentLocation, InventoryState>) stateConfiguration.get(ROLLBACK_BLC_INVENTORY_DECREMENTED);
if (inventoryToIncrement != null && !inventoryToIncrement.isEmpty()) {
Set<FulfillmentLocation> keys = inventoryToIncrement.keySet();
for (FulfillmentLocation location : keys) {
int retryCount = 0;
while (retryCount <= maxRetries) {
try {
inventoryService.incrementInventory(inventoryToIncrement.get(location).getSkuQuantityMap(), location);
inventoryService.incrementInventoryOnHand(inventoryToIncrement.get(location).getSkuQuantityMap(), location);
break;
} catch (ConcurrentInventoryModificationException ex) {
retryCount++;
if (retryCount == maxRetries) {
LOG.error("After an exception was encountered during checkout, where inventory was decremented. " + maxRetries + " attempts were made to compensate, " +
"but were unsuccessful for order ID: " + orderId + ". This should be corrected manually!", ex);
}
} catch (RuntimeException ex) {
LOG.error("An unexpected error occured in the error handler of the checkout workflow trying to compensate for inventory. This happend for order ID: " +
orderId + ". This should be corrected manually!", ex);
break;
}
}
}
}
@SuppressWarnings("unchecked")
Map<FulfillmentLocation, InventoryState> inventoryToDecrement = (Map<FulfillmentLocation, InventoryState>) stateConfiguration.get(ROLLBACK_BLC_INVENTORY_INCREMENTED);
if (inventoryToDecrement != null && !inventoryToDecrement.isEmpty()) {
Set<FulfillmentLocation> keys = inventoryToIncrement.keySet();
for (FulfillmentLocation location : keys) {
int retryCount = 0;
while (retryCount <= maxRetries) {
try {
inventoryService.decrementInventory(inventoryToDecrement.get(location).getSkuQuantityMap(), location);
inventoryService.decrementInventoryOnHand(inventoryToDecrement.get(location).getSkuQuantityMap(), location);
break;
} catch (ConcurrentInventoryModificationException ex) {
retryCount++;
if (retryCount == maxRetries) {
LOG.error("After an exception was encountered during checkout, where inventory was incremented. " + maxRetries + " attempts were made to compensate, " +
"but were unsuccessful for order ID: " + orderId + ". This should be corrected manually!", ex);
}
} catch (InventoryUnavailableException e) {
//This is an awkward, unlikely state. I just added some inventory, but something happened, and I want to remove it, but it's already gone!
LOG.error("While trying roll back (decrement) inventory, we found that there was none left decrement.", e);
} catch (RuntimeException ex) {
LOG.error("An unexpected error occured in the error handler of the checkout workflow trying to compensate for inventory. This happend for order ID: " +
orderId + ". This should be corrected manually!", ex);
break;
}
}
}
}
}
public void setMaxRetries(int maxRetries) {
if (this.maxRetries < 0) {
throw new IllegalArgumentException("Max retries cannot be less than 0.");
}
this.maxRetries = maxRetries;
}
}