package net.java.cargotracker.infrastructure.routing; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.ejb.Stateless; import javax.inject.Inject; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.GenericType; import javax.ws.rs.core.MediaType; import net.java.cargotracker.application.util.JsonMoxyConfigurationContextResolver; import net.java.cargotracker.domain.model.cargo.Itinerary; import net.java.cargotracker.domain.model.cargo.Leg; import net.java.cargotracker.domain.model.cargo.RouteSpecification; import net.java.cargotracker.domain.model.location.LocationRepository; import net.java.cargotracker.domain.model.location.UnLocode; import net.java.cargotracker.domain.model.voyage.VoyageNumber; import net.java.cargotracker.domain.model.voyage.VoyageRepository; import net.java.cargotracker.domain.service.RoutingService; import net.java.pathfinder.api.TransitEdge; import net.java.pathfinder.api.TransitPath; import org.glassfish.jersey.moxy.json.MoxyJsonFeature; /** * 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 routing team, * which operates in a different context from us. * */ @Stateless public class ExternalRoutingService implements RoutingService { @Resource(name = "graphTraversalUrl") private String graphTraversalUrl; // TODO Can I use injection? private final Client jaxrsClient = ClientBuilder.newClient(); private WebTarget graphTraversalResource; @Inject private LocationRepository locationRepository; @Inject private VoyageRepository voyageRepository; // TODO Use injection instead? private static final Logger log = Logger.getLogger( ExternalRoutingService.class.getName()); @PostConstruct public void init() { graphTraversalResource = jaxrsClient.target(graphTraversalUrl); graphTraversalResource.register(new MoxyJsonFeature()).register( new JsonMoxyConfigurationContextResolver()); } @Override public List<Itinerary> fetchRoutesForSpecification( RouteSpecification routeSpecification) { // The RouteSpecification is picked apart and adapted to the external API. String origin = routeSpecification.getOrigin().getUnLocode().getIdString(); String destination = routeSpecification.getDestination().getUnLocode() .getIdString(); List<TransitPath> transitPaths = graphTraversalResource .queryParam("origin", origin) .queryParam("destination", destination) .request(MediaType.APPLICATION_JSON_TYPE) .get(new GenericType<List<TransitPath>>() { }); // The returned result is then translated back into our domain model. List<Itinerary> itineraries = new ArrayList<>(); for (TransitPath transitPath : transitPaths) { Itinerary itinerary = toItinerary(transitPath); // Use the specification to safe-guard against invalid itineraries if (routeSpecification.isSatisfiedBy(itinerary)) { itineraries.add(itinerary); } else { log.log(Level.FINE, "Received itinerary that did not satisfy the route specification"); } } return itineraries; } private Itinerary toItinerary(TransitPath transitPath) { List<Leg> legs = new ArrayList<>(transitPath.getTransitEdges().size()); for (TransitEdge edge : transitPath.getTransitEdges()) { legs.add(toLeg(edge)); } return new Itinerary(legs); } private Leg toLeg(TransitEdge edge) { return new Leg( voyageRepository.find(new VoyageNumber(edge.getVoyageNumber())), locationRepository.find(new UnLocode(edge.getFromUnLocode())), locationRepository.find(new UnLocode(edge.getToUnLocode())), edge.getFromDate(), edge.getToDate()); } }