com.setronica.ucs.server.MessageServer.java Source code

Java tutorial

Introduction

Here is the source code for com.setronica.ucs.server.MessageServer.java

Source

/*
 * Copyright (C) 2014 SETRONICA - setronica.com
 * This source code is available under the terms of the GNU Lesser General Public License
 * as published by The Open Source Initiative (OSI), either version 3 of the License,
 * or (at your option) any later version.
 *
 * UCS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * See the GNU Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License.
 */

package com.setronica.ucs.server;

import com.mongodb.MongoClient;
import com.mongodb.ServerAddress;
import com.setronica.ucs.client.ServiceOperation;
import com.setronica.ucs.client.ServiceOperationResult;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.GetDataBuilder;
import org.apache.curator.framework.recipes.leader.LeaderLatch;
import org.apache.curator.framework.recipes.leader.LeaderLatchListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.x.discovery.ServiceDiscovery;
import org.apache.curator.x.discovery.ServiceDiscoveryBuilder;
import org.apache.curator.x.discovery.ServiceInstance;
import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;
import org.apache.zookeeper.KeeperException;
import org.msgpack.MessagePack;
import org.msgpack.rpc.Server;
import org.msgpack.rpc.loop.EventLoop;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;

import java.io.IOException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

/**
 * UCS Server main application.
 *
 * <p>Configuration parameters:<br/>
 * -DzkConnect - zookeeper connection string (comma separated pairs of host:port). Optional. If connection string not defined
 * then zookeeper will not be used.<br/>
 * -Dhost - host name for client connection (ip address). Server will listen to this address. Default is 'localhost'<br/>
 * -Dport - port to listen. Default is 1985<br/>
 * -Dspring.profiles.active - application profile. Available profiles are 'mongo', 'memory'. Default is 'memory'.<br/>
 *
 */
