/* * Copyright to the original author or authors. * * 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.rioproject.impl.exec; import org.rioproject.config.Constants; import org.rioproject.impl.system.OperatingSystemType; import org.rioproject.util.RioHome; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; /** * A helper to create and verify a command line that can be used to exec a service in it's own JVM. * * @author Dennis Reedy */ public final class CommandLineHelper { private static final Logger logger = LoggerFactory.getLogger(CommandLineHelper.class); private CommandLineHelper() {} /** * Generate a classpath that can be used to start a Rio service using a service starter * * @return A classpath that can be used to start a Rio service using a service starter */ public static String generateRioStarterClassPath() { StringBuilder builder = new StringBuilder(); File rioLib = new File(RioHome.get(), "lib"); builder.append(getFiles(rioLib, "rio-start", "groovy-all")); File javaHome = new File(System.getProperty("java.home", System.getenv("JAVA_HOME"))); File javaLib = new File(javaHome, "lib"); logger.info("java lib: {}", javaLib.getPath()); String toolsJar = getFiles(javaLib, "tools"); if (toolsJar.length() > 0) { builder.append(":").append(toolsJar); } else { File javaHomeParent = javaHome.getParentFile(); File javaHomeParentLib = new File(javaHomeParent, "lib"); logger.info("java lib: {}", javaHomeParentLib.getPath()); toolsJar = getFiles(javaHomeParentLib, "tools"); if (toolsJar.length() > 0) { builder.append(":").append(toolsJar); } else { logger.error("Unable to find tools.jar"); } } File rioLoggingLib = new File(rioLib, "logging"); builder.append(":").append(getFiles(rioLoggingLib)); return builder.toString(); } /** * Get the path to the java executable based on the "java.home" system property * * @return The path to the java executable */ public static String getJava() { StringBuilder jvmBuilder = new StringBuilder(); jvmBuilder.append(buildDirectory(System.getProperty("java.home", System.getenv("JAVA_HOME")), "bin")); jvmBuilder.append("java"); jvmBuilder.append(" "); return jvmBuilder.toString(); } /** * Get a class-path string * * @param classPath A {@code String} of class-path items * * @return A {@code String} that includes -cp followed by the class-path items */ public static String getClassPath(String classPath) { StringBuilder cpBuilder = new StringBuilder(); cpBuilder.append("-cp").append(" ").append(classPath).append(" "); return cpBuilder.toString(); } /** * Get the path of the starter for service-bean-exec * * @param rioHome The location of Rio * * @return The canonical path of the starter for service-bean-exec * * @throws IOException if the canonical path cannot be returned */ public static String getStarterConfig(final String rioHome) throws IOException { String startConfig = System.getProperty(Constants.START_SERVICE_BEAN_EXEC_CONFIG); if(startConfig!=null) return startConfig; StringBuilder configBuilder = new StringBuilder(); configBuilder.append(rioHome).append(File.separator).append("config").append(File.separator); configBuilder.append("start-service-bean-exec.groovy"); File f = new File(configBuilder.toString()); if(!f.exists()) throw new IOException(configBuilder.toString()+" does not exist, unable to fork service"); return f.getCanonicalPath(); } /** * Get the standard Rio JVM arguments * * @return The JVM arguments for java.rmi.server.useCodebaseOnly, java.protocol.handler.pkgs, and java.security.policy */ static String getStandardJVMArgs() { String rioHome = RioHome.get(); StringBuilder argsBuilder = new StringBuilder(); argsBuilder.append(getOption("java.protocol.handler.pkgs", "org.rioproject.url")); argsBuilder.append(" "); argsBuilder.append(getOption("java.rmi.server.useCodebaseOnly", "false")); argsBuilder.append(" "); String policyDir = buildDirectory(rioHome, "policy"); argsBuilder.append(getOption("java.security.policy", policyDir+"policy.all")); argsBuilder.append(" "); return argsBuilder.toString(); } /** * Create the options to pass to the JVM for starting a service bean in it's own JVM. * * @param normalizedServiceName The service name with no illegal characters * @param serviceBindName The name the service will use to bind to the RMI Registry * @param sRegPort the RMI Registry port (as a string) to bind to * @param declaredJVMOptions Service specific JVM options * @param logDir The directory for service logging * * @return A String suitable for to pass to the JVM for starting a service bean in it's own JVM. */ public static String createInputArgs(final String normalizedServiceName, final String serviceBindName, final String sRegPort, final String declaredJVMOptions, final String logDir) { StringBuilder extendedJVMOptions = new StringBuilder(); extendedJVMOptions.append(getStandardJVMArgs()); if(declaredJVMOptions!=null) extendedJVMOptions.append(declaredJVMOptions); extendedJVMOptions.append(" "); extendedJVMOptions.append(getOption("rio.log.dir", logDir)); extendedJVMOptions.append(" "); extendedJVMOptions.append(getOption("org.rioproject.service", normalizedServiceName)); extendedJVMOptions.append(" "); extendedJVMOptions.append(getOption(Constants.WEBSTER, System.getProperty(Constants.WEBSTER))); extendedJVMOptions.append(" "); extendedJVMOptions.append(getOption(Constants.WEBSTER_ROOTS, "\""+System.getProperty(Constants.WEBSTER_ROOTS)+"\"")); extendedJVMOptions.append(" "); extendedJVMOptions.append("-XX:HeapDumpPath=").append(logDir); StringBuilder argsBuilder = new StringBuilder(); String jvmInputArgs = JVMOptionChecker.getJVMInputArgs(extendedJVMOptions.toString()); String rioHome = buildDirectory(RioHome.get()); /* Check logging configuration */ argsBuilder.append(getLoggerConfig(jvmInputArgs, rioHome)); argsBuilder.append(jvmInputArgs); if(OperatingSystemType.isWindows()) { argsBuilder.append("-XX:OnOutOfMemoryError=\"taskkill /F /PID %%p\""); } else { argsBuilder.append("-XX:OnOutOfMemoryError=\"kill -9 %p\""); } argsBuilder.append(" "); argsBuilder.append(getOption(Constants.REGISTRY_PORT, sRegPort)); argsBuilder.append(" "); argsBuilder.append(getOption(Constants.SERVICE_BEAN_EXEC_NAME, serviceBindName)); argsBuilder.append(" "); argsBuilder.append(getOption(Constants.PROCESS_ID, VirtualMachineHelper.getID())); argsBuilder.append(" "); logger.trace("Resulting JVM Options for service [{}]: {}", serviceBindName, jvmInputArgs); return argsBuilder.toString(); } /** * Get the starter class * * @return The classname used as the service starter */ public static String getStarterClass() { return "org.rioproject.start.ServiceStarter"; } private static String getLoggerConfig(String inputArgs, String rioHome) { StringBuilder logger = new StringBuilder(); if(usingLogback()) { /* Check if logback is being used, if so set logback configuration */ if(!inputArgs.contains("logback.configurationFile")) { logger.append(getOption("logback.configurationFile", buildDirectory(rioHome, "config", "logging")+"logback.groovy")); logger.append(" "); } } else { if(!inputArgs.contains("java.util.logging.config.file")) { logger.append(getOption("java.util.logging.config.file", buildDirectory(rioHome, "config", "logging")+"rio-logging.properties")); logger.append(" "); } } return logger.toString(); } private static boolean usingLogback() { File rioLib = new File(RioHome.get(), "lib"); File rioLogging = new File(rioLib, "logging"); String loggingJars = getFiles(rioLogging); return loggingJars.contains("logback"); } private static String getOption(final String option, final String value) { StringBuilder optionBuilder = new StringBuilder(); optionBuilder.append("-D").append(option).append("=").append(value); return optionBuilder.toString(); } private static String buildDirectory(String... dirs) { StringBuilder dirBuilder = new StringBuilder(); for(String dir : dirs) { if(dirBuilder.length()>0) dirBuilder.append(File.separator); dirBuilder.append(dir); } dirBuilder.append(File.separator); return dirBuilder.toString(); } private static String getFiles(File dir, String... names) { StringBuilder builder = new StringBuilder(); File[] files = dir.listFiles(); if (files != null) { if (names.length > 0) { for (File file : files) { for (String name : names) { if (file.getName().startsWith(name)) { append(file.getPath(), builder); } } } } else { for (File file : files) { if (!file.isDirectory()) append(file.getPath(), builder); } } } return builder.toString(); } private static void append(String s, StringBuilder builder) { if (builder.length() > 0) { builder.append(":"); } builder.append(s); } }