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    
023    import java.lang.reflect.Method;
024    
025    /*
026     * Used by jsvc for Daemon management.
027     *
028     * @version 1.0 <i>(SVN $Revision: 925053 $)</i>
029     */
030    public final class DaemonLoader
031    {
032    
033        // N.B. These static mutable variables need to be accessed using synch.
034        private static Controller controller = null; //@GuardedBy("this")
035        private static Object daemon    = null; //@GuardedBy("this")
036        /* Methods to call */
037        private static Method init      = null; //@GuardedBy("this")
038        private static Method start     = null; //@GuardedBy("this")
039        private static Method stop      = null; //@GuardedBy("this")
040        private static Method destroy   = null; //@GuardedBy("this")
041    
042        public static void version()
043        {
044            System.err.println("java version \"" +
045                               System.getProperty("java.version") + "\"");
046            System.err.println(System.getProperty("java.runtime.name") +
047                               " (build " +
048                               System.getProperty("java.runtime.version") + ")");
049            System.err.println(System.getProperty("java.vm.name") +
050                               " (build " +
051                               System.getProperty("java.vm.version") +
052                               ", " + System.getProperty("java.vm.info") + ")");
053        }
054    
055        public static boolean check(String cn)
056        {
057            try {
058                /* Check the class name */
059                if (cn == null)
060                    throw new NullPointerException("Null class name specified");
061    
062                /* Get the ClassLoader loading this class */
063                ClassLoader cl = DaemonLoader.class.getClassLoader();
064                if (cl == null) {
065                    System.err.println("Cannot retrieve ClassLoader instance");
066                    return false;
067                }
068    
069                /* Find the required class */
070                Class c = cl.loadClass(cn);
071    
072                /* This should _never_ happen, but doublechecking doesn't harm */
073                if (c == null)
074                    throw new ClassNotFoundException(cn);
075    
076                /* Create a new instance of the daemon */
077                c.newInstance();
078    
079            } catch (Throwable t) {
080                /* In case we encounter ANY error, we dump the stack trace and
081                 * return false (load, start and stop won't be called).
082                 */
083                t.printStackTrace(System.err);
084                return false;
085            }
086            /* The class was loaded and instantiated correctly, we can return
087             */
088            return true;
089        }
090    
091        public static boolean load(String cn, String ar[])
092        {
093            try {
094                /* Make sure any previous instance is garbage collected */
095                System.gc();
096    
097                /* Check if the underlying libray supplied a valid list of
098                   arguments */
099                if (ar == null)
100                    ar = new String[0];
101    
102                /* Check the class name */
103                if (cn == null)
104                    throw new NullPointerException("Null class name specified");
105    
106                /* Get the ClassLoader loading this class */
107                ClassLoader cl = DaemonLoader.class.getClassLoader();
108                if (cl == null) {
109                    System.err.println("Cannot retrieve ClassLoader instance");
110                    return false;
111                }
112    
113                /* Find the required class */
114                Class c = cl.loadClass(cn);
115    
116                /* This should _never_ happen, but doublechecking doesn't harm */
117                if (c == null)
118                    throw new ClassNotFoundException(cn);
119    
120                /* Check interface */
121                boolean isdaemon = false;
122                try {
123                    Class dclass =
124                        cl.loadClass("org.apache.commons.daemon.Daemon");
125                    isdaemon = dclass.isAssignableFrom(c);
126                } catch (Exception cnfex) {
127                    // Swallow if Daemon not found.
128                }
129    
130                /* Check methods */
131                Class[]myclass = new Class[1];
132                if (isdaemon) {
133                    myclass[0] = DaemonContext.class;
134                }
135                else {
136                    myclass[0] = ar.getClass();
137                }
138    
139                init    = c.getMethod("init", myclass);
140    
141                myclass = null;
142                start   = c.getMethod("start", myclass);
143                stop    = c.getMethod("stop", myclass);
144                destroy = c.getMethod("destroy", myclass);
145    
146                /* Create a new instance of the daemon */
147                daemon = c.newInstance();
148    
149                if (isdaemon) {
150                    /* Create a new controller instance */
151                    controller = new Controller();
152    
153                    /* Set the availability flag in the controller */
154                    controller.setAvailable(false);
155    
156                    /* Create context */
157                    Context context = new Context();
158                    context.setArguments(ar);
159                    context.setController(controller);
160    
161                    /* Now we want to call the init method in the class */
162                    Object arg[] = new Object[1];
163                    arg[0] = context;
164                    init.invoke(daemon, arg);
165                }
166                else {
167                    Object arg[] = new Object[1];
168                    arg[0] = ar;
169                    init.invoke(daemon, arg);
170                }
171    
172            } catch (Throwable t) {
173                /* In case we encounter ANY error, we dump the stack trace and
174                 * return false (load, start and stop won't be called).
175                 */
176                t.printStackTrace(System.err);
177                return false;
178            }
179            /* The class was loaded and instantiated correctly, we can return */
180            return true;
181        }
182    
183        public static boolean start()
184        {
185            try {
186                /* Attempt to start the daemon */
187                Object arg[] = null;
188                start.invoke(daemon, arg);
189    
190                /* Set the availability flag in the controller */
191                if (controller != null)
192                    controller.setAvailable(true);
193    
194            } catch (Throwable t) {
195                /* In case we encounter ANY error, we dump the stack trace and
196                 * return false (load, start and stop won't be called).
197                 */
198                t.printStackTrace(System.err);
199                return false;
200            }
201            return true;
202        }
203    
204        public static boolean stop()
205        {
206            try {
207                /* Set the availability flag in the controller */
208                if (controller != null)
209                    controller.setAvailable(false);
210    
211                /* Attempt to stop the daemon */
212                Object arg[] = null;
213                stop.invoke(daemon, arg);
214    
215                /* Run garbage collector */
216                System.gc();
217    
218            }
219            catch (Throwable t) {
220                /* In case we encounter ANY error, we dump the stack trace and
221                 * return false (load, start and stop won't be called).
222                 */
223                t.printStackTrace(System.err);
224                return false;
225            }
226            return true;
227        }
228    
229        public static boolean destroy()
230        {
231            try {
232                /* Attempt to stop the daemon */
233                Object arg[] = null;
234                destroy.invoke(daemon, arg);
235    
236                /* Run garbage collector */
237                daemon = null;
238                controller = null;
239                System.gc();
240    
241            } catch (Throwable t) {
242                /* In case we encounter ANY error, we dump the stack trace and
243                 * return false (load, start and stop won't be called).
244                 */
245                t.printStackTrace(System.err);
246                return false;
247            }
248            return true;
249        }
250    
251        private static native void shutdown(boolean reload);
252        private static native void failed(String message);
253    
254        public static class Controller
255            implements DaemonController
256        {
257    
258            private boolean available = false;
259    
260            private Controller()
261            {
262                super();
263                this.setAvailable(false);
264            }
265    
266            private boolean isAvailable()
267            {
268                synchronized (this) {
269                    return this.available;
270                }
271            }
272    
273            private void setAvailable(boolean available)
274            {
275                synchronized (this) {
276                    this.available = available;
277                }
278            }
279    
280            public void shutdown()
281                throws IllegalStateException
282            {
283                synchronized (this) {
284                    if (!this.isAvailable()) {
285                        throw new IllegalStateException();
286                    }
287                    else {
288                        this.setAvailable(false);
289                        DaemonLoader.shutdown(false);
290                    }
291                }
292            }
293    
294            public void reload()
295                throws IllegalStateException
296            {
297                synchronized (this) {
298                    if (!this.isAvailable()) {
299                        throw new IllegalStateException();
300                    }
301                    else {
302                        this.setAvailable(false);
303                        DaemonLoader.shutdown(true);
304                    }
305                }
306            }
307    
308            public void fail()
309                throws IllegalStateException
310            {
311                fail(null, null);
312            }
313    
314            public void fail(String message)
315                throws IllegalStateException
316            {
317                fail(message, null);
318            }
319    
320            public void fail(Exception exception)
321                throws IllegalStateException
322            {
323                fail(null, exception);
324            }
325    
326            public void fail(String message, Exception exception)
327                throws IllegalStateException
328            {
329                synchronized (this) {
330                    if (!this.isAvailable()) {
331                        throw new IllegalStateException();
332                    }
333                    else {
334                        this.setAvailable(false);
335                        String msg = message;
336                        if (exception != null) {
337                            if (msg != null)
338                                msg = msg + ": " + exception.toString();
339                            else
340                                msg = exception.toString();
341                        }
342                        DaemonLoader.failed(msg);
343                    }
344                }
345            }
346    
347        }
348    
349        public static class Context
350            implements DaemonContext
351        {
352    
353            private DaemonController daemonController = null;
354    
355            private String[] args = null;
356    
357            public DaemonController getController()
358            {
359                return daemonController;
360            }
361    
362            public void setController(DaemonController controller)
363            {
364                this.daemonController = controller;
365            }
366    
367            public String[] getArguments()
368            {
369                return args;
370            }
371    
372            public void setArguments(String[]args)
373            {
374                this.args = args;
375            }
376    
377        }
378    }
379