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