org.openspaces.core.space.mode.SpaceModeContextLoader.java Source code

Java tutorial

Introduction

Here is the source code for org.openspaces.core.space.mode.SpaceModeContextLoader.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.core.space.mode;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openspaces.core.GigaSpace;
import org.openspaces.core.cluster.ClusterInfo;
import org.openspaces.core.cluster.ClusterInfoAware;
import org.openspaces.core.properties.BeanLevelProperties;
import org.openspaces.core.properties.BeanLevelPropertiesAware;
import org.openspaces.core.util.SpaceUtils;
import org.openspaces.pu.container.ProcessingUnitContainerConfig;
import org.openspaces.pu.container.support.ResourceApplicationContext;
import org.springframework.beans.BeansException;
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.event.ApplicationEventMulticaster;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.util.MethodInvoker;

/**
 * A Space mode based Spring context loader allows to load Spring application context if the Space
 * is in <code>PRIMARY</code> mode.
 *
 * <p>The space mode context loader allows to assemble beans that only operate when a space is in a
 * <code>PRIMARY</code> mode which basically applies when directly working with cluster members and
 * not a clustered space proxy (since in such cases it will always be <code>PRIMARY</code>).
 *
 * <p>The new Spring application context created will have the current context as its parent,
 * allowing to use any beans defined within the current context within the loaded context.
 *
 * <p>The context loader accepts a Spring {@link org.springframework.core.io.Resource} as the
 * location. A flag called {@link #setActiveWhenPrimary(boolean)} which defaults to
 * <code>true</code> allows to control if the context will be loaded only when the cluster member
 * moves to <code>PRIMARY</code> mode.
 *
 * @author kimchy
 */
public class SpaceModeContextLoader implements ApplicationContextAware, InitializingBean, DisposableBean,
        ApplicationListener, BeanLevelPropertiesAware, ClusterInfoAware, BeanNameAware {

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

    private Resource location;

    private GigaSpace gigaSpace;

    protected String beanName;

    private boolean activeWhenPrimary = true;

    private ApplicationContext parentApplicationContext;

    private ProcessingUnitContainerConfig config = new ProcessingUnitContainerConfig();

    protected volatile ResourceApplicationContext applicationContext;

    /**
     * The location of the Spring xml context application to be loaded.
     */
    public void setLocation(Resource location) {
        this.location = location;
    }

    /**
     * Allows to set the GigaSpace instance that will control (based on its Space mode - PRIMARY or
     * BACKUP) if the context will be loaded or not. Useful when more than one space is defined
     * within a Spring context.
     */
    public void setGigaSpace(GigaSpace gigaSpace) {
        this.gigaSpace = gigaSpace;
    }

    /**
     * Controls if the Spring context will be loaded when the space cluster member moves to
     * <code>PRIMARY</code> mode. Defaults to <code>true</code>.
     */
    public void setActiveWhenPrimary(boolean activeWhenPrimary) {
        this.activeWhenPrimary = activeWhenPrimary;
    }

    /**
     * Used to pass the {@link BeanLevelProperties} to the newly created application context.
     */
    @Override
    public void setBeanLevelProperties(BeanLevelProperties beanLevelProperties) {
        config.setBeanLevelProperties(beanLevelProperties);
    }

    /**
     * Used to pass {@link ClusterInfo} to the newly created application context.
     */
    @Override
    public void setClusterInfo(ClusterInfo clusterInfo) {
        config.setClusterInfo(clusterInfo);
    }

    /**
     * Injected by Spring and used as the parent application context for the newly created
     * application context.
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.parentApplicationContext = applicationContext;
    }

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

    @Override
    public void afterPropertiesSet() throws Exception {
        if (!activeWhenPrimary) {
            loadApplicationContext();
        }
    }

    @Override
    public void destroy() throws Exception {
        closeApplicationContext();
    }

    /**
     * If {@link #setActiveWhenPrimary(boolean)} is set to <code>true</code> (the default) will
     * listens for {@link AfterSpaceModeChangeEvent} and load an application context if received.
     */
    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        if (activeWhenPrimary) {
            if (applicationEvent instanceof AfterSpaceModeChangeEvent) {
                AfterSpaceModeChangeEvent spEvent = (AfterSpaceModeChangeEvent) applicationEvent;
                if (spEvent.isPrimary()) {
                    if (gigaSpace != null) {
                        if (SpaceUtils.isSameSpace(spEvent.getSpace(), gigaSpace.getSpace())) {
                            try {
                                loadApplicationContext();
                            } catch (Exception e) {
                                logger.error(
                                        "Failed to load context [" + location + "] when moving to primary mode", e);
                            }
                        }
                    } else {
                        try {
                            loadApplicationContext();
                        } catch (Exception e) {
                            logger.error("Failed to load context [" + location + "] when moving to primary mode",
                                    e);
                        }
                    }
                }
                publishEvent(applicationEvent);
            } else if (applicationEvent instanceof BeforeSpaceModeChangeEvent) {
                publishEvent(applicationEvent);
                BeforeSpaceModeChangeEvent spEvent = (BeforeSpaceModeChangeEvent) applicationEvent;
                if (!spEvent.isPrimary()) {
                    if (gigaSpace != null) {
                        if (SpaceUtils.isSameSpace(spEvent.getSpace(), gigaSpace.getSpace())) {
                            closeApplicationContext();
                        }
                    } else {
                        closeApplicationContext();
                    }
                }
            }
        } else {
            if (applicationEvent instanceof AbstractSpaceModeChangeEvent) {
                publishEvent(applicationEvent);
            }
        }
    }

    /**
     * Loads the application context and adding specific bean factory and bean post processors.
     * Won't load an application context if one is already defined.
     */
    protected void loadApplicationContext() throws Exception {
        if (applicationContext != null) {
            return;
        }
        if (logger.isDebugEnabled()) {
            logger.debug("Loading application context [" + location + "]");
        }
        applicationContext = new ResourceApplicationContext(new Resource[] { location }, parentApplicationContext,
                config);
        try {
            applicationContext.refresh();
        } catch (Exception e) {
            applicationContext = null;
            throw e;
        }
    }

    /**
     * Closes the application context. Won't close that application context if one is not defined.
     */
    protected void closeApplicationContext() {
        if (applicationContext != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Closing application context [" + location + "]");
            }
            try {
                // null the parent, so the close event won't be sent to it as well...
                applicationContext.setParent(null);
                applicationContext.close();
            } finally {
                applicationContext = null;
            }
        }
    }

    /**
     * A hack to only send the application event on the child application context we are loading,
     * without propogating it to the parent application context (when using {@link
     * ApplicationContext#publishEvent(org.springframework.context.ApplicationEvent)} which will
     * create a recursive event calling.
     */
    protected void publishEvent(ApplicationEvent applicationEvent) {
        if (applicationContext == null) {
            return;
        }
        ApplicationEventMulticaster eventMulticaster;
        try {
            MethodInvoker methodInvoker = new MethodInvoker();
            methodInvoker.setTargetClass(AbstractApplicationContext.class);
            methodInvoker.setTargetMethod("getApplicationEventMulticaster");
            methodInvoker.setTargetObject(applicationContext);
            methodInvoker.setArguments(null);
            methodInvoker.prepare();
            eventMulticaster = (ApplicationEventMulticaster) methodInvoker.invoke();
        } catch (Exception e) {
            logger.warn("Failed to get application event multicaster to publish event to child application context",
                    e);
            return;
        }
        eventMulticaster.multicastEvent(applicationEvent);
    }
}