View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.launcher;
19  
20  import java.awt.Frame;
21  import java.awt.Image;
22  import java.awt.Rectangle;
23  import java.awt.Toolkit;
24  import java.awt.event.WindowAdapter;
25  import java.awt.event.WindowEvent;
26  import java.io.FileOutputStream;
27  import java.io.PrintStream;
28  import java.lang.reflect.Method;
29  
30  /**
31   * A wrapper class that invokes another class'
32   * <code>main(String[])</code>. This particular class uses several system
33   * properties to control features:
34   * <ul>
35   * <li>Redirecting System.out and System.err.
36   * <li>Displaying a minimized window in the Windows taskbar.
37   * </ul>
38   * This class is normally not invoked directly. Instead, it is invoked by the
39   * {@link LaunchTask} class.
40   *
41   * @author Patrick Luby
42   */
43  public class ChildMain extends Thread {
44  
45      //----------------------------------------------------------- Static Fields
46  
47      /**
48       * The appendOutput system property name.
49       */
50      public final static String APPEND_OUTPUT_PROP_NAME =
51          "org.apache.commons.launcher.appendOutput";
52  
53      /**
54       * The displayMiminizedWindow system property name.
55       */
56      public final static String DISPLAY_MINIMIZED_WINDOW_PROP_NAME =
57          "org.apache.commons.launcher.displayMinimizedWindow";
58  
59      /**
60       * The disposeMiminizedWindow system property name.
61       */
62      public final static String DISPOSE_MINIMIZED_WINDOW_PROP_NAME =
63          "org.apache.commons.launcher.disposeMinimizedWindow";
64  
65      /**
66       * The executableName system property name.
67       */
68      public final static String EXECUTABLE_PROP_NAME =
69          "org.apache.commons.launcher.executableName";
70  
71      /**
72       * The heartbeatFile system property name.
73       */
74      public final static String HEARTBEAT_FILE_PROP_NAME =
75          "org.apache.commons.launcher.heartbeatFile";
76  
77      /**
78       * The miminizedWindowTitle system property name.
79       */
80      public final static String MINIMIZED_WINDOW_TITLE_PROP_NAME =
81          "org.apache.commons.launcher.minimizedWindowTitle";
82  
83      /**
84       * The miminizedWindowIcon system property name.
85       */
86      public final static String MINIMIZED_WINDOW_ICON_PROP_NAME=
87          "org.apache.commons.launcher.minimizedWindowIcon";
88  
89      /**
90       * The outputFile system property name.
91       */
92      public final static String OUTPUT_FILE_PROP_NAME =
93          "org.apache.commons.launcher.outputFile";
94  
95      /**
96       * The waitForChild system property name.
97       */
98      public final static String WAIT_FOR_CHILD_PROP_NAME =
99          "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 }