Java tutorial
/* * Copyright 2011 Marc Grue. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * * See the License for the specific language governing permissions and * limitations under the License. */ package org.qi4j.sample.dcicargo.sample_a.context.shipping.booking; import java.util.Date; import java.util.List; import org.joda.time.DateMidnight; import org.qi4j.api.common.Optional; import org.qi4j.api.injection.scope.Service; import org.qi4j.api.injection.scope.This; import org.qi4j.api.mixin.Mixins; import org.qi4j.api.value.ValueBuilder; import org.qi4j.api.value.ValueBuilderFactory; import org.qi4j.sample.dcicargo.sample_a.context.support.FoundNoRoutesException; import org.qi4j.sample.dcicargo.sample_a.context.support.RoutingService; import org.qi4j.sample.dcicargo.sample_a.data.entity.CargoEntity; import org.qi4j.sample.dcicargo.sample_a.data.entity.CargosEntity; import org.qi4j.sample.dcicargo.sample_a.data.entity.LocationEntity; import org.qi4j.sample.dcicargo.sample_a.data.shipping.cargo.Cargo; import org.qi4j.sample.dcicargo.sample_a.data.shipping.cargo.Cargos; import org.qi4j.sample.dcicargo.sample_a.data.shipping.cargo.RouteSpecification; import org.qi4j.sample.dcicargo.sample_a.data.shipping.cargo.TrackingId; import org.qi4j.sample.dcicargo.sample_a.data.shipping.delivery.Delivery; import org.qi4j.sample.dcicargo.sample_a.data.shipping.itinerary.Itinerary; import org.qi4j.sample.dcicargo.sample_a.data.shipping.location.Location; import org.qi4j.sample.dcicargo.sample_a.infrastructure.dci.Context; import org.qi4j.sample.dcicargo.sample_a.infrastructure.dci.RoleMixin; /** * Book New Cargo use case */ public class BookNewCargo extends Context { // ROLES --------------------------------------------------------------------- // Methodful Roles private CargoFactoryRole cargoFactory; private RoutingFacadeRole routingFacade; // Methodless Roles private Location origin; private Location destination; private Date arrivalDeadline; private Itinerary itinerary; // CONTEXT CONSTRUCTORS ------------------------------------------------------ public BookNewCargo(Cargos cargos, Location origin, Location destination, Date arrivalDeadline) throws Exception { cargoFactory = rolePlayer(CargoFactoryRole.class, cargos); this.origin = origin; this.destination = destination; this.arrivalDeadline = arrivalDeadline; } public BookNewCargo(Cargo cargo) { routingFacade = rolePlayer(RoutingFacadeRole.class, cargo); } public BookNewCargo(Cargo cargo, Itinerary itinerary) { routingFacade = rolePlayer(RoutingFacadeRole.class, cargo); this.itinerary = itinerary; } // Constructor proxies for communication layer public BookNewCargo(String originId, String destinationId, Date deadline) throws Exception { this(loadEntity(CargosEntity.class, CargosEntity.CARGOS_ID), loadEntity(Location.class, originId), loadEntity(Location.class, destinationId), deadline); } public BookNewCargo(String trackingIdString) { this(loadEntity(CargoEntity.class, trackingIdString)); } public BookNewCargo(String trackingIdString, Itinerary itinerary) { this(loadEntity(Cargo.class, trackingIdString), itinerary); } // INTERACTIONS -------------------------------------------------------------- public TrackingId book() { return cargoFactory.createCargo(null); } public TrackingId createCargo(String trackingIdString) { return cargoFactory.createCargo(trackingIdString); } public void changeDestination(String destination) { routingFacade.changeDestination(loadEntity(LocationEntity.class, destination)); } public List<Itinerary> routeCandidates() throws FoundNoRoutesException { return routingFacade.routeCandidates(); } public void assignCargoToRoute() { routingFacade.assignCargoToRoute(); } // METHODFUL ROLE IMPLEMENTATIONS -------------------------------------------- @Mixins(CargoFactoryRole.Mixin.class) public interface CargoFactoryRole { void setContext(BookNewCargo context); TrackingId createCargo(@Optional String trackingIdString); class Mixin extends RoleMixin<BookNewCargo> implements CargoFactoryRole { @This Cargos cargos; public TrackingId createCargo(String trackingIdString) { // New route specification RouteSpecification routeSpec = context.buildRouteSpecification(vbf, context.origin, context.destination, context.arrivalDeadline); // Build delivery snapshot from route specification Delivery delivery = new BuildDeliverySnapshot(routeSpec).get(); // Create cargo Cargo cargo = cargos.createCargo(routeSpec, delivery, trackingIdString); return cargo.trackingId().get(); } } } @Mixins(RoutingFacadeRole.Mixin.class) public interface RoutingFacadeRole { void setContext(BookNewCargo context); List<Itinerary> routeCandidates() throws FoundNoRoutesException; void assignCargoToRoute(); void changeDestination(Location destination); class Mixin extends RoleMixin<BookNewCargo> implements RoutingFacadeRole { @This Cargo cargo; @Service RoutingService routingService; // Use case step 3 - system calculates possible routes public List<Itinerary> routeCandidates() throws FoundNoRoutesException // Deviation 3a { return routingService.fetchRoutesForSpecification(cargo.routeSpecification().get()); } public void assignCargoToRoute() { cargo.itinerary().set(context.itinerary); if (cargo.delivery().get().lastHandlingEvent().get() != null) { // We treat subsequent route assignments as reroutes of misdirected cargo cargo.delivery().get().lastHandlingEvent().get().wasUnexpected().set(true); } // Build delivery snapshot with updated itinerary cargo.delivery().set(new BuildDeliverySnapshot(cargo).get()); } public void changeDestination(Location newDestination) { Location currentOrigin = cargo.routeSpecification().get().origin().get(); Date currentDeadline = cargo.routeSpecification().get().arrivalDeadline().get(); RouteSpecification newRouteSpecification = context.buildRouteSpecification(vbf, currentOrigin, newDestination, currentDeadline); cargo.routeSpecification().set(newRouteSpecification); // Build new delivery snapshot with updated route specification cargo.delivery().set(new BuildDeliverySnapshot(cargo).get()); } } } public RouteSpecification buildRouteSpecification(ValueBuilderFactory vbf, Location origin, Location destination, Date deadline) { if (origin == destination) { throw new RuntimeException("Origin location can't be same as destination location."); } if (deadline == null) { throw new RuntimeException("Arrival deadline cannot be null."); } Date endOfToday = new DateMidnight().plusDays(1).toDate(); if (deadline.before(endOfToday)) { throw new RuntimeException("Arrival deadline is in the past or Today." + "\nDeadline " + deadline + "\nToday (midnight) " + endOfToday); } ValueBuilder<RouteSpecification> routeSpec = vbf.newValueBuilder(RouteSpecification.class); routeSpec.prototype().origin().set(origin); routeSpec.prototype().destination().set(destination); routeSpec.prototype().arrivalDeadline().set(deadline); return routeSpec.newInstance(); } }