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 }