All Downloads are FREE. Search and download functionalities are using the official Maven repository.

oracle.kv.impl.sna.ManagedService Maven / Gradle / Ivy

Go to download

NoSQL Database Server - supplies build and runtime support for the server (store) side of the Oracle NoSQL Database.

The newest version!
/*-
 * Copyright (C) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This file was distributed by Oracle as part of a version of Oracle NoSQL
 * Database made available at:
 *
 * http://www.oracle.com/technetwork/database/database-technologies/nosqldb/downloads/index.html
 *
 * Please see the LICENSE file included in the top-level directory of the
 * appropriate version of Oracle NoSQL Database for a copy of the license and
 * additional information.
 */

package oracle.kv.impl.sna;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import oracle.kv.impl.admin.param.GlobalParams;
import oracle.kv.impl.admin.param.RepNodeParams;
import oracle.kv.impl.admin.param.SecurityParams;
import oracle.kv.impl.admin.param.StorageNodeParams;
import oracle.kv.impl.param.LoadParameters;
import oracle.kv.impl.param.Parameter;
import oracle.kv.impl.param.ParameterMap;
import oracle.kv.impl.param.ParameterState;
import oracle.kv.impl.topo.ResourceId;
import oracle.kv.impl.util.CommandParser;
import oracle.kv.impl.util.ConfigUtils;
import oracle.kv.impl.util.FileNames;
import oracle.kv.impl.util.server.LoggerUtils;

/**
 * A class to implement a service managed by the Storage Node Agent.  It has a
 * main() and expects specific arguments which are used by the SNA to identify
 * the process as one it manages.  It's main() method should be called in the
 * execution context of the service to be started, which is either a new
 * process or thread.  See the usage() method for usage.
 *
 * This class can be used in either the context of the Storage Node Agent or
 * the target service.  In the SNA it provides a consistent way to create and
 * pass required arguments to the service.
 *
 * It has a convenience method, createArgs(), which can be used to construct
 * the arguments to main() from the current state of the object.
 *
 * TODO: absolute paths for jps, kill, taskkill
 */
public abstract class ManagedService {

    /**
     * The service started by the main method, or null if no main service has
     * been started.
     */
    private static volatile ManagedService mainService;

    /**
     * These are protected so they can be accessed by sub-classes.  They are
     * "final" for the most part but not set as such to allow
     * ManagedBootstrapAdmin to reset them if necessary.
     */
    protected File kvRootDir;
    protected File kvSecDir;
    protected File kvSNDir;
    protected String kvName;
    protected String serviceName;
    final protected String serviceClass;
    protected ParameterMap params;
    protected Logger logger;
    protected StringBuilder startupBuffer;
    protected static boolean usingThreads;

    public static final String REP_NODE_NAME = "RepNode";
    public static final String ADMIN_NAME = "Admin";
    public static final String BOOTSTRAP_ADMIN_NAME = "BootstrapAdmin";
    public static final String ARB_NODE_NAME = "ArbNode";
    public static final String LOG_CONFIG_PREFIX = "config.";

    /* Flags used for exec args */
    public static final String ROOT_FLAG = CommandParser.ROOT_FLAG;
    public static final String SECDIR_FLAG = "-secdir";
    public static final String STORE_FLAG = CommandParser.STORE_FLAG;
    public static final String CLASS_FLAG = "-class";
    public static final String SERVICE_FLAG = "-service";
    public static final String THREADS_FLAG = StorageNodeAgent.THREADS_FLAG;
    public static final String STARTUP_OK = "ManagedServiceStarted";

    private static final String JAVA_VERSION_KEY = "java.version";

    private static final String IBM_VENDOR_PREFIX = "IBM";

    public ManagedService(File kvRootDir,
                          File kvSecDir,
                          File kvSNDir,
                          String kvName,
                          String serviceClass,
                          String serviceName,
                          ParameterMap params) {
        this.kvRootDir = kvRootDir;
        this.kvSecDir = kvSecDir;
        this.kvSNDir = kvSNDir;
        this.kvName = kvName;
        this.serviceClass = serviceClass;
        this.serviceName = serviceName;
        this.params = params;
        logger = null;
    }

    /**
     * This method must be run in the execution context of the service.
     */
    public abstract void start(boolean usingThreads1);

    /**
     * Whether a request has been made to stop this service.
     *
     * @return whether a stop has been requested
     */
    public abstract boolean stopRequested();

