/******************************************************************************* * Copyright (c) quickfixengine.org All rights reserved. * * This file is part of the QuickFIX FIX Engine * * This file may be distributed under the terms of the quickfixengine.org * license as defined by quickfixengine.org and appearing in the file * LICENSE included in the packaging of this file. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. * * See http://www.quickfixengine.org/LICENSE for licensing information. * * Contact ask@quickfixengine.org if any conditions of this licensing * are not clear to you. ******************************************************************************/ package quickfix; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Map; /** * Helper class for delegating message types for various FIX versions to * type-safe onMessage methods. */ public class MessageCracker { private Map<Class<?>, Invoker> invokers = new HashMap<Class<?>, Invoker>(); @Target( { ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface Handler { } public class RedundantHandlerException extends RuntimeException { private final Class<?> messageClass; private final Method originalMethod; private final Method redundantMethod; public RedundantHandlerException(Class<?> messageClass, Method originalMethod, Method redundantMethod) { this.messageClass = messageClass; this.originalMethod = originalMethod; this.redundantMethod = redundantMethod; } @Override public String toString() { return "Duplicate handler method for " + messageClass + ", orginal method is " + originalMethod + ", redundant method is " + redundantMethod; } } protected MessageCracker() { initialize(this); } public MessageCracker(Object messageHandler) { initialize(messageHandler); } public void initialize(Object messageHandler) { Class<?> handlerClass = messageHandler.getClass(); for (Method method : handlerClass.getMethods()) { if (isHandlerMethod(method)) { Class<?> messageClass = method.getParameterTypes()[0]; method.setAccessible(true); Invoker invoker = new Invoker(messageHandler, method); Invoker existingInvoker = invokers.get(messageClass); if (existingInvoker != null) { throw new RedundantHandlerException(messageClass, existingInvoker.getMethod(), method); } invokers.put(messageClass, invoker); } } } private boolean isHandlerMethod(Method method) { int modifiers = method.getModifiers(); Class<?>[] parameterTypes = method.getParameterTypes(); return !Modifier.isPrivate(modifiers) && matchesConventionOrAnnotation(method) && parameterTypes.length == 2 && Message.class.isAssignableFrom(parameterTypes[0]) && parameterTypes[1] == SessionID.class; } private boolean matchesConventionOrAnnotation(Method method) { return method.getName().equals("onMessage") || method.isAnnotationPresent(Handler.class); } private class Invoker { private final Object target; private final Method method; public Invoker(Object target, Method method) { this.target = target; this.method = method; } public Method getMethod() { return method; } public void Invoke(Message message, SessionID sessionID) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { method.invoke(target, message, sessionID); } } /** * Process ("crack") a FIX message and call the registered handlers for that type, if any */ public void crack(quickfix.Message message, SessionID sessionID) throws UnsupportedMessageType, FieldNotFound, IncorrectTagValue { Invoker invoker = invokers.get(message.getClass()); if (invoker != null) { try { invoker.Invoke(message, sessionID); } catch (InvocationTargetException ite) { try { throw ((InvocationTargetException)ite).getTargetException(); } catch (UnsupportedMessageType e) { throw e; } catch (FieldNotFound e) { throw e; } catch (IncorrectTagValue e) { throw e; } catch (Throwable t) { propagate(t); } } catch (Exception e) { propagate(e); } } else { onMessage(message, sessionID); } } private void propagate(Throwable e) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } else if (e instanceof Error) { throw (Error)e; } else { throw new RuntimeException(e); } } /** * Fallback method that is called if no invokers are found. */ protected void onMessage(quickfix.Message message, SessionID sessionID) throws FieldNotFound, UnsupportedMessageType, IncorrectTagValue { throw new UnsupportedMessageType(); } }