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 package org.apache.commons.daemon.support;
019
020 import org.apache.commons.daemon.DaemonContext;
021 import org.apache.commons.daemon.DaemonController;
022
023 import java.lang.reflect.Method;
024
025 /*
026 * Used by jsvc for Daemon management.
027 *
028 * @version 1.0 <i>(SVN $Revision: 925053 $)</i>
029 */
030 public final class DaemonLoader
031 {
032
033 // N.B. These static mutable variables need to be accessed using synch.
034 private static Controller controller = null; //@GuardedBy("this")
035 private static Object daemon = null; //@GuardedBy("this")
036 /* Methods to call */
037 private static Method init = null; //@GuardedBy("this")
038 private static Method start = null; //@GuardedBy("this")
039 private static Method stop = null; //@GuardedBy("this")
040 private static Method destroy = null; //@GuardedBy("this")
041
042 public static void version()
043 {
044 System.err.println("java version \"" +
045 System.getProperty("java.version") + "\"");
046 System.err.println(System.getProperty("java.runtime.name") +
047 " (build " +
048 System.getProperty("java.runtime.version") + ")");
049 System.err.println(System.getProperty("java.vm.name") +
050 " (build " +
051 System.getProperty("java.vm.version") +
052 ", " + System.getProperty("java.vm.info") + ")");
053 }
054
055 public static boolean check(String cn)
056 {
057 try {
058 /* Check the class name */
059 if (cn == null)
060 throw new NullPointerException("Null class name specified");
061
062 /* Get the ClassLoader loading this class */
063 ClassLoader cl = DaemonLoader.class.getClassLoader();
064 if (cl == null) {
065 System.err.println("Cannot retrieve ClassLoader instance");
066 return false;
067 }
068
069 /* Find the required class */
070 Class c = cl.loadClass(cn);
071
072 /* This should _never_ happen, but doublechecking doesn't harm */
073 if (c == null)
074 throw new ClassNotFoundException(cn);
075
076 /* Create a new instance of the daemon */
077 c.newInstance();
078
079 } catch (Throwable t) {
080 /* In case we encounter ANY error, we dump the stack trace and
081 * return false (load, start and stop won't be called).
082 */
083 t.printStackTrace(System.err);
084 return false;
085 }
086 /* The class was loaded and instantiated correctly, we can return
087 */
088 return true;
089 }
090
091 public static boolean load(String cn, String ar[])
092 {
093 try {
094 /* Make sure any previous instance is garbage collected */
095 System.gc();
096
097 /* Check if the underlying libray supplied a valid list of
098 arguments */
099 if (ar == null)
100 ar = new String[0];
101
102 /* Check the class name */
103 if (cn == null)
104 throw new NullPointerException("Null class name specified");
105
106 /* Get the ClassLoader loading this class */
107 ClassLoader cl = DaemonLoader.class.getClassLoader();
108 if (cl == null) {
109 System.err.println("Cannot retrieve ClassLoader instance");
110 return false;
111 }
112
113 /* Find the required class */
114 Class c = cl.loadClass(cn);
115
116 /* This should _never_ happen, but doublechecking doesn't harm */
117 if (c == null)
118 throw new ClassNotFoundException(cn);
119
120 /* Check interface */
121 boolean isdaemon = false;
122 try {
123 Class dclass =
124 cl.loadClass("org.apache.commons.daemon.Daemon");
125 isdaemon = dclass.isAssignableFrom(c);
126 } catch (Exception cnfex) {
127 // Swallow if Daemon not found.
128 }
129
130 /* Check methods */
131 Class[]myclass = new Class[1];
132 if (isdaemon) {
133 myclass[0] = DaemonContext.class;
134 }
135 else {
136 myclass[0] = ar.getClass();
137 }
138
139 init = c.getMethod("init", myclass);
140
141 myclass = null;
142 start = c.getMethod("start", myclass);
143 stop = c.getMethod("stop", myclass);
144 destroy = c.getMethod("destroy", myclass);
145
146 /* Create a new instance of the daemon */
147 daemon = c.newInstance();
148
149 if (isdaemon) {
150 /* Create a new controller instance */
151 controller = new Controller();
152
153 /* Set the availability flag in the controller */
154 controller.setAvailable(false);
155
156 /* Create context */
157 Context context = new Context();
158 context.setArguments(ar);
159 context.setController(controller);
160
161 /* Now we want to call the init method in the class */
162 Object arg[] = new Object[1];
163 arg[0] = context;
164 init.invoke(daemon, arg);
165 }
166 else {
167 Object arg[] = new Object[1];
168 arg[0] = ar;
169 init.invoke(daemon, arg);
170 }
171
172 } catch (Throwable t) {
173 /* In case we encounter ANY error, we dump the stack trace and
174 * return false (load, start and stop won't be called).
175 */
176 t.printStackTrace(System.err);
177 return false;
178 }
179 /* The class was loaded and instantiated correctly, we can return */
180 return true;
181 }
182
183 public static boolean start()
184 {
185 try {
186 /* Attempt to start the daemon */
187 Object arg[] = null;
188 start.invoke(daemon, arg);
189
190 /* Set the availability flag in the controller */
191 if (controller != null)
192 controller.setAvailable(true);
193
194 } catch (Throwable t) {
195 /* In case we encounter ANY error, we dump the stack trace and
196 * return false (load, start and stop won't be called).
197 */
198 t.printStackTrace(System.err);
199 return false;
200 }
201 return true;
202 }
203
204 public static boolean stop()
205 {
206 try {
207 /* Set the availability flag in the controller */
208 if (controller != null)
209 controller.setAvailable(false);
210
211 /* Attempt to stop the daemon */
212 Object arg[] = null;
213 stop.invoke(daemon, arg);
214
215 /* Run garbage collector */
216 System.gc();
217
218 }
219 catch (Throwable t) {
220 /* In case we encounter ANY error, we dump the stack trace and
221 * return false (load, start and stop won't be called).
222 */
223 t.printStackTrace(System.err);
224 return false;
225 }
226 return true;
227 }
228
229 public static boolean destroy()
230 {
231 try {
232 /* Attempt to stop the daemon */
233 Object arg[] = null;
234 destroy.invoke(daemon, arg);
235
236 /* Run garbage collector */
237 daemon = null;
238 controller = null;
239 System.gc();
240
241 } catch (Throwable t) {
242 /* In case we encounter ANY error, we dump the stack trace and
243 * return false (load, start and stop won't be called).
244 */
245 t.printStackTrace(System.err);
246 return false;
247 }
248 return true;
249 }
250
251 private static native void shutdown(boolean reload);
252 private static native void failed(String message);
253
254 public static class Controller
255 implements DaemonController
256 {
257
258 private boolean available = false;
259
260 private Controller()
261 {
262 super();
263 this.setAvailable(false);
264 }
265
266 private boolean isAvailable()
267 {
268 synchronized (this) {
269 return this.available;
270 }
271 }
272
273 private void setAvailable(boolean available)
274 {
275 synchronized (this) {
276 this.available = available;
277 }
278 }
279
280 public void shutdown()
281 throws IllegalStateException
282 {
283 synchronized (this) {
284 if (!this.isAvailable()) {
285 throw new IllegalStateException();
286 }
287 else {
288 this.setAvailable(false);
289 DaemonLoader.shutdown(false);
290 }
291 }
292 }
293
294 public void reload()
295 throws IllegalStateException
296 {
297 synchronized (this) {
298 if (!this.isAvailable()) {
299 throw new IllegalStateException();
300 }
301 else {
302 this.setAvailable(false);
303 DaemonLoader.shutdown(true);
304 }
305 }
306 }
307
308 public void fail()
309 throws IllegalStateException
310 {
311 fail(null, null);
312 }
313
314 public void fail(String message)
315 throws IllegalStateException
316 {
317 fail(message, null);
318 }
319
320 public void fail(Exception exception)
321 throws IllegalStateException
322 {
323 fail(null, exception);
324 }
325
326 public void fail(String message, Exception exception)
327 throws IllegalStateException
328 {
329 synchronized (this) {
330 if (!this.isAvailable()) {
331 throw new IllegalStateException();
332 }
333 else {
334 this.setAvailable(false);
335 String msg = message;
336 if (exception != null) {
337 if (msg != null)
338 msg = msg + ": " + exception.toString();
339 else
340 msg = exception.toString();
341 }
342 DaemonLoader.failed(msg);
343 }
344 }
345 }
346
347 }
348
349 public static class Context
350 implements DaemonContext
351 {
352
353 private DaemonController daemonController = null;
354
355 private String[] args = null;
356
357 public DaemonController getController()
358 {
359 return daemonController;
360 }
361
362 public void setController(DaemonController controller)
363 {
364 this.daemonController = controller;
365 }
366
367 public String[] getArguments()
368 {
369 return args;
370 }
371
372 public void setArguments(String[]args)
373 {
374 this.args = args;
375 }
376
377 }
378 }
379