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