org.alfresco.bm.event.AbstractEventProcessor.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.bm.event.AbstractEventProcessor.java

Source

/*
 * Copyright (C) 2005-2014 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */
package org.alfresco.bm.event;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.time.StopWatch;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanNameAware;

/**
 * Basic services for the {@link EventProcessor event processor} implementations
 * <p/>
 * <b>Read {@link EventProcessor 'good practice'} notes for documenting behaviour.
 * 
 * @author Derek Hulley
 * @since 1.0
 */
public abstract class AbstractEventProcessor implements EventProcessor, BeanNameAware {
    protected static final long DEFAULT_WARN_DELAY = Long.MAX_VALUE;
    protected static final boolean DEFAULT_CHART = Boolean.TRUE;
    protected static final boolean DEFAULT_AUTO_PROPAGATE_SESSION_ID = Boolean.TRUE;
    protected static final boolean DEFAULT_AUTO_CLOSE_SESSION_ID = Boolean.TRUE;

    /** Resource for derived classes to use for logging */
    protected Log logger = LogFactory.getLog(this.getClass());
    private EventProcessorRegistry registry;
    private String name = "unknown";
    private List<String> eventNames;
    private long warnDelay;
    private boolean chart;
    private boolean autoPropagateSessionId;
    private boolean autoCloseSessionId;

    /**
     * Default constructor
     */
    public AbstractEventProcessor() {
        eventNames = new ArrayList<String>(1);
        warnDelay = DEFAULT_WARN_DELAY;
        chart = DEFAULT_CHART;
        autoPropagateSessionId = DEFAULT_AUTO_PROPAGATE_SESSION_ID;
        autoCloseSessionId = DEFAULT_AUTO_CLOSE_SESSION_ID;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append(this.getClass().getSimpleName() + " [eventNames=").append(eventNames);
        builder.append(", type=").append(this.getClass().getName());
        builder.append("]");
        return builder.toString();
    }

    @Override
    public void setBeanName(String beanName) {
        String eventName = beanName.replaceFirst(Event.EVENT_BEAN_PREFIX, "");
        setEventName(eventName);
        this.name = beanName;
    }

    /**
     * @return                  the name of the bean as configured in Spring
     */
    @Override
    public String getName() {
        return name;
    }

    /**
     * Set the single event name that this processor can handle
     */
    public void setEventName(String eventName) {
        this.eventNames.add(eventName);
    }

    /**
     * Set the names of the events that this processor can handle 
     */
    public void setEventNames(List<String> eventNames) {
        this.eventNames.addAll(eventNames);
    }

    /**
     * Inject the registry that this instance will {@link #register() register} with.
     */
    public synchronized void setRegistry(EventProcessorRegistry registry) {
        this.registry = registry;
    }

    /**
     * @return              the processing time after which an warning is logged (default {@link #DEFAULT_WARN_DELAY})
     */
    @Override
    public long getWarnDelay() {
        return warnDelay;
    }

    /**
     * @param warnDelay     the time allowed for processing before a warning is logged
     * @see #getWarnDelay()
     */
    public void setWarnDelay(long warnDelay) {
        this.warnDelay = warnDelay;
    }

    /**
     * @return              <tt>true</tt> if results from this processor must be included
     *                      in generated charts
     */
    @Override
    public boolean isChart() {
        return chart;
    }

    /**
     * @param chart         <tt>true</tt> to tag results for charting
     * @see #isChart()
     */
    public void setChart(boolean chart) {
        this.chart = chart;
    }

    /**
     * Change the {@link #DEFAULT_AUTO_PROPAGATE_SESSION_ID default} session ID propagation
     * behaviour
     * 
     * @param autoPropagateSessionId    <tt>true</tt> to propagate session IDs to the next event
     *                                  otherwise <tt>false</tt>  (default: <tt>true</tt>)
     */
    public void setAutoPropagateSessionId(boolean autoPropagateSessionId) {
        this.autoPropagateSessionId = autoPropagateSessionId;
    }

