|
@@ -0,0 +1,1044 @@
|
|
|
|
+/*
|
|
|
|
+ * Licensed to the Apache Software Foundation (ASF) under one
|
|
|
|
+ * or more contributor license agreements. See the NOTICE file
|
|
|
|
+ * distributed with this work for additional information
|
|
|
|
+ * regarding copyright ownership. The ASF licenses this file
|
|
|
|
+ * to you 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.apache.hadoop.service.launcher;
|
|
|
|
+
|
|
|
|
+import java.io.File;
|
|
|
|
+import java.io.IOException;
|
|
|
|
+import java.net.URL;
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
+import java.util.Arrays;
|
|
|
|
+import java.util.List;
|
|
|
|
+
|
|
|
|
+import com.google.common.annotations.VisibleForTesting;
|
|
|
|
+import com.google.common.base.Preconditions;
|
|
|
|
+import org.slf4j.Logger;
|
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
|
+
|
|
|
|
+import org.apache.commons.cli.CommandLine;
|
|
|
|
+import org.apache.commons.cli.Option;
|
|
|
|
+import org.apache.commons.cli.OptionBuilder;
|
|
|
|
+import org.apache.commons.cli.Options;
|
|
|
|
+import org.apache.hadoop.conf.Configuration;
|
|
|
|
+import org.apache.hadoop.net.NetUtils;
|
|
|
|
+import org.apache.hadoop.service.Service;
|
|
|
|
+import org.apache.hadoop.util.ExitCodeProvider;
|
|
|
|
+import org.apache.hadoop.util.ExitUtil;
|
|
|
|
+import org.apache.hadoop.util.GenericOptionsParser;
|
|
|
|
+import org.apache.hadoop.util.StringUtils;
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * A class to launch any YARN service by name.
|
|
|
|
+ *
|
|
|
|
+ * It's designed to be subclassed for custom entry points.
|
|
|
|
+ *
|
|
|
|
+ * Workflow:
|
|
|
|
+ * <ol>
|
|
|
|
+ * <li>An instance of the class is created. It must be of the type
|
|
|
|
+ * {@link Service}</li>
|
|
|
|
+ * <li>If it implements
|
|
|
|
+ * {@link LaunchableService#bindArgs(Configuration, List)},
|
|
|
|
+ * it is given the binding args off the CLI after all general configuration
|
|
|
|
+ * arguments have been stripped.</li>
|
|
|
|
+ * <li>Its {@link Service#init(Configuration)} and {@link Service#start()}
|
|
|
|
+ * methods are called.</li>
|
|
|
|
+ * <li>If it implements it, {@link LaunchableService#execute()}
|
|
|
|
+ * is called and its return code used as the exit code.</li>
|
|
|
|
+ * <li>Otherwise: it waits for the service to stop, assuming that the
|
|
|
|
+ * {@link Service#start()} method spawns one or more thread
|
|
|
|
+ * to perform work</li>
|
|
|
|
+ * <li>If any exception is raised and provides an exit code,
|
|
|
|
+ * that is, it implements {@link ExitCodeProvider},
|
|
|
|
+ * the return value of {@link ExitCodeProvider#getExitCode()},
|
|
|
|
+ * becomes the exit code of the command.</li>
|
|
|
|
+ * </ol>
|
|
|
|
+ * Error and warning messages are logged to {@code stderr}.
|
|
|
|
+ *
|
|
|
|
+ * @param <S> service class to cast the generated service to.
|
|
|
|
+ */
|
|
|
|
+@SuppressWarnings("UseOfSystemOutOrSystemErr")
|
|
|
|
+public class ServiceLauncher<S extends Service>
|
|
|
|
+ implements LauncherExitCodes, LauncherArguments,
|
|
|
|
+ Thread.UncaughtExceptionHandler {
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Logger.
|
|
|
|
+ */
|
|
|
|
+ private static final Logger LOG =
|
|
|
|
+ LoggerFactory.getLogger(ServiceLauncher.class);
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Priority for the shutdown hook: {@value}.
|
|
|
|
+ */
|
|
|
|
+ protected static final int SHUTDOWN_PRIORITY = 30;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * The name of this class.
|
|
|
|
+ */
|
|
|
|
+ public static final String NAME = "ServiceLauncher";
|
|
|
|
+
|
|
|
|
+ protected static final String USAGE_NAME = "Usage: " + NAME;
|
|
|
|
+ protected static final String USAGE_SERVICE_ARGUMENTS =
|
|
|
|
+ "service-classname <service arguments>";
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Usage message.
|
|
|
|
+ *
|
|
|
|
+ * Text: {@value}
|
|
|
|
+ */
|
|
|
|
+ public static final String USAGE_MESSAGE =
|
|
|
|
+ USAGE_NAME
|
|
|
|
+ + " [" + ARG_CONF_PREFIXED + " <conf file>]"
|
|
|
|
+ + " [" + ARG_CONFCLASS_PREFIXED + " <configuration classname>]"
|
|
|
|
+ + " " + USAGE_SERVICE_ARGUMENTS;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * The shutdown time on an interrupt: {@value}.
|
|
|
|
+ */
|
|
|
|
+ private static final int SHUTDOWN_TIME_ON_INTERRUPT = 30 * 1000;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * The launched service.
|
|
|
|
+ *
|
|
|
|
+ * Invalid until the service has been created.
|
|
|
|
+ */
|
|
|
|
+ private volatile S service;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Exit code of the service.
|
|
|
|
+ *
|
|
|
|
+ * Invalid until a service has
|
|
|
|
+ * executed or stopped, depending on the service type.
|
|
|
|
+ */
|
|
|
|
+ private int serviceExitCode;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Any exception raised during execution.
|
|
|
|
+ */
|
|
|
|
+ private ExitUtil.ExitException serviceException;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * The interrupt escalator for the service.
|
|
|
|
+ */
|
|
|
|
+ private InterruptEscalator interruptEscalator;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Configuration used for the service.
|
|
|
|
+ */
|
|
|
|
+ private Configuration configuration;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Text description of service for messages.
|
|
|
|
+ */
|
|
|
|
+ private String serviceName;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Classname for the service to create.; empty string otherwise.
|
|
|
|
+ */
|
|
|
|
+ private String serviceClassName = "";
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * List of the standard configurations to create (and so load in properties).
|
|
|
|
+ * The values are Hadoop, HDFS and YARN configurations.
|
|
|
|
+ */
|
|
|
|
+ protected static final String[] DEFAULT_CONFIGS = {
|
|
|
|
+ "org.apache.hadoop.conf.Configuration",
|
|
|
|
+ "org.apache.hadoop.hdfs.HdfsConfiguration",
|
|
|
|
+ "org.apache.hadoop.yarn.conf.YarnConfiguration"
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * List of classnames to load to configuration before creating a
|
|
|
|
+ * {@link Configuration} instance.
|
|
|
|
+ */
|
|
|
|
+ private List<String> confClassnames = new ArrayList<>(DEFAULT_CONFIGS.length);
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * URLs of configurations to load into the configuration instance created.
|
|
|
|
+ */
|
|
|
|
+ private List<URL> confResourceUrls = new ArrayList<>(1);
|
|
|
|
+
|
|
|
|
+ /** Command options. Preserved for usage statements. */
|
|
|
|
+ private Options commandOptions;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Create an instance of the launcher.
|
|
|
|
+ * @param serviceClassName classname of the service
|
|
|
|
+ */
|
|
|
|
+ public ServiceLauncher(String serviceClassName) {
|
|
|
|
+ this(serviceClassName, serviceClassName);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Create an instance of the launcher.
|
|
|
|
+ * @param serviceName name of service for text messages
|
|
|
|
+ * @param serviceClassName classname of the service
|
|
|
|
+ */
|
|
|
|
+ public ServiceLauncher(String serviceName, String serviceClassName) {
|
|
|
|
+ this.serviceClassName = serviceClassName;
|
|
|
|
+ this.serviceName = serviceName;
|
|
|
|
+ // set up initial list of configurations
|
|
|
|
+ confClassnames.addAll(Arrays.asList(DEFAULT_CONFIGS));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Get the service.
|
|
|
|
+ *
|
|
|
|
+ * Null until
|
|
|
|
+ * {@link #coreServiceLaunch(Configuration, List, boolean, boolean)}
|
|
|
|
+ * has completed.
|
|
|
|
+ * @return the service
|
|
|
|
+ */
|
|
|
|
+ public final S getService() {
|
|
|
|
+ return service;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Setter is to give subclasses the ability to manipulate the service.
|
|
|
|
+ * @param s the new service
|
|
|
|
+ */
|
|
|
|
+ protected void setService(S s) {
|
|
|
|
+ this.service = s;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Get the configuration constructed from the command line arguments.
|
|
|
|
+ * @return the configuration used to create the service
|
|
|
|
+ */
|
|
|
|
+ public final Configuration getConfiguration() {
|
|
|
|
+ return configuration;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * The exit code from a successful service execution.
|
|
|
|
+ * @return the exit code.
|
|
|
|
+ */
|
|
|
|
+ public final int getServiceExitCode() {
|
|
|
|
+ return serviceExitCode;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Get the exit exception used to end this service.
|
|
|
|
+ * @return an exception, which will be null until the service
|
|
|
|
+ * has exited (and {@code System.exit} has not been called)
|
|
|
|
+ */
|
|
|
|
+ public final ExitUtil.ExitException getServiceException() {
|
|
|
|
+ return serviceException;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Probe for service classname being defined.
|
|
|
|
+ * @return true if the classname is set
|
|
|
|
+ */
|
|
|
|
+ private boolean isClassnameDefined() {
|
|
|
|
+ return serviceClassName != null && !serviceClassName.isEmpty();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public String toString() {
|
|
|
|
+ final StringBuilder sb = new StringBuilder("\"ServiceLauncher for \"");
|
|
|
|
+ sb.append(serviceName);
|
|
|
|
+ if (isClassnameDefined()) {
|
|
|
|
+ sb.append(", serviceClassName='").append(serviceClassName).append('\'');
|
|
|
|
+ }
|
|
|
|
+ if (service != null) {
|
|
|
|
+ sb.append(", service=").append(service);
|
|
|
|
+ }
|
|
|
|
+ return sb.toString();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Launch the service and exit.
|
|
|
|
+ *
|
|
|
|
+ * <ol>
|
|
|
|
+ * <li>Parse the command line.</li>
|
|
|
|
+ * <li>Build the service configuration from it.</li>
|
|
|
|
+ * <li>Start the service.</li>.
|
|
|
|
+ * <li>If it is a {@link LaunchableService}: execute it</li>
|
|
|
|
+ * <li>Otherwise: wait for it to finish.</li>
|
|
|
|
+ * <li>Exit passing the status code to the {@link #exit(int, String)}
|
|
|
|
+ * method.</li>
|
|
|
|
+ * </ol>
|
|
|
|
+ * @param args arguments to the service. {@code arg[0]} is
|
|
|
|
+ * assumed to be the service classname.
|
|
|
|
+ */
|
|
|
|
+ public void launchServiceAndExit(List<String> args) {
|
|
|
|
+ StringBuilder builder = new StringBuilder();
|
|
|
|
+ for (String arg : args) {
|
|
|
|
+ builder.append('"').append(arg).append("\" ");
|
|
|
|
+ }
|
|
|
|
+ String argumentString = builder.toString();
|
|
|
|
+ if (LOG.isDebugEnabled()) {
|
|
|
|
+ LOG.debug(startupShutdownMessage(serviceName, args));
|
|
|
|
+ LOG.debug(argumentString);
|
|
|
|
+ }
|
|
|
|
+ registerFailureHandling();
|
|
|
|
+ // set up the configs, using reflection to push in the -site.xml files
|
|
|
|
+ loadConfigurationClasses();
|
|
|
|
+ Configuration conf = createConfiguration();
|
|
|
|
+ for (URL resourceUrl : confResourceUrls) {
|
|
|
|
+ conf.addResource(resourceUrl);
|
|
|
|
+ }
|
|
|
|
+ bindCommandOptions();
|
|
|
|
+ ExitUtil.ExitException exitException;
|
|
|
|
+ try {
|
|
|
|
+ List<String> processedArgs = extractCommandOptions(conf, args);
|
|
|
|
+ exitException = launchService(conf, processedArgs, true, true);
|
|
|
|
+ } catch (ExitUtil.ExitException e) {
|
|
|
|
+ exitException = e;
|
|
|
|
+ noteException(exitException);
|
|
|
|
+ }
|
|
|
|
+ if (exitException.getExitCode() != 0) {
|
|
|
|
+ // something went wrong. Print the usage and commands
|
|
|
|
+ System.err.println(getUsageMessage());
|
|
|
|
+ System.err.println("Command: " + argumentString);
|
|
|
|
+ }
|
|
|
|
+ System.out.flush();
|
|
|
|
+ System.err.flush();
|
|
|
|
+ exit(exitException);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Set the {@link #commandOptions} field to the result of
|
|
|
|
+ * {@link #createOptions()}; protected for subclasses and test access.
|
|
|
|
+ */
|
|
|
|
+ protected void bindCommandOptions() {
|
|
|
|
+ commandOptions = createOptions();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Record that an Exit Exception has been raised.
|
|
|
|
+ * Save it to {@link #serviceException}, with its exit code in
|
|
|
|
+ * {@link #serviceExitCode}
|
|
|
|
+ * @param exitException exception
|
|
|
|
+ */
|
|
|
|
+ void noteException(ExitUtil.ExitException exitException) {
|
|
|
|
+ LOG.debug("Exception raised", exitException);
|
|
|
|
+ serviceExitCode = exitException.getExitCode();
|
|
|
|
+ serviceException = exitException;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Get the usage message, ideally dynamically.
|
|
|
|
+ * @return the usage message
|
|
|
|
+ */
|
|
|
|
+ protected String getUsageMessage() {
|
|
|
|
+ String message = USAGE_MESSAGE;
|
|
|
|
+ if (commandOptions != null) {
|
|
|
|
+ message = USAGE_NAME
|
|
|
|
+ + " " + commandOptions.toString()
|
|
|
|
+ + " " + USAGE_SERVICE_ARGUMENTS;
|
|
|
|
+ }
|
|
|
|
+ return message;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Override point: create an options instance to combine with the
|
|
|
|
+ * standard options set.
|
|
|
|
+ * <i>Important. Synchronize uses of {@link OptionBuilder}</i>
|
|
|
|
+ * with {@code OptionBuilder.class}
|
|
|
|
+ * @return the new options
|
|
|
|
+ */
|
|
|
|
+ @SuppressWarnings("static-access")
|
|
|
|
+ protected Options createOptions() {
|
|
|
|
+ synchronized (OptionBuilder.class) {
|
|
|
|
+ Options options = new Options();
|
|
|
|
+ Option oconf = OptionBuilder.withArgName("configuration file")
|
|
|
|
+ .hasArg()
|
|
|
|
+ .withDescription("specify an application configuration file")
|
|
|
|
+ .withLongOpt(ARG_CONF)
|
|
|
|
+ .create(ARG_CONF_SHORT);
|
|
|
|
+ Option confclass = OptionBuilder.withArgName("configuration classname")
|
|
|
|
+ .hasArg()
|
|
|
|
+ .withDescription(
|
|
|
|
+ "Classname of a Hadoop Configuration subclass to load")
|
|
|
|
+ .withLongOpt(ARG_CONFCLASS)
|
|
|
|
+ .create(ARG_CONFCLASS_SHORT);
|
|
|
|
+ Option property = OptionBuilder.withArgName("property=value")
|
|
|
|
+ .hasArg()
|
|
|
|
+ .withDescription("use value for given property")
|
|
|
|
+ .create('D');
|
|
|
|
+ options.addOption(oconf);
|
|
|
|
+ options.addOption(property);
|
|
|
|
+ options.addOption(confclass);
|
|
|
|
+ return options;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Override point: create the base configuration for the service.
|
|
|
|
+ *
|
|
|
|
+ * Subclasses can override to create HDFS/YARN configurations etc.
|
|
|
|
+ * @return the configuration to use as the service initializer.
|
|
|
|
+ */
|
|
|
|
+ protected Configuration createConfiguration() {
|
|
|
|
+ return new Configuration();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Override point: Get a list of configuration classes to create.
|
|
|
|
+ * @return the array of configs to attempt to create. If any are off the
|
|
|
|
+ * classpath, that is logged
|
|
|
|
+ */
|
|
|
|
+ @SuppressWarnings("ReturnOfCollectionOrArrayField")
|
|
|
|
+ protected List<String> getConfigurationsToCreate() {
|
|
|
|
+ return confClassnames;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * This creates all the configurations defined by
|
|
|
|
+ * {@link #getConfigurationsToCreate()} , ensuring that
|
|
|
|
+ * the resources have been pushed in.
|
|
|
|
+ * If one cannot be loaded it is logged and the operation continues
|
|
|
|
+ * except in the case that the class does load but it isn't actually
|
|
|
|
+ * a subclass of {@link Configuration}.
|
|
|
|
+ * @throws ExitUtil.ExitException if a loaded class is of the wrong type
|
|
|
|
+ */
|
|
|
|
+ @VisibleForTesting
|
|
|
|
+ public int loadConfigurationClasses() {
|
|
|
|
+ List<String> toCreate = getConfigurationsToCreate();
|
|
|
|
+ int loaded = 0;
|
|
|
|
+ for (String classname : toCreate) {
|
|
|
|
+ try {
|
|
|
|
+ Class<?> loadClass = getClassLoader().loadClass(classname);
|
|
|
|
+ Object instance = loadClass.getConstructor().newInstance();
|
|
|
|
+ if (!(instance instanceof Configuration)) {
|
|
|
|
+ throw new ExitUtil.ExitException(EXIT_SERVICE_CREATION_FAILURE,
|
|
|
|
+ "Could not create " + classname
|
|
|
|
+ + " because it is not a Configuration class/subclass");
|
|
|
|
+ }
|
|
|
|
+ loaded++;
|
|
|
|
+ } catch (ClassNotFoundException e) {
|
|
|
|
+ // class could not be found -implies it is not on the current classpath
|
|
|
|
+ LOG.debug("Failed to load {} because it is not on the classpath",
|
|
|
|
+ classname);
|
|
|
|
+ } catch (ExitUtil.ExitException e) {
|
|
|
|
+ // rethrow
|
|
|
|
+ throw e;
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ // any other exception
|
|
|
|
+ LOG.info("Failed to create {}", classname, e);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return loaded;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Launch a service catching all exceptions and downgrading them to exit codes
|
|
|
|
+ * after logging.
|
|
|
|
+ *
|
|
|
|
+ * Sets {@link #serviceException} to this value.
|
|
|
|
+ * @param conf configuration to use
|
|
|
|
+ * @param processedArgs command line after the launcher-specific arguments
|
|
|
|
+ * have been stripped out.
|
|
|
|
+ * @param addShutdownHook should a shutdown hook be added to terminate
|
|
|
|
+ * this service on shutdown. Tests should set this to false.
|
|
|
|
+ * @param execute execute/wait for the service to stop.
|
|
|
|
+ * @return an exit exception, which will have a status code of 0 if it worked
|
|
|
|
+ */
|
|
|
|
+ @VisibleForTesting
|
|
|
|
+ public ExitUtil.ExitException launchService(Configuration conf,
|
|
|
|
+ List<String> processedArgs,
|
|
|
|
+ boolean addShutdownHook,
|
|
|
|
+ boolean execute) {
|
|
|
|
+
|
|
|
|
+ ExitUtil.ExitException exitException;
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ int exitCode = coreServiceLaunch(conf, processedArgs, addShutdownHook,
|
|
|
|
+ execute);
|
|
|
|
+ if (service != null) {
|
|
|
|
+ // check to see if the service failed
|
|
|
|
+ Throwable failure = service.getFailureCause();
|
|
|
|
+ if (failure != null) {
|
|
|
|
+ // the service exited with a failure.
|
|
|
|
+ // check what state it is in
|
|
|
|
+ Service.STATE failureState = service.getFailureState();
|
|
|
|
+ if (failureState == Service.STATE.STOPPED) {
|
|
|
|
+ // the failure occurred during shutdown, not important enough
|
|
|
|
+ // to bother the user as it may just scare them
|
|
|
|
+ LOG.debug("Failure during shutdown: {} ", failure, failure);
|
|
|
|
+ } else {
|
|
|
|
+ //throw it for the catch handlers to deal with
|
|
|
|
+ throw failure;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ String name = getServiceName();
|
|
|
|
+
|
|
|
|
+ if (exitCode == 0) {
|
|
|
|
+ exitException = new ServiceLaunchException(exitCode,
|
|
|
|
+ "%s succeeded",
|
|
|
|
+ name);
|
|
|
|
+ } else {
|
|
|
|
+ exitException = new ServiceLaunchException(exitCode,
|
|
|
|
+ "%s failed ", name);
|
|
|
|
+ }
|
|
|
|
+ // either the service succeeded, or an error raised during shutdown,
|
|
|
|
+ // which we don't worry that much about
|
|
|
|
+ } catch (ExitUtil.ExitException ee) {
|
|
|
|
+ // exit exceptions are passed through unchanged
|
|
|
|
+ exitException = ee;
|
|
|
|
+ } catch (Throwable thrown) {
|
|
|
|
+ exitException = convertToExitException(thrown);
|
|
|
|
+ }
|
|
|
|
+ noteException(exitException);
|
|
|
|
+ return exitException;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Launch the service.
|
|
|
|
+ *
|
|
|
|
+ * All exceptions that occur are propagated upwards.
|
|
|
|
+ *
|
|
|
|
+ * If the method returns a status code, it means that it got as far starting
|
|
|
|
+ * the service, and if it implements {@link LaunchableService}, that the
|
|
|
|
+ * method {@link LaunchableService#execute()} has completed.
|
|
|
|
+ *
|
|
|
|
+ * After this method returns, the service can be retrieved returned by
|
|
|
|
+ * {@link #getService()}.
|
|
|
|
+ *
|
|
|
|
+ * @param conf configuration
|
|
|
|
+ * @param processedArgs arguments after the configuration parameters
|
|
|
|
+ * have been stripped out.
|
|
|
|
+ * @param addShutdownHook should a shutdown hook be added to terminate
|
|
|
|
+ * this service on shutdown. Tests should set this to false.
|
|
|
|
+ * @param execute execute/wait for the service to stop
|
|
|
|
+ * @throws ClassNotFoundException classname not on the classpath
|
|
|
|
+ * @throws IllegalAccessException not allowed at the class
|
|
|
|
+ * @throws InstantiationException not allowed to instantiate it
|
|
|
|
+ * @throws InterruptedException thread interrupted
|
|
|
|
+ * @throws ExitUtil.ExitException any exception defining the status code.
|
|
|
|
+ * @throws Exception any other failure -if it implements
|
|
|
|
+ * {@link ExitCodeProvider} then it defines the exit code for any
|
|
|
|
+ * containing exception
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ protected int coreServiceLaunch(Configuration conf,
|
|
|
|
+ List<String> processedArgs,
|
|
|
|
+ boolean addShutdownHook,
|
|
|
|
+ boolean execute) throws Exception {
|
|
|
|
+
|
|
|
|
+ // create the service instance
|
|
|
|
+ instantiateService(conf);
|
|
|
|
+ ServiceShutdownHook shutdownHook = null;
|
|
|
|
+
|
|
|
|
+ // and the shutdown hook if requested
|
|
|
|
+ if (addShutdownHook) {
|
|
|
|
+ shutdownHook = new ServiceShutdownHook(service);
|
|
|
|
+ shutdownHook.register(SHUTDOWN_PRIORITY);
|
|
|
|
+ }
|
|
|
|
+ String name = getServiceName();
|
|
|
|
+ LOG.debug("Launched service {}", name);
|
|
|
|
+ LaunchableService launchableService = null;
|
|
|
|
+
|
|
|
|
+ if (service instanceof LaunchableService) {
|
|
|
|
+ // it's a LaunchableService, pass in the conf and arguments before init)
|
|
|
|
+ LOG.debug("Service {} implements LaunchableService", name);
|
|
|
|
+ launchableService = (LaunchableService) service;
|
|
|
|
+ if (launchableService.isInState(Service.STATE.INITED)) {
|
|
|
|
+ LOG.warn("LaunchableService {}"
|
|
|
|
+ + " initialized in constructor before CLI arguments passed in",
|
|
|
|
+ name);
|
|
|
|
+ }
|
|
|
|
+ Configuration newconf = launchableService.bindArgs(configuration,
|
|
|
|
+ processedArgs);
|
|
|
|
+ if (newconf != null) {
|
|
|
|
+ configuration = newconf;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ //some class constructors init; here this is picked up on.
|
|
|
|
+ if (!service.isInState(Service.STATE.INITED)) {
|
|
|
|
+ service.init(configuration);
|
|
|
|
+ }
|
|
|
|
+ int exitCode;
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ // start the service
|
|
|
|
+ service.start();
|
|
|
|
+ exitCode = EXIT_SUCCESS;
|
|
|
|
+ if (execute && service.isInState(Service.STATE.STARTED)) {
|
|
|
|
+ if (launchableService != null) {
|
|
|
|
+ // assume that runnable services are meant to run from here
|
|
|
|
+ try {
|
|
|
|
+ exitCode = launchableService.execute();
|
|
|
|
+ LOG.debug("Service {} execution returned exit code {}",
|
|
|
|
+ name, exitCode);
|
|
|
|
+ } finally {
|
|
|
|
+ // then stop the service
|
|
|
|
+ service.stop();
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ //run the service until it stops or an interrupt happens
|
|
|
|
+ // on a different thread.
|
|
|
|
+ LOG.debug("waiting for service threads to terminate");
|
|
|
|
+ service.waitForServiceToStop(0);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } finally {
|
|
|
|
+ if (shutdownHook != null) {
|
|
|
|
+ shutdownHook.unregister();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return exitCode;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Instantiate the service defined in {@code serviceClassName}.
|
|
|
|
+ *
|
|
|
|
+ * Sets the {@code configuration} field
|
|
|
|
+ * to the the value of {@code conf},
|
|
|
|
+ * and the {@code service} field to the service created.
|
|
|
|
+ *
|
|
|
|
+ * @param conf configuration to use
|
|
|
|
+ */
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
|
+ public Service instantiateService(Configuration conf) {
|
|
|
|
+ Preconditions.checkArgument(conf != null, "null conf");
|
|
|
|
+ Preconditions.checkArgument(serviceClassName != null,
|
|
|
|
+ "null service classname");
|
|
|
|
+ Preconditions.checkArgument(!serviceClassName.isEmpty(),
|
|
|
|
+ "undefined service classname");
|
|
|
|
+ configuration = conf;
|
|
|
|
+
|
|
|
|
+ // Instantiate the class. this requires the service to have a public
|
|
|
|
+ // zero-argument or string-argument constructor
|
|
|
|
+ Object instance;
|
|
|
|
+ try {
|
|
|
|
+ Class<?> serviceClass = getClassLoader().loadClass(serviceClassName);
|
|
|
|
+ try {
|
|
|
|
+ instance = serviceClass.getConstructor().newInstance();
|
|
|
|
+ } catch (NoSuchMethodException noEmptyConstructor) {
|
|
|
|
+ // no simple constructor, fall back to a string
|
|
|
|
+ LOG.debug("No empty constructor {}", noEmptyConstructor,
|
|
|
|
+ noEmptyConstructor);
|
|
|
|
+ instance = serviceClass.getConstructor(String.class)
|
|
|
|
+ .newInstance(serviceClassName);
|
|
|
|
+ }
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ throw serviceCreationFailure(e);
|
|
|
|
+ }
|
|
|
|
+ if (!(instance instanceof Service)) {
|
|
|
|
+ //not a service
|
|
|
|
+ throw new ServiceLaunchException(
|
|
|
|
+ LauncherExitCodes.EXIT_SERVICE_CREATION_FAILURE,
|
|
|
|
+ "Not a service class: \"%s\"", serviceClassName);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // cast to the specific instance type of this ServiceLauncher
|
|
|
|
+ service = (S) instance;
|
|
|
|
+ return service;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Convert an exception to an {@code ExitException}.
|
|
|
|
+ *
|
|
|
|
+ * This process may just be a simple pass through, otherwise a new
|
|
|
|
+ * exception is created with an exit code, the text of the supplied
|
|
|
|
+ * exception, and the supplied exception as an inner cause.
|
|
|
|
+ *
|
|
|
|
+ * <ol>
|
|
|
|
+ * <li>If is already the right type, pass it through.</li>
|
|
|
|
+ * <li>If it implements {@link ExitCodeProvider#getExitCode()},
|
|
|
|
+ * the exit code is extracted and used in the new exception.</li>
|
|
|
|
+ * <li>Otherwise, the exit code
|
|
|
|
+ * {@link LauncherExitCodes#EXIT_EXCEPTION_THROWN} is used.</li>
|
|
|
|
+ * </ol>
|
|
|
|
+ *
|
|
|
|
+ * @param thrown the exception thrown
|
|
|
|
+ * @return an {@code ExitException} with a status code
|
|
|
|
+ */
|
|
|
|
+ protected static ExitUtil.ExitException convertToExitException(
|
|
|
|
+ Throwable thrown) {
|
|
|
|
+ ExitUtil.ExitException exitException;
|
|
|
|
+ // get the exception message
|
|
|
|
+ String message = thrown.toString();
|
|
|
|
+ int exitCode;
|
|
|
|
+ if (thrown instanceof ExitCodeProvider) {
|
|
|
|
+ // the exception provides a status code -extract it
|
|
|
|
+ exitCode = ((ExitCodeProvider) thrown).getExitCode();
|
|
|
|
+ message = thrown.getMessage();
|
|
|
|
+ if (message == null) {
|
|
|
|
+ // some exceptions do not have a message; fall back
|
|
|
|
+ // to the string value.
|
|
|
|
+ message = thrown.toString();
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ // no exception code: use the default
|
|
|
|
+ exitCode = EXIT_EXCEPTION_THROWN;
|
|
|
|
+ }
|
|
|
|
+ // construct the new exception with the original message and
|
|
|
|
+ // an exit code
|
|
|
|
+ exitException = new ServiceLaunchException(exitCode, message);
|
|
|
|
+ exitException.initCause(thrown);
|
|
|
|
+ return exitException;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Generate an exception announcing a failure to create the service.
|
|
|
|
+ * @param exception inner exception.
|
|
|
|
+ * @return a new exception, with the exit code
|
|
|
|
+ * {@link LauncherExitCodes#EXIT_SERVICE_CREATION_FAILURE}
|
|
|
|
+ */
|
|
|
|
+ protected ServiceLaunchException serviceCreationFailure(Exception exception) {
|
|
|
|
+ return new ServiceLaunchException(EXIT_SERVICE_CREATION_FAILURE, exception);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Override point: register this class as the handler for the control-C
|
|
|
|
+ * and SIGINT interrupts.
|
|
|
|
+ *
|
|
|
|
+ * Subclasses can extend this with extra operations, such as
|
|
|
|
+ * an exception handler:
|
|
|
|
+ * <pre>
|
|
|
|
+ * Thread.setDefaultUncaughtExceptionHandler(
|
|
|
|
+ * new YarnUncaughtExceptionHandler());
|
|
|
|
+ * </pre>
|
|
|
|
+ */
|
|
|
|
+ protected void registerFailureHandling() {
|
|
|
|
+ try {
|
|
|
|
+ interruptEscalator = new InterruptEscalator(this,
|
|
|
|
+ SHUTDOWN_TIME_ON_INTERRUPT);
|
|
|
|
+ interruptEscalator.register(IrqHandler.CONTROL_C);
|
|
|
|
+ interruptEscalator.register(IrqHandler.SIGTERM);
|
|
|
|
+ } catch (IllegalArgumentException e) {
|
|
|
|
+ // downgrade interrupt registration to warnings
|
|
|
|
+ LOG.warn("{}", e, e);
|
|
|
|
+ }
|
|
|
|
+ Thread.setDefaultUncaughtExceptionHandler(
|
|
|
|
+ new HadoopUncaughtExceptionHandler(this));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Handler for uncaught exceptions: terminate the service.
|
|
|
|
+ * @param thread thread
|
|
|
|
+ * @param exception exception
|
|
|
|
+ */
|
|
|
|
+ @Override
|
|
|
|
+ public void uncaughtException(Thread thread, Throwable exception) {
|
|
|
|
+ LOG.error("Uncaught exception in thread {} -exiting", thread, exception);
|
|
|
|
+ exit(convertToExitException(exception));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Get the service name via {@link Service#getName()}.
|
|
|
|
+ *
|
|
|
|
+ * If the service is not instantiated, the classname is returned instead.
|
|
|
|
+ * @return the service name
|
|
|
|
+ */
|
|
|
|
+ public String getServiceName() {
|
|
|
|
+ Service s = service;
|
|
|
|
+ String name = null;
|
|
|
|
+ if (s != null) {
|
|
|
|
+ try {
|
|
|
|
+ name = s.getName();
|
|
|
|
+ } catch (Exception ignored) {
|
|
|
|
+ // ignored
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (name != null) {
|
|
|
|
+ return "service " + name;
|
|
|
|
+ } else {
|
|
|
|
+ return "service " + serviceName;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Print a warning message.
|
|
|
|
+ * <p>
|
|
|
|
+ * This tries to log to the log's warn() operation.
|
|
|
|
+ * If the log at that level is disabled it logs to system error
|
|
|
|
+ * @param text warning text
|
|
|
|
+ */
|
|
|
|
+ protected void warn(String text) {
|
|
|
|
+ if (LOG.isWarnEnabled()) {
|
|
|
|
+ LOG.warn(text);
|
|
|
|
+ } else {
|
|
|
|
+ System.err.println(text);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Report an error.
|
|
|
|
+ * <p>
|
|
|
|
+ * This tries to log to {@code LOG.error()}.
|
|
|
|
+ * <p>
|
|
|
|
+ * If that log level is disabled disabled the message
|
|
|
|
+ * is logged to system error along with {@code thrown.toString()}
|
|
|
|
+ * @param message message for the user
|
|
|
|
+ * @param thrown the exception thrown
|
|
|
|
+ */
|
|
|
|
+ protected void error(String message, Throwable thrown) {
|
|
|
|
+ String text = "Exception: " + message;
|
|
|
|
+ if (LOG.isErrorEnabled()) {
|
|
|
|
+ LOG.error(text, thrown);
|
|
|
|
+ } else {
|
|
|
|
+ System.err.println(text);
|
|
|
|
+ if (thrown != null) {
|
|
|
|
+ System.err.println(thrown.toString());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Exit the JVM.
|
|
|
|
+ *
|
|
|
|
+ * This is method can be overridden for testing, throwing an
|
|
|
|
+ * exception instead. Any subclassed method MUST raise an
|
|
|
|
+ * {@code ExitException} instance/subclass.
|
|
|
|
+ * The service launcher code assumes that after this method is invoked,
|
|
|
|
+ * no other code in the same method is called.
|
|
|
|
+ * @param exitCode code to exit
|
|
|
|
+ */
|
|
|
|
+ protected void exit(int exitCode, String message) {
|
|
|
|
+ ExitUtil.terminate(exitCode, message);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Exit the JVM using an exception for the exit code and message,
|
|
|
|
+ * invoking {@link ExitUtil#terminate(ExitUtil.ExitException)}.
|
|
|
|
+ *
|
|
|
|
+ * This is the standard way a launched service exits.
|
|
|
|
+ * An error code of 0 means success -nothing is printed.
|
|
|
|
+ *
|
|
|
|
+ * If {@link ExitUtil#disableSystemExit()} has been called, this
|
|
|
|
+ * method will throw the exception.
|
|
|
|
+ *
|
|
|
|
+ * The method <i>may</i> be subclassed for testing
|
|
|
|
+ * @param ee exit exception
|
|
|
|
+ * @throws ExitUtil.ExitException if ExitUtil exceptions are disabled
|
|
|
|
+ */
|
|
|
|
+ protected void exit(ExitUtil.ExitException ee) {
|
|
|
|
+ ExitUtil.terminate(ee);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Override point: get the classloader to use.
|
|
|
|
+ * @return the classloader for loading a service class.
|
|
|
|
+ */
|
|
|
|
+ protected ClassLoader getClassLoader() {
|
|
|
|
+ return this.getClass().getClassLoader();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Extract the command options and apply them to the configuration,
|
|
|
|
+ * building an array of processed arguments to hand down to the service.
|
|
|
|
+ *
|
|
|
|
+ * @param conf configuration to update.
|
|
|
|
+ * @param args main arguments. {@code args[0]}is assumed to be
|
|
|
|
+ * the service classname and is skipped.
|
|
|
|
+ * @return the remaining arguments
|
|
|
|
+ * @throws ExitUtil.ExitException if JVM exiting is disabled.
|
|
|
|
+ */
|
|
|
|
+ public List<String> extractCommandOptions(Configuration conf,
|
|
|
|
+ List<String> args) {
|
|
|
|
+ int size = args.size();
|
|
|
|
+ if (size <= 1) {
|
|
|
|
+ return new ArrayList<>(0);
|
|
|
|
+ }
|
|
|
|
+ List<String> coreArgs = args.subList(1, size);
|
|
|
|
+
|
|
|
|
+ return parseCommandArgs(conf, coreArgs);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Parse the command arguments, extracting the service class as the last
|
|
|
|
+ * element of the list (after extracting all the rest).
|
|
|
|
+ *
|
|
|
|
+ * The field {@link #commandOptions} field must already have been set.
|
|
|
|
+ * @param conf configuration to use
|
|
|
|
+ * @param args command line argument list
|
|
|
|
+ * @return the remaining arguments
|
|
|
|
+ * @throws ServiceLaunchException if processing of arguments failed
|
|
|
|
+ */
|
|
|
|
+ protected List<String> parseCommandArgs(Configuration conf,
|
|
|
|
+ List<String> args) {
|
|
|
|
+ Preconditions.checkNotNull(commandOptions,
|
|
|
|
+ "Command options have not been created");
|
|
|
|
+ StringBuilder argString = new StringBuilder(args.size() * 32);
|
|
|
|
+ for (String arg : args) {
|
|
|
|
+ argString.append("\"").append(arg).append("\" ");
|
|
|
|
+ }
|
|
|
|
+ LOG.debug("Command line: {}", argString);
|
|
|
|
+ try {
|
|
|
|
+ String[] argArray = args.toArray(new String[args.size()]);
|
|
|
|
+ // parse this the standard way. This will
|
|
|
|
+ // update the configuration in the parser, and potentially
|
|
|
|
+ // patch the user credentials
|
|
|
|
+ GenericOptionsParser parser = createGenericOptionsParser(conf, argArray);
|
|
|
|
+ if (!parser.isParseSuccessful()) {
|
|
|
|
+ throw new ServiceLaunchException(EXIT_COMMAND_ARGUMENT_ERROR,
|
|
|
|
+ E_PARSE_FAILED + " %s", argString);
|
|
|
|
+ }
|
|
|
|
+ CommandLine line = parser.getCommandLine();
|
|
|
|
+ List<String> remainingArgs = Arrays.asList(parser.getRemainingArgs());
|
|
|
|
+ LOG.debug("Remaining arguments {}", remainingArgs);
|
|
|
|
+
|
|
|
|
+ // Scan the list of configuration files
|
|
|
|
+ // and bail out if they don't exist
|
|
|
|
+ if (line.hasOption(ARG_CONF)) {
|
|
|
|
+ String[] filenames = line.getOptionValues(ARG_CONF);
|
|
|
|
+ verifyConfigurationFilesExist(filenames);
|
|
|
|
+ // Add URLs of files as list of URLs to load
|
|
|
|
+ for (String filename : filenames) {
|
|
|
|
+ File file = new File(filename);
|
|
|
|
+ LOG.debug("Configuration files {}", file);
|
|
|
|
+ confResourceUrls.add(file.toURI().toURL());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (line.hasOption(ARG_CONFCLASS)) {
|
|
|
|
+ // new resources to instantiate as configurations
|
|
|
|
+ List<String> classnameList = Arrays.asList(
|
|
|
|
+ line.getOptionValues(ARG_CONFCLASS));
|
|
|
|
+ LOG.debug("Configuration classes {}", classnameList);
|
|
|
|
+ confClassnames.addAll(classnameList);
|
|
|
|
+ }
|
|
|
|
+ // return the remainder
|
|
|
|
+ return remainingArgs;
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
+ // parsing problem: convert to a command argument error with
|
|
|
|
+ // the original text
|
|
|
|
+ throw new ServiceLaunchException(EXIT_COMMAND_ARGUMENT_ERROR, e);
|
|
|
|
+ } catch (RuntimeException e) {
|
|
|
|
+ // lower level issue such as XML parse failure
|
|
|
|
+ throw new ServiceLaunchException(EXIT_COMMAND_ARGUMENT_ERROR,
|
|
|
|
+ E_PARSE_FAILED + " %s : %s", argString, e);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Override point: create a generic options parser or subclass thereof.
|
|
|
|
+ * @param conf Hadoop configuration
|
|
|
|
+ * @param argArray array of arguments
|
|
|
|
+ * @return a generic options parser to parse the arguments
|
|
|
|
+ * @throws IOException on any failure
|
|
|
|
+ */
|
|
|
|
+ protected GenericOptionsParser createGenericOptionsParser(Configuration conf,
|
|
|
|
+ String[] argArray) throws IOException {
|
|
|
|
+ return new MinimalGenericOptionsParser(conf, commandOptions, argArray);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Verify that all the specified filenames exist.
|
|
|
|
+ * @param filenames a list of files
|
|
|
|
+ * @throws ServiceLaunchException if a file is not found
|
|
|
|
+ */
|
|
|
|
+ protected void verifyConfigurationFilesExist(String[] filenames) {
|
|
|
|
+ if (filenames == null) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ for (String filename : filenames) {
|
|
|
|
+ File file = new File(filename);
|
|
|
|
+ LOG.debug("Conf file {}", file.getAbsolutePath());
|
|
|
|
+ if (!file.exists()) {
|
|
|
|
+ // no configuration file
|
|
|
|
+ throw new ServiceLaunchException(EXIT_NOT_FOUND,
|
|
|
|
+ ARG_CONF_PREFIXED + ": configuration file not found: %s",
|
|
|
|
+ file.getAbsolutePath());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Build a log message for starting up and shutting down.
|
|
|
|
+ * @param classname the class of the server
|
|
|
|
+ * @param args arguments
|
|
|
|
+ */
|
|
|
|
+ protected static String startupShutdownMessage(String classname,
|
|
|
|
+ List<String> args) {
|
|
|
|
+ final String hostname = NetUtils.getHostname();
|
|
|
|
+
|
|
|
|
+ return StringUtils.createStartupShutdownMessage(classname, hostname,
|
|
|
|
+ args.toArray(new String[args.size()]));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Exit with a printed message.
|
|
|
|
+ * @param status status code
|
|
|
|
+ * @param message message message to print before exiting
|
|
|
|
+ * @throws ExitUtil.ExitException if exceptions are disabled
|
|
|
|
+ */
|
|
|
|
+ protected static void exitWithMessage(int status, String message) {
|
|
|
|
+ ExitUtil.terminate(new ServiceLaunchException(status, message));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Exit with the usage exit code {@link #EXIT_USAGE}
|
|
|
|
+ * and message {@link #USAGE_MESSAGE}.
|
|
|
|
+ * @throws ExitUtil.ExitException if exceptions are disabled
|
|
|
|
+ */
|
|
|
|
+ protected static void exitWithUsageMessage() {
|
|
|
|
+ exitWithMessage(EXIT_USAGE, USAGE_MESSAGE);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * This is the JVM entry point for the service launcher.
|
|
|
|
+ *
|
|
|
|
+ * Converts the arguments to a list, then invokes {@link #serviceMain(List)}
|
|
|
|
+ * @param args command line arguments.
|
|
|
|
+ */
|
|
|
|
+ public static void main(String[] args) {
|
|
|
|
+ serviceMain(Arrays.asList(args));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Varargs version of the entry point for testing and other in-JVM use.
|
|
|
|
+ * Hands off to {@link #serviceMain(List)}
|
|
|
|
+ * @param args command line arguments.
|
|
|
|
+ */
|
|
|
|
+ public static void serviceMain(String... args) {
|
|
|
|
+ serviceMain(Arrays.asList(args));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* ====================================================================== */
|
|
|
|
+ /**
|
|
|
|
+ * The real main function, which takes the arguments as a list.
|
|
|
|
+ * Argument 0 MUST be the service classname
|
|
|
|
+ * @param argsList the list of arguments
|
|
|
|
+ */
|
|
|
|
+ /* ====================================================================== */
|
|
|
|
+
|
|
|
|
+ public static void serviceMain(List<String> argsList) {
|
|
|
|
+ if (argsList.isEmpty()) {
|
|
|
|
+ // no arguments: usage message
|
|
|
|
+ exitWithUsageMessage();
|
|
|
|
+ } else {
|
|
|
|
+ ServiceLauncher<Service> serviceLauncher =
|
|
|
|
+ new ServiceLauncher<>(argsList.get(0));
|
|
|
|
+ serviceLauncher.launchServiceAndExit(argsList);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * A generic options parser which does not parse any of the traditional
|
|
|
|
+ * Hadoop options.
|
|
|
|
+ */
|
|
|
|
+ protected static class MinimalGenericOptionsParser
|
|
|
|
+ extends GenericOptionsParser {
|
|
|
|
+ public MinimalGenericOptionsParser(Configuration conf,
|
|
|
|
+ Options options, String[] args) throws IOException {
|
|
|
|
+ super(conf, options, args);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ protected Options buildGeneralOptions(Options opts) {
|
|
|
|
+ return opts;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|