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

org.killbill.queue.dispatching.Dispatcher Maven / Gradle / Ivy

/*
 * Copyright 2015 Groupon, Inc
 * Copyright 2015 The Billing Project, LLC
 *
 * The Billing Project licenses this file to you 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 org.killbill.queue.dispatching;

import org.killbill.commons.concurrent.DynamicThreadPoolExecutorWithLoggingOnExceptions;
import org.killbill.queue.api.QueueEvent;
import org.killbill.queue.dao.EventEntryModelDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

public class Dispatcher {


    private static final Logger log = LoggerFactory.getLogger(Dispatcher.class);

    private final int corePoolSize;
    private final int maximumPoolSize;
    private final long keepAliveTime;
    private final TimeUnit keepAliveTimeUnit;
    private final BlockingQueue workQueue;
    private final ThreadFactory threadFactory;
    private final RejectedExecutionHandler rejectionHandler;

    // Deferred in start sequence to allow for restart, which is not possible after the shutdown (mostly for test purpose)
    private ExecutorService executor;

    public Dispatcher(final int corePoolSize,
                      final int maximumPoolSize,
                      final long keepAliveTime,
                      final TimeUnit keepAliveTimeUnit,
                      final BlockingQueue workQueue,
                      final ThreadFactory threadFactory,
                      final RejectedExecutionHandler rejectionHandler) {
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.keepAliveTime = keepAliveTime;
        this.keepAliveTimeUnit = keepAliveTimeUnit;
        this.workQueue = workQueue;
        this.threadFactory = threadFactory;
        this.rejectionHandler = rejectionHandler;
    }

    public void start() {
        this.executor = new DynamicThreadPoolExecutorWithLoggingOnExceptions(corePoolSize, maximumPoolSize, keepAliveTime, keepAliveTimeUnit, workQueue, threadFactory, rejectionHandler);
    }

    public void stop() {
        executor.shutdown();
        try {
            executor.awaitTermination(5, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            log.info(String.format("Stop sequence has been interrupted"));
        }
    }


    public  void dispatch(final M modelDao, final CallableCallback callback) {
        log.debug("Dispatching entry: recordId={} className={} json={}", modelDao.getRecordId(), modelDao.getClassName(), modelDao.getEventJson());
        final CallableQueue entry = new CallableQueue(modelDao, callback);
        executor.submit(entry);
    }

    public static class CallableQueue implements Callable {

        private final M entry;
        private final CallableCallback callback;

        public CallableQueue(final M entry, final CallableCallback callback) {
            this.entry = entry;
            this.callback = callback;
        }

        @Override
        public E call() throws Exception {
            final E event = (E) callback.deserialize(entry);
            if (event != null) {
                Throwable lastException = null;
                long errorCount = entry.getErrorCount();
                try {
                    callback.dispatch(event, entry);
                } catch (final Exception e) {
                    if (e.getCause() != null && e.getCause() instanceof InvocationTargetException) {
                        lastException = e.getCause().getCause();
                    } else {
                        lastException = e;
                    }
                    lastException = e;
                    errorCount++;
                } finally {
                    callback.updateErrorCountOrMoveToHistory(event, entry, errorCount, lastException);
                }
            }
            return event;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy