org.springframework.data.gemfire.CacheFactoryBean.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.data.gemfire.CacheFactoryBean.java

Source

/*
 * Copyright 2010-2011 the original author or authors.
 *
 * 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.springframework.data.gemfire;

import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import com.gemstone.gemfire.GemFireException;
import com.gemstone.gemfire.cache.Cache;
import com.gemstone.gemfire.cache.CacheClosedException;
import com.gemstone.gemfire.cache.CacheFactory;
import com.gemstone.gemfire.cache.GemFireCache;
import com.gemstone.gemfire.distributed.DistributedMember;
import com.gemstone.gemfire.distributed.DistributedSystem;
import com.gemstone.gemfire.pdx.PdxSerializable;
import com.gemstone.gemfire.pdx.PdxSerializer;

/**
 * Factory used for configuring a Gemfire Cache manager. Allows either retrieval of an existing, opened cache 
 * or the creation of a new one.
    
 * <p>This class implements the {@link org.springframework.dao.support.PersistenceExceptionTranslator}
 * interface, as auto-detected by Spring's
 * {@link org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor},
 * for AOP-based translation of native exceptions to Spring DataAccessExceptions.
 * Hence, the presence of this class automatically enables
 * a PersistenceExceptionTranslationPostProcessor to translate GemFire exceptions.
 * 
 * @author Costin Leau
 */
