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 018package org.apache.commons.launcher; 019 020import java.awt.Frame; 021import java.awt.Image; 022import java.awt.Rectangle; 023import java.awt.Toolkit; 024import java.awt.event.WindowAdapter; 025import java.awt.event.WindowEvent; 026import java.io.FileOutputStream; 027import java.io.PrintStream; 028import 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 */ 043public 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}