com.janrain.backplane.config.BackplaneConfig.java Source code

Java tutorial

Introduction

Here is the source code for com.janrain.backplane.config.BackplaneConfig.java

Source

/*
 * Copyright 2012 Janrain, Inc.
 *
 * 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.janrain.backplane.config;

import com.janrain.backplane.config.dao.ConfigDAOs;
import com.janrain.backplane.config.model.ServerConfigFields;
import com.janrain.backplane.dao.redis.RedisMessageProcessor;
import com.janrain.backplane.server1.dao.BP1DAOs;
import com.janrain.backplane.server1.dao.redis.RedisBackplane1DualFormatMessageProcessor;
import com.janrain.backplane.server2.dao.BP2DAOs;
import com.janrain.backplane.server2.model.Backplane2Message;
import com.janrain.backplane.server2.model.Backplane2MessageFields;
import com.janrain.commons.util.AwsUtility;
import com.janrain.commons.util.Pair;
import com.janrain.redis.Redis;
import com.netflix.curator.framework.CuratorFramework;
import com.netflix.curator.framework.CuratorFrameworkFactory;
import com.netflix.curator.framework.recipes.leader.LeaderSelector;
import com.netflix.curator.framework.recipes.leader.LeaderSelectorListener;
import com.netflix.curator.retry.ExponentialBackoffRetry;
import com.yammer.metrics.Metrics;
import com.yammer.metrics.reporting.ConsoleReporter;
import com.yammer.metrics.reporting.GraphiteReporter;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.context.annotation.Scope;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * Holds configuration settings for the Backplane server
 * 
 * @author Jason Cowley, Johnny Bufu
 */
@Scope(value = "singleton")
public class BackplaneConfig {

    // - PUBLIC

    /**
    * @return the debugMode
    */
    public static boolean isDebugMode() {
        return ConfigDAOs.serverConfigDao().oneServerConfig().get().isDebugMode();
    }

    /**
     * @return the server default max message value per channel
     * @throws SimpleDBException
     */
    public static long getDefaultMaxMessageLimit() {
        Long max = Long.valueOf(ConfigDAOs.serverConfigDao().oneServerConfig().get()
                .get(ServerConfigFields.DEFAULT_MESSAGES_MAX()).get());
        return max == null ? BackplaneConfig.BP_MAX_MESSAGES_DEFAULT : max;
    }

    public static boolean isLeaderDisabled() {
        // skip DAO layer, not so crazy about editing serialized streams for debug, yay FED-76
        return isDebugMode() && Redis.getInstance().get(EC2InstanceId) != null;
    }

    public static Throwable getDebugException(Throwable e) {
        return isDebugMode() ? e : null;
    }

    /**
     * Retrieve the server instance id Amazon assigned
     * @return
     */
    public static String getEC2InstanceId() {
        return EC2InstanceId;
    }

    public static void addToBackgroundServices(String workerName, ScheduledExecutorService messageWorkerTask) {
        backgroundServices.put(workerName, messageWorkerTask);
    }

    public String getBuildVersion() {
        return buildProperties.getProperty(BUILD_VERSION_PROPERTY);
    }

    // - PRIVATE

    private static final Logger logger = Logger.getLogger(BackplaneConfig.class);

    private static final String BUILD_PROPERTIES = "/build.properties";
    private static final String BUILD_VERSION_PROPERTY = "build.version";
    private static final Properties buildProperties = new Properties();
    private static final long BP_MAX_MESSAGES_DEFAULT = 100;

    private static final Map<String, ExecutorService> backgroundServices = new HashMap<String, ExecutorService>();

    // Amazon specific instance-id value
    private static String EC2InstanceId = AwsUtility.retrieveEC2InstanceId();

