package won.node.camel.route.fixed;
import org.apache.camel.Exchange;
import org.apache.camel.Expression;
import org.apache.camel.LoggingLevel;
import org.apache.camel.builder.PredicateBuilder;
import org.apache.camel.builder.RouteBuilder;
import won.node.camel.predicate.IsResponseMessagePredicate;
import won.node.camel.predicate.ShouldCallFacetImplForMessagePredicate;
import won.protocol.message.WonMessage;
import won.protocol.message.processor.camel.WonCamelConstants;
import won.protocol.vocabulary.WONMSG;
import java.net.URI;
/**
* User: syim
* Date: 02.03.2015
*/
public class WonMessageRoutes extends RouteBuilder
{
@Override
public void configure() throws Exception {
onException(Exception.class)
.to("bean:failResponder")
.handled(true);
/**
* owner protocol, incoming
*/
from("activemq:queue:OwnerProtocol.in?concurrentConsumers=2")
.choice()
.when(header("methodName").isEqualTo("register"))
.to("bean:ownerManagementService?method=registerOwnerApplication")
.when(header("methodName").isEqualTo("getEndpoints"))
.to("bean:queueManagementService?method=getEndpointsForOwnerApplication")
.otherwise()
.routeId("WonMessageOwnerRoute")
.setHeader(WonCamelConstants.DIRECTION_HEADER, new ConstantURIExpression(URI.create(WONMSG.TYPE_FROM_OWNER_STRING)))
.to("bean:wonMessageIntoCamelProcessor")
.to("bean:wellformednessChecker")
.to("bean:uriNodePathChecker")
.to("bean:uriInUseChecker")
.to("bean:signatureChecker")
.to("bean:envelopeAdder")
.to("bean:directionFromOwnerAdder")
.to("bean:receivedTimestampAdder")
//route to msg processing logic
.to("seda:OwnerProtocolLogic");
/**
* Owner protocol, outgoing.
* The well-formed, signed message is expected to be in the body.
*/
from("seda:OwnerProtocolOut?concurrentConsumers=2")
.routeId("OwnerProtocolOut")
.to("bean:ownerProtocolOutgoingMessagesProcessor")
//note: here it might happen that the recipient list contains only endpoint URIs that cannot be found
// e.g. when client's public keys changed and hence their queuenames did, too. Added stopOnException to deal
// with that.
.recipientList(header("ownerApplicationIDs")).stopOnException();
/**
* System messages: treated almost as incoming from owner.
*/
from("seda:SystemMessageIn?concurrentConsumers=2")
.to("bean:wonMessageIntoCamelProcessor")
.routeId("SystemMessageIn")
.setHeader(WonCamelConstants.DIRECTION_HEADER, new ConstantURIExpression(URI.create(WONMSG.TYPE_FROM_SYSTEM_STRING)))
.to("bean:receivedTimestampAdder")
//route to message processing logic
.to("seda:OwnerProtocolLogic");
/**
* System-to-owner messages: treated almost as incoming from remote node.
* Intended for messages that do not need facet processing (like response messages).
*/
from("seda:SystemMessageToOwner?concurrentConsumers=2")
.to("bean:wonMessageIntoCamelProcessor")
.routeId("SystemMessageToOwner")
.setHeader(WonCamelConstants.DIRECTION_HEADER, new ConstantURIExpression(URI.create(WONMSG.TYPE_FROM_SYSTEM_STRING)))
//TODO: as soon as messages are signed when they reach this route, perform signature/wellformedness checks here?
.to("bean:receivedTimestampAdder")
.to("bean:signatureAdder")
//route to message processing logic
.to("bean:persister")
.to("bean:toOwnerSender");
/**
* Owner protocol logic: expects messages from OwnerProtocolIn and SystemIntoOwnerProtocol routes.
*/
from("seda:OwnerProtocolLogic?concurrentConsumers=2")
//call the default implementation, which may alter the message.
// Also, it puts any outbound message in the respective header
.routeId("OwnerProtocolLogic")
.routingSlip(method("fixedMessageProcessorSlip"))
.to("bean:referenceAdder")
.to("bean:signatureAdder")
.to("bean:persister")
//swap: outbound becomes 'normal' message, 'normal' becomes 'original' - note: in some cases (create, activate,
// deactivate) there is no outbound message, hence no 'normal' message after this step.
.setHeader(WonCamelConstants.ORIGINAL_MESSAGE_HEADER, header(WonCamelConstants.MESSAGE_HEADER))
.setHeader(WonCamelConstants.MESSAGE_HEADER, header(WonCamelConstants.OUTBOUND_MESSAGE_HEADER))
//now if the outbound message is one that facet implementations can
//process, let them do that, then send the resulting message to the remote end.
.choice()
.when(
//check if the outbound message header is set, otherwise we don't have anything to
//send to the remote node
//(CAUTION: the actual message is in the default message header).
header(WonCamelConstants.OUTBOUND_MESSAGE_HEADER).isNotNull())
//swap back: outbound into normal
.setHeader(WonCamelConstants.MESSAGE_HEADER, header(WonCamelConstants.OUTBOUND_MESSAGE_HEADER))
.to("bean:toNodeSender")
.endChoice()
.end()
.choice()
.when(
//check if the direction of the original message is 'FromSystem'.
//If this is the case, we want to send it to the owner (so they know it happened)
// BUT: we don't do this for response messages: the owner does not need to know we sent
// a response message to the remote end.
PredicateBuilder.and(
PredicateBuilder.and(
header(WonCamelConstants.MESSAGE_HEADER).isNotNull(),
PredicateBuilder.not(new IsResponseMessagePredicate())),
header(WonCamelConstants.DIRECTION_HEADER).isEqualTo(WONMSG.TYPE_FROM_SYSTEM_STRING)
))
//swap back: original into normal
.setHeader(WonCamelConstants.MESSAGE_HEADER, header(WonCamelConstants.ORIGINAL_MESSAGE_HEADER))
.to("bean:toOwnerSender")
.endChoice()
.end()
//if we didn't raise an exception so far, send a success response
//for that, we have to re-instate the original (not the outbound) messagge, so we reply to the right one
.setHeader(WonCamelConstants.MESSAGE_HEADER, header(WonCamelConstants.ORIGINAL_MESSAGE_HEADER))
.choice()
.when(PredicateBuilder.and(
header(WonCamelConstants.MESSAGE_HEADER).isNotNull(),
PredicateBuilder.not(new IsResponseMessagePredicate())))
.to("bean:successResponder")
.endChoice()
.end()
//now, call the facet implementation
.choice()
.when(PredicateBuilder.and(
header(WonCamelConstants.DIRECTION_HEADER).isEqualTo(URI.create(WONMSG.TYPE_FROM_OWNER_STRING)),
PredicateBuilder.and(
header(WonCamelConstants.MESSAGE_HEADER).isNotNull(),
new ShouldCallFacetImplForMessagePredicate())))
//put the local connection URI into the header
.setHeader(WonCamelConstants.CONNECTION_URI_HEADER,
new GetEnvelopePropertyExpression(WonCamelConstants.ORIGINAL_MESSAGE_HEADER,
URI.create(WONMSG.SENDER_PROPERTY.getURI().toString())))
//look into the db to find the facet we are using
.to("bean:facetExtractor")
.routingSlip(method("facetTypeSlip"))
.endChoice() //end choice
.end()
.routingSlip(method("fixedMessageReactionProcessorSlip"));
/**
* Need protocol, incoming
*/
from("activemq:queue:NeedProtocol.in?concurrentConsumers=2")
.routeId("WonMessageNodeRoute")
.choice()
// (won nodes register the same way as owner applications do)
.when(header("methodName").isEqualTo("register"))
.to("bean:ownerManagementService?method=registerOwnerApplication")
.when(header("methodName").isEqualTo("getEndpoints"))
.to("bean:queueManagementService?method=getEndpointsForOwnerApplication")
.otherwise()
.to("bean:wonMessageIntoCamelProcessor")
.setHeader(WonCamelConstants.DIRECTION_HEADER, new ConstantURIExpression(URI.create(WONMSG.TYPE_FROM_EXTERNAL.getURI())))
.to("bean:wellformednessChecker")
.to("bean:uriNodePathChecker")
.to("bean:uriInUseChecker")
.to("bean:signatureChecker")
.to("bean:envelopeAdder")
.to("bean:directionFromExternalAdder")
.to("bean:receivedTimestampAdder")
//call the default implementation, which may alter the message.
.routingSlip(method("fixedMessageProcessorSlip"))
.to("bean:referenceAdder")
.to("bean:signatureAdder")
.to("bean:persister")
//put the local connection URI into the header
.setHeader(WonCamelConstants.CONNECTION_URI_HEADER,
new GetEnvelopePropertyExpression(WonCamelConstants.MESSAGE_HEADER,
URI.create(WONMSG.RECEIVER_PROPERTY.getURI().toString())))
//look into the db to find the facet we are using
.to("bean:facetExtractor")
//inbound messages are always passed to facet implementations as they
//are always directed at connections
.routingSlip(method("facetTypeSlip"))
//now, we have the message we want to pass on to the owner in the exchange's in header.
//do we want to send a response back? only if we're not currently processing a respon
.to("bean:toOwnerSender")
.choice()
.when(PredicateBuilder.and(
header(WonCamelConstants.MESSAGE_HEADER).isNotNull(),
PredicateBuilder.not(new IsResponseMessagePredicate())))
.to("bean:successResponder")
.endChoice()
.end()
.routingSlip(method("fixedMessageReactionProcessorSlip"));
/**
* Need protocol, outgoing
* We add our signature, but we can't persist the message in this form
* because its URI says it lives on the recipient node. The recipient will persist it.
*/
from("seda:NeedProtocolOut?concurrentConsumers=2").routeId("Node2NodeRoute")
.to("bean:signatureAdder")
.to("bean:needProtocolOutgoingMessagesProcessor");
/**
* Matcher protocol, incoming
*/
from("activemq:queue:MatcherProtocol.in?concurrentConsumers=2")
.to("bean:wonMessageIntoCamelProcessor")
.routeId("WonMessageMatcherRoute")
.choice()
//we only handle hint messages
.when(header(WonCamelConstants.MESSAGE_TYPE_HEADER).isEqualTo(URI.create(WONMSG.TYPE_HINT.getURI().toString())))
//TODO as soon as Matcher can sign his messages, perform here .to("bean:wellformednessChecker") and .to("bean:signatureChecker")
.to("bean:uriNodePathChecker")
.to("bean:uriInUseChecker")
.to("bean:envelopeAdder")
.to("bean:directionFromExternalAdder")
.to("bean:receivedTimestampAdder")
.to("bean:hintMessageProcessor?method=process")
.to("bean:referenceAdder")
.to("bean:signatureAdder")
.to("bean:persister")
.to("bean:toOwnerSender")
.otherwise()
.log(LoggingLevel.INFO, "could not route message");
/**
* Matcher protocol, outgoing
*/
from("seda:MatcherProtocolOut?concurrentConsumers=2")
.routeId("Node2MatcherRoute")
.to("activemq:topic:MatcherProtocol.Out.Need");
}
private class ConstantURIExpression implements Expression
{
private URI uri;
public ConstantURIExpression(final URI uri) {
this.uri = uri;
}
@Override
public <T> T evaluate(final Exchange exchange, final Class<T> type) {
return type.cast(uri);
}
}
/**
* Gets the value of a property inside the message. Only works if
* a WonMessage object is found in the specified header.
*/
private class GetEnvelopePropertyExpression implements Expression
{
String header;
URI property;
private GetEnvelopePropertyExpression(String header, URI property) {
this.property = property;
this.header = header;
}
@Override
public <T> T evaluate(Exchange exchange, Class<T> type) {
WonMessage message = (WonMessage) exchange.getIn().getHeader(header);
return type.cast(message.getEnvelopePropertyURIValue(property));
}
}
}