com.ottogroup.bi.spqr.node.server.SPQRNodeServer.java Source code

Java tutorial

Introduction

Here is the source code for com.ottogroup.bi.spqr.node.server.SPQRNodeServer.java

Source

/**
 * Copyright 2015 Otto (GmbH & Co KG)
 *
 * 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.ottogroup.bi.spqr.node.server;

import io.dropwizard.Application;
import io.dropwizard.client.JerseyClientBuilder;
import io.dropwizard.setup.Environment;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import com.codahale.metrics.jvm.ClassLoadingGaugeSet;
import com.codahale.metrics.jvm.FileDescriptorRatioGauge;
import com.codahale.metrics.jvm.GarbageCollectorMetricSet;
import com.codahale.metrics.jvm.MemoryUsageGaugeSet;
import com.codahale.metrics.jvm.ThreadStatesGaugeSet;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.ottogroup.bi.spqr.exception.RemoteClientConnectionFailedException;
import com.ottogroup.bi.spqr.exception.RequiredInputMissingException;
import com.ottogroup.bi.spqr.metrics.MetricsHandler;
import com.ottogroup.bi.spqr.metrics.MetricsReporterFactory;
import com.ottogroup.bi.spqr.node.message.NodeRegistration.NodeRegistrationResponse;
import com.ottogroup.bi.spqr.node.message.NodeRegistration.NodeRegistrationState;
import com.ottogroup.bi.spqr.node.resman.SPQRResourceManagerClient;
import com.ottogroup.bi.spqr.node.resource.pipeline.MicroPipelineResource;
import com.ottogroup.bi.spqr.node.server.cfg.SPQRNodeMetricsConfiguration;
import com.ottogroup.bi.spqr.node.server.cfg.SPQRNodeServerConfiguration;
import com.ottogroup.bi.spqr.node.server.cfg.SPQRResourceManagerConfiguration;
import com.ottogroup.bi.spqr.node.server.cfg.SPQRResourceManagerConfiguration.ResourceManagerMode;
import com.ottogroup.bi.spqr.pipeline.MicroPipeline;
import com.ottogroup.bi.spqr.pipeline.MicroPipelineManager;
import com.ottogroup.bi.spqr.pipeline.metrics.MicroPipelineMetricsReporterConfiguration;
import com.ottogroup.bi.spqr.repository.ComponentDescriptor;
import com.ottogroup.bi.spqr.repository.ComponentRepository;

/**
 * Ramps up SPQR processing nodes which receive requests for {@link MicroPipeline} execution
 * @author mnxfst
 * @since Mar 13, 2015
 */
public class SPQRNodeServer extends Application<SPQRNodeServerConfiguration> {

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

    private MicroPipelineManager microPipelineManager;
    private String nodeId;
    private SPQRResourceManagerClient resourceManagerClient;

    /**
     * @see io.dropwizard.Application#run(io.dropwizard.Configuration, io.dropwizard.setup.Environment)
     */
    public void run(SPQRNodeServerConfiguration configuration, Environment environment) throws Exception {

        // initialize log4j environment using the settings provided via node configuration 
        initializeLog4j(configuration.getSpqrNode().getLog4jConfiguration());

        // check which type of resource management is requested via configuration 
        if (configuration.getResourceManagerConfiguration().getMode() == ResourceManagerMode.REMOTE) {

            // prepare for use remote resource management: fetch configuration  
            SPQRResourceManagerConfiguration resManCfg = configuration.getResourceManagerConfiguration();
            logger.info("[resource manager [mode=" + ResourceManagerMode.REMOTE + ", protocol="
                    + resManCfg.getProtocol() + ", host=" + resManCfg.getHost() + ", port=" + resManCfg.getPort()
                    + "]");

            // initialize client to use for remote communication
            this.resourceManagerClient = new SPQRResourceManagerClient(resManCfg.getProtocol(), resManCfg.getHost(),
                    resManCfg.getPort(), new JerseyClientBuilder(environment).using(configuration.getHttpClient())
                            .build("resourceManagerClient"));

            // finally: register node with remote resource manager
            this.nodeId = registerProcessingNode(configuration.getSpqrNode().getProtocol(),
                    configuration.getSpqrNode().getHost(), configuration.getSpqrNode().getServicePort(),
                    configuration.getSpqrNode().getAdminPort(), this.resourceManagerClient);
            logger.info("node successfully registered [id=" + nodeId + ", proto="
                    + configuration.getSpqrNode().getProtocol() + ", host=" + configuration.getSpqrNode().getHost()
                    + ", servicePort=" + configuration.getSpqrNode().getServicePort() + ", adminPort="
                    + configuration.getSpqrNode().getAdminPort() + "]");

        } else {
            this.nodeId = "standalone";
            this.resourceManagerClient = null;
            logger.info("resource manager [mode=" + ResourceManagerMode.LOCAL + "]");
        }

        // initialize the micro pipeline manager
        this.microPipelineManager = new MicroPipelineManager(this.nodeId,
                loadAndDeployApplicationRepository(configuration.getSpqrNode().getComponentRepositoryFolder()),
                configuration.getSpqrNode().getNumOfThreads());
        logger.info("pipeline manager initialized [threads=" + configuration.getSpqrNode().getNumOfThreads()
                + ", repo=" + configuration.getSpqrNode().getComponentRepositoryFolder() + "]");

        // register exposed resources
        environment.jersey().register(new MicroPipelineResource(this.microPipelineManager));

        // register shutdown handler
        Runtime.getRuntime().addShutdownHook(
                new SPQRNodeShutdownHandler(this.microPipelineManager, this.resourceManagerClient, nodeId));

        // register metrics handler
        MetricsHandler handler = new MetricsHandler();
        if (configuration.getSpqrNode().getSpqrMetrics() != null) {
            final SPQRNodeMetricsConfiguration metricsCfg = configuration.getSpqrNode().getSpqrMetrics();

            if (metricsCfg.getMetricsReporter() != null && !metricsCfg.getMetricsReporter().isEmpty()) {

                if (metricsCfg.isAttachClassLoadingMetricCollector()) {
                    handler.register(new ClassLoadingGaugeSet());
                }
                if (metricsCfg.isAttachFileDescriptorMetricCollector()) {
                    handler.register("fs", new FileDescriptorRatioGauge());
                }
                if (metricsCfg.isAttachGCMetricCollector()) {
                    handler.register(new GarbageCollectorMetricSet());
                }
                if (metricsCfg.isAttachMemoryUsageMetricCollector()) {
                    handler.register(new MemoryUsageGaugeSet());
                }
                if (metricsCfg.isAttachThreadStateMetricCollector()) {
                    handler.register(new ThreadStatesGaugeSet());
                }

                MetricsReporterFactory.attachReporters(handler, metricsCfg.getMetricsReporter());

                logger.info("metrics[classloader=" + metricsCfg.isAttachClassLoadingMetricCollector()
                        + ",fileSystem=" + metricsCfg.isAttachFileDescriptorMetricCollector() + ",gc="
                        + metricsCfg.isAttachGCMetricCollector() + ",memory="
                        + metricsCfg.isAttachMemoryUsageMetricCollector() + ",threadState="
                        + metricsCfg.isAttachThreadStateMetricCollector() + "]");

                StringBuffer buf = new StringBuffer();
                for (final MicroPipelineMetricsReporterConfiguration mr : metricsCfg.getMetricsReporter()) {
                    buf.append("(id=").append(mr.getId()).append(", type=").append(mr.getType()).append(")");
                }
                logger.info("metricReporters[" + buf.toString() + "]");
            }
        } else {
            logger.info("no metrics and metric reporters configured for processing node '" + nodeId + "'");
        }
    }

    /**
     * Initializes the log4j framework by pointing it to the configuration file referenced by parameter
     * @param log4jConfigurationFile
     */
    protected void initializeLog4j(final String log4jConfigurationFile) {
        String log4jPropertiesFile = StringUtils.lowerCase(StringUtils.trim(log4jConfigurationFile));
        if (StringUtils.isNoneBlank(log4jConfigurationFile)) {
            File log4jFile = new File(log4jPropertiesFile);
            if (log4jFile.isFile()) {
                try {
                    PropertyConfigurator.configure(new FileInputStream(log4jFile));
                } catch (FileNotFoundException e) {
                    System.out.println("No log4j configuration found at '" + log4jConfigurationFile + "'");
                }
            } else {
                System.out.println("No log4j configuration found at '" + log4jConfigurationFile + "'");
            }
        } else {
            System.out.println("No log4j configuration file provided");
        }
    }

    /**
     * Registers this processing node with the remote resource manager
     * @param protocol
     * @param host
     * @param servicePort
     * @param adminPort
     * @return
     * @throws RequiredInputMissingException
     * @throws RemoteClientConnectionFailedException
     * @throws IOException
     */
    protected String registerProcessingNode(final String protocol, final String host, final int servicePort,
            final int adminPort, final SPQRResourceManagerClient client)
            throws RequiredInputMissingException, RemoteClientConnectionFailedException, IOException {

        ///////////////////////////////////////////////////////////////////////////
        // validate provided input
        if (StringUtils.isBlank(protocol))
            throw new RequiredInputMissingException(
                    "Missing require communication protocol used by resource manager for accessing this node. See 'protocol' property in config file.");
        if (StringUtils.isBlank(host))
            throw new RequiredInputMissingException(
                    "Missing required host name used by resource manager for accessing this node. See 'host' property in config file.");
        if (servicePort < 1)
            throw new RequiredInputMissingException(
                    "Missing required service port used by resource manager for accessing this node. See 'servicePort' property in config file.");
        if (adminPort < 1)
            throw new RequiredInputMissingException(
                    "Missing required admin port used by resource manager for accessing this node. See 'adminPort' property in config file.");
        //
        ///////////////////////////////////////////////////////////////////////////

        final NodeRegistrationResponse registrationResponse = client.registerNode(protocol, host, servicePort,
                adminPort);
        if (registrationResponse == null)
            throw new RemoteClientConnectionFailedException(
                    "Failed to connect with resource manager. Error: no response received");
        if (registrationResponse.getState() != NodeRegistrationState.OK)
            throw new RemoteClientConnectionFailedException(
                    "Failed to register processing node with resource manage. Reason: "
                            + registrationResponse.getState() + ". Message: " + registrationResponse.getMessage());

        return registrationResponse.getId();
    }

    /**
     * Reads contents from {@link ApplicationRepositoryConfiguration referenced repository} and deploys them to given
     * {@link ActorRef deployment receiver}.
     * @param cfg
     * @param jarDeploymentReceiverRef
     * @throws RequiredInputMissingException
     * @throws JsonParseException
     * @throws JsonMappingException
     * @throws IOException
     */
    protected ComponentRepository loadAndDeployApplicationRepository(final String repositoryPath)
            throws RequiredInputMissingException, JsonParseException, JsonMappingException, IOException {

        ////////////////////////////////////////////////////////////////////////////
        // validate provided input and ensure that folder exists
        if (StringUtils.isBlank(repositoryPath))
            throw new RequiredInputMissingException("Missing required input for parameter 'repositoryPath'");

        File repositoryFolder = new File(StringUtils.trim(repositoryPath));
        if (repositoryFolder == null || !repositoryFolder.isDirectory())
            throw new RequiredInputMissingException(
                    "No repository found at provided folder '" + repositoryPath + "'");
        //
        ////////////////////////////////////////////////////////////////////////////

        ////////////////////////////////////////////////////////////////////////////

        final ComponentRepository library = new ComponentRepository();

        ////////////////////////////////////////////////////////////////////////////
        // find component repositories below repository folder 
        File[] repoFolders = repositoryFolder.listFiles();
        if (repoFolders == null || repoFolders.length < 1)
            return library;
        //
        ////////////////////////////////////////////////////////////////////////////

        ////////////////////////////////////////////////////////////////////////////
        // for each entry check if it is a folder and pass it on the library
        int folderCount = 0;
        int componentsCount = 0;
        for (File folder : repoFolders) {
            if (folder.isDirectory()) {
                try {
                    logger.info("Processing folder '" + folder.getAbsolutePath() + "'");
                    Map<String, ComponentDescriptor> descriptors = library
                            .addComponentFolder(folder.getAbsolutePath());
                    componentsCount = componentsCount + descriptors.size();
                    folderCount++;
                } catch (Exception e) {
                    logger.error("Failed to add folder '" + folder.getAbsolutePath()
                            + "' to component repository. Error: " + e.getMessage());
                }
            }
        }
        //
        ////////////////////////////////////////////////////////////////////////////

        logger.info("Components deployment finished [repo=" + repositoryPath + ", componentFolders=" + folderCount
                + ", componets=" + componentsCount + "]");
        return library;
    }

    /**
     * Ramps up the node server instance
     * @param args
     */
    public static void main(String[] args) throws Exception {
        new SPQRNodeServer().run(args);
    }

}