View Javadoc

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  
18  /* @version $Id: DaemonWrapper.java 1453245 2013-03-06 09:54:38Z mturk $ */
19  
20  package org.apache.commons.daemon.support;
21  
22  import java.lang.reflect.Method;
23  import java.lang.reflect.Modifier;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import org.apache.commons.daemon.Daemon;
27  import org.apache.commons.daemon.DaemonContext;
28  
29  /**
30   * Implementation of the Daemon that allows running
31   * standard applications as daemons.
32   * The applications must have the mechanism to manage
33   * the application lifecycle.
34   *
35   * @version $Id: DaemonWrapper.java 1453245 2013-03-06 09:54:38Z mturk $
36   * @author Mladen Turk
37   */
38  public class DaemonWrapper implements Daemon
39  {
40  
41      private final static String ARGS            = "args";
42      private final static String START_CLASS     = "start";
43      private final static String START_METHOD    = "start.method";
44      private final static String STOP_CLASS      = "stop";
45      private final static String STOP_METHOD     = "stop.method";
46      private final static String STOP_ARGS       = "stop.args";
47      private String              configFileName  = null;
48      private final DaemonConfiguration config;
49  
50      private final Invoker             startup;
51      private final Invoker             shutdown;
52  
53      public DaemonWrapper()
54      {
55          super();
56          config   = new DaemonConfiguration();
57          startup  = new Invoker();
58          shutdown = new Invoker();
59      }
60  
61      /**
62       * Called from DaemonLoader on init stage.
63       * <p>
64       * Accepts the following configuration arguments:
65       * <ul>
66       * <li>-daemon-properties: - load DaemonConfiguration properties from the specified file to act as defaults</li>
67       * <li>-start: set start class name</li>
68       * <li>-start-method: set start method name</li>
69       * <li>-stop: set stop class name</li>
70       * <li>-stop-method: set stop method name</li>
71       * <li>-stop-argument: set optional argument to stop method</li>
72       * <li>Anything else is treated as a startup argument</li>
73       * </ul>
74       * <p>
75       * The following "-daemon-properties" are recognised:
76       * <ul>
77       * <li>args (startup argument)</li>
78       * <li>start</li>
79       * <li>start.method</li>
80       * <li>stop</li>
81       * <li>stop.method</li>
82       * <li>stop.args</li>
83       * </ul>
84       * These are used to set the corresponding item if it has not already been
85       * set by the command arguments. <b>However, note that args and stop.args are
86       * appended to any existing values.</b>
87       */
88      public void init(DaemonContext context)
89          throws Exception
90      {
91          String[] args = context.getArguments();
92  
93          if (args != null) {
94              int i;
95              // Parse our arguments and remove them
96              // from the final argument array we are
97              // passing to our child.
98              for (i = 0; i < args.length; i++) {
99                  if (args[i].equals("--")) {
100                     // Done with argument processing
101                     break;
102                 }
103                 else if (args[i].equals("-daemon-properties")) {
104                     if (++i == args.length)
105                         throw new IllegalArgumentException(args[i - 1]);
106                     configFileName = args[i];
107                 }
108                 else if (args[i].equals("-start")) {
109                     if (++i == args.length)
110                         throw new IllegalArgumentException(args[i - 1]);
111                     startup.setClassName(args[i]);
112                 }
113                 else if (args[i].equals("-start-method")) {
114                     if (++i == args.length)
115                         throw new IllegalArgumentException(args[i - 1]);
116                     startup.setMethodName(args[i]);
117                 }
118                 else if (args[i].equals("-stop")) {
119                     if (++i == args.length)
120                         throw new IllegalArgumentException(args[i - 1]);
121                     shutdown.setClassName(args[i]);
122                 }
123                 else if (args[i].equals("-stop-method")) {
124                     if (++i == args.length)
125                         throw new IllegalArgumentException(args[i - 1]);
126                     shutdown.setMethodName(args[i]);
127                 }
128                 else if (args[i].equals("-stop-argument")) {
129                     if (++i == args.length)
130                         throw new IllegalArgumentException(args[i - 1]);
131                     String[] aa = new String[1];
132                     aa[0] = args[i];
133                     shutdown.addArguments(aa);
134                 }
135                 else {
136                     // This is not our option.
137                     // Everything else will be forwarded to the main
138                     break;
139                 }
140             }
141             if (args.length > i) {
142                 String[] copy = new String[args.length - i];
143                 System.arraycopy(args, i, copy, 0, copy.length);
144                 startup.addArguments(copy);
145             }
146         }
147         if (config.load(configFileName)) {
148             // Setup params if not set via cmdline.
149             startup.setClassName(config.getProperty(START_CLASS));
150             startup.setMethodName(config.getProperty(START_METHOD));
151             // Merge the config with command line arguments
152             startup.addArguments(config.getPropertyArray(ARGS));
153 
154             shutdown.setClassName(config.getProperty(STOP_CLASS));
155             shutdown.setMethodName(config.getProperty(STOP_METHOD));
156             shutdown.addArguments(config.getPropertyArray(STOP_ARGS));
157         }
158         startup.validate();
159         shutdown.validate();
160     }
161 
162     /**
163      */
164     public void start()
165         throws Exception
166     {
167         startup.invoke();
168     }
169 
170     /**
171      */
172     public void stop()
173         throws Exception
174     {
175         shutdown.invoke();
176     }
177 
178     /**
179      */
180     public void destroy()
181     {
182         // Nothing for the moment
183         System.err.println("DaemonWrapper: instance " + this.hashCode() + " destroy");
184     }
185 
186     // Internal class for wrapping the start/stop methods
187     class Invoker
188     {
189         private String      name = null;
190         private String      call = null;
191         private String[]    args = null;
192         private Method      inst = null;
193         private Class       main = null;
194 
195         protected Invoker()
196         {
197         }
198 
199         protected void setClassName(String name)
200         {
201             if (this.name == null)
202                 this.name = name;
203         }
204         protected void setMethodName(String name)
205         {
206             if (this.call == null)
207                 this.call = name;
208         }
209         protected void addArguments(String[] args)
210         {
211             if (args != null) {
212                 ArrayList aa = new ArrayList();
213                 if (this.args != null)
214                     aa.addAll(Arrays.asList(this.args));
215                 aa.addAll(Arrays.asList(args));
216                 this.args = (String[])aa.toArray(new String[aa.size()]);
217             }
218         }
219 
220         protected void invoke()
221             throws Exception
222         {
223             if (name.equals("System") && call.equals("exit")) {
224                 // Just call a System.exit()
225                 // The start method was probably installed
226                 // a shutdown hook.
227                 System.exit(0);
228             }
229             else {
230                 Object obj   = null;
231                 if ((inst.getModifiers() & Modifier.STATIC) == 0) {
232                     // We only need object instance for non-static methods.
233                     obj = main.newInstance();
234                 }
235                 Object arg[] = new Object[1];
236 
237                 arg[0] = args;
238                 inst.invoke(obj, arg);
239             }
240         }
241         // Load the class using reflection
242         protected void validate()
243             throws Exception
244         {
245             /* Check the class name */
246             if (name == null) {
247                 name = "System";
248                 call = "exit";
249                 return;
250             }
251             if (args == null)
252                 args = new String[0];
253             if (call == null)
254                 call = "main";
255 
256             // Get the ClassLoader loading this class
257             ClassLoader cl = DaemonWrapper.class.getClassLoader();
258             if (cl == null)
259                 throw new NullPointerException("Cannot retrieve ClassLoader instance");
260             Class[] ca = new Class[1];
261             ca[0]      = args.getClass();
262             // Find the required class
263             main = cl.loadClass(name);
264             if (main == null)
265                 throw new ClassNotFoundException(name);
266             // Find the required method.
267             // NoSuchMethodException will be thrown if matching method
268             // is not found.
269             inst = main.getMethod(call, ca);
270         }
271     }
272 }