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

forklift.consumer.MessageRunnable Maven / Gradle / Ivy

package forklift.consumer;

import forklift.classloader.RunAsClassLoader;
import forklift.connectors.ConnectorException;
import forklift.connectors.ForkliftMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class MessageRunnable implements Runnable {
    private static final Logger log = LoggerFactory.getLogger(MessageRunnable.class);
    private static final String RESPONSE = "@ResponseUri";

    private final Consumer consumer;
    private ForkliftMessage msg;
    private ClassLoader classLoader;
    private Object handler;
    private List onMessage;
    private List onValidate;
    private Map> onProcessStep;
    private List errors;
    private List closeMe;
    private boolean warnOnly = false;

    MessageRunnable(Consumer consumer, ForkliftMessage msg, ClassLoader classLoader, Object handler, List onMessage,
                    List onValidate, Map> onProcessStep,
                    List closeMe) {
        this.consumer = consumer;
        this.msg = msg;
        this.classLoader = classLoader;
        if (this.classLoader == null)
            this.classLoader = Thread.currentThread().getContextClassLoader();

        this.handler = handler;
        this.onMessage = onMessage;
        this.onValidate = onValidate;
        this.onProcessStep = onProcessStep;
        this.errors = new ArrayList<>();
        this.closeMe = closeMe;

        LifeCycleMonitors.call(ProcessStep.Pending, this);
    }

    @Override
    public void run() {
        RunAsClassLoader.run(classLoader, () -> {
            // Always ack message to prevent activemq deadlock
            boolean acknowledged = false;
            try {
                acknowledged = msg.acknowledge();
            } catch (ConnectorException e) {
                log.error("Error while acking message.", e);
                acknowledged = false;
            }
            if (!acknowledged) {
                close();
                return;
            }

            // { Validating }
            runHooks(ProcessStep.Validating);
            LifeCycleMonitors.call(ProcessStep.Validating, this);
            for (Method m : onValidate) {
                if (m.getReturnType() == List.class) {
                    addError(runLoggingErrors(() -> (List)m.invoke(handler)));
                } else if (m.getReturnType() == boolean.class) {
                    boolean valid = runLoggingErrors(() -> (boolean)m.invoke(handler));
                    if (!valid)
                        addError("Validator " + m.getName() + " returned false");
                } else {
                    addError("onValidate method " + m.getName() + " has wrong return type " + m.getReturnType());
                }
            }

            if (errors.size() > 0) {
                // { Invalid }
                runHooks(ProcessStep.Invalid);
                LifeCycleMonitors.call(ProcessStep.Invalid, this);
            } else {
                // { Processing }
                runHooks(ProcessStep.Processing);
                LifeCycleMonitors.call(ProcessStep.Processing, this);
                for (Method m : onMessage) {
                    runLoggingErrors(() -> m.invoke(handler));
                }
                if (errors.size() > 0) {
                    // { Error }
                    runHooks(ProcessStep.Error);
                    LifeCycleMonitors.call(ProcessStep.Error, this);
                } else {
                    // { Complete }
                    runHooks(ProcessStep.Complete);
                    LifeCycleMonitors.call(ProcessStep.Complete, this);
                }
            }
            // Always log all non-null errors
            if (this.warnOnly)
                getErrors().stream().filter(e -> e != null).forEach(e -> log.warn(e));
            else
                getErrors().stream().filter(e -> e != null).forEach(e -> log.error(e));
            close();
        });
    }

    public void addError(List errors) {
        if (errors == null)
            return;

        this.errors.addAll(errors);
    }

    public void addError(String e) {
        this.errors.add(e);
    }

    public List getErrors() {
        return errors;
    }

    public ForkliftMessage getMsg() {
        return msg;
    }

    public Object getHandler() {
        return handler;
    }

    public Consumer getConsumer() {
        return consumer;
    }

    /**
     * Set logging to warn only. This allows exceptions that would normally be logged as error to be warnings.
     *
     * @param b
     */
    public void setWarnOnly(boolean b) {
        this.warnOnly = b;
    }

    private void close() {
        // Close resources.
        try {
            for (Closeable c : closeMe)
                c.close();
        } catch (IOException e) {
            log.error("Unable to close a resource", e);
        }
    }

    // This interface and method are for wrapping functions that throw errors, logging and swa
    @FunctionalInterface
    private interface DangerousSupplier {
        T get() throws Throwable;
    }

    private  T runLoggingErrors(DangerousSupplier func) {
        try {
            return func.get();
        } catch (Throwable e) {
            StringWriter sw = new StringWriter();
            e.printStackTrace(new PrintWriter(sw)); // stack trace as a string

            if (e.getCause() == null) {
                addError(e.getMessage() + '\n' + sw.toString());
            } else {
                addError(e.getCause().getMessage() + '\n' + sw.toString());
            }

            return null;
        }
    }

    private void runHooks(ProcessStep step) {
        for (Method m : onProcessStep.get(step)) {
            runLoggingErrors(() -> m.invoke(handler));
        }
    }

    public static void main(String args[]) throws Exception {
        URI uri = new URI("queue://duh");
        System.out.println(uri.getScheme());
        System.out.println(uri.getHost());
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy