blob: 383c2280c35bb0f6fb50f4c5bd3f51bf3586e516 [file] [log] [blame]
/*
* 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.apache.zest.sample.dcicargo.sample_a.context.shipping.booking;
import java.util.Date;
import java.util.List;
import org.joda.time.DateMidnight;
import org.apache.zest.api.common.Optional;
import org.apache.zest.api.injection.scope.Service;
import org.apache.zest.api.injection.scope.This;
import org.apache.zest.api.mixin.Mixins;
import org.apache.zest.api.value.ValueBuilder;
import org.apache.zest.api.value.ValueBuilderFactory;
import org.apache.zest.sample.dcicargo.sample_a.context.support.FoundNoRoutesException;
import org.apache.zest.sample.dcicargo.sample_a.context.support.RoutingService;
import org.apache.zest.sample.dcicargo.sample_a.data.entity.CargoEntity;
import org.apache.zest.sample.dcicargo.sample_a.data.entity.CargosEntity;
import org.apache.zest.sample.dcicargo.sample_a.data.entity.LocationEntity;
import org.apache.zest.sample.dcicargo.sample_a.data.shipping.cargo.Cargo;
import org.apache.zest.sample.dcicargo.sample_a.data.shipping.cargo.Cargos;
import org.apache.zest.sample.dcicargo.sample_a.data.shipping.cargo.RouteSpecification;
import org.apache.zest.sample.dcicargo.sample_a.data.shipping.cargo.TrackingId;
import org.apache.zest.sample.dcicargo.sample_a.data.shipping.delivery.Delivery;
import org.apache.zest.sample.dcicargo.sample_a.data.shipping.itinerary.Itinerary;
import org.apache.zest.sample.dcicargo.sample_a.data.shipping.location.Location;
import org.apache.zest.sample.dcicargo.sample_a.infrastructure.dci.Context;
import org.apache.zest.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 RouteException( "Origin location can't be same as destination location." );
}
if( deadline == null )
{
throw new RouteException( "Arrival deadline cannot be null." );
}
Date endOfToday = new DateMidnight().plusDays( 1 ).toDate();
if( deadline.before( endOfToday ) )
{
throw new RouteException( "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();
}
}