    /**
     * Change the {@link #DEFAULT_AUTO_CLOSE_SESSION_ID default} session ID auto-close
     * behaviour
     * 
     * @param autoCloseSessionId    <tt>true</tt> to allow the framework to aut-close sessions (default: <tt>true</tt>)
     */
    public void setAutoCloseSessionId(boolean autoCloseSessionId) {
        this.autoCloseSessionId = autoCloseSessionId;
    }

    @Override
    public boolean isAutoPropagateSessionId() {
        return autoPropagateSessionId;
    }

    @Override
    public boolean isAutoCloseSessionId() {
        return autoCloseSessionId;
    }

    /**
     * Register this instance for the event names that are handled.
     */
    public synchronized void register() {
        if (registry == null) {
            // Nothing to do
            return;
        }
        for (String eventName : eventNames) {
            registry.register(eventName, this);
        }
    }

    /** A thread-local timer that is reused as each event is processed */
    private ThreadLocal<StopWatch> stopWatchThreadLocal = new ThreadLocal<StopWatch>();

    /**
     * @return          Return the thread-local stop watch
     */
    private StopWatch getStopWatch() {
        StopWatch stopWatch = stopWatchThreadLocal.get();
        if (stopWatch == null) {
            throw new IllegalStateException("No stop watch has been provided to the processing thread.");
        }
        return stopWatch;
    }

    /**
     * Suspend the event processing timer.
     * 
     * @throws IllegalStateException        if the timer is not running
     */
    protected long suspendTimer() {
        StopWatch stopWatch = getStopWatch();
        stopWatch.suspend();
        return stopWatch.getTime();
    }

    /**
     * Continue timing the event processing
     * 
     * @throws IllegalStateException        if the timer is is not suspended
     */
    protected long resumeTimer() {
        StopWatch stopWatch = getStopWatch();
        stopWatch.resume();
        return stopWatch.getTime();
    }

    /**
     * Stop timing the event processing.  This does not need to be called by derived classes.
     * 
     * @throws IllegalStateException        if the timer is is not running or suspended
     */
    protected long stopTimer() {
        StopWatch stopWatch = getStopWatch();
        stopWatch.stop();
        return stopWatch.getTime();
    }

    /**
     * {@inheritDoc}
     * <p/>
     * The implementation overrides this method to store the {@link StopWatch} in a thread-local.
     */
    @Override
    public final EventResult processEvent(Event event, StopWatch stopWatch) throws Exception {
        if (stopWatch == null) {
            throw new IllegalArgumentException("'stopWatch' must be supplied.");
        }
        stopWatchThreadLocal.set(stopWatch);
        // We have to be able to start the timer here
        try {
            stopWatch.start();
        } catch (IllegalStateException e) {
            throw new IllegalArgumentException("The timer provided could not be started.", e);
        }
        try {
            // Do the actual processing
            return processEvent(event);
        } finally {
            // Stop the timer
            try {
                stopWatch.stop();
            } catch (IllegalStateException e) {
                // We are OK with this.  Derived classes could have stopped the timer.
            }
        }
    }

    /**
     * Process an event.
     * <p/>
     * <b>NOTE:</b> All errors are treated as <b>terminal</b> i.e. there will be no
     * follow-up events published.  The event and result services are provided to allow
     * event processing to be aware of previous and future work.  If the service is used
     * to modify queues, the results could be unexpected e.g. clearing the queue will not
     * always mean that no more events will be processed.
     * <p/>
     * Timing: Where required, implementations can manually control the times recorded
     *          for event processing.
     * 
     * @param event             the event (along with associated data)
     * @return                  the result of the process (includes errors and next events)
     * 
     * @see #suspendTimer()
     * @see #resumeTimer()
     * @see #stopTimer()
     */
    protected abstract EventResult processEvent(Event event) throws Exception;
}