001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 /* @version $Id: Main.java 937350 2010-04-23 16:03:39Z mturk $ */
019
020 package org.apache.commons.daemon.support;
021
022 import java.lang.reflect.Method;
023 import java.util.ArrayList;
024 import java.util.Arrays;
025 import org.apache.commons.daemon.Daemon;
026 import org.apache.commons.daemon.DaemonContext;
027
028 /**
029 * Implementation of the Daemon that allows running
030 * standard applications as daemons.
031 * The applications must have the mechanism to manage
032 * the application lifecycle.
033 *
034 * @version 1.0 <i>(SVN $Revision: 925053 $)</i>
035 * @author Mladen Turk
036 */
037 public class DaemonWrapper implements Daemon
038 {
039
040 private final static String ARGS = "args";
041 private final static String START_CLASS = "start";
042 private final static String START_METHOD = "start.method";
043 private final static String STOP_CLASS = "stop";
044 private final static String STOP_METHOD = "stop.method";
045 private final static String STOP_ARGS = "stop.args";
046 private String configFileName = null;
047 private final DaemonConfiguration config;
048
049 private final Invoker startup;
050 private final Invoker shutdown;
051
052 public DaemonWrapper()
053 {
054 super();
055 config = new DaemonConfiguration();
056 startup = new Invoker();
057 shutdown = new Invoker();
058 }
059
060 /**
061 * Called from DaemonLoader on init stage.
062 * <p>
063 * Accepts the following configuration arguments:
064 * <ul>
065 * <li>-daemon-properties: - load DaemonConfiguration properties from the specified file to act as defaults</li>
066 * <li>-start: set start class name</li>
067 * <li>-start-method: set start method name</li>
068 * <li>-stop: set stop class name</li>
069 * <li>-stop-method: set stop method name</li>
070 * <li>-stop-argument: set optional argument to stop method</li>
071 * <li>Anything else is treated as a startup argument</li>
072 * </ul>
073 * <p>
074 * The following "-daemon-properties" are recognised:
075 * <ul>
076 * <li>args (startup argument)</li>
077 * <li>start</li>
078 * <li>start.method</li>
079 * <li>stop</li>
080 * <li>stop.method</li>
081 * <li>stop.args</li>
082 * </ul>
083 * These are used to set the corresponding item if it has not already been
084 * set by the command arguments. <b>However, note that args and stop.args are
085 * appended to any existing values.</b>
086 */
087 public void init(DaemonContext context)
088 throws Exception
089 {
090 String[] args = context.getArguments();
091
092 if (args != null) {
093 int i;
094 // Parse our arguments and remove them
095 // from the final argument array we are
096 // passing to our child.
097 for (i = 0; i < args.length; i++) {
098 if (args[i].equals("--")) {
099 // Done with argument processing
100 break;
101 }
102 else if (args[i].equals("-daemon-properties")) {
103 if (++i == args.length)
104 throw new IllegalArgumentException(args[i - 1]);
105 configFileName = args[i];
106 }
107 else if (args[i].equals("-start")) {
108 if (++i == args.length)
109 throw new IllegalArgumentException(args[i - 1]);
110 startup.setClassName(args[i]);
111 }
112 else if (args[i].equals("-start-method")) {
113 if (++i == args.length)
114 throw new IllegalArgumentException(args[i - 1]);
115 startup.setMethodName(args[i]);
116 }
117 else if (args[i].equals("-stop")) {
118 if (++i == args.length)
119 throw new IllegalArgumentException(args[i - 1]);
120 shutdown.setClassName(args[i]);
121 }
122 else if (args[i].equals("-stop-method")) {
123 if (++i == args.length)
124 throw new IllegalArgumentException(args[i - 1]);
125 shutdown.setMethodName(args[i]);
126 }
127 else if (args[i].equals("-stop-argument")) {
128 if (++i == args.length)
129 throw new IllegalArgumentException(args[i - 1]);
130 String[] aa = new String[1];
131 aa[0] = args[i];
132 shutdown.addArguments(aa);
133 }
134 else {
135 // This is not our option.
136 // Everything else will be forwarded to the main
137 break;
138 }
139 }
140 if (args.length > i) {
141 String[] copy = new String[args.length - i];
142 System.arraycopy(args, i, copy, 0, copy.length);
143 startup.addArguments(copy);
144 }
145 }
146 if (config.load(configFileName)) {
147 // Setup params if not set via cmdline.
148 startup.setClassName(config.getProperty(START_CLASS));
149 startup.setMethodName(config.getProperty(START_METHOD));
150 // Merge the config with command line arguments
151 startup.addArguments(config.getPropertyArray(ARGS));
152
153 shutdown.setClassName(config.getProperty(STOP_CLASS));
154 shutdown.setMethodName(config.getProperty(STOP_METHOD));
155 shutdown.addArguments(config.getPropertyArray(STOP_ARGS));
156 }
157 startup.validate();
158 shutdown.validate();
159 }
160
161 /**
162 */
163 public void start()
164 throws Exception
165 {
166 startup.invoke();
167 }
168
169 /**
170 */
171 public void stop()
172 throws Exception
173 {
174 shutdown.invoke();
175 }
176
177 /**
178 */
179 public void destroy()
180 {
181 // Nothing for the moment
182 System.err.println("DaemonWrapper: instance " + this.hashCode() + " destroy");
183 }
184
185 // Internal class for wrapping the start/stop methods
186 class Invoker
187 {
188 private String name = null;
189 private String call = null;
190 private String[] args = null;
191 private Method inst = null;
192 private Class main = null;
193
194 protected Invoker()
195 {
196 }
197
198 protected void setClassName(String name)
199 {
200 if (this.name == null)
201 this.name = name;
202 }
203 protected void setMethodName(String name)
204 {
205 if (this.call == null)
206 this.call = name;
207 }
208 protected void addArguments(String[] args)
209 {
210 if (args != null) {
211 ArrayList aa = new ArrayList();
212 if (this.args != null)
213 aa.addAll(Arrays.asList(this.args));
214 aa.addAll(Arrays.asList(args));
215 this.args = (String[])aa.toArray(new String[aa.size()]);
216 }
217 }
218
219 protected void invoke()
220 throws Exception
221 {
222 if (name.equals("System") && call.equals("exit")) {
223 // Just call a System.exit()
224 // The start method was probably installed
225 // a shutdown hook.
226 System.exit(0);
227 }
228 else {
229 Object obj = main.newInstance();
230 Object arg[] = new Object[1];
231
232 arg[0] = args;
233 inst.invoke(obj, arg);
234 }
235 }
236 // Load the class using reflection
237 protected void validate()
238 throws Exception
239 {
240 /* Check the class name */
241 if (name == null) {
242 name = "System";
243 call = "exit";
244 return;
245 }
246 if (args == null)
247 args = new String[0];
248 if (call == null)
249 call = "main";
250
251 // Get the ClassLoader loading this class
252 ClassLoader cl = DaemonWrapper.class.getClassLoader();
253 if (cl == null)
254 throw new NullPointerException("Cannot retrieve ClassLoader instance");
255 Class[] ca = new Class[1];
256 ca[0] = args.getClass();
257 // Find the required class
258 main = cl.loadClass(name);
259 if (main == null)
260 throw new ClassNotFoundException(name);
261 // Find the required method.
262 // NoSuchMethodException will be thrown if matching method
263 // is not found.
264 inst = main.getMethod(call, ca);
265 }
266 }
267 }