    /* Log InetAddress dns properties. */
    protected void logInetAddressProperties() {
        if (logger != null) {
            logger.info
                ("JVM java.net.InetAddress properties: cache.ttl=" +
                 java.security.Security.getProperty
                 ("networkaddress.cache.ttl") +
                 " cache.negative.ttl=" +
                 java.security.Security.getProperty
                 ("networkaddress.cache.negative.ttl"));
        }
    }

    public String getServiceName() {
        return serviceName;
    }

    public String getKvName() {
        return kvName;
    }

    public Logger getLogger() {
        return logger;
    }

    public String getJVMArgs() {
        if (params != null) {
            return params.get(ParameterState.JVM_MISC).asString();
        }
        return null;
    }

    /**
     * Returns the environment variables and values associated with the
     * service process.
     */
    public Map getEnvironment() {
        return null;
    }

    public String getLoggingConfig() {
        if (params != null) {
            return params.get(ParameterState.JVM_LOGGING).asString();
        }
        return null;
    }

    /**
     * Returns the JVM parameter string used to configure GC logging
     */
    String getGCLoggingArgs(final Parameter gcLogFiles,
                            final Parameter gcLogFileSize,
                            final String resourceName,
                            final RepNodeParams rnp) {

        final int javaVersion = getJavaMajorVersion();
        if (javaVersion < 7) {

            /* Versions before 1.7 do not support log rotation. */
            return "";
        }

        final String jvmVendor = System.getProperty("java.vendor");
        if (jvmVendor != null && jvmVendor.startsWith(IBM_VENDOR_PREFIX)) {
            /* Do not use log rotation for IBM java vendor. */
            return "";
        }

        /*
         * Support for rgx-rny.gc logs in RN log directory
         */
        final String gcFileName;
        if (rnp != null && rnp.getLogDirectoryFile()!= null) {
            gcFileName =
                FileNames.getRNGCLoggingDir
                    (rnp.getLogDirectoryFile(), resourceName).toString() +
                    ".gc";
        } else {
            gcFileName =
                new File(FileNames.getLoggingDir(kvRootDir, kvName),
                    resourceName).toString() + ".gc";
        }

        if (javaVersion >= 9) {
            return " -Xlog" +

                /*
                 * These "tag" items control what is logged:
                 *   all=warning - All entries at level "warning" or higher
                 *   gc*=info - Detailed GC information, equivalent to
                 *     -XX:+PrintGCDetails
                 *   safepoint=info - Application times and stop times.  The
                 *     stop times are equivalent to
                 *     -XX:+PrintGCApplicationStoppedTime; there is no way to
                 *     filter out the application times.
                 */
                ":all=warning,gc*=info,safepoint=info" +

                /*
                 * Using -Xlog with "file" and "filecount" makes sure to rotate
                 * existing log files created by earlier JVM invocations with
                 * the same log file name, so earlier log files will be
                 * preserved across JVM restarts. [#25585]
                 */
                ":file=" + gcFileName +
                ":utctime" +
                ":filecount=" + gcLogFiles.asString() +
                ",filesize=" + gcLogFileSize.asString() +
                " ";
        }

        return " -XX:+PrintGCDetails -XX:+PrintGCDateStamps "
            + " -XX:+PrintGCApplicationStoppedTime"
            + " -XX:+UseGCLogFileRotation"
            + " -XX:NumberOfGCLogFiles=" + gcLogFiles.asString()
            + " -XX:GCLogFileSize=" + gcLogFileSize.asString()
            + " -Xloggc:" + gcFileName + " " ;
    }

    /**
     * Returns the default args to be associated with the JVM, given the
     * specific args supplied for the service. Knowledge of the specific
     * args is used to customize the default args so that there are not
     * conflicting arguments, eg. use of both the CMS and G1 GC.
     *
     * @param overrideJvmArgs the overriding jvm args
     */
    public String getDefaultJavaArgs(String overrideJvmArgs) {
        return null;
    }

