org.stem.ClusterManagerDaemon.java Source code

Java tutorial

Introduction

Here is the source code for org.stem.ClusterManagerDaemon.java

Source

/*
 * Copyright 2014 Alexey Plotnik
 *
 * 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.stem;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
import org.glassfish.grizzly.filterchain.FilterChainContext;
import org.glassfish.grizzly.filterchain.NextAction;
import org.glassfish.grizzly.http.HttpBaseFilter;
import org.glassfish.grizzly.http.HttpContent;
import org.glassfish.grizzly.http.HttpRequestPacket;
import org.glassfish.grizzly.http.server.*;
import org.glassfish.grizzly.threadpool.ThreadPoolConfig;
import org.glassfish.jersey.server.ContainerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.stem.coordination.ZooException;
import org.stem.coordination.ZookeeperClient;
import org.stem.coordination.ZookeeperFactoryCached;
import org.stem.coordination.ZookeeperPaths;
import org.stem.domain.Cluster;
import org.stem.exceptions.DefaultExceptionMapper;
import org.stem.exceptions.StemException;
import org.stem.exceptions.StemExceptionMapper;
import org.stem.net.CLStaticByPassHttpHandler;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;

public class ClusterManagerDaemon {

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

    private static final String STEM_CONFIG_PROPERTY = "stem.cluster.config";
    private static final String DEFAULT_CONFIG = "cluster.yaml";
    public static final String RESOURCES_PACKAGE = "org.stem.api.resources";

    static Config config;

    private ZookeeperClient zookeeperClient;

    static {
        applyConfig(loadConfig());
    }

    private static void applyConfig(Config config) {
        logger.info("Zookeeper address: {}", config.zookeeper_endpoint);
        logger.info("REST API listen on {}", config.web_listen_address);
    }

    public static String zookeeperEndpoint() {
        return config.zookeeper_endpoint;
    }

    public static Config loadConfig() {
        URL url = getConfigUrl();
        logger.info("Loading settings from " + url);

        InputStream stream;
        try {
            stream = url.openStream();
        } catch (IOException e) {
            throw new AssertionError(e);
        }

        Constructor constructor = new Constructor(Config.class);
        Yaml yaml = new Yaml(constructor);
        config = (Config) yaml.load(stream);
        return config;
    }

    static URL getConfigUrl() {
        String configPath = System.getProperty(STEM_CONFIG_PROPERTY);
        if (null == configPath)
            configPath = DEFAULT_CONFIG;

        URL url;

        try {
            File file = new File(configPath);
            url = file.toURI().toURL();
            url.openStream().close();
        } catch (Exception e) {
            ClassLoader loader = ClusterManagerDaemon.class.getClassLoader();
            url = loader.getResource(configPath);
            if (null == url)
                throw new RuntimeException("Cannot load " + configPath + ". Ensure \"" + STEM_CONFIG_PROPERTY
                        + "\" system property is set correctly.");
        }

        return url;
    }

    private HttpServer server;

    public static void main(String[] args) throws InterruptedException {
        environmentDetect();
        ClusterManagerDaemon daemon = new ClusterManagerDaemon();
        daemon.start();
        Thread.currentThread().join();
    }

    private static void environmentDetect() {
        String javaVersion = System.getProperty("java.version");
        String javaVmName = System.getProperty("java.vm.name");
        logger.info("Java Virtual Machine: {}/{}", javaVmName, javaVersion);
        logger.info("Heap size: {}/{}", Runtime.getRuntime().totalMemory(), Runtime.getRuntime().maxMemory());
        logger.info("Classpath: {}", System.getProperty("java.class.path"));
    }

    public void start() {
        try {
            connectToZookeeper();
            initZookeeperPaths();
            loadClusterConfiguration();
            configureWebServer();
            startWebServer();
        } catch (Exception e) {
            throw new RuntimeException("Failed to start cluster manager", e);
        }
    }

    private void loadClusterConfiguration() throws Exception {
        logger.info("Try to load cluster configuration from Zookeeper");
        Cluster.instance.load();
    }

    private void configureWebServer() throws URISyntaxException, IOException {
        ResourceConfig resourceCfg = new ResourceConfig();
        setupJsonSerialization(resourceCfg);
        resourceCfg.packages(RESOURCES_PACKAGE);
        resourceCfg.registerClasses(StemExceptionMapper.class);
        resourceCfg.registerClasses(DefaultExceptionMapper.class);

        server = createServer(webListenAddress(), resourceCfg);
        configureStaticResources(server, resourceCfg);
    }

    private URI webListenAddress() throws URISyntaxException {
        return new URI("http://" + config.web_listen_address);
    }

    private void startWebServer() throws IOException {
        server.start();
        server.getListener("grizzly").getFileCache().setEnabled(false);

        // Monkey patching to rewrite URL without nginx
        server.getListener("grizzly").getFilterChain().add(2, new HttpBaseFilter() // TODO: indexOfType
        {
            @Override
            public NextAction handleRead(FilterChainContext ctx) throws IOException {
                HttpContent message = ctx.getMessage();
                HttpRequestPacket httpHeader = (HttpRequestPacket) message.getHttpHeader();
                String uri = httpHeader.getRequestURI();
                if (uri.equals("/")) {
                    uri = "/admin/index.html";
                    httpHeader.getRequestURIRef().init(uri.getBytes(), 0, uri.getBytes().length);
                }

                return super.handleRead(ctx);
            }
        });
    }

    public void stop() {
        if (null != server) // TODO: why this may happen
            server.shutdownNow();

        Cluster.instance.destroy();
    }

    private void configureStaticResources(HttpServer server, ResourceConfig resourceCfg) {
        HttpHandler handler = getStaticHandler();
        server.getServerConfiguration().addHttpHandler(handler, "/admin", "/static");
    }

    private void connectToZookeeper() {
        logger.info("Establishing connection to Zookeeper cluster ({})...", config.zookeeper_endpoint);
        try {
            zookeeperClient = ZookeeperFactoryCached.newClient(config.zookeeper_endpoint);
            logger.info("Connected to Zookeeper endpoint {}", config.zookeeper_endpoint);
        } catch (ZooException e) {
            throw new StemException("Failed to connect to Zookeeper", e);
        } catch (Exception e) {
            throw new StemException("Error while creating Zookeeper client", e);
        }
    }

    private void initZookeeperPaths() {
        logger.info("Initialize Zookeeper z-node hierarchy");
        try {
            for (String path : ZookeeperPaths.containerNodes()) {
                zookeeperClient.createIfNotExists(path);
                logger.info("z-node '{}' initialized", path);
            }
            //client.createIfNotExists(ZooConstants.CLUSTER_DESCRIPTOR_PATH);
        } catch (ZooException e) {
            throw new StemException("Failed connect to Zookeeper", e);
        } catch (Exception e) {
            throw new StemException("Can not initialize Zookeeper paths", e);
        }
    }

    private HttpServer createServer(URI uri, ResourceConfig resourceCfg) {
        final String host = uri.getHost();
        final int port = uri.getPort();
        final HttpServer server = new HttpServer();
        final NetworkListener listener = new NetworkListener("grizzly", host, port);
        listener.getTransport().setWorkerThreadPoolConfig(adjustThreadPool());
        server.addListener(listener);

        final ServerConfiguration config = server.getServerConfiguration();
        final HttpHandler handler = ContainerFactory.createContainer(HttpHandler.class, resourceCfg);

        if (handler != null)
            config.addHttpHandler(handler, uri.getPath());

        return server;
    }

    private void setupJsonSerialization(ResourceConfig cfg) {
        cfg.registerInstances(getJsonProvider());
    }

    public static JacksonJaxbJsonProvider getJsonProvider() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        //mapper.getSerializationConfig().addMixInAnnotations(File.class, MixIn_File.class);
        JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider(mapper,
                JacksonJaxbJsonProvider.DEFAULT_ANNOTATIONS);
        provider.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, Boolean.TRUE);
        provider.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, Boolean.FALSE);
        return provider;
    }

    private ThreadPoolConfig adjustThreadPool() {
        Integer corePoolSize = 2;
        Integer maxPoolSize = 8;
        ThreadPoolConfig workerPoolConfig = ThreadPoolConfig.defaultConfig();
        workerPoolConfig.setCorePoolSize(corePoolSize);
        workerPoolConfig.setMaxPoolSize(maxPoolSize);

        workerPoolConfig.setPoolName("Worker");
        return workerPoolConfig;
    }

    public HttpHandler getStaticHandler() {
        if (null != System.getProperty("development")) {
            // User can update static content on-fly in development mode
            logger.warn("Initialize static resources in development mode");
            return new StaticHttpHandler("src/main/resources/static/");
        } else {
            logger.info("Initialize static resources in production mode");
            CLStaticByPassHttpHandler handler = new CLStaticByPassHttpHandler(this.getClass().getClassLoader(),
                    "static/");
            handler.setFileCacheEnabled(false);
            return handler;
        }
    }
}