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 }