com.adaptris.core.DefaultFailedMessageRetrier.java Source code

Java tutorial

Introduction

Here is the source code for com.adaptris.core.DefaultFailedMessageRetrier.java

Source

/*
 * Copyright 2015 Adaptris Ltd.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

package com.adaptris.core;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;

import javax.management.MalformedObjectNameException;

import org.apache.commons.io.IOUtils;

import com.adaptris.annotation.AdapterComponent;
import com.adaptris.annotation.ComponentProfile;
import com.adaptris.core.runtime.AdapterManager;
import com.adaptris.core.runtime.ParentRuntimeInfoComponent;
import com.adaptris.core.runtime.RuntimeInfoComponent;
import com.adaptris.core.runtime.RuntimeInfoComponentFactory;
import com.adaptris.util.TimeInterval;
import com.thoughtworks.xstream.annotations.XStreamAlias;

/**
 * <p>
 * Implementation of <code>FailedMessageRetrier</code> that does not allow duplicate workflows.
 * </p>
 * 
 * @config default-failed-message-retrier
 */
@XStreamAlias("default-failed-message-retrier")
@AdapterComponent
@ComponentProfile(summary = "A Configurable Failed Message Retrier", tag = "error-handling,base")
public class DefaultFailedMessageRetrier extends FailedMessageRetrierImp {
    /**
     * The default shutdown wait.
     *
     */
    private static final TimeInterval DEFAULT_SHUTDOWN_WAIT = new TimeInterval(1L, TimeUnit.MINUTES.name());
    static {
        RuntimeInfoComponentFactory.registerComponentFactory(new JmxFactory());
    }

    private transient ExecutorService failedMessageExecutor = null;

    private TimeInterval shutdownWaitTime;

    @Override
    public void addWorkflow(Workflow workflow) throws CoreException {
        String key = workflow.obtainWorkflowId();
        if (getWorkflows().keySet().contains(key)) {
            log.warn("duplicate workflow ID [" + key + "]");
            throw new CoreException("Workflows cannot be uniquely identified");
        }
        log.debug("adding workflow with key [" + key + "]");
        getWorkflows().put(key, workflow);
    }

    boolean retryMessage(final AdaptrisMessage msg) {
        boolean submitted = false;
        try {
            Workflow workflow = getWorkflow(msg);
            failedMessageExecutor.execute(new Thread() {
                @Override
                public void run() {
                    Thread.currentThread().setName("Retry Failed Message");
                    onAdaptrisMessage(msg);
                }
            });
            submitted = true;
        } catch (CoreException e) {
            log.warn(e.getMessage(), e);
            // getWorkflow() throws a CoreException if it can't find it, so it can't have been submitted properly.
            submitted = false;
        } catch (RejectedExecutionException e) {
            // The pool is "closed".
            log.warn("Failed to submit message for retry", e);
            submitted = false;
        }
        return submitted;
    }

    boolean retryMessage(File file) throws IOException, CoreException {
        MimeEncoder encoder = new MimeEncoder();
        encoder.setRetainUniqueId(true);
        InputStream in = null;
        AdaptrisMessage msg = null;
        try {
            in = new FileInputStream(file);
            msg = encoder.readMessage(in);
        } finally {
            IOUtils.closeQuietly(in);
        }
        return retryMessage(msg);
    }

    private static class JmxFactory extends RuntimeInfoComponentFactory {

        @Override
        protected boolean isSupported(AdaptrisComponent e) {
            if (e != null && e instanceof DefaultFailedMessageRetrier) {
                return true;
            }
            return false;
        }

        @Override
        protected RuntimeInfoComponent createComponent(ParentRuntimeInfoComponent parent, AdaptrisComponent e)
                throws MalformedObjectNameException {
            return new DefaultFailedMessageRetrierJmx((AdapterManager) parent, (DefaultFailedMessageRetrier) e);
        }
    }

    @Override
    public void init() throws CoreException {
        super.init();
        failedMessageExecutor = Executors.newSingleThreadExecutor();
    }

    @Override
    public void start() throws CoreException {
        super.start();
    }

    @Override
    public void stop() {
        super.stop();
    }

    @Override
    public void close() {
        shutdownExecutors();
        super.close();
    }

    private void shutdownExecutors() {
        if (failedMessageExecutor != null) {
            log.trace("FailedMessageRetrier Shutdown Requested, awaiting ExecutorService Shutdown");
            failedMessageExecutor.shutdown();
            boolean success = false;
            try {
                success = failedMessageExecutor.awaitTermination(shutdownWaitTimeMs(), TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
            }
            if (!success) {
                log.trace("Pool failed to shutdown in " + shutdownWaitTimeMs() + "ms, forcing shutdown");
                failedMessageExecutor.shutdownNow();
            }
        }
    }

    private long shutdownWaitTimeMs() {
        return getShutdownWaitTime() != null ? getShutdownWaitTime().toMilliseconds()
                : DEFAULT_SHUTDOWN_WAIT.toMilliseconds();
    }

    public TimeInterval getShutdownWaitTime() {
        return shutdownWaitTime;
    }

    /**
     * Set the shutdown wait timeout for the pool.
     * <p>
     * When {@link #close()} is invoked, this shutdowns the internal retry submission pool used by
     * {@link DefaultFailedMessageRetrierJmxMBean}. The specified value is the amount of time to wait for a clean shutdown. If this
     * timeout is exceeded then a forced shutdown ensues, which may mean some messages will not have been retried.
     * </p>
     * 
     * @param interval the shutdown time (default is 60 seconds)
     * @see #stop()
     */
    public void setShutdownWaitTime(TimeInterval interval) {
        shutdownWaitTime = interval;
    }

}