Java tutorial
/* * 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); } } }