com.turbospaces.api.AbstractSpaceConfiguration.java Source code

Java tutorial

Introduction

Here is the source code for com.turbospaces.api.AbstractSpaceConfiguration.java

Source

/**
 * Copyright (C) 2011-2012 Andrey Borisov <aandrey.borisov@gmail.com>
 *
 * 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 com.turbospaces.api;

import java.io.InputStream;
import java.util.Collection;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.jgroups.JChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.core.io.ClassPathResource;
import org.springframework.data.mapping.context.AbstractMappingContext;
import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.turbospaces.core.EffectiveMemoryManager;
import com.turbospaces.core.SpaceUtility;
import com.turbospaces.core.UnsafeMemoryManager;
import com.turbospaces.model.BO;
import com.turbospaces.network.ServerCommunicationDispatcher;
import com.turbospaces.serialization.DecoratedKryo;
import com.turbospaces.spaces.tx.SpaceTransactionManager;

/**
 * abstract jspace configuration suitable for both client/server node configuration.
 * 
 * @see SpaceConfiguration
 * @see ClientSpaceConfiguration
 * @since 0.1
 */
@SuppressWarnings("rawtypes")
public abstract class AbstractSpaceConfiguration
        implements ApplicationContextAware, DisposableBean, InitializingBean, SpaceErrors {
    private static final int DEFAULT_TRANSACTION_TIMEOUT = (int) TimeUnit.SECONDS.toSeconds(10);
    private static final long DEFAULT_COMMUNICATION_TIMEOUT = TimeUnit.SECONDS.toMillis(5);
    private static final long DEFAULT_CACHE_CLEANUP_PERIOD = TimeUnit.SECONDS.toMillis(1);

    private final Logger logger = LoggerFactory.getLogger(getClass());
    /**
     * spring application context reference (if inside IOC container, not standalone)
     */
    private ApplicationContext applicationContext;
    /**
     * space logical name
     * 
     * @see #setGroup(String)
     */
    private String group = defaultGroupName();
    /**
     * POJO mapping context(mongoDB, JPA, JDBC, etc)
     * 
     * @see #setMappingContext(AbstractMappingContext)
     */
    private AbstractMappingContext mappingContext;
    /**
     * jgroups communication channel
     * 
     * @see #setjChannel(JChannel)
     */
    private JChannel jChannel;
    /**
     * kryo serialization configuration
     */
    private DecoratedKryo kryo;
    /**
     * off-heap memory manager abstraction(SSD,OS).
     */
    private EffectiveMemoryManager memoryManager;
    /**
     * jspace executor service
     */
    private ListeningExecutorService executorService;
    /**
     * jspace scheduler executor service
     */
    private ListeningScheduledExecutorService scheduledExecutorService;
    /**
     * default network communication timeout.
     */
    private long communicationTimeout = defaultCommunicationTimeout();
    /**
     * default period for cleanup maintenance tasks.
     */
    private long cleanupPeriod = defaultCacheCleanupPeriod();

    /**
     * space deployment topology
     */
    SpaceTopology topology = SpaceTopology.SYNC_REPLICATED;

    private final ConcurrentMap<Class<?>, BO> bos = SpaceUtility.newCompMap(new Function<Class<?>, BO>() {
        @SuppressWarnings("unchecked")
        @Override
        public BO apply(final Class<?> input) {
            for (;;)
                try {
                    BO bo = new BO((BasicPersistentEntity) getMappingContext().getPersistentEntity(input));
                    adjustBO(bo);
                    return bo;
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                    Throwables.propagate(e);
                }
        }
    });

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

    /**
     * set custom kryo serialization instance. typically you would prefer to use default one, but just in case you want
     * to do something special or add some custom serializers.</p>
     * 
     * @param kryo
     *            custom kryo configuration(and implementation)
     */
    public void setKryo(final DecoratedKryo kryo) {
        this.kryo = Preconditions.checkNotNull(kryo);
    }

    /**
     * set the custom(something different from jgroups default configuration). typically you would use default
     * configuration, but in case of advanced jgroups users... </p>
     * 
     * also another typical case for using custom channel configuration is simply registering view listeners via
     * {@link JChannel#removeChannelListener(org.jgroups.ChannelListener)} </p>
     * 
     * @param jChannel
     *            custom communication channel
     */
    public void setjChannel(final JChannel jChannel) {
        this.jChannel = Preconditions.checkNotNull(jChannel);
    }

    /**
     * each jspace has its own unique name which is exposed through network and can be used by remote client to connect
     * to the cluster(or particular node). typically you would set something meaningful because default value is just
     * <b>turbospaces-jspace-group</b>. Good example are: userSpace, contactsSpace ,flightSpace, inventorySpace
     * etc... </p>
     * 
     * @param spaceLogicalName
     *            logical(and network lookup) group
     */
    public void setGroup(final String spaceLogicalName) {
        this.group = Preconditions.checkNotNull(spaceLogicalName);
    }

    /**
     * set preferred mapping context. currently turbospaces is built on top of spring-data's abstraction, so you would
     * choose persistent provider(mongoDB, JPA), then pick appropriate implementation and inject mappingContext here.
     * </p>
     * 
     * @param mappingContext
     *            Concrete mapping context implementation.
     */
    public void setMappingContext(final AbstractMappingContext mappingContext) {
        this.mappingContext = Preconditions.checkNotNull(mappingContext);
    }

    /**
     * @return the default network communication timeout
     */
    public long getCommunicationTimeoutInMillis() {
        return communicationTimeout;
    }

    /**
     * change the default communication timeout (which is about <b>5 seconds</b>) to new value in milliseconds
     * 
     * @param timeout
     *            new timeout in milliseconds
     */
    public void setCommunicationTimeoutInMillis(final long timeout) {
        this.communicationTimeout = timeout;
    }

    /**
     * @return the default cache cleanup period in milliseconds.
     */
    public long getCacheCleanupPeriod() {
        return cleanupPeriod;
    }

    /**
     * set the custom period in milliseconds for cache cleanup maintenance tasks.
     * 
     * @param period
     *            value in milliseconds
     */
    public void setCacheCleanupPeriod(final long period) {
        Preconditions.checkArgument(period > 0);
        this.cleanupPeriod = period;
    }

    /**
     * associate executor service with jspace
     * 
     * @param executorService
     *            asynchronous task executor
     */
    public void setExecutorService(final ExecutorService executorService) {
        this.executorService = MoreExecutors.listeningDecorator(Preconditions.checkNotNull(executorService));
    }

    /**
     * associate scheduled executor service
     * 
     * @param scheduledExecutorService
     *            asynchronous task executor
     */
    public void setScheduledExecutorService(final ScheduledExecutorService scheduledExecutorService) {
        this.scheduledExecutorService = MoreExecutors
                .listeningDecorator(Preconditions.checkNotNull(scheduledExecutorService));
    }

    /**
     * associate custom off-heap memory manager with the space(it can be SSD cache for example).
     * 
     * @param memoryManager
     *            custom memory manager
     */
    public void setMemoryManager(final EffectiveMemoryManager memoryManager) {
        this.memoryManager = memoryManager;
    }

    /**
     * @return entity mapping context associated with this configuration(this is abstract spring-data's class which
     *         hides actual persistent storage's mapping details).
     */
    public AbstractMappingContext getMappingContext() {
        return mappingContext;
    }

    /**
     * @return the jspace topology associated with this space
     */
    public SpaceTopology getTopology() {
        return topology;
    }

    /**
     * @return the network communication jChannel
     */
    public JChannel getJChannel() {
        return jChannel;
    }

    /**
     * @return message dispatcher associated with the jChannel.
     */
    public ServerCommunicationDispatcher getMessageDispatcher() {
        return (ServerCommunicationDispatcher) getJChannel().getReceiver();
    }

    /**
     * get the kryo serialization instance associated with jspace.
     * 
     * @return kryo serializer
     */
    public DecoratedKryo getKryo() {
        return kryo;
    }

    /**
     * @return the jspace logical name (and group in term of networking discovery)
     */
    public String getGroup() {
        return group;
    }

    /**
     * @return executor service associated with jspace
     */
    public ListeningExecutorService getListeningExecutorService() {
        return executorService;
    }

    /**
     * @return scheduled executor service associated with jspace
     */
    public ScheduledExecutorService getScheduledExecutorService() {
        return scheduledExecutorService;
    }

    /**
     * @return off-heap memory manager associated with jspace
     */
    public EffectiveMemoryManager getMemoryManager() {
        return memoryManager;
    }

    @Override
    public void destroy() {
        if (jChannel != null) {
            jChannel.disconnect();
            jChannel.close();
        }
        if (executorService != null)
            executorService.shutdown();
    }

    /**
     * 1. initialize jchannel
     * 2. initialize conversion service
     * 3. initialize mapping context
     * 4. initialize kryo
     */
    @Override
    @SuppressWarnings("unchecked")
    public void afterPropertiesSet() throws Exception {
        logger.info("Initializing JSpace configuration: group = {}", getGroup());

        if (getJChannel() == null) {
            ClassPathResource largeClusterCfg = new ClassPathResource("turbospaces-jgroups-udp.xml");
            InputStream inputStream = largeClusterCfg.getInputStream();
            setjChannel(new JChannel(inputStream));
            inputStream.close();
        }
        getJChannel().setDiscardOwnMessages(true);

        if (getMemoryManager() == null)
            setMemoryManager(new UnsafeMemoryManager());
        if (getMappingContext() == null)
            if (applicationContext != null)
                setMappingContext(applicationContext.getBean(AbstractMappingContext.class));

        if (getListeningExecutorService() == null)
            setExecutorService(Executors.newFixedThreadPool(1 << 6, new ThreadFactoryBuilder().setDaemon(false)
                    .setNameFormat("jspace-execpool-thread-%s").build()));
        if (getScheduledExecutorService() == null)
            setScheduledExecutorService(Executors.newScheduledThreadPool(1 << 2, new ThreadFactoryBuilder()
                    .setDaemon(true).setNameFormat("jspace-scheduledpool-thread-%s").build()));

        Preconditions.checkState(mappingContext != null, MAPPING_CONTEXT_IS_NOT_REGISTERED);

        Collection<BasicPersistentEntity> persistentEntities = mappingContext.getPersistentEntities();
        for (BasicPersistentEntity e : persistentEntities)
            boFor(e.getType());

        if (kryo == null)
            kryo = new DecoratedKryo();
        SpaceUtility.registerSpaceClasses(this, kryo);
    }

    /**
     * get the {@link BO} class wrapper for target class (retrieve from internal cache)
     * 
     * @param clazz
     *            space entity class
     * @return cached BO class wrapper
     */
    public BO boFor(final Class<?> clazz) {
        return bos.get(clazz);
    }

    /**
     * restrict space capacity on class level for particular class
     * 
     * @param clazz
     *            class that needs capacity restriction
     * @param restriction
     *            capacity restrictor
     * @return BO for given class
     */
    public BO restrictCapacity(final Class<?> clazz, final CapacityRestriction restriction) {
        Preconditions.checkNotNull(restriction);
        BO bo = boFor(clazz);
        bo.setCapacityRestriction(restriction);
        return bo;
    }

    protected void dumpConfiguration() {
        logger.info("Kryo: {}", kryo);
        logger.info("MappingContext: {}", mappingContext);
        logger.info("JChannel: \n {}", jChannel.toString(true));

        logger.info("JSpace configuration initialization finished: group = {}", getGroup());
    }

    protected void adjustBO(final BO bo) {
        logger.trace("adjusted BO = {}", bo);
    }

    /**
     * join the network and begin communications with other nodes
     * 
     * @throws Exception
     *             re-throw jgroup's exception
     */
    public void joinNetwork() throws Exception {
        logger.info("{} joining network group {}", getJChannel().getName(), group);
        jChannel.connect(group);
    }

    /**
     * @return default jspace group name if no specified by client (typical case for such scenario is product
     *         evaluation).
     */
    public static String defaultGroupName() {
        return String.format("jspace-%s-v-%s", System.getProperty("user.name"), SpaceUtility.projectVersion());
    }

    /**
     * @return default network lookup/communication timeout in milliseconds
     * @see #setCommunicationTimeoutInMillis(long)
     */
    public static long defaultCommunicationTimeout() {
        return DEFAULT_COMMUNICATION_TIMEOUT;
    }

    /**
     * @return default period for scheduled automatic cache cleanup activities in milliseconds
     */
    public static long defaultCacheCleanupPeriod() {
        return DEFAULT_CACHE_CLEANUP_PERIOD;
    }

    /**
     * Return the default timeout that this transaction manager should apply
     * if there is no timeout specified at the transaction level, in seconds.
     * 
     * @return value in seconds
     * @see DefaultTransactionDefinition#setTimeout(int)
     * @see SpaceTransactionManager#setDefaultTimeout(int)
     */
    public static int defaultTransactionTimeout() {
        return DEFAULT_TRANSACTION_TIMEOUT;
    }
}