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/* @version $Id: DaemonWrapper.java 1204007 2011-11-19 16:10:29Z ggregory $ */ 019 020package org.apache.commons.daemon.support; 021 022import java.lang.reflect.Method; 023import java.util.ArrayList; 024import java.util.Arrays; 025import org.apache.commons.daemon.Daemon; 026import org.apache.commons.daemon.DaemonContext; 027 028/** 029 * Implementation of the Daemon that allows running 030 * standard applications as daemons. 031 * The applications must have the mechanism to manage 032 * the application lifecycle. 033 * 034 * @version $Id: DaemonWrapper.java 1204007 2011-11-19 16:10:29Z ggregory $ 035 * @author Mladen Turk 036 */ 037public class DaemonWrapper implements Daemon 038{ 039 040 private final static String ARGS = "args"; 041 private final static String START_CLASS = "start"; 042 private final static String START_METHOD = "start.method"; 043 private final static String STOP_CLASS = "stop"; 044 private final static String STOP_METHOD = "stop.method"; 045 private final static String STOP_ARGS = "stop.args"; 046 private String configFileName = null; 047 private final DaemonConfiguration config; 048 049 private final Invoker startup; 050 private final Invoker shutdown; 051 052 public DaemonWrapper() 053 { 054 super(); 055 config = new DaemonConfiguration(); 056 startup = new Invoker(); 057 shutdown = new Invoker(); 058 } 059 060 /** 061 * Called from DaemonLoader on init stage. 062 * <p> 063 * Accepts the following configuration arguments: 064 * <ul> 065 * <li>-daemon-properties: - load DaemonConfiguration properties from the specified file to act as defaults</li> 066 * <li>-start: set start class name</li> 067 * <li>-start-method: set start method name</li> 068 * <li>-stop: set stop class name</li> 069 * <li>-stop-method: set stop method name</li> 070 * <li>-stop-argument: set optional argument to stop method</li> 071 * <li>Anything else is treated as a startup argument</li> 072 * </ul> 073 * <p> 074 * The following "-daemon-properties" are recognised: 075 * <ul> 076 * <li>args (startup argument)</li> 077 * <li>start</li> 078 * <li>start.method</li> 079 * <li>stop</li> 080 * <li>stop.method</li> 081 * <li>stop.args</li> 082 * </ul> 083 * These are used to set the corresponding item if it has not already been 084 * set by the command arguments. <b>However, note that args and stop.args are 085 * appended to any existing values.</b> 086 */ 087 public void init(DaemonContext context) 088 throws Exception 089 { 090 String[] args = context.getArguments(); 091 092 if (args != null) { 093 int i; 094 // Parse our arguments and remove them 095 // from the final argument array we are 096 // passing to our child. 097 for (i = 0; i < args.length; i++) { 098 if (args[i].equals("--")) { 099 // Done with argument processing 100 break; 101 } 102 else if (args[i].equals("-daemon-properties")) { 103 if (++i == args.length) 104 throw new IllegalArgumentException(args[i - 1]); 105 configFileName = args[i]; 106 } 107 else if (args[i].equals("-start")) { 108 if (++i == args.length) 109 throw new IllegalArgumentException(args[i - 1]); 110 startup.setClassName(args[i]); 111 } 112 else if (args[i].equals("-start-method")) { 113 if (++i == args.length) 114 throw new IllegalArgumentException(args[i - 1]); 115 startup.setMethodName(args[i]); 116 } 117 else if (args[i].equals("-stop")) { 118 if (++i == args.length) 119 throw new IllegalArgumentException(args[i - 1]); 120 shutdown.setClassName(args[i]); 121 } 122 else if (args[i].equals("-stop-method")) { 123 if (++i == args.length) 124 throw new IllegalArgumentException(args[i - 1]); 125 shutdown.setMethodName(args[i]); 126 } 127 else if (args[i].equals("-stop-argument")) { 128 if (++i == args.length) 129 throw new IllegalArgumentException(args[i - 1]); 130 String[] aa = new String[1]; 131 aa[0] = args[i]; 132 shutdown.addArguments(aa); 133 } 134 else { 135 // This is not our option. 136 // Everything else will be forwarded to the main 137 break; 138 } 139 } 140 if (args.length > i) { 141 String[] copy = new String[args.length - i]; 142 System.arraycopy(args, i, copy, 0, copy.length); 143 startup.addArguments(copy); 144 } 145 } 146 if (config.load(configFileName)) { 147 // Setup params if not set via cmdline. 148 startup.setClassName(config.getProperty(START_CLASS)); 149 startup.setMethodName(config.getProperty(START_METHOD)); 150 // Merge the config with command line arguments 151 startup.addArguments(config.getPropertyArray(ARGS)); 152 153 shutdown.setClassName(config.getProperty(STOP_CLASS)); 154 shutdown.setMethodName(config.getProperty(STOP_METHOD)); 155 shutdown.addArguments(config.getPropertyArray(STOP_ARGS)); 156 } 157 startup.validate(); 158 shutdown.validate(); 159 } 160 161 /** 162 */ 163 public void start() 164 throws Exception 165 { 166 startup.invoke(); 167 } 168 169 /** 170 */ 171 public void stop() 172 throws Exception 173 { 174 shutdown.invoke(); 175 } 176 177 /** 178 */ 179 public void destroy() 180 { 181 // Nothing for the moment 182 System.err.println("DaemonWrapper: instance " + this.hashCode() + " destroy"); 183 } 184 185 // Internal class for wrapping the start/stop methods 186 class Invoker 187 { 188 private String name = null; 189 private String call = null; 190 private String[] args = null; 191 private Method inst = null; 192 private Class main = null; 193 194 protected Invoker() 195 { 196 } 197 198 protected void setClassName(String name) 199 { 200 if (this.name == null) 201 this.name = name; 202 } 203 protected void setMethodName(String name) 204 { 205 if (this.call == null) 206 this.call = name; 207 } 208 protected void addArguments(String[] args) 209 { 210 if (args != null) { 211 ArrayList aa = new ArrayList(); 212 if (this.args != null) 213 aa.addAll(Arrays.asList(this.args)); 214 aa.addAll(Arrays.asList(args)); 215 this.args = (String[])aa.toArray(new String[aa.size()]); 216 } 217 } 218 219 protected void invoke() 220 throws Exception 221 { 222 if (name.equals("System") && call.equals("exit")) { 223 // Just call a System.exit() 224 // The start method was probably installed 225 // a shutdown hook. 226 System.exit(0); 227 } 228 else { 229 Object obj = main.newInstance(); 230 Object arg[] = new Object[1]; 231 232 arg[0] = args; 233 inst.invoke(obj, arg); 234 } 235 } 236 // Load the class using reflection 237 protected void validate() 238 throws Exception 239 { 240 /* Check the class name */ 241 if (name == null) { 242 name = "System"; 243 call = "exit"; 244 return; 245 } 246 if (args == null) 247 args = new String[0]; 248 if (call == null) 249 call = "main"; 250 251 // Get the ClassLoader loading this class 252 ClassLoader cl = DaemonWrapper.class.getClassLoader(); 253 if (cl == null) 254 throw new NullPointerException("Cannot retrieve ClassLoader instance"); 255 Class[] ca = new Class[1]; 256 ca[0] = args.getClass(); 257 // Find the required class 258 main = cl.loadClass(name); 259 if (main == null) 260 throw new ClassNotFoundException(name); 261 // Find the required method. 262 // NoSuchMethodException will be thrown if matching method 263 // is not found. 264 inst = main.getMethod(call, ca); 265 } 266 } 267}