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