1. /*
  2. * Copyright 1999-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.launcher;
  17. import java.awt.Frame;
  18. import java.awt.Image;
  19. import java.awt.Rectangle;
  20. import java.awt.Toolkit;
  21. import java.awt.event.WindowAdapter;
  22. import java.awt.event.WindowEvent;
  23. import java.io.FileOutputStream;
  24. import java.io.PrintStream;
  25. import java.lang.reflect.Method;
  26. /**
  27. * A wrapper class that invokes another class'
  28. * <code>main(String[])</code>. This particular class uses several system
  29. * properties to control features:
  30. * <ul>
  31. * <li>Redirecting System.out and System.err.
  32. * <li>Displaying a minimized window in the Windows taskbar.
  33. * </ul>
  34. * This class is normally not invoked directly. Instead, it is invoked by the
  35. * {@link LaunchTask} class.
  36. *
  37. * @author Patrick Luby
  38. */
  39. public class ChildMain extends Thread {
  40. //----------------------------------------------------------- Static Fields
  41. /**
  42. * The appendOutput system property name.
  43. */
  44. public final static String APPEND_OUTPUT_PROP_NAME =
  45. "org.apache.commons.launcher.appendOutput";
  46. /**
  47. * The displayMiminizedWindow system property name.
  48. */
  49. public final static String DISPLAY_MINIMIZED_WINDOW_PROP_NAME =
  50. "org.apache.commons.launcher.displayMinimizedWindow";
  51. /**
  52. * The disposeMiminizedWindow system property name.
  53. */
  54. public final static String DISPOSE_MINIMIZED_WINDOW_PROP_NAME =
  55. "org.apache.commons.launcher.disposeMinimizedWindow";
  56. /**
  57. * The executableName system property name.
  58. */
  59. public final static String EXECUTABLE_PROP_NAME =
  60. "org.apache.commons.launcher.executableName";
  61. /**
  62. * The heartbeatFile system property name.
  63. */
  64. public final static String HEARTBEAT_FILE_PROP_NAME =
  65. "org.apache.commons.launcher.heartbeatFile";
  66. /**
  67. * The miminizedWindowTitle system property name.
  68. */
  69. public final static String MINIMIZED_WINDOW_TITLE_PROP_NAME =
  70. "org.apache.commons.launcher.minimizedWindowTitle";
  71. /**
  72. * The miminizedWindowIcon system property name.
  73. */
  74. public final static String MINIMIZED_WINDOW_ICON_PROP_NAME=
  75. "org.apache.commons.launcher.minimizedWindowIcon";
  76. /**
  77. * The outputFile system property name.
  78. */
  79. public final static String OUTPUT_FILE_PROP_NAME =
  80. "org.apache.commons.launcher.outputFile";
  81. /**
  82. * The waitForChild system property name.
  83. */
  84. public final static String WAIT_FOR_CHILD_PROP_NAME =
  85. "org.apache.commons.launcher.waitForChild";
  86. //------------------------------------------------------------------ Fields
  87. /**
  88. * Cached command line arguments
  89. */
  90. private String[] args = null;
  91. //------------------------------------------------------------ Constructors
  92. /**
  93. * Construct an instance of this {@link Thread} subclass and cache the
  94. * args parameter for use by the {@link #run()} method.
  95. *
  96. * @param group the ThreadGroup to use for this thread
  97. * @param args the command line arguments
  98. */
  99. private ChildMain(ThreadGroup group, String[] args) {
  100. super(group, ChildMain.class.getName());
  101. this.args = args;
  102. }
  103. //---------------------------------------------------------- Static Methods
  104. /**
  105. * Main entry point for the child process. This method should only be
  106. * invoked by the {@link LaunchTask} class.
  107. *
  108. * @param args command line arguments
  109. */
  110. public static void main(String[] args) {
  111. // Invoke the target application in a separate thread so that we
  112. // caught any uncaught errors thrown by the target application
  113. Thread mainThread = new ChildMain(new ExitOnErrorThreadGroup(ChildMain.class.getName()), args);
  114. mainThread.start();
  115. }
  116. //----------------------------------------------------------------- Methods
  117. /**
  118. * Invoke the target application.
  119. *
  120. * @param args command line arguments
  121. */
  122. public void run() {
  123. // If there are no arguments, do nothing
  124. if (args == null || args.length == 0)
  125. return;
  126. // Invoke the target application
  127. try {
  128. // Start the thread to check if the parent JVM exits.
  129. boolean waitForChild = false;
  130. if (System.getProperty(ChildMain.WAIT_FOR_CHILD_PROP_NAME) != null) {
  131. waitForChild = true;
  132. String heartbeatFile = System.getProperty(ChildMain.HEARTBEAT_FILE_PROP_NAME);
  133. ParentListener heartbeat = new ParentListener(heartbeatFile);
  134. // Make the thread a daemon thread so that it does not
  135. // prevent this process from exiting when all of the
  136. // appliation's threads finish.
  137. heartbeat.setDaemon(true);
  138. heartbeat.start();
  139. }
  140. // If applicable, redirect output and error streams
  141. String outputPath = System.getProperty(ChildMain.OUTPUT_FILE_PROP_NAME);
  142. if (outputPath != null) {
  143. boolean appendOutput = false;
  144. if (System.getProperty(ChildMain.APPEND_OUTPUT_PROP_NAME) != null)
  145. appendOutput = true;
  146. PrintStream ps = new PrintStream(new FileOutputStream(outputPath, appendOutput), true);
  147. System.setOut(ps);
  148. System.setErr(ps);
  149. }
  150. // The first argument should be the class that we really want to
  151. // invoke. Try to load the class and invoke its main(String[])
  152. // method with the first argument shifted out.
  153. Class mainClass = Class.forName(args[0]);
  154. Class[] paramTypes = new Class[1];
  155. Object[] paramValues = new Object[1];
  156. String[] params = new String[args.length - 1];
  157. // Shift args[0] out of the arguments
  158. for (int i = 0; i < params.length; i++)
  159. params[i] = args[i + 1];
  160. paramTypes[0] = params.getClass();
  161. paramValues[0] = params;
  162. // Create the icon window if this is a waitForChild task
  163. Frame frame = null;
  164. boolean displayMinimizedWindow = false;
  165. if (System.getProperty(ChildMain.DISPLAY_MINIMIZED_WINDOW_PROP_NAME) != null)
  166. displayMinimizedWindow = true;
  167. String osname = System.getProperty("os.name").toLowerCase();
  168. if (displayMinimizedWindow && osname.indexOf("windows") >= 0) {
  169. try {
  170. frame = new Frame();
  171. String title = System.getProperty(ChildMain.MINIMIZED_WINDOW_TITLE_PROP_NAME);
  172. if (title != null)
  173. frame.setTitle(title);
  174. frame.setState(Frame.ICONIFIED);
  175. String icon = System.getProperty(ChildMain.MINIMIZED_WINDOW_TITLE_PROP_NAME);
  176. if (icon != null) {
  177. Image iconImage = Toolkit.getDefaultToolkit().createImage(icon);
  178. if (iconImage != null)
  179. frame.setIconImage(iconImage);
  180. }
  181. // Ensure that window always remains minimized
  182. frame.addWindowListener(new ChildWindowAdapter());
  183. Rectangle bounds = frame.getGraphicsConfiguration().getBounds();
  184. int width = (int)frame.getBounds().getWidth();
  185. int height = frame.getInsets().top + frame.getInsets().bottom;
  186. int x = (int)bounds.getWidth() - width;
  187. int y = (int)bounds.getHeight() - height;
  188. frame.setBounds(x, y, width, height);
  189. frame.setResizable(false);
  190. frame.setVisible(true);
  191. } catch(Exception fe) {}
  192. }
  193. // Invoke the main() method
  194. Method mainMethod = mainClass.getDeclaredMethod("main", paramTypes);
  195. mainMethod.invoke(null, paramValues);
  196. // Close the frame if it exists
  197. if (frame != null && System.getProperty(ChildMain.DISPOSE_MINIMIZED_WINDOW_PROP_NAME) != null) {
  198. // Exit this process. Closing or disposing of the window is not
  199. // enough to allow the process to exit.
  200. System.exit(0);
  201. }
  202. } catch (Throwable t) {
  203. String message = t.getMessage();
  204. t.printStackTrace();
  205. System.exit(1);
  206. }
  207. }
  208. /**
  209. * A WindowAdapter subclass that causes the application to exit when its
  210. * {@link #windowClosing(WindowEvent)} method is invoked.
  211. */
  212. private static class ChildWindowAdapter extends WindowAdapter {
  213. /**
  214. * Invoked when a window is in the process of being closed.
  215. *
  216. * @param e the event
  217. */
  218. public void windowClosing(WindowEvent e) {
  219. System.exit(0);
  220. }
  221. }
  222. }