    /**
     * Returns the major version number of the current Java VM as an integer,
     * returning -1 if the version cannot be determined.  Handles three version
     * styles:
     * 
    *
  • 1.8.0_151 returns 8 (for versions 1.8 and earlier) *
  • 9.0.4 returns 9 (for release versions 9 and later) *
  • 10-ea returns 10 (for early access versions) *
*/ public static int getJavaMajorVersion() { return getJavaMajorVersion(System.getProperty(JAVA_VERSION_KEY)); } /** Provide version as an argument, for testing. */ static int getJavaMajorVersion(String version) { if (version != null) { if (version.endsWith("-ea")) { /* major-ea */ final String v1 = version.substring(0, version.length() - 3); try { return Integer.parseInt(v1); } catch (NumberFormatException e) { } } else { final int dot1 = version.indexOf('.'); if (dot1 > 0) { final String v1 = version.substring(0, dot1); if ("1".equals(v1)) { /* 1.major.xxx */ final int dot2 = version.indexOf('.', dot1 + 1); if (dot2 > 0) { final String v2 = version.substring(dot1 + 1, dot2); try { return Integer.parseInt(v2); } catch (NumberFormatException e) { } } } else { /* major.xxx */ try { return Integer.parseInt(v1); } catch (NumberFormatException e) { } } } } } /* Bad Java version */ return -1; } /** * Returns the service started by the {@link #main} method, or null if no * main service has been started. If the main method is called more than * once, say because multiple services are being starting using multiple * threads in the same process, then the most recently started service will * be returned. * * @return the service or null */ public static ManagedService getMainService() { return mainService; } public synchronized void setStartupBuffer(StringBuilder buf) { startupBuffer = buf; } public synchronized StringBuilder getStartupBuffer() { return startupBuffer; } public static void setUsingThreads(boolean value) { usingThreads = value; } public abstract ResourceId getResourceId(); public abstract void resetHandles(); public abstract void resetParameters(boolean inTarget); /** * Does this service need to reset its command line argument on restart? * Default to no. */ public boolean resetOnRestart() { return false; } /** * Start logging, which is only done in the execution context. */ protected void startLogger(Class cl, ResourceId rid, LoadParameters lp) { GlobalParams globalParams = new GlobalParams(lp.getMapByType(ParameterState.GLOBAL_TYPE)); StorageNodeParams storageNodeParams = new StorageNodeParams (lp.getMapByType(ParameterState.SNA_TYPE)); logger = LoggerUtils.getLogger(cl, rid.toString(), rid, globalParams, storageNodeParams); /* Log the JVM command line if using processes */ if (!usingThreads) { RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean(); logger.info("Starting service process: " + rid.toString() + ", Java command line arguments: " + runtimeBean.getInputArguments()); } } /** * Start RN logging, which is only done in the execution context. */ protected void startRNLogger(Class cl, RepNodeParams rnp, ResourceId rid, LoadParameters lp) { GlobalParams globalParams = new GlobalParams(lp.getMapByType(ParameterState.GLOBAL_TYPE)); StorageNodeParams storageNodeParams = new StorageNodeParams(lp.getMapByType(ParameterState.SNA_TYPE)); RepNodeParams repNodeParams = rnp; logger = LoggerUtils.getLogger(cl, rid.toString(), rid, globalParams, storageNodeParams, repNodeParams); /* Log the JVM command line if using processes */ if (!usingThreads) { RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean(); logger.info("Starting service process: " + rid.toString() + ", Java command line arguments: " + runtimeBean.getInputArguments()); } } /** * Get the PropertySheet for this instance. This is called in the * execution context. */ protected LoadParameters getParameters() { File kvConfigPath = new File(kvSNDir, FileNames.SNA_CONFIG_FILE); LoadParameters lp = LoadParameters.getParameters(kvConfigPath, logger); return lp; } /** * Get the SecurityParams for this instance. This is called in the * execution context. */ protected SecurityParams getSecurityParameters() { if (kvSecDir == null) { return SecurityParams.makeDefault(); } File securityConfigPath = new File(kvSecDir, FileNames.SECURITY_CONFIG_FILE); if (!securityConfigPath.exists()) { throw new IllegalStateException( "The security configuraton file " + securityConfigPath + " does not exist."); } LoadParameters lp = LoadParameters.getParameters(securityConfigPath, logger); SecurityParams sp = new SecurityParams(lp, securityConfigPath); return sp; } /** * Create a logging config file based on the properties string. Exceptions * in the function are logged but are not fatal. */ public String createLoggingConfigFile(String properties) { String pathToFile = null; try { /** * Logging config files are per-service and kept in the store's log * directory, named as "config.servicename." */ File logConfigFile = new File(FileNames.getLoggingDir(kvRootDir, kvName), LOG_CONFIG_PREFIX + serviceName); /** * Save properties to the file. */ Properties props = new Properties(); String header = "Logging properties for " + serviceName + ". DO NOT EDIT!"; props.load(ConfigUtils.getPropertiesStream(properties)); try (FileOutputStream fos = new FileOutputStream(logConfigFile)) { props.store(fos, header); } pathToFile = logConfigFile.toString(); } catch (Exception e) { if (logger != null) { logger.warning("Could not configure logging config file for " + serviceName + ": " + e.getMessage()); } } return pathToFile; } /** * Kill a process. This is somewhat complicated by the need to handle * Windows as well. TODO: consider absolute paths to kill, taskkill. */ public static void killProcess(Integer pid) { boolean isWindows; String os = System.getProperty("os.name"); if (os.indexOf("Windows") != -1) { isWindows = true; } else { isWindows = false; } String[] command; int i = 0; if (isWindows) { command = new String[4]; command[i++] = "taskkill"; command[i++] = "/f"; command[i++] = "/pid"; } else { command = new String[3]; command[i++] = "kill"; command[i++] = "-9"; } command[i] = pid.toString(); try { Process p = Runtime.getRuntime().exec(command); p.waitFor(); } catch (Exception ignored) { } } /** * Kill any processes that match the pattern. * * @param storeName the KVStore name to find * * @param serviceName if non-null the service name to find * * @param logger if non-null, log the kills * */ public static void killManagedProcesses(String storeName, String serviceName, Logger logger) { List list = findManagedProcesses(storeName, serviceName, logger); for (Integer pid : list) { if (logger != null) { logger.info("Killing managed process " + pid + " for " + "store, serviceName: "+ storeName + ", " + serviceName); } killProcess(pid); } } protected static void killManagedProcesses(String className, String storeName, String serviceName, Logger logger) { List list = findManagedProcesses(className, storeName, serviceName, logger); for (Integer pid : list) { if (logger != null) { logger.info("Killing managed process " + pid + " matching " + "these fields: "+ storeName + ", " + serviceName); } killProcess(pid); } } /** * Return a list of process IDs for processes that are managed by * a particular SNA. * * @param storeName the KVStore name to find (can be null) * * @param serviceName if non-null the service name to find * * @param logger if non-null, log failure information * * @return list of process ids that match the parameters */ public static List findManagedProcesses(String storeName, String serviceName, Logger logger) { return findManagedProcesses("ManagedService", storeName, serviceName, logger); } /** * "Internal" version of findManagedProcesses that also takes the class * name. */ protected static List findManagedProcesses(String className, String storeName, String serviceName, Logger logger) { List list = new ArrayList(); try { List command = new ArrayList(); command.add("jps"); command.add("-m"); ProcessBuilder builder = new ProcessBuilder(command); builder.redirectErrorStream(true); Process process = builder.start(); BufferedReader reader = new BufferedReader (new InputStreamReader(process.getInputStream())); for (String line = reader.readLine(); line != null; line = reader.readLine()) { if ((line.indexOf(className) >= 0) && (storeName == null || line.indexOf(storeName) >= 0) && (serviceName == null || line.indexOf(serviceName) >= 0) && /* exclude -shutdown -- it is probably this process */ line.indexOf(StorageNodeAgent.SHUTDOWN_FLAG) < 0) { String[] args = line.split(" "); list.add(Integer.valueOf(args[0])); } } } catch (Exception e) { if (logger != null) { logger.info ("findManagedProcesses exception: " + e.getMessage()); } } return list; } public static void usage() throws IllegalArgumentException { /** * At this point there is no log file. Stderr will be captured by the * managing SNA process and put into its log. */ System.err.println("Usage: ...ManagedService " + ROOT_FLAG + " " + STORE_FLAG + " " + CLASS_FLAG + " " + SERVICE_FLAG + " "); throw new IllegalArgumentException ("Could not parse ManagedService args"); } /** * Construct the arguments expected by this class from its * state. This called when using threads to create the service. */ public String[] createArgs() { String[] args; final int kvNameCount = (null == kvName) ? 0 : 2; final int secDirCount = (null == kvSecDir) ? 0 : 2; args = new String[11 + kvNameCount + secDirCount]; int i = 0; args[i++] = ROOT_FLAG; args[i++] = kvSNDir.toString(); if (kvSecDir != null) { args[i++] = SECDIR_FLAG; args[i++] = kvSecDir.toString(); } if (kvName != null) { args[i++] = STORE_FLAG; args[i++] = kvName; } args[i++] = CLASS_FLAG; args[i++] = serviceClass; args[i++] = SERVICE_FLAG; args[i++] = serviceName; args[i++] = THREADS_FLAG; additionalArgs(args, i); return args; } public List addExecArgs(List command) { /** * Use ManagedService here rather than getClass().getName() so that * findManagedProcesses() can work more simply. */ command.add("oracle.kv.impl.sna.ManagedService"); command.add(ROOT_FLAG); command.add(kvSNDir.toString()); if (kvSecDir != null) { command.add(SECDIR_FLAG); command.add(kvSecDir.toString()); } if (kvName != null) { command.add(STORE_FLAG); command.add(kvName); } command.add(CLASS_FLAG); command.add(serviceClass); command.add(SERVICE_FLAG); command.add(serviceName); additionalExecArgs(command); return command; } /** * Allow subclasses to add additional arguments. */ public void additionalExecArgs (@SuppressWarnings("unused") List command) { } public int additionalArgs(@SuppressWarnings("unused") String[] args, int index) { return index; } public static void main(String[] args) { String kvSecDir = null; String kvSNDir = null; String kvName = null; String serviceClass = null; String serviceName = null; String bootstrapConfigFile = null; boolean usingThreads1 = false; int argc = 0; int nArgs = args.length; while (argc < nArgs) { String thisArg = args[argc++]; if (thisArg == null) { continue; } if (thisArg.equals(ROOT_FLAG)) { if (argc < nArgs) { kvSNDir = args[argc++]; } else { usage(); } } else if (thisArg.equals(SECDIR_FLAG)) { if (argc < nArgs) { kvSecDir = args[argc++]; } else { usage(); } } else if (thisArg.equals(StorageNodeAgent.CONFIG_FLAG)) { if (argc < nArgs) { bootstrapConfigFile = args[argc++]; } else { usage(); } } else if (thisArg.equals(STORE_FLAG)) { if (argc < nArgs) { kvName = args[argc++]; } else { usage(); } } else if (thisArg.equals(CLASS_FLAG)) { if (argc < nArgs) { serviceClass = args[argc++]; } else { usage(); } } else if (thisArg.equals(SERVICE_FLAG)) { if (argc < nArgs) { serviceName = args[argc++]; } else { usage(); } } else if (thisArg.equals(THREADS_FLAG)) { usingThreads1 = true; } else { usage(); } } /** * kvName can be null in the case of a bootstrap admin instance. */ if (kvSNDir == null || serviceClass == null || serviceName == null ) { usage(); } /** * Set up dns cache ttl parameter for java.net.InetAddress; */ String cacheTTL = System.getProperty ("kvdns.networkaddress.cache.ttl"); if (cacheTTL != null) { java.security.Security.setProperty ("networkaddress.cache.ttl" , cacheTTL); java.security.Security.setProperty ("networkaddress.cache.negative.ttl", cacheTTL); } ManagedService.setUsingThreads(usingThreads1); ManagedService ms = null; try { if (REP_NODE_NAME.equals(serviceClass)) { ms = new ManagedRepNode(kvSecDir, kvSNDir, kvName, serviceClass, serviceName); } else if (ADMIN_NAME.equals(serviceClass) && serviceName != null) { if (serviceName.indexOf(BOOTSTRAP_ADMIN_NAME) >= 0) { if (bootstrapConfigFile == null) { usage(); } ms = new ManagedBootstrapAdmin(kvSNDir, kvSecDir, bootstrapConfigFile, serviceName); } else { ms = new ManagedAdmin(kvSecDir, kvSNDir, kvName, serviceClass, serviceName); } } else if (ARB_NODE_NAME.equals(serviceClass)) { ms = new ManagedArbNode(kvSecDir, kvSNDir, kvName, serviceClass, serviceName); } else { throw new IllegalArgumentException ("Unknown service name " + serviceClass); } ms.start(usingThreads1); /* * This tells the SNA that the service got this far. This allows * the SNA to isolate JVM and service startup problems from * "runtime" issues. */ if (!usingThreads1) { System.err.println(STARTUP_OK + ": " + serviceName); } /* Note the service after it is started */ mainService = ms; } catch (Exception e) { String msg = "Exception creating service " + serviceName + ": " + e.getMessage() + ": " + LoggerUtils.getStackTrace(e); if (ms != null && ms.getLogger() != null) { ms.getLogger().log( ms.stopRequested() ? Level.INFO : Level.SEVERE, msg); } else { System.err.println(msg); } /** * Also print to System.err in the event the logger isn't yet * initialized. */ System.err.println(msg); LoggerUtils.closeAllHandlers(); } System.err.flush(); } /** * Build a File from a fileName, if not null. */ static File nullableFile(String fileName) { return (fileName == null) ? null : new File(fileName); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy