org.openspaces.events.AbstractEventListenerContainer.java Source code

Java tutorial

Introduction

Here is the source code for org.openspaces.events.AbstractEventListenerContainer.java

Source

/*
 * Copyright (c) 2008-2016, GigaSpaces Technologies, Inc. All Rights Reserved.
 *
 * 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 org.openspaces.events;

import com.gigaspaces.admin.quiesce.QuiesceState;
import com.gigaspaces.admin.quiesce.QuiesceStateChangedEvent;
import com.gigaspaces.cluster.activeelection.ISpaceModeListener;
import com.gigaspaces.cluster.activeelection.SpaceInitializationIndicator;
import com.gigaspaces.cluster.activeelection.SpaceMode;
import com.gigaspaces.internal.dump.InternalDump;
import com.gigaspaces.internal.dump.InternalDumpProcessor;
import com.gigaspaces.internal.dump.InternalDumpProcessorFailedException;
import com.gigaspaces.metrics.BeanMetricManager;
import com.gigaspaces.metrics.LongCounter;
import com.j_spaces.core.IJSpace;
import com.j_spaces.core.admin.IInternalRemoteJSpaceAdmin;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openspaces.admin.quiesce.QuiesceStateChangedListener;
import org.openspaces.core.GigaSpace;
import org.openspaces.core.space.mode.AfterSpaceModeChangeEvent;
import org.openspaces.core.space.mode.BeforeSpaceModeChangeEvent;
import org.openspaces.core.transaction.manager.JiniPlatformTransactionManager;
import org.openspaces.core.util.SpaceUtils;
import org.openspaces.events.adapter.EventListenerAdapter;
import org.openspaces.events.support.AnnotationProcessorUtils;
import org.openspaces.pu.container.ProcessingUnitContainerContext;
import org.openspaces.pu.container.ProcessingUnitContainerContextAware;
import org.openspaces.pu.service.ServiceDetailsProvider;
import org.openspaces.pu.service.ServiceMonitorsProvider;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.Lifecycle;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.rmi.RemoteException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

/**
 * A simple based class for {@link SpaceDataEventListener} based containers. Allowing to register a
 * listener and provides several support methods like {@link #invokeListener(SpaceDataEventListener,
 * Object, org.springframework.transaction.TransactionStatus, Object)} in order to simplify event
 * listener based containers.
 *
 * @author kimchy
 */
