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: Main.java 937350 2010-04-23 16:03:39Z mturk $ */
019    
020    package org.apache.commons.daemon.support;
021    
022    import java.lang.reflect.Method;
023    import java.util.ArrayList;
024    import java.util.Arrays;
025    import org.apache.commons.daemon.Daemon;
026    import 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 1.0 <i>(SVN $Revision: 925053 $)</i>
035     * @author Mladen Turk
036     */
037    public 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    }