org.nuxeo.ecm.core.event.pipe.QueueBaseEventBundlePipe.java Source code

Java tutorial

Introduction

Here is the source code for org.nuxeo.ecm.core.event.pipe.QueueBaseEventBundlePipe.java

Source

/*
 * (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others.
 *
 * 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.
 *
 * Contributors:
 *     tiry
 */
package org.nuxeo.ecm.core.event.pipe;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.core.event.EventBundle;
import org.nuxeo.ecm.core.event.pipe.local.LocalEventBundlePipeConsumer;
import org.nuxeo.runtime.api.Framework;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Simple Queue based implementation that starts a dedicated thread to consume an in-memory message queue.
 *
 * @since 8.4
 */
public class QueueBaseEventBundlePipe extends AbstractEventBundlePipe<EventBundle> {

    protected static Log log = LogFactory.getLog(QueueBaseEventBundlePipe.class);

    protected ConcurrentLinkedQueue<EventBundle> queue;

    protected ThreadPoolExecutor consumerTPE;

    protected LocalEventBundlePipeConsumer consumer;

    protected boolean stop;

    protected int batchSize = 10;

    @Override
    public void initPipe(String name, Map<String, String> params) {
        super.initPipe(name, params);
        stop = false;

        if (params.containsKey("batchSize")) {
            try {
                batchSize = Integer.parseInt(params.get(batchSize));
            } catch (NumberFormatException e) {
                log.error("Unable to read batchSize parameter", e);
            }
        }
        queue = new ConcurrentLinkedQueue<>();
        consumerTPE = new ThreadPoolExecutor(1, 1, 60, TimeUnit.MINUTES, new LinkedBlockingQueue<>());
        consumerTPE.prestartCoreThread();
        consumerTPE.execute(new Runnable() {

            private boolean send(List<EventBundle> messages) {
                if (consumer.receiveMessage(messages)) {
                    messages.clear();
                    return true;
                }

                // keep the events that can not be processed ?
                queue.addAll(messages);
                return false;
            }

            @Override
            public void run() {

                consumer = new LocalEventBundlePipeConsumer();
                consumer.initConsumer(getName(), getParameters());
                boolean interrupted = false;
                try {
                    while (!stop) {
                        List<EventBundle> messages = new ArrayList<>();
                        EventBundle message;
                        while ((message = queue.poll()) != null) {
                            messages.add(message);
                            if (messages.size() >= batchSize) {
                                send(messages);
                            }
                        }
                        if (messages.size() > 0) {
                            send(messages);
                        }

                        // XXX this is a hack ! TODO: find a better approach
                        try {
                            if (Framework.isTestModeSet()) {
                                Thread.sleep(5);
                            } else {
                                Thread.sleep(200);
                            }
                        } catch (InterruptedException e) {
                            interrupted = true;
                        }
                    }
                } finally {
                    if (interrupted) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        });
        consumerTPE.shutdown();
    }

    @Override
    protected EventBundle marshall(EventBundle events) {
        return events;
    }

    @Override
    protected void send(EventBundle message) {
        queue.offer(message);
    }

    @Override
    public void shutdown() throws InterruptedException {
        stop = true;
        // XXX
        waitForCompletion(5000L);
        if (consumer != null) {
            consumer.shutdown();
        }
    }

    @Override
    public boolean waitForCompletion(long timeoutMillis) throws InterruptedException {
        long deadline = System.currentTimeMillis() + timeoutMillis;
        int pause = (int) Math.min(timeoutMillis, 500L);
        // XXX use Condition
        try {
            do {
                if (queue.size() == 0) {
                    return true;
                }
                Thread.sleep(pause);
            } while (System.currentTimeMillis() < deadline);
        } finally {
            if (consumerTPE != null) {
                consumerTPE.awaitTermination(pause, TimeUnit.MILLISECONDS);
            }
        }

        return false;
    }

}