public abstract class AbstractEventListenerContainer
        implements ApplicationContextAware, Lifecycle, BeanNameAware, InitializingBean, DisposableBean,
        ApplicationListener<ApplicationEvent>, QuiesceStateChangedListener, ServiceDetailsProvider,
        ServiceMonitorsProvider, ProcessingUnitContainerContextAware, InternalDumpProcessor {

    protected final Log logger = LogFactory.getLog(getClass());

    private GigaSpace gigaSpace;
    protected String beanName;
    private boolean activeWhenPrimary = true;
    private boolean registerSpaceModeListener = false;
    private volatile boolean active = false;
    private volatile boolean running = false;
    private final List<Object> pausedTasks = new LinkedList<Object>();
    private final Object lifecycleMonitor = new Object();
    private PrimaryBackupListener primaryBackupListener;
    private SpaceMode currentSpaceMode;
    private volatile boolean autoStart = true;
    private volatile boolean quiesced = false;
    private volatile boolean resumeAfterUnquiesce = false;
    private BeanMetricManager beanMetricManager;

    private SpaceDataEventListener eventListener;
    private String eventListenerRef;
    private ApplicationContext applicationContext;
    protected EventExceptionHandler exceptionHandler;
    private final LongCounter processedEvents = new LongCounter();
    private final LongCounter failedEvents = new LongCounter();

    private Object template;
    private boolean performSnapshot = true; // enabled by default
    private Object receiveTemplate;
    private DynamicEventTemplateProvider dynamicTemplate;
    private Object dynamicTemplateRef;

    private PlatformTransactionManager transactionManager;
    private DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
    protected boolean disableTransactionValidation = false;

    /**
     * Sets the GigaSpace instance to be used for space event listening operations.
     */
    public void setGigaSpace(GigaSpace gigaSpace) {
        this.gigaSpace = gigaSpace;
    }

    /**
     * Returns the GigaSpace instance to be used for Space operations.
     */
    protected final GigaSpace getGigaSpace() {
        return this.gigaSpace;
    }

    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }

    /**
     * Return the bean name that this listener container has been assigned in its containing bean
     * factory, if any.
     */
    protected final String getBeanName() {
        return this.beanName;
    }

    /**
     * Set whether this container will start only when it is primary (space mode).
     *
     * <p>Default is <code>true</code>. Set to <code>false</code> in order for this container to
     * always start regardless of the space mode.
     */
    public void setActiveWhenPrimary(boolean activeWhenPrimary) {
        this.activeWhenPrimary = activeWhenPrimary;
    }

    /**
     */
    public void setRegisterSpaceModeListener(boolean registerSpaceModeListener) {
        this.registerSpaceModeListener = registerSpaceModeListener;
    }

    /**
     * Set whether this container will start once instantiated.
     *
     * <p>Default is <code>true</code>. Set to <code>false</code> in order for this container to be
     * started using {@link #start()}.
     */
    public void setAutoStart(boolean initOnStartup) {
        this.autoStart = initOnStartup;
    }

    /**
     * Return whether this container is currently running, that is, whether it has been started and
     * not stopped yet.
     */
    public final boolean isRunning() {
        return this.running && !this.quiesced;
    }

    /**
     * Return whether this container is currently active, that is, whether it has been set up but
     * not shut down yet.
     */
    public final boolean isActive() {
        return this.active;
    }

    protected String getStatus() {
        if (running)
            return "started";
        if (quiesced)
            return "quiesced";
        return "stopped";
    }

    protected EventExceptionHandler getExceptionHandler() {
        return exceptionHandler;
    }

    /**
     * Sets an exception handler that will be invoked when an exception occurs on the listener
     * allowing to customize the handling of such cases.
     */
    public void setExceptionHandler(EventExceptionHandler exceptionHandler) {
        this.exceptionHandler = exceptionHandler;
    }

    /**
     * Sets the event listener implementation that will be used to delegate events to. Also see
     * different adapter classes provided for simpler event listeners integration.
     *
     * @param eventListener The event listener used
     */
    public void setEventListener(SpaceDataEventListener eventListener) {
        this.eventListener = eventListener;
    }

    /**
     * Sets an event listener bean reference name that will be used to lookup the actual listener
     * bean (based on its name). Mainly used when configuring a listener with specific scope setting
     * (such as prototype) allowing to scope to take affect by using <code>getBean</code> for each
     * request of the listener.
     */
    public void setEventListenerRef(String eventListenerRef) {
        this.eventListenerRef = eventListenerRef;
    }

    protected SpaceDataEventListener getEventListener() {
        if (eventListener != null) {
            return eventListener;
        }
        if (eventListenerRef == null) {
            return null;
        }
        return (SpaceDataEventListener) applicationContext.getBean(eventListenerRef);
    }

    protected Object getActualEventListener() {
        Object listener = getEventListener();
        while (listener instanceof EventListenerAdapter) {
            listener = ((EventListenerAdapter) listener).getActualEventListener();
        }
        return listener;
    }

    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Override
    public void setProcessingUnitContainerContext(ProcessingUnitContainerContext processingUnitContainerContext) {
        this.beanMetricManager = processingUnitContainerContext.createBeanMetricManager(getBeanName());
        if (running)
            registerMetrics();
    }

    protected ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    protected Class<?> getEventListenerClass() {
        if (eventListener != null) {
            return eventListener.getClass();
        }
        return applicationContext.getType(eventListenerRef);
    }

    /**
     * Delegates to {@link #validateConfiguration()} and {@link #initialize()}.
     */
    public void afterPropertiesSet() {
        validateConfiguration();
        initialize();
    }

    /**
     * Initialize this container. If this container is not configured with "activeWhenPrimary" flag
     * set to <code>true</code> will call {@link #doStart()} (if it is set to <code>true</code>,
     * lifecycle of the container will be controlled by the current space mode). {@link
     * #doInitialize()} will be called for additional initialization after the possible {@link
     * #doStart()} call.
     *
     * @see #onApplicationEvent(org.springframework.context.ApplicationEvent)
     */
    public void initialize() throws DataAccessException {
        initializeTransactionManager();
        initializeTemplate();
        initializeExceptionHandler();
        synchronized (this.lifecycleMonitor) {
            this.active = true;
            this.lifecycleMonitor.notifyAll();
        }

        doInitialize();

        if (!activeWhenPrimary) {
            doStart();
        }

        if (registerSpaceModeListener) {
            SpaceMode currentMode = SpaceMode.PRIMARY;
            if (!SpaceUtils.isRemoteProtocol(gigaSpace.getSpace())) {
                primaryBackupListener = new PrimaryBackupListener();
                try {
                    IJSpace clusterMemberSpace = SpaceUtils.getClusterMemberSpace(gigaSpace.getSpace());
                    ISpaceModeListener remoteListener = (ISpaceModeListener) clusterMemberSpace.getDirectProxy()
                            .getStubHandler().exportObject(primaryBackupListener);
                    currentMode = ((IInternalRemoteJSpaceAdmin) clusterMemberSpace.getAdmin())
                            .addSpaceModeListener(remoteListener);
                } catch (RemoteException e) {
                    throw new InvalidDataAccessResourceUsageException(
                            "Failed to register space mode listener with space [" + gigaSpace.getSpace() + "]", e);
                }
            }
            SpaceInitializationIndicator.setInitializer();
            try {
                onApplicationEvent(new BeforeSpaceModeChangeEvent(gigaSpace.getSpace(), currentMode));
                onApplicationEvent(new AfterSpaceModeChangeEvent(gigaSpace.getSpace(), currentMode));
            } finally {
                SpaceInitializationIndicator.unsetInitializer();
            }
        }
    }

    /**
     * A callback to perform custom initialization steps.
     */
    protected abstract void doInitialize() throws DataAccessException;

    private void initializeTransactionManager() {
        // Use bean name as default transaction name.
        if (this.transactionDefinition.getName() == null) {
            this.transactionDefinition.setName(getBeanName());
        }
    }

    /**
     * Validate the configuration of this container.
     */
    protected void validateConfiguration() {
        Assert.notNull(gigaSpace, "gigaSpace property is required");
        if (transactionManager != null && !disableTransactionValidation) {
            if (!getGigaSpace().getTxProvider().isEnabled()) {
                throw new IllegalStateException(message(
                        "event container is configured to run under transactions (transaction manager is provided) "
                                + "but GigaSpace is not transactional. Please pass the transaction manager to the GigaSpace bean as well"));
            }
        }
    }

    private void initializeTemplate() {
        Object possibleTemplateProvider = null;

        if (template != null) {
            // check if template object is actually a template provider
            possibleTemplateProvider = template;
        } else {
            Class<?> eventListenerType = getEventListenerClass();
            if (eventListenerType != null) {
                //check if listener object is also a template provider
                possibleTemplateProvider = getActualEventListener();
            }
        }

        if (possibleTemplateProvider != null) {
            Object templateFromProvider = AnnotationProcessorUtils
                    .findTemplateFromProvider(possibleTemplateProvider);
            if (templateFromProvider != null) {
                setTemplate(templateFromProvider);
            }
        }

        if (dynamicTemplate == null && dynamicTemplateRef != null) {
            Object dynamicTemplateProviderBean = dynamicTemplateRef;
            dynamicTemplate = AnnotationProcessorUtils
                    .findDynamicEventTemplateProvider(dynamicTemplateProviderBean);
            if (dynamicTemplate == null) {
                throw new IllegalArgumentException(
                        "Cannot find dynamic template provider in " + dynamicTemplateRef.getClass());
            }
        }

        if (template != null && dynamicTemplate != null) {
            throw new IllegalArgumentException("dynamicTemplate and template are mutually exclusive.");
        }

        if (performSnapshot && template != null) {
            if (logger.isTraceEnabled()) {
                logger.trace(message("Performing snapshot on template [" + template + "]"));
            }
            receiveTemplate = getGigaSpace().prepareTemplate(template);
        } else {
            receiveTemplate = template;
        }
    }

    private void initializeExceptionHandler() {
        if (exceptionHandler == null && getActualEventListener() != null) {
            final AtomicReference<Method> ref = new AtomicReference<Method>();
            ReflectionUtils.doWithMethods(AopUtils.getTargetClass(getActualEventListener()),
                    new ReflectionUtils.MethodCallback() {
                        public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
                            if (method.isAnnotationPresent(ExceptionHandler.class)) {
                                ref.set(method);
                            }
                        }
                    });
            if (ref.get() != null) {
                ref.get().setAccessible(true);
                try {
                    setExceptionHandler((EventExceptionHandler) ref.get().invoke(getActualEventListener()));
                } catch (Exception e) {
                    throw new IllegalArgumentException(
                            "Failed to set EventExceptionHandler from method [" + ref.get().getName() + "]", e);
                }
            }
        }
    }

    /**
     * Calls {@link #shutdown()} when the BeanFactory destroys the container instance.
     *
     * @see #shutdown()
     */
    public void destroy() {
        shutdown();
    }

    /**
     * Stop container, call {@link #doShutdown()}, and close this container.
     */
    public void shutdown() throws DataAccessException {
        if (logger.isDebugEnabled()) {
            logger.debug(message("Shutting down Space Event listener container"));
        }
        synchronized (this.lifecycleMonitor) {
            this.active = false;
            this.running = false;
            this.lifecycleMonitor.notifyAll();
            unregisterMetrics();
        }

        if (registerSpaceModeListener) {
            if (!SpaceUtils.isRemoteProtocol(gigaSpace.getSpace())) {
                IJSpace clusterMemberSpace = SpaceUtils.getClusterMemberSpace(gigaSpace.getSpace());
                try {
                    ISpaceModeListener remoteListener = (ISpaceModeListener) clusterMemberSpace.getDirectProxy()
                            .getStubHandler().exportObject(primaryBackupListener);
                    ((IInternalRemoteJSpaceAdmin) clusterMemberSpace.getAdmin())
                            .removeSpaceModeListener(remoteListener);
                } catch (RemoteException e) {
                    logger.warn(
                            "Failed to unregister space mode listener with space [" + gigaSpace.getSpace() + "]",
                            e);
                }
            }
        }

        // Shut down the invokers.
        doShutdown();
    }

    /**
     * Perform any custom shutdown operations.
     *
     * @see #shutdown()
     */
    protected abstract void doShutdown() throws DataAccessException;

    // -------------------------------------------------------------------------
    // Lifecycle methods for dynamically starting and stopping the container
    // -------------------------------------------------------------------------

    /**
     * Start this container.
     *
     * @see #doStart
     */
    public void start() throws DataAccessException {
        if (!autoStart) {
            autoStart = true;
        }
        if (!activeWhenPrimary) {
            doStart();
        } else {
            if (currentSpaceMode != null && currentSpaceMode == SpaceMode.PRIMARY) {
                doStart();
            }
        }
    }

    /**
     * Only start if we have a listener registered. If we don't, then explicit start should be
     * called.
     */
    protected void doStart() throws DataAccessException {
        if (!autoStart || running || quiesced || getEventListener() == null)
            return;

        synchronized (this.lifecycleMonitor) {
            if (running || quiesced)
                return;

            this.running = true;
            registerMetrics();
            this.lifecycleMonitor.notifyAll();
            for (Iterator<Object> it = this.pausedTasks.iterator(); it.hasNext();) {
                doRescheduleTask(it.next());
                it.remove();
            }
        }
        doAfterStart();
    }

    protected void doAfterStart() throws DataAccessException {

    }

    /**
     * Stop this container.
     *
     * @see #doStop
     */
    public void stop() throws DataAccessException {
        doStop();
    }

    /**
     * Notify all invoker tasks to stop
     */
    protected void doStop() throws DataAccessException {
        if (!running) {
            return;
        }
        doBeforeStop();
        synchronized (this.lifecycleMonitor) {
            this.running = false;
            this.resumeAfterUnquiesce = false;
            this.lifecycleMonitor.notifyAll();
            unregisterMetrics();
        }
    }

    protected void doBeforeStop() throws DataAccessException {

    }

    /**
     * If the {@link #setActiveWhenPrimary(boolean)} is set to <code>true</code> (the default), the
     * container lifecycle will be controlled by the space mode. The container will start when the
     * space is in <code>PRIMARY</code> mode, and will stop when the space is in <code>BACKUP</code>
     * mode.
     *
     * <p>Note, this might cause {@link #doStart()} or {@link #doStop()} to be called several times
     * in a row, and sub classes should take this into account.
     */
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        if (activeWhenPrimary) {
            if (applicationEvent instanceof AfterSpaceModeChangeEvent) {
                AfterSpaceModeChangeEvent spEvent = (AfterSpaceModeChangeEvent) applicationEvent;
                if (spEvent.isPrimary() && SpaceUtils.isSameSpace(spEvent.getSpace(), gigaSpace.getSpace())) {
                    if (logger.isTraceEnabled()) {
                        logger.trace(
                                message("Space [" + getGigaSpace() + "] became primary, starting the container"));
                    }
                    doStart();
                }
            } else if (applicationEvent instanceof BeforeSpaceModeChangeEvent) {
                BeforeSpaceModeChangeEvent spEvent = (BeforeSpaceModeChangeEvent) applicationEvent;
                if (!spEvent.isPrimary() && SpaceUtils.isSameSpace(spEvent.getSpace(), gigaSpace.getSpace())) {
                    if (logger.isTraceEnabled()) {
                        logger.trace(
                                message("Space [" + getGigaSpace() + "] became backup, stopping the container"));
                    }
                    doStop();
                }
                currentSpaceMode = spEvent.getSpaceMode();
            }
        }
    }

    /**
     * Wait while this container is not running.
     *
     * <p>To be called by asynchronous tasks that want to block while the container is in stopped
     * state.
     */
    protected final void waitWhileNotRunning() {
        while (this.active && !this.running) {
            synchronized (this.lifecycleMonitor) {
                if (this.active && !this.running) {
                    try {
                        this.lifecycleMonitor.wait();
                    } catch (InterruptedException ex) {
                        // Re-interrupt current thread, to allow other threads to react.
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }
    }

    /**
     * Take the given task object and reschedule it, either immediately if this container is
     * currently running, or later once this container has been restarted.
     *
     * <p>If this container has already been shut down, the task will not get rescheduled at all.
     *
     * @param task the task object to reschedule
     * @return whether the task has been rescheduled (either immediately or for a restart of this
     * container)
     * @see #doRescheduleTask
     */
    protected final boolean rescheduleTaskIfNecessary(Object task) {
        Assert.notNull(task, "Task object must not be null");
        if (this.running) {
            doRescheduleTask(task);
            return true;
        } else if (this.active) {
            synchronized (lifecycleMonitor) {
                if (this.running) {
                    doRescheduleTask(task);
                    return true;
                }
                this.pausedTasks.add(task);
                return true;
            }
        } else {
            return false;
        }
    }

    /**
     * Reschedule the given task object immediately.
     *
     * <p>To be implemented by subclasses if they ever call <code>rescheduleTaskIfNecessary</code>.
     * This implementation throws an UnsupportedOperationException.
     *
     * @param task the task object to reschedule
     * @see #rescheduleTaskIfNecessary
     */
    protected void doRescheduleTask(Object task) {
        throw new UnsupportedOperationException(
                ClassUtils.getShortName(getClass()) + " does not support rescheduling of tasks");
    }

    // -------------------------------------------------------------------------
    // Template methods for listener execution
    // -------------------------------------------------------------------------

    /**
     * Executes the given listener if the container is running ({@link #isRunning()}.
     *
     * @param eventData The event data object
     * @param txStatus  An optional transaction status allowing to rollback a transaction
     *                  programmatically
     * @param source    An optional source (or additional event information)
     */
    protected void executeListener(SpaceDataEventListener eventListener, Object eventData,
            TransactionStatus txStatus, Object source) throws Throwable {
        if (!isRunning()) {
            return;
        }
        invokeListener(eventListener, eventData, txStatus, source);
    }

    /**
     * Invokes the configured {@link org.openspaces.events.SpaceDataEventListener} based on the
     * provided data. Currently simply delegates to {@link org.openspaces.events.SpaceDataEventListener#onEvent(Object,
     * org.openspaces.core.GigaSpace, org.springframework.transaction.TransactionStatus, Object)}.
     *
     * @param eventData The event data object
     * @param txStatus  An optional transaction status allowing to rollback a transaction
     *                  programmatically
     * @param source    An optional source (or additional event information)
     */
    protected void invokeListener(SpaceDataEventListener eventListener, Object eventData,
            TransactionStatus txStatus, Object source) throws Throwable {
        if (exceptionHandler != null) {
            try {
                eventListener.onEvent(eventData, getGigaSpace(), txStatus, source);
                exceptionHandler.onSuccess(eventData, getGigaSpace(), txStatus, source);
            } catch (Throwable e) {
                if (!(e instanceof ListenerExecutionFailedException)) {
                    e = new ListenerExecutionFailedException(e.getMessage(), e);
                }
                exceptionHandler.onException((ListenerExecutionFailedException) e, eventData, getGigaSpace(),
                        txStatus, source);
            }
        } else {
            eventListener.onEvent(eventData, getGigaSpace(), txStatus, source);
        }
        processedEvents.inc();
    }

    /**
     * Handles exception that occurs during the event listening process. Currently simply logs it.
     *
     * @param ex the exception to handle
     */
    protected void handleListenerException(Throwable ex) {
        if (ex instanceof Exception) {
            invokeExceptionListener((Exception) ex);
        }
        if (isActive()) {
            incrementFailedEvents();
            // Regular case: failed while active. Log at error level.
            logger.error(message("Execution of event listener failed"), ex);
        } else {
            // Rare case: listener thread failed after container shutdown.
            // Log at debug level, to avoid spamming the shutdown log.
            logger.debug(message("Listener exception after container shutdown"), ex);
        }
    }

    /**
     * A callback to handle exception. Possible extension point for registered exception listeners.
     */
    protected void invokeExceptionListener(Exception e) {
    }

    public long getProcessedEvents() {
        return processedEvents.getCount();
    }

    public long getFailedEvents() {
        return failedEvents.getCount();
    }

    protected void incrementFailedEvents() {
        failedEvents.inc();
    }

    /**
     * Sets the specified template to be used with the polling space operation.
     *
     * @see org.openspaces.core.GigaSpace#take(Object, long)
     */
    public void setTemplate(Object template) {
        this.template = template;
    }

    /**
     * Returns the template that will be used. Note, in order to perform receive operations, the
     * {@link #getReceiveTemplate()} should be used.
     */
    protected Object getTemplate() {
        return this.template;
    }

    /**
     * Called before each take and read polling operation to change the template Overrides any
     * template defined with {@link #setTemplate(Object)}
     *
     * @param dynamicTemplate - An object that implements {@link DynamicEventTemplateProvider} or
     *                        has a method annotated with {@link DynamicEventTemplateProvider}
     */
    public void setDynamicTemplate(Object dynamicTemplate) {
        this.dynamicTemplateRef = dynamicTemplate;
    }

    /**
     * Returns whether dynamic template is configured
     */
    protected boolean isDynamicTemplate() {
        return dynamicTemplate != null;
    }

    /**
     * If set to <code>true</code> will perform snapshot operation on the provided template before
     * invoking registering as an event listener.
     *
     * @see org.openspaces.core.GigaSpace#snapshot(Object)
     */
    public void setPerformSnapshot(boolean performSnapshot) {
        this.performSnapshot = performSnapshot;
    }

    protected boolean isPerformSnapshot() {
        return this.performSnapshot;
    }

    /**
     * Returns the template to be used for receive operations. If {@link
     * #setPerformSnapshot(boolean)} is set to <code>true</code> (the default) will return the
     * snapshot of the provided template.
     */
    protected Object getReceiveTemplate() {

        if (dynamicTemplate != null) {
            return dynamicTemplate.getDynamicTemplate();
        }

        return receiveTemplate;
    }

    /**
     * Specify the Spring {@link org.springframework.transaction.PlatformTransactionManager} to use
     * for transactional wrapping of listener execution.
     *
     * <p> Default is none, not performing any transactional wrapping.
     */
    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    /**
     * Return the Spring PlatformTransactionManager to use for transactional wrapping of message
     * reception plus listener execution.
     */
    protected final PlatformTransactionManager getTransactionManager() {
        return this.transactionManager;
    }

    /**
     * Specify the transaction name to use for transactional wrapping. Default is the bean name of
     * this listener container, if any.
     *
     * @see org.springframework.transaction.TransactionDefinition#getName()
     */
    public void setTransactionName(String transactionName) {
        this.transactionDefinition.setName(transactionName);
    }

    /**
     * Specify the transaction timeout to use for transactional wrapping, in <b>seconds</b>. Default
     * is none, using the transaction manager's default timeout.
     *
     * @see org.springframework.transaction.TransactionDefinition#getTimeout()
     */
    public void setTransactionTimeout(int transactionTimeout) {
        this.transactionDefinition.setTimeout(transactionTimeout);
    }

    /**
     * Specify the transaction isolation to use for transactional wrapping.
     *
     * @see org.springframework.transaction.support.DefaultTransactionDefinition#setIsolationLevel(int)
     */
    public void setTransactionIsolationLevel(int transactionIsolationLevel) {
        this.transactionDefinition.setIsolationLevel(transactionIsolationLevel);
    }

    /**
     * Specify the transaction isolation to use for transactional wrapping.
     *
     * @see org.springframework.transaction.support.DefaultTransactionDefinition#setIsolationLevelName(String)
     */
    public void setTransactionIsolationLevelName(String transactionIsolationLevelName) {
        this.transactionDefinition.setIsolationLevelName(transactionIsolationLevelName);
    }

    protected DefaultTransactionDefinition getTransactionDefinition() {
        return transactionDefinition;
    }

    /**
     * Should transaction validation be enabled or not (verify and fail if transaction manager is
     * provided and the GigaSpace is not transactional). Default to <code>false</code>.
     */
    public void setDisableTransactionValidation(boolean disableTransactionValidation) {
        this.disableTransactionValidation = disableTransactionValidation;
    }

    public String getTransactionManagerName() {
        if (transactionManager instanceof JiniPlatformTransactionManager) {
            return ((JiniPlatformTransactionManager) transactionManager).getBeanName();
        }
        if (transactionManager != null) {
            return "<<unknown>>";
        }
        return null;
    }

    protected boolean isTransactional() {
        return transactionManager != null;
    }

    private class PrimaryBackupListener implements ISpaceModeListener {

        public void beforeSpaceModeChange(SpaceMode spaceMode) throws RemoteException {
            onApplicationEvent(new BeforeSpaceModeChangeEvent(gigaSpace.getSpace(), spaceMode));
        }

        public void afterSpaceModeChange(SpaceMode spaceMode) throws RemoteException {
            onApplicationEvent(new AfterSpaceModeChangeEvent(gigaSpace.getSpace(), spaceMode));
        }
    }

    protected String message(String message) {
        return "[" + getBeanName() + "] " + message;
    }

    public void quiesceStateChanged(QuiesceStateChangedEvent event) {
        quiesced = event.getQuiesceState().equals(QuiesceState.QUIESCED);
        if (quiesced) {
            // if container was running before calling quiesce it should resume working after unquiesce
            boolean runningBeforeQuiesce = this.running;
            stop();
            this.resumeAfterUnquiesce = runningBeforeQuiesce;
        } else {
            // resume only if container was running before calling quiesce
            if (resumeAfterUnquiesce)
                start();
        }
    }

    public void process(InternalDump dump) throws InternalDumpProcessorFailedException {
        dump.addPrefix("event-containers/");
        try {
            PrintWriter writer = new PrintWriter(dump.createFileWriter(beanName + ".txt"));
            dump(writer);
            writer.println();
            writer.close();
        } finally {
            dump.removePrefix();
        }
    }

    protected void dump(PrintWriter writer) {
        writer.println("===== RUNTIME =====");
        writer.println("Status: " + getStatus());
        writer.println("Processed events: " + getProcessedEvents());
        writer.println("Failed events: " + getFailedEvents());
        writer.println("===== CONFIGURATION =====");
        writer.println("Type                  : [" + getEventListenerContainerType() + "]");
        writer.println("GigaSpace             : [" + getGigaSpace().getName() + "]");
        writer.println("Template              : [" + getTemplate() + "]");
        writer.println("Transactional         : [" + getTransactionManagerName() + "]");
    }

    protected abstract String getEventListenerContainerType();

    protected void registerMetrics() {
        if (beanMetricManager != null) {
            beanMetricManager.register("processed-events", processedEvents);
            beanMetricManager.register("failed-events", failedEvents);
        }
    }

    protected void unregisterMetrics() {
        if (beanMetricManager != null)
            beanMetricManager.clear();
    }
}