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