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