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.launcher;
019    
020    import java.awt.Frame;
021    import java.awt.Image;
022    import java.awt.Rectangle;
023    import java.awt.Toolkit;
024    import java.awt.event.WindowAdapter;
025    import java.awt.event.WindowEvent;
026    import java.io.FileOutputStream;
027    import java.io.PrintStream;
028    import java.lang.reflect.Method;
029    
030    /**
031     * A wrapper class that invokes another class'
032     * <code>main(String[])</code>. This particular class uses several system
033     * properties to control features:
034     * <ul>
035     * <li>Redirecting System.out and System.err.
036     * <li>Displaying a minimized window in the Windows taskbar.
037     * </ul>
038     * This class is normally not invoked directly. Instead, it is invoked by the
039     * {@link LaunchTask} class.
040     *
041     * @author Patrick Luby
042     */
043    public class ChildMain extends Thread {
044    
045        //----------------------------------------------------------- Static Fields
046    
047        /**
048         * The appendOutput system property name.
049         */
050        public final static String APPEND_OUTPUT_PROP_NAME =
051            "org.apache.commons.launcher.appendOutput";
052    
053        /**
054         * The displayMiminizedWindow system property name.
055         */
056        public final static String DISPLAY_MINIMIZED_WINDOW_PROP_NAME =
057            "org.apache.commons.launcher.displayMinimizedWindow";
058    
059        /**
060         * The disposeMiminizedWindow system property name.
061         */
062        public final static String DISPOSE_MINIMIZED_WINDOW_PROP_NAME =
063            "org.apache.commons.launcher.disposeMinimizedWindow";
064    
065        /**
066         * The executableName system property name.
067         */
068        public final static String EXECUTABLE_PROP_NAME =
069            "org.apache.commons.launcher.executableName";
070    
071        /**
072         * The heartbeatFile system property name.
073         */
074        public final static String HEARTBEAT_FILE_PROP_NAME =
075            "org.apache.commons.launcher.heartbeatFile";
076    
077        /**
078         * The miminizedWindowTitle system property name.
079         */
080        public final static String MINIMIZED_WINDOW_TITLE_PROP_NAME =
081            "org.apache.commons.launcher.minimizedWindowTitle";
082    
083        /**
084         * The miminizedWindowIcon system property name.
085         */
086        public final static String MINIMIZED_WINDOW_ICON_PROP_NAME=
087            "org.apache.commons.launcher.minimizedWindowIcon";
088    
089        /**
090         * The outputFile system property name.
091         */
092        public final static String OUTPUT_FILE_PROP_NAME =
093            "org.apache.commons.launcher.outputFile";
094    
095        /**
096         * The waitForChild system property name.
097         */
098        public final static String WAIT_FOR_CHILD_PROP_NAME =
099            "org.apache.commons.launcher.waitForChild";
100    
101        //------------------------------------------------------------------ Fields
102    
103        /**
104         * Cached command line arguments
105         */
106        private String[] args = null;
107    
108        //------------------------------------------------------------ Constructors
109    
110        /**
111         * Construct an instance of this {@link Thread} subclass and cache the
112         * args parameter for use by the {@link #run()} method.
113         *
114         * @param group the ThreadGroup to use for this thread
115         * @param args the command line arguments
116         */
117        private ChildMain(ThreadGroup group, String[] args) {
118    
119            super(group, ChildMain.class.getName());
120            this.args = args;
121    
122        }
123    
124        //---------------------------------------------------------- Static Methods
125    
126        /**
127         * Main entry point for the child process. This method should only be
128         * invoked by the {@link LaunchTask} class.
129         *
130         * @param args command line arguments
131         */
132        public static void main(String[] args) {
133    
134            // Invoke the target application in a separate thread so that we
135            // caught any uncaught errors thrown by the target application
136            Thread mainThread = new ChildMain(new ExitOnErrorThreadGroup(ChildMain.class.getName()), args);
137            mainThread.start();
138    
139        }
140    
141        //----------------------------------------------------------------- Methods
142    
143        /**
144         * Invoke the target application.
145         */
146        public void run() {
147    
148            // If there are no command line arguments, do nothing
149            if (args == null || args.length == 0)
150                return;
151    
152            // Invoke the target application
153            try {
154    
155                // Start the thread to check if the parent JVM exits.
156                boolean waitForChild = false;
157                if (System.getProperty(ChildMain.WAIT_FOR_CHILD_PROP_NAME) != null) {
158                    waitForChild = true;
159                    String heartbeatFile = System.getProperty(ChildMain.HEARTBEAT_FILE_PROP_NAME);
160                    ParentListener heartbeat = new ParentListener(heartbeatFile);
161                    // Make the thread a daemon thread so that it does not
162                    // prevent this process from exiting when all of the
163                    // appliation's threads finish.
164                    heartbeat.setDaemon(true);
165                    heartbeat.start();
166                }
167    
168                // If applicable, redirect output and error streams
169                String outputPath = System.getProperty(ChildMain.OUTPUT_FILE_PROP_NAME);
170                if (outputPath != null) {
171                    boolean appendOutput = false;
172                    if (System.getProperty(ChildMain.APPEND_OUTPUT_PROP_NAME) != null)
173                        appendOutput = true;
174                    PrintStream ps = new PrintStream(new FileOutputStream(outputPath, appendOutput), true);
175                    System.setOut(ps);
176                    System.setErr(ps);
177                }
178    
179                // The first argument should be the class that we really want to
180                // invoke. Try to load the class and invoke its main(String[])
181                // method with the first argument shifted out.
182                Class mainClass = Class.forName(args[0]);
183                Class[] paramTypes = new Class[1];
184                Object[] paramValues = new Object[1];
185                String[] params = new String[args.length - 1];
186                // Shift args[0] out of the arguments
187                for (int i = 0; i < params.length; i++)
188                    params[i] = args[i + 1];
189                paramTypes[0] = params.getClass();
190                paramValues[0] = params;
191    
192                // Create the icon window if this is a waitForChild task
193                Frame frame = null;
194                boolean displayMinimizedWindow = false;
195                if (System.getProperty(ChildMain.DISPLAY_MINIMIZED_WINDOW_PROP_NAME) != null)
196                    displayMinimizedWindow = true;
197                String osname = System.getProperty("os.name").toLowerCase();
198                if (displayMinimizedWindow && osname.indexOf("windows") >= 0) {
199                    try {
200                        frame = new Frame();
201                        String title = System.getProperty(ChildMain.MINIMIZED_WINDOW_TITLE_PROP_NAME);
202                        if (title != null)
203                            frame.setTitle(title);
204                        frame.setState(Frame.ICONIFIED);
205                        String icon = System.getProperty(ChildMain.MINIMIZED_WINDOW_ICON_PROP_NAME);
206                        if (icon != null) {
207                            Image iconImage = Toolkit.getDefaultToolkit().createImage(icon);
208                            if (iconImage != null)
209                                frame.setIconImage(iconImage);
210                        }
211    
212                        // Ensure that window always remains minimized
213                        frame.addWindowListener(new ChildWindowAdapter());
214                        Rectangle bounds = frame.getGraphicsConfiguration().getBounds();
215                        int width = (int)frame.getBounds().getWidth();
216                        int height = frame.getInsets().top + frame.getInsets().bottom;
217                        int x = (int)bounds.getWidth() - width;
218                        int y = (int)bounds.getHeight() - height;
219                        frame.setBounds(x, y, width, height);
220                        frame.setResizable(false);
221                        frame.setVisible(true);
222                    } catch(Exception fe) {}
223                }
224    
225                // Invoke the main() method
226                Method mainMethod = mainClass.getDeclaredMethod("main", paramTypes);
227                mainMethod.invoke(null, paramValues);
228    
229                // Close the frame if it exists
230                if (frame != null && System.getProperty(ChildMain.DISPOSE_MINIMIZED_WINDOW_PROP_NAME) != null) {
231                    // Exit this process. Closing or disposing of the window is not
232                    // enough to allow the process to exit.
233                    System.exit(0);
234                }
235    
236            } catch (Throwable t) {
237                String message = t.getMessage();
238                t.printStackTrace();
239                System.exit(1);
240            }
241    
242        }
243    
244        /**
245         * A WindowAdapter subclass that causes the application to exit when its
246         * {@link #windowClosing(WindowEvent)} method is invoked.
247         */
248        private static class ChildWindowAdapter extends WindowAdapter {
249    
250            /**
251             * Invoked when a window is in the process of being closed.
252             *
253             * @param e the event
254             */
255            public void windowClosing(WindowEvent e) {
256    
257                System.exit(0);
258    
259            }
260    
261        }
262    
263    }