blob: 694322c780a7cf97852accbc713deef2b2754a6b [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.qi4j.sample.dcicargo.sample_a.context.support;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.joda.time.LocalDate;
import org.qi4j.api.injection.scope.Service;
import org.qi4j.api.injection.scope.Structure;
import org.qi4j.api.mixin.Mixins;
import org.qi4j.api.service.ServiceComposite;
import org.qi4j.api.unitofwork.UnitOfWork;
import org.qi4j.api.unitofwork.UnitOfWorkFactory;
import org.qi4j.api.value.ValueBuilder;
import org.qi4j.api.value.ValueBuilderFactory;
import org.qi4j.sample.dcicargo.pathfinder_a.api.GraphTraversalService;
import org.qi4j.sample.dcicargo.pathfinder_a.api.TransitEdge;
import org.qi4j.sample.dcicargo.pathfinder_a.api.TransitPath;
import org.qi4j.sample.dcicargo.sample_a.data.shipping.cargo.RouteSpecification;
import org.qi4j.sample.dcicargo.sample_a.data.shipping.itinerary.Itinerary;
import org.qi4j.sample.dcicargo.sample_a.data.shipping.itinerary.Leg;
import org.qi4j.sample.dcicargo.sample_a.data.shipping.location.Location;
import org.qi4j.sample.dcicargo.sample_a.data.shipping.voyage.Voyage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Routing service.
*
* Our end of the routing service. This is basically a data model
* translation layer between our domain model and the API put forward
* by the path finder team, which operates in a different context from us.
*/
@Mixins( RoutingService.Mixin.class )
public interface RoutingService
extends ServiceComposite
{
/**
* @param routeSpecification route specification
*
* @return A list of itineraries that satisfy the specification. May be an empty list if no route is found.
*/
List<Itinerary> fetchRoutesForSpecification( RouteSpecification routeSpecification )
throws FoundNoRoutesException;
abstract class Mixin
implements RoutingService
{
private static final Logger logger = LoggerFactory.getLogger( RoutingService.class );
@Structure
ValueBuilderFactory vbf;
@Structure
UnitOfWorkFactory uowf;
@Service
GraphTraversalService graphTraversalService;
final static int MAX_TRIES = 10;
public List<Itinerary> fetchRoutesForSpecification( RouteSpecification routeSpecification )
throws FoundNoRoutesException
{
final Location origin = routeSpecification.origin().get();
final Location destination = routeSpecification.destination().get();
List<TransitPath> transitPaths;
List<Itinerary> itineraries = new ArrayList<Itinerary>();
// Try a MAX_TRIES times to avoid empty results too often
int tries = 0;
do
{
try
{
transitPaths = graphTraversalService.findShortestPath( origin.getCode(), destination.getCode() );
}
catch( RemoteException e )
{
logger.error( e.getMessage(), e );
return Collections.emptyList();
}
// The returned result is then translated back into our domain model.
for( TransitPath transitPath : transitPaths )
{
final Itinerary itinerary = toItinerary( transitPath );
// Use the specification to safe-guard against invalid itineraries
// We can use the side-effects free method of the RouteSpecification data object
if( routeSpecification.isSatisfiedBy( itinerary ) )
{
itineraries.add( itinerary );
}
}
}
while( tries++ < MAX_TRIES && itineraries.size() == 0 );
if( itineraries.size() == 0 )
{
throw new FoundNoRoutesException( destination.name().get(),
new LocalDate( routeSpecification.arrivalDeadline().get() ) );
}
return itineraries;
}
private Itinerary toItinerary( TransitPath transitPath )
{
ValueBuilder<Itinerary> itinerary = vbf.newValueBuilder( Itinerary.class );
List<Leg> legs = new ArrayList<Leg>();
for( TransitEdge edge : transitPath.getTransitEdges() )
{
legs.add( toLeg( edge ) );
}
itinerary.prototype().legs().set( legs );
return itinerary.newInstance();
}
private Leg toLeg( TransitEdge edge )
{
UnitOfWork uow = uowf.currentUnitOfWork();
// Build Leg value object
ValueBuilder<Leg> leg = vbf.newValueBuilder( Leg.class );
leg.prototype().voyage().set( uow.get( Voyage.class, edge.getVoyageNumber() ) );
leg.prototype().loadLocation().set( uow.get( Location.class, edge.getFromUnLocode() ) );
leg.prototype().unloadLocation().set( uow.get( Location.class, edge.getToUnLocode() ) );
leg.prototype().loadTime().set( edge.getFromDate() );
leg.prototype().unloadTime().set( edge.getToDate() );
return leg.newInstance();
}
}
}