DaemonLoader.java

  1. /*
  2.  *  Licensed to the Apache Software Foundation (ASF) under one or more
  3.  *  contributor license agreements.  See the NOTICE file distributed with
  4.  *  this work for additional information regarding copyright ownership.
  5.  *  The ASF licenses this file to You under the Apache License, Version 2.0
  6.  *  (the "License"); you may not use this file except in compliance with
  7.  *  the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  *  Unless required by applicable law or agreed to in writing, software
  12.  *  distributed under the License is distributed on an "AS IS" BASIS,
  13.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  *  See the License for the specific language governing permissions and
  15.  *  limitations under the License.
  16.  */

  17. package org.apache.commons.daemon.support;

  18. import org.apache.commons.daemon.DaemonContext;
  19. import org.apache.commons.daemon.DaemonController;
  20. import org.apache.commons.daemon.DaemonInitException;

  21. import java.lang.reflect.InvocationTargetException;
  22. import java.lang.reflect.Method;
  23. import java.util.Objects;

  24. /**
  25.  * Used by jsvc for Daemon management.
  26.  */
  27. public final class DaemonLoader
  28. {

  29.     // N.B. These static mutable variables need to be accessed using synch.
  30.     private static Controller controller; //@GuardedBy("this")
  31.     private static Object daemon; //@GuardedBy("this")
  32.     /* Methods to call */
  33.     private static Method init; //@GuardedBy("this")
  34.     private static Method start; //@GuardedBy("this")
  35.     private static Method stop; //@GuardedBy("this")
  36.     private static Method destroy; //@GuardedBy("this")
  37.     private static Method signal; //@GuardedBy("this")

  38.     /**
  39.      * Prints version information to {@link System#err}.
  40.      */
  41.     public static void version()
  42.     {
  43.         System.err.println("java version \"" +
  44.                            System.getProperty("java.version") + "\"");
  45.         System.err.println(System.getProperty("java.runtime.name") +
  46.                            " (build " +
  47.                            System.getProperty("java.runtime.version") + ")");
  48.         System.err.println(System.getProperty("java.vm.name") +
  49.                            " (build " +
  50.                            System.getProperty("java.vm.version") +
  51.                            ", " + System.getProperty("java.vm.info") + ")");
  52.         System.err.println("commons daemon version \"" +
  53.                 System.getProperty("commons.daemon.version") + "\"");
  54.         System.err.println("commons daemon process (id: " +
  55.                            System.getProperty("commons.daemon.process.id") +
  56.                            ", parent: " +
  57.                            System.getProperty("commons.daemon.process.parent") + ")");
  58.     }

  59.     /**
  60.      * Checks whether the given class name can be instantiated with a zero-argument constructor.
  61.      *
  62.      * @param className The class name.
  63.      * @return true if the given class name can be instantiated, false otherwise.
  64.      */
  65.     public static boolean check(final String className)
  66.     {
  67.         try {
  68.             /* Check the class name */
  69.             Objects.requireNonNull(className, "className");
  70.             /* Gets the ClassLoader loading this class */
  71.             final ClassLoader cl = DaemonLoader.class.getClassLoader();
  72.             if (cl == null) {
  73.                 System.err.println("Cannot retrieve ClassLoader instance");
  74.                 return false;
  75.             }

  76.             /* Find the required class */
  77.             final Class<?> c = cl.loadClass(className);

  78.             /* This should _never_ happen, but double-checking doesn't harm */
  79.             if (c == null) {
  80.                 throw new ClassNotFoundException(className);
  81.             }

  82.             /* Create a new instance of the daemon */
  83.             c.getConstructor().newInstance();

  84.         } catch (final Throwable t) {
  85.             /* In case we encounter ANY error, we dump the stack trace and
  86.              * return false (load, start and stop won't be called).
  87.              */
  88.             t.printStackTrace(System.err);
  89.             return false;
  90.         }
  91.         /* The class was loaded and instantiated correctly, we can return
  92.          */
  93.         return true;
  94.     }

  95.     /**
  96.      * Invokes the wrapped {@code signal} method.
  97.      *
  98.      * @return whether the call succeeded.
  99.      */
  100.     public static boolean signal()
  101.     {
  102.         try {
  103.             if (signal != null) {
  104.                 signal.invoke(daemon);
  105.                 return true;
  106.             }
  107.             System.out.println("Daemon doesn't support signaling");
  108.         } catch (final Throwable ex) {
  109.             System.err.println("Cannot send signal: " + ex);
  110.             ex.printStackTrace(System.err);
  111.         }
  112.         return false;
  113.     }

  114.     /**
  115.      * Loads the given class by name, initializing wrapper methods.
  116.      *
  117.      * @param className The class name to load.
  118.      * @param args arguments for the context.
  119.      * @return whether the operation succeeded.
  120.      */
  121.     public static boolean load(final String className, String[] args)
  122.     {
  123.         try {
  124.             /* Check if the underlying library supplied a valid list of
  125.                arguments */
  126.             if (args == null) {
  127.                 args = new String[0];
  128.             }

  129.             /* Check the class name */
  130.             Objects.requireNonNull(className, "className");

  131.             /* Gets the ClassLoader loading this class */
  132.             final ClassLoader cl = DaemonLoader.class.getClassLoader();
  133.             if (cl == null) {
  134.                 System.err.println("Cannot retrieve ClassLoader instance");
  135.                 return false;
  136.             }
  137.             final Class<?> c;
  138.             if (className.charAt(0) == '@') {
  139.                 /* Wrap the class with DaemonWrapper
  140.                  * and modify arguments to include the real class name.
  141.                  */
  142.                 c = DaemonWrapper.class;
  143.                 final String[] a = new String[args.length + 2];
  144.                 a[0] = "-start";
  145.                 a[1] = className.substring(1);
  146.                 System.arraycopy(args, 0, a, 2, args.length);
  147.                 args = a;
  148.             }
  149.             else {
  150.                 c = cl.loadClass(className);
  151.             }
  152.             /* This should _never_ happen, but double-checking doesn't harm */
  153.             if (c == null) {
  154.                 throw new ClassNotFoundException(className);
  155.             }
  156.             /* Check interfaces */
  157.             boolean isdaemon = false;

  158.             try {
  159.                 final Class<?> dclass = cl.loadClass("org.apache.commons.daemon.Daemon");
  160.                 isdaemon = dclass.isAssignableFrom(c);
  161.             }
  162.             catch (final Exception ignored) {
  163.                 // Swallow if Daemon not found.
  164.             }

  165.             /* Check methods */
  166.             final Class<?>[] myclass = new Class[1];
  167.             if (isdaemon) {
  168.                 myclass[0] = DaemonContext.class;
  169.             }
  170.             else {
  171.                 myclass[0] = args.getClass();
  172.             }

  173.             init    = c.getMethod("init", myclass);
  174.             start   = c.getMethod("start");
  175.             stop    = c.getMethod("stop");
  176.             destroy = c.getMethod("destroy");

  177.             try {
  178.                 signal = c.getMethod("signal");
  179.             } catch (final NoSuchMethodException ignored) {
  180.                 // Signaling will be disabled.
  181.             }

  182.             /* Create a new instance of the daemon */
  183.             daemon = c.getConstructor().newInstance();

  184.             if (isdaemon) {
  185.                 // Create a new controller instance
  186.                 controller = new Controller();

  187.                 // Set the availability flag in the controller
  188.                 controller.setAvailable(false);

  189.                 /* Create context */
  190.                 final Context context = new Context();
  191.                 context.setArguments(args);
  192.                 context.setController(controller);

  193.                 // Now we want to call the init method in the class
  194.                 final Object[] arg = new Object[1];
  195.                 arg[0] = context;
  196.                 init.invoke(daemon, arg);
  197.             }
  198.             else {
  199.                 final Object[] arg = new Object[1];
  200.                 arg[0] = args;
  201.                 init.invoke(daemon, arg);
  202.             }

  203.         }
  204.         catch (final InvocationTargetException e) {
  205.             final Throwable thrown = e.getTargetException();
  206.             // DaemonInitExceptions can fail with a nicer message
  207.             if (thrown instanceof DaemonInitException) {
  208.                 failed(((DaemonInitException) thrown).getMessageWithCause());
  209.             }
  210.             else {
  211.                 thrown.printStackTrace(System.err);
  212.             }
  213.             return false;
  214.         }
  215.         catch (final Throwable t) {
  216.             // In case we encounter ANY error, we dump the stack trace and
  217.             // return false (load, start and stop won't be called).
  218.             t.printStackTrace(System.err);
  219.             return false;
  220.         }
  221.         // The class was loaded and instantiated correctly, we can return
  222.         return true;
  223.     }

  224.     /**
  225.      * Invokes the wrapped {@code start} method.
  226.      *
  227.      * @return whether the call succeeded.
  228.      */
  229.     public static boolean start()
  230.     {
  231.         try {
  232.             // Attempt to start the daemon
  233.             start.invoke(daemon);
  234.             // Set the availability flag in the controller
  235.             if (controller != null) {
  236.                 controller.setAvailable(true);
  237.             }
  238.         } catch (final Throwable t) {
  239.             // In case we encounter ANY error, we dump the stack trace and
  240.             // return false (load, start and stop won't be called).
  241.             t.printStackTrace(System.err);
  242.             return false;
  243.         }
  244.         return true;
  245.     }

  246.     /**
  247.      * Invokes the wrapped {@code stop} method.
  248.      *
  249.      * @return whether the call succeeded.
  250.      */
  251.     public static boolean stop()
  252.     {
  253.         try {
  254.             // Set the availability flag in the controller
  255.             if (controller != null) {
  256.                 controller.setAvailable(false);
  257.             }
  258.             /* Attempt to stop the daemon */
  259.             stop.invoke(daemon);
  260.         }
  261.         catch (final Throwable t) {
  262.             // In case we encounter ANY error, we dump the stack trace and
  263.             // return false (load, start and stop won't be called).
  264.             t.printStackTrace(System.err);
  265.             return false;
  266.         }
  267.         return true;
  268.     }

  269.     /**
  270.      * Invokes the wrapped {@code destroy} method.
  271.      *
  272.      * @return whether the call succeeded.
  273.      */
  274.     public static boolean destroy()
  275.     {
  276.         try {
  277.             /* Attempt to stop the daemon */
  278.             destroy.invoke(daemon);
  279.             daemon = null;
  280.             controller = null;
  281.         } catch (final Throwable t) {
  282.             // In case we encounter ANY error, we dump the stack trace and
  283.             // return false (load, start and stop won't be called).
  284.             t.printStackTrace(System.err);
  285.             return false;
  286.         }
  287.         return true;
  288.     }

  289.     private static native void shutdown(boolean reload);
  290.     private static native void failed(String message);

  291.     /**
  292.      * A DaemonController that acts on the the global {@link DaemonLoader} state.
  293.      */
  294.     public static class Controller
  295.         implements DaemonController
  296.     {

  297.         private boolean available;

  298.         private Controller()
  299.         {
  300.             setAvailable(false);
  301.         }

  302.         private boolean isAvailable()
  303.         {
  304.             synchronized (this) {
  305.                 return this.available;
  306.             }
  307.         }

  308.         private void setAvailable(final boolean available)
  309.         {
  310.             synchronized (this) {
  311.                 this.available = available;
  312.             }
  313.         }

  314.         @Override
  315.         public void shutdown()
  316.             throws IllegalStateException
  317.         {
  318.             synchronized (this) {
  319.                 if (!isAvailable()) {
  320.                     throw new IllegalStateException();
  321.                 }
  322.                 setAvailable(false);
  323.                 DaemonLoader.shutdown(false);
  324.             }
  325.         }

  326.         @Override
  327.         public void reload()
  328.             throws IllegalStateException
  329.         {
  330.             synchronized (this) {
  331.                 if (!isAvailable()) {
  332.                     throw new IllegalStateException();
  333.                 }
  334.                 setAvailable(false);
  335.                 DaemonLoader.shutdown(true);
  336.             }
  337.         }

  338.         @Override
  339.         public void fail()
  340.         {
  341.             fail(null, null);
  342.         }

  343.         @Override
  344.         public void fail(final String message)
  345.         {
  346.             fail(message, null);
  347.         }

  348.         @Override
  349.         public void fail(final Exception exception)
  350.         {
  351.             fail(null, exception);
  352.         }

  353.         @Override
  354.         public void fail(final String message, final Exception exception)
  355.         {
  356.             synchronized (this) {
  357.                 setAvailable(false);
  358.                 String msg = message;
  359.                 if (exception != null) {
  360.                     if (msg != null) {
  361.                         msg = msg + ": " + exception.toString();
  362.                     }
  363.                     else {
  364.                         msg = exception.toString();
  365.                     }
  366.                 }
  367.                 failed(msg);
  368.             }
  369.         }

  370.     }

  371.     /**
  372.      * A concrete {@link DaemonContext} that acts as a simple value container.
  373.      */
  374.     public static class Context
  375.         implements DaemonContext
  376.     {

  377.         private DaemonController daemonController;

  378.         private String[] args;

  379.         @Override
  380.         public DaemonController getController()
  381.         {
  382.             return daemonController;
  383.         }

  384.         /**
  385.          * Sets the daemon controller.
  386.          *
  387.          * @param controller the daemon controller.
  388.          */
  389.         public void setController(final DaemonController controller)
  390.         {
  391.             this.daemonController = controller;
  392.         }

  393.         @Override
  394.         public String[] getArguments()
  395.         {
  396.             return args;
  397.         }

  398.         /**
  399.          * Sets arguments. Note that this implementation doesn't currently make a defensive copy.
  400.          *
  401.          * @param args arguments.
  402.          */
  403.         public void setArguments(final String[] args)
  404.         {
  405.             this.args = args;
  406.         }

  407.     }
  408. }