    @SuppressWarnings({ "UnusedDeclaration" })
    private BackplaneConfig() {
        ConsoleReporter.enable(10, TimeUnit.MINUTES);

        // Dump metrics to graphite server
        String graphiteServer = System.getProperty(SystemProperties.GRAPHITE_SERVER());
        if (StringUtils.isNotBlank(graphiteServer)) {
            try {
                String args[] = graphiteServer.split(":");
                String server = args[0];
                int port = Integer.parseInt(args[1]);
                GraphiteReporter.enable(10, TimeUnit.SECONDS, server, port,
                        SystemProperties.machineName().replace(".", "_") + "_" + SystemProperties.INSTANCE_ID());
                logger.info("Graphite server enabled at " + graphiteServer);
            } catch (Exception e) {
                logger.warn(
                        "could not enable Graphite from " + graphiteServer + " must be in the form SERVER:PORT");
            }
        }
        try {
            buildProperties.load(BackplaneConfig.class.getResourceAsStream(BUILD_PROPERTIES));
            //assert(StringUtils.isNotBlank(getEncryptionKey()));
        } catch (Exception e) {
            String err = "Error loading build properties from " + BUILD_PROPERTIES;
            logger.error(err, e);
            throw new RuntimeException(err, e);
        }

        logger.info("Configured Backplane Server instance: " + SystemProperties.INSTANCE_ID());
    }

    private Pair<String, ExecutorService> createPingTask() {
        final String label = "redis/jedis";
        ScheduledExecutorService ping = Executors.newScheduledThreadPool(1);
        ping.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                com.janrain.redis.Redis.getInstance().ping(label);
            }
        }, 30, 10, TimeUnit.SECONDS);
        return new Pair<String, ExecutorService>(label, ping);
    }

    private void addTask(Map<String, ExecutorService> backgroundServices,
            Pair<String, ExecutorService> nameAndService) {
        backgroundServices.put(nameAndService.getLeft(), nameAndService.getRight());
    }

    @PostConstruct
    private void init() {
        addTask(backgroundServices, createPingTask());
        // todo: replace with this after transition to new serialization is complete
        //initZk("/v1_worker", new RedisMessageProcessor<Backplane1MessageFields.EnumVal, Backplane1Message>(BP1DAOs.messageDao()));
        initZk("/v1_worker", new RedisBackplane1DualFormatMessageProcessor(BP1DAOs.messageDao()));
        initZk("/v2_worker", new RedisMessageProcessor<Backplane2MessageFields.EnumVal, Backplane2Message>(
                BP2DAOs.messageDao()));
    }

    private void initZk(String leaderPath, LeaderSelectorListener listener) {
        try {
            String zkServerConfig = System.getProperty(SystemProperties.ZOOKEEPER_SERVERS());
            if (StringUtils.isEmpty(zkServerConfig)) {
                logger.error("Cannot find configuration entry for ZooKeeper server ('"
                        + SystemProperties.ZOOKEEPER_SERVERS() + "' system property)");
                System.exit(1);
            }
            CuratorFramework client = CuratorFrameworkFactory.newClient(zkServerConfig,
                    new ExponentialBackoffRetry(50, 20));
            client.start();
            LeaderSelector leaderSelector = new LeaderSelector(client, leaderPath, listener);
            leaderSelector.autoRequeue();
            leaderSelector.start();
            com.janrain.redis.Redis.getInstance().setActiveRedisInstance(client);
        } catch (Exception e) {
            logger.error(e);
        }
    }

    @PreDestroy
    private void cleanup() {
        Metrics.shutdown();
        for (Map.Entry<String, ExecutorService> serviceEntry : backgroundServices.entrySet()) {
            shutdownExecutor(serviceEntry.getKey(), serviceEntry.getValue());
        }
    }

    private void shutdownExecutor(String serviceName, ExecutorService executor) {
        try {
            executor.shutdown();
            if (executor.awaitTermination(10, TimeUnit.SECONDS)) {
                logger.info(serviceName + " background thread shutdown properly");
            } else {
                executor.shutdownNow();
                if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
                    logger.error(serviceName + " background thread did not terminate");
                }
            }
        } catch (InterruptedException e) {
            logger.error(serviceName + " termination threw an exception", e);
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}