public class MessageServer {
    static {
        try {
            DOMConfigurator.configureAndWatch("log4j.xml");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static Logger logger = Logger.getLogger(MessageServer.class);
    public static final String LEADER_PATH = "/ucs/MsgPackRPCServer";
    public static final String SERVICE_PATH = "/ucs/MsgPackRPCServer";

    public static void main(String[] args) throws Exception {
        String hostname = System.getProperty("host", "localhost");
        String portStr = System.getProperty("port", "1985");
        Integer port = Integer.valueOf(portStr);
        String zkConnect = System.getProperty("zkConnect");

        if (zkConnect != null && zkConnect.length() > 0) {
            MessageServer server = new MessageServer();
            server.registerService(hostname, port, zkConnect);
        } else {
            ClassPathXmlApplicationContext applicationContext = bootstrapMemory();
            EventLoop eventLoop = createMsgPackRPCServer(applicationContext, hostname, port);
            try {
                eventLoop.join();
            } catch (InterruptedException e) {
                eventLoop.shutdown();
                eventLoop.join();
            }
        }
    }

    private static ClassPathXmlApplicationContext initApplication(String profile,
            DefaultListableBeanFactory parentBeanFactory) {
        ClassPathXmlApplicationContext applicationContext;

        if (parentBeanFactory != null) {
            //wrap BeanFactory inside ApplicationContext
            GenericApplicationContext parentContext = new GenericApplicationContext(parentBeanFactory);
            parentContext.refresh();
            applicationContext = new ClassPathXmlApplicationContext(parentContext);
        } else {
            applicationContext = new ClassPathXmlApplicationContext();
        }
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        environment.setDefaultProfiles(profile);
        applicationContext.setConfigLocation("spring-application.xml");

        // log active profile

        String[] profiles = environment.getActiveProfiles();
        if (profiles.length == 0) {
            profiles = environment.getDefaultProfiles();
        }
        for (String activeProfile : profiles) {
            if (environment.acceptsProfiles(activeProfile)) {
                logger.info("Profile " + activeProfile + " is active");
            }
        }

        applicationContext.refresh();
        return applicationContext;
    }

    private static ClassPathXmlApplicationContext bootstrapMemory() {
        return initApplication("memory", null);
    }

    private static ClassPathXmlApplicationContext bootstrapMongo(String mongoUrls) throws UnknownHostException {
        List<ServerAddress> seeds = new ArrayList<>();
        for (String s : mongoUrls.split(",")) {
            int idx = s.indexOf(":");
            ServerAddress address;
            if (idx > 0) {
                address = new ServerAddress(s.substring(0, idx), Integer.parseInt(s.substring(idx + 1)));
            } else {
                address = new ServerAddress(s);
            }
            seeds.add(address);
        }
        //create parent BeanFactory
        DefaultListableBeanFactory parentBeanFactory = new DefaultListableBeanFactory();
        //register your pre-fabricated object in it
        parentBeanFactory.registerSingleton("addresses", seeds);
        ClassPathXmlApplicationContext applicationContext = initApplication("mongo", parentBeanFactory);

        MongoClient client = (MongoClient) applicationContext.getBean("mongoClient");
        logger.info("addresses: " + client.getAllAddress());
        return applicationContext;
    }

    private static ClassPathXmlApplicationContext bootstrapPostgres(String url) throws UnknownHostException {
        //create parent BeanFactory
        DefaultListableBeanFactory parentBeanFactory = new DefaultListableBeanFactory();
        //register your pre-fabricated object in it
        parentBeanFactory.registerSingleton("address", url);
        return initApplication("postgres", parentBeanFactory);
    }

    private void registerService(String host, int port, String zookeeperConnectStr) throws Exception {
        final CuratorFramework client = CuratorFrameworkFactory.newClient(zookeeperConnectStr,
                new ExponentialBackoffRetry(1000, 3));
        //        LeaderSelector leaderSelector = new LeaderSelector(client, LEADER_PATH, new MyLeaderSelectorListener(host, port));
        //        leaderSelector.start();
        client.start();
        final LeaderLatch leaderLatch = new LeaderLatch(client, LEADER_PATH);
        leaderLatch.addListener(new MyLeaderLatchListener(client, host, port));
        leaderLatch.start();
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    leaderLatch.close();
                    client.close();
                } catch (IOException e) {
                    logger.error("", e);
                }
            }
        }));
        leaderLatch.await();

    }

    private static EventLoop createMsgPackRPCServer(ConfigurableApplicationContext applicationContext, String host,
            int port) throws IOException, InterruptedException {
        EventLoop loop = EventLoop.defaultEventLoop();
        MessagePack messagePack = loop.getMessagePack();
        messagePack.register(ServiceOperation.class);
        messagePack.register(ServiceOperationResult.class);

        Server svr = new Server();
        Object handler = new MsgPackUCSCoreService(applicationContext);
        svr.serve(handler);

        svr.listen(host, port);
        return loop;
    }

    private static class MyLeaderLatchListener implements LeaderLatchListener {
        private String host;
        protected int port;
        protected ClassPathXmlApplicationContext applicationContext;
        protected EventLoop eventLoop;
        private CuratorFramework client;

        private MyLeaderLatchListener(CuratorFramework client, String serviceHost, int servicePort) {
            this.client = client;
            this.host = serviceHost;
            this.port = servicePort;
        }

        @Override
        public void isLeader() {
            try {
                GetDataBuilder dataBuilder = client.getData();
                String profile = "memory";
                try {
                    profile = new String(dataBuilder.forPath("/ucs/server/profile"));
                } catch (KeeperException e) {
                    logger.warn("ZK profile not found");
                }
                logger.info("ZK profile : " + profile);
                switch (profile) {
                case "mongo":
                    String mongoUrls = "localhost";
                    try {
                        mongoUrls = new String(dataBuilder.forPath("/ucs/server/profile/mongo_urls"));
                    } catch (KeeperException e) {
                        logger.warn("ZK /ucs/server/profile/mongo_urls urls not found");
                    }
                    logger.info("ZK mongoUrls: " + mongoUrls);
                    applicationContext = bootstrapMongo(mongoUrls);
                    break;
                case "postgres":
                    String dbUrl = "localhost";
                    try {
                        dbUrl = new String(dataBuilder.forPath("/ucs/server/profile/postgres_url"));
                    } catch (KeeperException e) {
                        logger.warn("ZK /ucs/server/profile/postgres_url urls not found");
                    }
                    logger.info("ZK dbUrl: " + dbUrl);
                    applicationContext = bootstrapPostgres(dbUrl);
                    break;
                default:
                    applicationContext = bootstrapMemory();
                    break;
                }

                eventLoop = createMsgPackRPCServer(applicationContext, host, port);
                ServiceDiscovery<String> discovery = ServiceDiscoveryBuilder.builder(String.class)
                        .basePath(SERVICE_PATH).client(client).build();

                ServiceInstance<String> serviceInstance = ServiceInstance.<String>builder().id("ucs").address(host)
                        .port(port).name("ucs").build();
                discovery.start();
                discovery.registerService(serviceInstance);
            } catch (Exception e) {
                logger.fatal("UCS bootstrap failed", e);
                //                System.exit(1);
            }
        }

        @Override
        public void notLeader() {
            eventLoop.shutdown();
            try {
                eventLoop.join();
            } catch (InterruptedException e) {
                logger.info("Got another interruption signal");
                // ignore rest of processing
            }
            applicationContext.close();
            System.exit(0);
        }
    }
}