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