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