public class CacheFactoryBean implements BeanNameAware, BeanFactoryAware, BeanClassLoaderAware, DisposableBean,
        InitializingBean, FactoryBean<GemFireCache>, PersistenceExceptionTranslator {

    /**
     * Inner class to avoid a hard dependency on the GemFire 6.6 API.
     * 
     * @author Costin Leau
     */
    private class PdxOptions implements Runnable {

        private CacheFactory factory;

        PdxOptions(CacheFactory factory) {
            this.factory = factory;
        }

        public void run() {
            if (pdxSerializer != null) {
                Assert.isAssignable(PdxSerializer.class, pdxSerializer.getClass(), "Invalid pdx serializer used");
                factory.setPdxSerializer((PdxSerializer) pdxSerializer);
            }
            if (pdxDiskStoreName != null) {
                factory.setPdxDiskStore(pdxDiskStoreName);
            }
            if (pdxIgnoreUnreadFields != null) {
                factory.setPdxIgnoreUnreadFields(pdxIgnoreUnreadFields);
            }
            if (pdxPersistent != null) {
                factory.setPdxPersistent(pdxPersistent);
            }
            if (pdxReadSerialized != null) {
                factory.setPdxReadSerialized(pdxReadSerialized);
            }
        }
    }

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

    private GemFireCache cache;
    private Resource cacheXml;
    private Properties properties;
    private ClassLoader beanClassLoader;
    private GemfireBeanFactoryLocator factoryLocator;

    private BeanFactory beanFactory;
    private String beanName;
    private boolean useBeanFactoryLocator = true;
    // PDX options
    protected Object pdxSerializer;
    protected Boolean pdxPersistent;
    protected Boolean pdxReadSerialized;
    protected Boolean pdxIgnoreUnreadFields;
    protected String pdxDiskStoreName;

    public void afterPropertiesSet() throws Exception {
        // initialize locator
        if (useBeanFactoryLocator) {
            factoryLocator = new GemfireBeanFactoryLocator();
            factoryLocator.setBeanFactory(beanFactory);
            factoryLocator.setBeanName(beanName);
            factoryLocator.afterPropertiesSet();
        }
        Properties cfgProps = mergeProperties();

        // use the bean class loader to load Declarable classes
        Thread th = Thread.currentThread();
        ClassLoader oldTCCL = th.getContextClassLoader();

        try {
            th.setContextClassLoader(beanClassLoader);
            // first look for open caches
            String msg = null;
            try {
                cache = fetchCache();
                msg = "Retrieved existing";
            } catch (CacheClosedException ex) {
                Object factory = createFactory(cfgProps);

                // GemFire 6.6 specific options
                if (pdxSerializer != null || pdxPersistent != null || pdxReadSerialized != null
                        || pdxIgnoreUnreadFields != null || pdxDiskStoreName != null) {
                    Assert.isTrue(ClassUtils.isPresent("com.gemstone.gemfire.pdx.PdxSerializer", beanClassLoader),
                            "Cannot set PDX options since GemFire 6.6 not detected");
                    applyPdxOptions(factory);
                }

                // fall back to cache creation
                cache = createCache(factory);
                msg = "Created";
            }

            DistributedSystem system = cache.getDistributedSystem();
            DistributedMember member = system.getDistributedMember();
            log.info("Connected to Distributed System [" + system.getName() + "=" + member.getId() + "@"
                    + member.getHost() + "]");

            log.info(msg + " GemFire v." + CacheFactory.getVersion() + " Cache [" + cache.getName() + "]");

            // load/init cache.xml
            if (cacheXml != null) {
                cache.loadCacheXml(cacheXml.getInputStream());

                if (log.isDebugEnabled())
                    log.debug("Initialized cache from " + cacheXml);
            }

        } finally {
            th.setContextClassLoader(oldTCCL);
        }
    }

    /**
     * Sets the PDX properties for the given object. Note this is implementation specific as it depends on the type
     * of the factory passed in.
     * 
     * @param factory
     */
    protected void applyPdxOptions(Object factory) {
        if (factory instanceof CacheFactory) {
            new PdxOptions((CacheFactory) factory).run();
        }
    }

    protected Object createFactory(Properties props) {
        return new CacheFactory(props);
    }

    protected GemFireCache fetchCache() {
        return CacheFactory.getAnyInstance();
    }

    protected GemFireCache createCache(Object factory) {
        return ((CacheFactory) factory).create();
    }

    protected Properties mergeProperties() {
        Properties cfgProps = (properties != null ? (Properties) properties.clone() : new Properties());
        return cfgProps;
    }

    public void destroy() throws Exception {
        if (cache != null && !cache.isClosed()) {
            cache.close();
        }

        cache = null;

        if (factoryLocator != null) {
            factoryLocator.destroy();
            factoryLocator = null;
        }
    }

    public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
        if (ex instanceof GemFireException) {
            return GemfireCacheUtils.convertGemfireAccessException((GemFireException) ex);
        }
        if (ex instanceof IllegalArgumentException) {
            DataAccessException wrapped = GemfireCacheUtils.convertQueryExceptions(ex);
            // ignore conversion if the generic exception is returned
            if (!(wrapped instanceof GemfireSystemException)) {
                return wrapped;
            }
        }

        return null;
    }

    public GemFireCache getObject() throws Exception {
        return cache;
    }

    public Class<? extends GemFireCache> getObjectType() {
        return (cache != null ? cache.getClass() : Cache.class);
    }

    public boolean isSingleton() {
        return true;
    }

    public void setBeanClassLoader(ClassLoader classLoader) {
        this.beanClassLoader = classLoader;
    }

    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

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

    /**
     * Sets the cache properties.
     * 
     * @param properties the properties to set
     */
    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    /**
     * Sets the cache configuration.
     * 
     * @param cacheXml the cacheXml to set
     */
    public void setCacheXml(Resource cacheXml) {
        this.cacheXml = cacheXml;
    }

    /**
     * Indicates whether a bean factory locator is enabled (default) for this cache definition or not. The locator stores
     * the enclosing bean factory reference to allow auto-wiring of Spring beans into GemFire managed classes. Usually disabled
     * when the same cache is used in multiple application context/bean factories inside the same VM.
     * 
     * @param usage true if the bean factory locator is used underneath or not
     */
    public void setUseBeanFactoryLocator(boolean usage) {
        this.useBeanFactoryLocator = usage;
    }

    /**
     * Sets the {@link PdxSerializable} for this cache. Applicable on GemFire 6.6 or higher.
     * The argument is of type object for compatibility with GemFire 6.5.
     * 
     * @param serializer pdx serializer configured for this cache.
     */
    public void setPdxSerializer(Object serializer) {
        this.pdxSerializer = serializer;
    }

    /**
     * Sets the object preference to PdxInstance type. Applicable on GemFire 6.6 or higher.
     *  
     * @param pdxPersistent the pdxPersistent to set
     */
    public void setPdxPersistent(Boolean pdxPersistent) {
        this.pdxPersistent = pdxPersistent;
    }

    /**
     * Controls whether the type metadata for PDX objects is persisted to disk. Applicable on GemFire 6.6 or higher.
     * 
     * @param pdxReadSerialized the pdxReadSerialized to set
     */
    public void setPdxReadSerialized(Boolean pdxReadSerialized) {
        this.pdxReadSerialized = pdxReadSerialized;
    }

    /**
     * Controls whether pdx ignores fields that were unread during deserialization. Applicable on GemFire 6.6 or higher.
     * 
     * @param pdxIgnoreUnreadFields the pdxIgnoreUnreadFields to set
     */
    public void setPdxIgnoreUnreadFields(Boolean pdxIgnoreUnreadFields) {
        this.pdxIgnoreUnreadFields = pdxIgnoreUnreadFields;
    }

    /**
     * Set the disk store that is used for PDX meta data. Applicable on GemFire 6.6 or higher.
     *  
     * @param pdxDiskStoreName the pdxDiskStoreName to set
     */
    public void setPdxDiskStoreName(String pdxDiskStoreName) {
        this.pdxDiskStoreName = pdxDiskStoreName;
    }

    /**
     * @return the beanFactory
     */
    protected BeanFactory getBeanFactory() {
        return beanFactory;
    }
}