All Downloads are FREE. Search and download functionalities are using the official Maven repository.

quickfix.MessageCracker Maven / Gradle / Ivy

/*******************************************************************************
 * 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 [email protected] 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 final Map, Invoker> invokers = new HashMap<>();

    @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 ite.getTargetException();
                } catch (UnsupportedMessageType | IncorrectTagValue | FieldNotFound 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();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy