com.opengamma.engine.calcnode.CalculationNodeProcess.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.engine.calcnode.CalculationNodeProcess.java

Source

/**
 * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
 * 
 * Please see distribution for license.
 */
package com.opengamma.engine.calcnode;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.support.GenericApplicationContext;
import org.xml.sax.InputSource;

/**
 * Starts a calculation node and joins a job dispatcher.
 */
public final class CalculationNodeProcess {

    private static final Logger s_logger = LoggerFactory.getLogger(CalculationNodeProcess.class);

    private static final int CONFIGURATION_RETRY = 3;
    private static final int CONFIGURATION_POLL_PERIOD = 5;

    private static HttpClient s_httpClient;

    /**
     * A job item execution watchdog that can terminate the host process if one (or all) calculation threads hang.
     */
    public static class JobItemExecutionWatchdog extends MaximumJobItemExecutionWatchdog {

        public JobItemExecutionWatchdog() {
            setTimeoutAction(new Action() {
                @Override
                public void jobItemExecutionLimitExceeded(final CalculationJobItem jobItem, final Thread thread) {
                    s_logger.error("Starting graceful shutdown after thread {} hung on {}", thread, jobItem);
                    startGracefulShutdown();
                    if (!areThreadsAlive()) {
                        s_logger.error("Halting remote calc process", thread, jobItem);
                        System.exit(0);
                    }
                }
            });
        }

    }

    private CalculationNodeProcess() {
    }

    private static void sleep(int period) {
        try {
            Thread.sleep(1000 * period);
        } catch (InterruptedException e) {
        }
    }

    private static String getConfigurationXml(final String url) {
        if (s_httpClient == null) {
            s_httpClient = new DefaultHttpClient();
        }
        s_logger.debug("Fetching {}", url);
        final HttpResponse resp;
        try {
            resp = s_httpClient.execute(new HttpGet(url));
        } catch (Exception e) {
            s_logger.warn("Error fetching {} - {}", url, e.getMessage());
            return null;
        }
        s_logger.debug("HTTP result {}", resp.getStatusLine());
        if (resp.getStatusLine().getStatusCode() != 200) {
            s_logger.warn("No configuration available (HTTP {})", resp.getStatusLine().getStatusCode());
            return null;
        }
        try {
            final Reader in = new InputStreamReader(resp.getEntity().getContent());
            final StringBuilder sb = new StringBuilder();
            final char[] buf = new char[1024];
            int i = in.read(buf);
            while (i > 0) {
                sb.append(buf, 0, i);
                i = in.read(buf);
            }
            s_logger.debug("Configuration document received - {} characters", sb.length());
            return sb.toString();
        } catch (IOException e) {
            s_logger.warn("Error retrieving response from {} - {}", url, e.getMessage());
            return null;
        }
    }

    private static boolean startContext(final String configuration) {
        try {
            final GenericApplicationContext context = new GenericApplicationContext();
            final XmlBeanDefinitionReader beanReader = new XmlBeanDefinitionReader(context);
            beanReader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_NONE);
            s_logger.debug("Loading configuration");
            beanReader.loadBeanDefinitions(new InputSource(new StringReader(configuration)));
            s_logger.debug("Instantiating beans");
            context.refresh();
            s_logger.debug("Starting node");
            context.start();
            return true;
        } catch (RuntimeException e) {
            s_logger.warn("Spring initialisation error", e);
            return false;
        }
    }

    private static String getBaseUrl(final String url) {
        final int slash = url.lastIndexOf('/');
        return url.substring(0, slash + 1);
    }

    private static void setConnectionDefaults(final String url) {
        try {
            final URI uri = new URI(url);
            if (uri.getHost() != null) {
                System.setProperty("opengamma.engine.calcnode.host", uri.getHost());
            }
            if (uri.getPort() != -1) {
                System.setProperty("opengamma.engine.calcnode.port", Integer.toString(uri.getPort()));
            }
        } catch (URISyntaxException e) {
            s_logger.warn("Couldn't set connection defaults", e);
        }
    }

    private static void startGracefulShutdown() {
        s_logger.error("TODO: [PLAT-2351] start graceful shutdown");
        // TODO: [PLAT-2351] stop accepting jobs and allow current ones to run to completion 
    }

    /**
     * Starts a calculation node, retrieving configuration from the given URL
     * 
     * @param url The URL to use
     */
    public static void main(final String url) {
        s_logger.info("Using configuration URL {}", url);
        String configuration = getConfigurationXml(url);
        if (configuration == null) {
            for (int i = 0; i < CONFIGURATION_RETRY; i++) {
                s_logger.warn("Failed to retrieve configuration - retrying");
                sleep(1);
                configuration = getConfigurationXml(url);
                if (configuration != null) {
                    break;
                }
            }
            if (configuration == null) {
                s_logger.error("No response from {}", url);
                System.exit(1);
            }
        }
        // Create and start the spring config
        System.setProperty("opengamma.engine.calcnode.baseurl", getBaseUrl(url));
        setConnectionDefaults(url);
        if (startContext(configuration)) {
            s_logger.info("Calculation node started");
        } else {
            s_logger.error("Couldn't start calculation node");
            System.exit(1);
        }
        // Terminate if the configuration changes - the O/S will restart us
        int retry = 0;
        do {
            sleep(CONFIGURATION_POLL_PERIOD);
            final String newConfiguration = getConfigurationXml(url);
            if (newConfiguration != null) {
                if (!configuration.equals(newConfiguration)) {
                    s_logger.info("Configuration at {} has changed", url);
                    System.exit(0);
                }
                retry = 0;
            } else {
                switch (++retry) {
                case 1:
                    s_logger.debug("No response from configuration at {}", url);
                    break;
                case 2:
                    s_logger.info("No response from configuration at {}", url);
                    break;
                case 3:
                    s_logger.warn("No response from configuration at {}", url);
                    break;
                case 4:
                    s_logger.error("No response from configuration at {}", url);
                    startGracefulShutdown();
                    // TODO: wait for the graceful shutdown to complete (i.e. node goes idle)
                    System.exit(0);
                    break;
                }
            }
            s_logger.info("Free memory = {}Mb, total memory = {}Mb",
                    (double) Runtime.getRuntime().freeMemory() / (1024d * 1024d),
                    (double) Runtime.getRuntime().totalMemory() / (1024d * 1024d));
        } while (true);
    }

    /**
     * Starts a calculation node
     * 
     * @param args the arguments, should contain one parameter - the configuration URL to use
     */
    public static void main(String[] args) { // CSIGNORE
        if (args.length != 1) {
            s_logger.error("Configuration URL not specified");
            System.exit(1);
        }
        main(args[0]);
    }

}