package won.node.camel.processor.general;
import org.apache.camel.Exchange;
import org.apache.camel.Expression;
import org.apache.camel.Processor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import won.protocol.message.WonMessage;
import won.protocol.message.WonMessageDirection;
import won.protocol.message.WonMessageType;
import won.protocol.message.processor.camel.WonCamelConstants;
import won.protocol.message.processor.exception.MissingMessagePropertyException;
import won.protocol.message.processor.exception.WonMessageProcessingException;
import won.protocol.vocabulary.WONMSG;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* Computes a message slip for message processors that are annotated with appropriate marker annotations.
* The annotation class to look for has to be passed to this slip in the constructor.
*
*/
public class MessageTypeSlipComputer implements InitializingBean, ApplicationContextAware, Expression
{
Logger logger = LoggerFactory.getLogger(this.getClass());
HashMap<String, Object> fixedMessageProcessorsMap;
private ApplicationContext applicationContext;
private Class annotationClazz;
private boolean allowNoMatchingProcessor = false;
public MessageTypeSlipComputer(final String annotationClazzName) throws ClassNotFoundException {
this.annotationClazz = Class.forName(annotationClazzName);
}
public MessageTypeSlipComputer(final String annotationClazzName, final boolean allowNoMatchingProcessor)
throws ClassNotFoundException {
this(annotationClazzName);
this.allowNoMatchingProcessor = allowNoMatchingProcessor;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
public void afterPropertiesSet() throws Exception {
fixedMessageProcessorsMap = (HashMap)applicationContext.getBeansWithAnnotation(this.annotationClazz);
}
@Override
public <T> T evaluate(final Exchange exchange, final Class<T> type) {
WonMessage message = (WonMessage) exchange.getIn().getHeader(WonCamelConstants.MESSAGE_HEADER);
assert message != null : "wonMessage header must not be null";
String slip ="";
// exchange.getIn().setHeader();
URI messageType = (URI) exchange.getIn().getHeader(WonCamelConstants.MESSAGE_TYPE_HEADER);
assert messageType != null : "messageType header must not be null";
URI direction = (URI) exchange.getIn().getHeader(WonCamelConstants.DIRECTION_HEADER);
assert direction != null : "direction header must not be null";
String method = "process";
if (WonMessageDirection.FROM_EXTERNAL.isIdentifiedBy(direction)){
//check if we're handling a response. If so, do special routing
//the response comes from the remote node, but the handler we need is the
//one that sent the original message, so we have to switch direction
//and we have to set the type to the type of the original message that
//we are now handling the response to
if (WonMessageType.SUCCESS_RESPONSE.isIdentifiedBy(messageType)){
method = "onSuccessResponse";
direction = URI.create(WonMessageDirection.FROM_OWNER.getResource().toString());
WonMessageType origType = message.getIsResponseToMessageType();
if (origType == null) {
throw new MissingMessagePropertyException(URI.create(WONMSG.IS_RESPONSE_TO_MESSAGE_TYPE.getURI().toString()));
}
messageType = origType.getURI();
} else if (WonMessageType.FAILURE_RESPONSE.isIdentifiedBy(messageType)){
WonMessageType isResponseToType = message.getIsResponseToMessageType();
if (WonMessageType.FAILURE_RESPONSE == isResponseToType
|| WonMessageType.SUCCESS_RESPONSE == isResponseToType) {
//exception from the exception: if we're handling a FailureResponse
// to a response - in that case, don't compute a slip value - no bean
// will specially process this.
return null;
}
method = "onFailureResponse";
direction = URI.create(WonMessageDirection.FROM_OWNER.getResource().toString());
WonMessageType origType = message.getIsResponseToMessageType();
if (origType == null) {
throw new MissingMessagePropertyException(
URI.create(WONMSG.IS_RESPONSE_TO_MESSAGE_TYPE.getURI().toString()));
}
messageType = origType.getURI();
}
}
try {
String bean = computeMessageTypeSlip(messageType,direction);
if (bean == null) {
return null;
}
slip = "bean:"+ bean + "?method=" + method;
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return type.cast(slip);
}
private String computeMessageTypeSlip(URI messageType, URI direction)
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Iterator iter = fixedMessageProcessorsMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry pair = (Map.Entry)iter.next();
Processor wonMessageProcessor = (Processor)pair.getValue();
Annotation annotation = AopUtils.getTargetClass(wonMessageProcessor).getAnnotation(annotationClazz);
if(matches(annotation, messageType, direction, null)){
return pair.getKey().toString();
}
}
if (allowNoMatchingProcessor) {
return null;
}
logger.debug("unexpected combination of messageType {} and direction {} encountered " +
"- this causes an exception,which triggers a FailureResponse",
messageType, direction);
throw new WonMessageProcessingException(String.format("unexpected combination of messageType %s " +
"and direction %s encountered", messageType, direction));
}
private boolean matches(Annotation annotation, URI messageType, URI direction, URI facetType)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
if (annotation == null || messageType==null||direction==null) return false;
if (messageType != null){
if (!annotationFeatureMatches(annotation, messageType.toString(), "messageType")){
return false;
}
}
if (direction != null){
if (!annotationFeatureMatches(annotation, direction.toString(), "direction")){
return false;
}
}
if (facetType != null){
if (!annotationFeatureMatches(annotation, facetType.toString(), "facetType")){
return false;
}
}
return true;
}
private boolean annotationFeatureMatches(Annotation annotation, String expected, String featureName)
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
return expected.equals(annotation.annotationType().getDeclaredMethod(featureName).invoke(annotation));
}
}