DaemonWrapper.java
- /*
- * 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.commons.daemon.support;
- import java.lang.reflect.Method;
- import java.lang.reflect.Modifier;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Objects;
- import org.apache.commons.daemon.Daemon;
- import org.apache.commons.daemon.DaemonContext;
- /**
- * Implementation of the Daemon that allows running
- * standard applications as daemons.
- * The applications must have the mechanism to manage
- * the application lifecycle.
- */
- public class DaemonWrapper implements Daemon
- {
- private final static String ARGS = "args";
- private final static String START_CLASS = "start";
- private final static String START_METHOD = "start.method";
- private final static String STOP_CLASS = "stop";
- private final static String STOP_METHOD = "stop.method";
- private final static String STOP_ARGS = "stop.args";
- private String configFileName;
- private final DaemonConfiguration config;
- private final Invoker startup;
- private final Invoker shutdown;
- /**
- * Constructs a new initialized instance.
- */
- public DaemonWrapper()
- {
- config = new DaemonConfiguration();
- startup = new Invoker();
- shutdown = new Invoker();
- }
- /**
- * Called from DaemonLoader on init stage.
- * <p>
- * Accepts the following configuration arguments:
- * <ul>
- * <li>-daemon-properties: - load DaemonConfiguration properties from the specified file to act as defaults</li>
- * <li>-start: set start class name</li>
- * <li>-start-method: set start method name</li>
- * <li>-stop: set stop class name</li>
- * <li>-stop-method: set stop method name</li>
- * <li>-stop-argument: set optional argument to stop method</li>
- * <li>Anything else is treated as a startup argument</li>
- * </ul>
- * <p>
- * The following "-daemon-properties" are recognized:
- * <ul>
- * <li>args (startup argument)</li>
- * <li>start</li>
- * <li>start.method</li>
- * <li>stop</li>
- * <li>stop.method</li>
- * <li>stop.args</li>
- * </ul>
- * These are used to set the corresponding item if it has not already been
- * set by the command arguments. <b>However, note that args and stop.args are
- * appended to any existing values.</b>
- */
- @Override
- public void init(final DaemonContext context)
- throws Exception
- {
- final String[] args = context.getArguments();
- if (args != null) {
- int i;
- // Parse our arguments and remove them
- // from the final argument array we are
- // passing to our child.
- arguments:
- for (i = 0; i < args.length; i++) {
- if (args[i].equals("--")) {
- // Done with argument processing
- break;
- }
- switch (args[i]) {
- case "-daemon-properties":
- if (++i == args.length) {
- throw new IllegalArgumentException(args[i - 1]);
- }
- configFileName = args[i];
- break;
- case "-start":
- if (++i == args.length) {
- throw new IllegalArgumentException(args[i - 1]);
- }
- startup.setClassName(args[i]);
- break;
- case "-start-method":
- if (++i == args.length) {
- throw new IllegalArgumentException(args[i - 1]);
- }
- startup.setMethodName(args[i]);
- break;
- case "-stop":
- if (++i == args.length) {
- throw new IllegalArgumentException(args[i - 1]);
- }
- shutdown.setClassName(args[i]);
- break;
- case "-stop-method":
- if (++i == args.length) {
- throw new IllegalArgumentException(args[i - 1]);
- }
- shutdown.setMethodName(args[i]);
- break;
- case "-stop-argument":
- if (++i == args.length) {
- throw new IllegalArgumentException(args[i - 1]);
- }
- final String[] aa = new String[1];
- aa[0] = args[i];
- shutdown.addArguments(aa);
- break;
- default:
- // This is not our option.
- // Everything else will be forwarded to the main
- break arguments;
- }
- }
- if (args.length > i) {
- final String[] copy = new String[args.length - i];
- System.arraycopy(args, i, copy, 0, copy.length);
- startup.addArguments(copy);
- }
- }
- if (config.load(configFileName)) {
- // Setup params if not set via cmdline.
- startup.setClassName(config.getProperty(START_CLASS));
- startup.setMethodName(config.getProperty(START_METHOD));
- // Merge the config with command line arguments
- startup.addArguments(config.getPropertyArray(ARGS));
- shutdown.setClassName(config.getProperty(STOP_CLASS));
- shutdown.setMethodName(config.getProperty(STOP_METHOD));
- shutdown.addArguments(config.getPropertyArray(STOP_ARGS));
- }
- startup.validate();
- shutdown.validate();
- }
- /**
- */
- @Override
- public void start()
- throws Exception
- {
- startup.invoke();
- }
- /**
- */
- @Override
- public void stop()
- throws Exception
- {
- shutdown.invoke();
- }
- /**
- */
- @Override
- public void destroy()
- {
- // Nothing for the moment
- System.err.println("DaemonWrapper: instance " + hashCode() + " destroy");
- }
- // Internal class for wrapping the start/stop methods
- static class Invoker
- {
- private String name;
- private String call;
- private String[] args;
- private Method inst;
- private Class<?> main;
- protected Invoker()
- {
- }
- protected void setClassName(final String name)
- {
- if (this.name == null) {
- this.name = name;
- }
- }
- protected void setMethodName(final String name)
- {
- if (this.call == null) {
- this.call = name;
- }
- }
- protected void addArguments(final String[] args)
- {
- if (args != null) {
- final ArrayList<String> aa = new ArrayList<>();
- if (this.args != null) {
- aa.addAll(Arrays.asList(this.args));
- }
- aa.addAll(Arrays.asList(args));
- this.args = aa.toArray(DaemonConfiguration.EMPTY_STRING_ARRAY);
- }
- }
- protected void invoke()
- throws Exception
- {
- if (name.equals("System") && call.equals("exit")) {
- // Just call a System.exit()
- // The start method was probably installed
- // a shutdown hook.
- System.exit(0);
- }
- else {
- Object obj = null;
- if ((inst.getModifiers() & Modifier.STATIC) == 0) {
- // We only need object instance for non-static methods.
- obj = main.getConstructor().newInstance();
- }
- final Object[] arg = new Object[1];
- arg[0] = args;
- inst.invoke(obj, arg);
- }
- }
- // Load the class using reflection
- protected void validate()
- throws Exception
- {
- /* Check the class name */
- if (name == null) {
- name = "System";
- call = "exit";
- return;
- }
- if (args == null) {
- args = new String[0];
- }
- if (call == null) {
- call = "main";
- }
- // Get the ClassLoader loading this class
- final ClassLoader classLoader = DaemonWrapper.class.getClassLoader();
- Objects.requireNonNull(classLoader, "classLoader");
- final Class<?>[] ca = new Class[1];
- ca[0] = args.getClass();
- // Find the required class
- main = classLoader.loadClass(name);
- if (main == null) {
- throw new ClassNotFoundException(name);
- }
- // Find the required method.
- // NoSuchMethodException will be thrown if matching method
- // is not found.
- inst = main.getMethod(call, ca);
- }
- }
- }