/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 com.apachecon.camel.expedium; import java.io.File; import org.apache.camel.Exchange; import org.apache.camel.Expression; import org.apache.camel.Message; import org.apache.camel.Predicate; import org.apache.camel.Processor; import org.apache.camel.RuntimeCamelException; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.converter.jaxb.JaxbDataFormat; import org.apache.camel.impl.DefaultExchange; import org.apache.camel.processor.aggregate.AggregationStrategy; import org.apache.camel.spi.DataFormat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.apachecon.camel.claimcheck.ClaimCheck; import com.apachecon.camel.claimcheck.ClaimCheck.BayInfo; import com.apachecon.camel.expedium.types.Request; import com.apachecon.camel.expedium.types.Reservation; public class ReservationBuilder extends RouteBuilder { public static final String REQUESTS_RECIPIENTS = "CamelRecipients"; public static final String EXPEDIUM_BAY = "expedium"; private static final Logger LOG = LoggerFactory.getLogger(ReservationBuilder.class); @Override public void configure() throws Exception { LOG.info("STARTING..."); JaxbDataFormat jaxb = new JaxbDataFormat("com.apachecon.camel.expedium.types"); from("file:target/expedium/reservations") .unmarshal(jaxb) .process(checkinReservation()) .process(generateRequests()) .recipientList(property(REQUESTS_RECIPIENTS).tokenize(",")); from("direct:complete") .process(checkoutCompletion(jaxb)); from("direct:exit") .marshal(jaxb) .to("file:target/expedium/completed"); // Mock routes sending actual requests asynchronously to make reservations from("seda:airline") .marshal(jaxb) .to("log:com.apachecon.camel.AIRPORT"); from("seda:hotel") .marshal(jaxb) .to("log:com.apachecon.camel.HOTEL"); from("seda:car") .marshal(jaxb) .to("log:com.apachecon.camel.CAR"); // Route(s) receiving replies asynchronously about reservations made from("file:target/expedium/reply") .unmarshal(jaxb) .setProperty(ClaimCheck.CLAIMCHECK_TAG_HEADER, reservationId()) .to("seda:reply"); handleReply("seda:reply", EXPEDIUM_BAY); // test routes from("direct:reservations") .marshal(jaxb) .to("file:target/expedium/reservations"); } private Processor checkinReservation() { return ClaimCheck.checkin() .at(constant("direct:complete")) .attach(reservationId()) .keep(body()) .ttl(10000); } private Processor checkoutCompletion(DataFormat df) { Processor completionHandler = ClaimCheck.co() .bay(EXPEDIUM_BAY) .aggregate(updateReservation()) .check(processedAllRequests()) .proceed("direct:exit"); // Let's use a more persistent message store BayInfo bay = ClaimCheck.getBay(EXPEDIUM_BAY); File storageBay = new File("target/expedium/store/"); File main = new File(storageBay, "main"); File carousel = new File(storageBay, "carousel"); main.mkdirs(); carousel.mkdirs(); bay.main = new FileMessageStore(main, df, getContext(), expediumIdReader()); bay.carousel = new FileMessageStore(main, df, getContext(), expediumIdReader()); return completionHandler; } private Expression expediumIdReader() { return new Expression() { @SuppressWarnings("unchecked") public <T> T evaluate(Exchange exchange, Class<T> type) { Message in = exchange.getIn(); if (in != null && in.getBody() != null) { if (in.getBody() instanceof Reservation) { return (T)((Reservation)in.getBody()).getId(); } else if (in.getBody() instanceof Request) { return (T)((Request)in.getBody()).getId(); } } return (T)((DefaultExchange)exchange).getExchangeId(); } }; } private void handleReply(String uri, String bay) { ClaimCheck.arrival(this) .unload(uri) .bay(bay); } private AggregationStrategy updateReservation() { return new AggregationStrategy() { public Exchange aggregate(Exchange oldExchange, Exchange newExchange) { if (oldExchange == null || newExchange == null) { throw new RuntimeCamelException("Can only aggregate when claimcheck exchanges are paired"); } Reservation reservation = oldExchange.getIn().getBody(Reservation.class); Request reply = newExchange.getIn().getBody(Request.class); if ("airline".equalsIgnoreCase(reply.getType())) { reservation.setAirline(reply.getValue()); } else if ("hotel".equalsIgnoreCase(reply.getType())) { reservation.setHotel(reply.getValue()); } else if ("car".equalsIgnoreCase(reply.getType())) { reservation.setCar(reply.getValue()); } return oldExchange; } }; } private Processor generateRequests() { return new Processor() { @Override public void process(Exchange exchange) throws Exception { Reservation body = exchange.getIn().getBody(Reservation.class); if (body == null) { LOG.warn("Could not unmarshal reservation..."); return; } exchange.setProperty(REQUESTS_RECIPIENTS, ""); if (body.getRequest() != null) { String recipients = ""; String[] request = body.getRequest().split(","); for (String r : request) { if (recipients.length() > 0) { recipients += ","; } recipients += "seda:" + r; } LOG.info("Sending reservation requests to followig systems: {}", recipients); exchange.setProperty(REQUESTS_RECIPIENTS, recipients); Request req = new Request(); req.setId(body.getId()); req.setName(body.getName()); exchange.getOut().setBody(req); } } }; } private static Expression reservationId() { return new Expression() { @SuppressWarnings("unchecked") public <T> T evaluate(Exchange exchange, Class<T> type) { Object body = exchange.getIn().getBody(); if (body == null) { return (T)"n/a"; } else if (body instanceof Reservation) { return (T)((Reservation)body).getId(); } else if (body instanceof Request) { return (T)((Request)body).getId(); } return (T)"n/a"; } }; } private static Predicate processedAllRequests() { return new Predicate() { public boolean matches(Exchange exchange) { Reservation res = exchange.getIn().getBody(Reservation.class); if (res != null) { String[] request = res.getRequest().split(","); for (String r : request) { boolean all = "airline".equalsIgnoreCase(r) ? (res.getAirline() != null && res.getAirline().length() > 0) : "hotel".equalsIgnoreCase(r) ? (res.getHotel() != null && res.getHotel().length() > 0) : "car".equalsIgnoreCase(r) ? (res.getCar() != null && res.getCar().length() > 0) : false; if (!all) { LOG.info("Still waiting for replies on open requests"); return false; } } LOG.info("All open requests satisified - completing reservation"); return true; } return false; } }; } }