Release inventory

This commit is contained in:
Kenny Bastani
2017-01-11 22:14:45 -08:00
parent d50330ba34
commit 240ff2da3a
7 changed files with 168 additions and 3 deletions

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
*.class
dump.rdb
# Mobile Tools for Java (J2ME)
.mtj.tmp/

View File

@@ -8,6 +8,9 @@ import demo.order.event.OrderEventProcessor;
import demo.order.event.OrderEventType;
import demo.order.event.OrderEvents;
import demo.payment.domain.Payment;
import demo.reservation.domain.Reservation;
import demo.reservation.domain.ReservationStatus;
import demo.reservation.domain.Reservations;
import org.apache.log4j.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -329,7 +332,7 @@ public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderS
@Bean
public Action<OrderStatus, OrderEventType> reservationFailed() {
return context -> applyEvent(context,
new ReservationSucceeded(context, event -> {
new ReservationFailed(context, event -> {
log.info(event.getType() + ": " + event.getLink("order").getHref());
// Get the order resource for the event
Traverson traverson = new Traverson(
@@ -337,6 +340,22 @@ public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderS
MediaTypes.HAL_JSON
);
// Release the reservations
Reservations reservations = traverson.follow("self", "reservations")
.toObject(Reservations.class);
reservations.getContent().stream()
.filter(r -> r.getStatus() == ReservationStatus.RESERVATION_SUCCEEDED)
.forEach(r -> {
Traverson res = new Traverson(
URI.create(r.getLink("self").getHref()),
MediaTypes.HAL_JSON
);
res.follow("self", "commands", "releaseInventory")
.toObject(Reservation.class);
});
return traverson.follow("self")
.toEntity(Order.class)
.getBody();
@@ -360,6 +379,5 @@ public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderS
}));
}
}

View File

@@ -0,0 +1,76 @@
package demo.reservation.action;
import demo.domain.Action;
import demo.inventory.domain.Inventory;
import demo.inventory.domain.InventoryService;
import demo.inventory.domain.InventoryStatus;
import demo.inventory.event.InventoryEvent;
import demo.reservation.domain.Reservation;
import demo.reservation.domain.ReservationModule;
import demo.reservation.domain.ReservationService;
import demo.reservation.domain.ReservationStatus;
import demo.reservation.event.ReservationEvent;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import java.util.function.Function;
import static demo.inventory.event.InventoryEventType.INVENTORY_RELEASED;
import static demo.reservation.event.ReservationEventType.RESERVATION_FAILED;
/**
* Release inventory for a {@link Reservation}.
*
* @author Kenny Bastani
*/
@Service
public class ReleaseInventory extends Action<Reservation> {
private final Logger log = Logger.getLogger(this.getClass());
private final InventoryService inventoryService;
public ReleaseInventory(InventoryService inventoryService) {
this.inventoryService = inventoryService;
}
public Function<Reservation, Reservation> getFunction() {
return (reservation) -> {
Assert.isTrue(reservation.getStatus() == ReservationStatus.RESERVATION_SUCCEEDED,
"Reservation must be in a succeeded state");
Assert.notNull(reservation.getInventory(), "The reservation has no connected inventory");
ReservationService reservationService = reservation.getModule(ReservationModule.class).getDefaultService();
Inventory inventory = reservation.getInventory();
try {
// Remove the inventory and set the reservation to failed
reservation.setInventory(null);
reservation.setStatus(ReservationStatus.RESERVATION_FAILED);
reservation = reservationService.update(reservation);
// Trigger the reservation failed event
reservation.sendAsyncEvent(new ReservationEvent(RESERVATION_FAILED, reservation));
} catch (Exception ex) {
log.error("Could not release the reservation's inventory", ex);
if (reservation.getStatus() == ReservationStatus.RESERVATION_FAILED) {
// Rollback the attempt
reservation.setInventory(inventory);
reservation.setStatus(ReservationStatus.RESERVATION_SUCCEEDED);
reservation = reservationService.update(reservation);
}
throw ex;
} finally {
// Release the inventory
inventory.setReservation(null);
inventory.setStatus(InventoryStatus.RESERVATION_PENDING);
inventory = inventoryService.update(inventory);
// Trigger the inventory released event
inventory.sendAsyncEvent(new InventoryEvent(INVENTORY_RELEASED, inventory));
}
return reservation;
};
}
}

View File

@@ -101,6 +101,13 @@ public class ReservationController {
.orElseThrow(() -> new RuntimeException("The command could not be applied"));
}
@RequestMapping(path = "/reservations/{id}/commands/releaseInventory")
public ResponseEntity releaseInventory(@PathVariable Long id) {
return Optional.ofNullable(reservationService.get(id))
.map(e -> new ResponseEntity<>(getReservationResource(e.releaseInventory()), HttpStatus.OK))
.orElseThrow(() -> new RuntimeException("The command could not be applied"));
}
@RequestMapping(path = "/reservations/{id}/commands/connectOrder")
public ResponseEntity connectOrder(@PathVariable Long id, @RequestParam(value = "orderId") Long orderId) {
return Optional.ofNullable(reservationService.get(id)

View File

@@ -9,6 +9,7 @@ import demo.domain.Module;
import demo.inventory.domain.Inventory;
import demo.reservation.action.ConnectOrder;
import demo.reservation.action.ConnectInventory;
import demo.reservation.action.ReleaseInventory;
import demo.reservation.controller.ReservationController;
import demo.reservation.event.ReservationEvent;
import demo.warehouse.domain.Warehouse;
@@ -114,6 +115,13 @@ public class Reservation extends AbstractEntity<ReservationEvent, Long> {
.apply(this);
}
@Command(method = "releaseInventory", controller = ReservationController.class)
public Reservation releaseInventory() {
return getAction(ReleaseInventory.class)
.getFunction()
.apply(this);
}
@Command(method = "connectOrder", controller = ReservationController.class)
public Reservation connectOrder(Long orderId) {
return getAction(ConnectOrder.class)

View File

@@ -114,7 +114,13 @@ public class InventoryStateMachineConfig extends EnumStateMachineConfigurerAdapt
.source(InventoryStatus.RESERVATION_CONNECTED)
.target(InventoryStatus.INVENTORY_RESERVED)
.event(InventoryEventType.INVENTORY_RESERVED)
.action(inventoryReserved());
.action(inventoryReserved())
.and()
.withExternal()
.source(InventoryStatus.INVENTORY_RESERVED)
.target(InventoryStatus.RESERVATION_PENDING)
.event(InventoryEventType.INVENTORY_RELEASED)
.action(inventoryReleased());
} catch (Exception e) {
throw new RuntimeException("Could not configure state machine transitions", e);
}
@@ -173,5 +179,22 @@ public class InventoryStateMachineConfig extends EnumStateMachineConfigurerAdapt
.getBody();
}));
}
@Bean
public Action<InventoryStatus, InventoryEventType> inventoryReleased() {
return context -> applyEvent(context,
new InventoryReleased(context, event -> {
log.info(event.getType() + ": " + event.getLink("inventory").getHref());
// Get the inventory resource for the event
Traverson traverson = new Traverson(
URI.create(event.getLink("inventory").getHref()),
MediaTypes.HAL_JSON
);
return traverson.follow("self")
.toEntity(Inventory.class)
.getBody();
}));
}
}

View File

@@ -0,0 +1,32 @@
package demo.inventory.function;
import demo.inventory.domain.Inventory;
import demo.inventory.domain.InventoryStatus;
import demo.inventory.event.InventoryEvent;
import demo.inventory.event.InventoryEventType;
import org.apache.log4j.Logger;
import org.springframework.statemachine.StateContext;
import java.util.function.Function;
public class InventoryReleased extends InventoryFunction {
final private Logger log = Logger.getLogger(InventoryReleased.class);
public InventoryReleased(StateContext<InventoryStatus, InventoryEventType> context, Function<InventoryEvent,
Inventory> lambda) {
super(context, lambda);
}
/**
* Apply an {@link InventoryEvent} to the lambda function that was provided through the
* constructor of this {@link InventoryFunction}.
*
* @param event is the {@link InventoryEvent} to apply to the lambda function
*/
@Override
public Inventory apply(InventoryEvent event) {
log.info("Executing workflow for inventory released...");
return super.apply(event);
}
}