1 /* 2 * Copyright (c) 2006 Stiftung Deutsches Elektronen-Synchroton, 3 * Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY. 4 * 5 * THIS SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "../AS IS" BASIS. 6 * WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED 7 * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND 8 * NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 9 * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 10 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR 11 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. SHOULD THE SOFTWARE PROVE DEFECTIVE 12 * IN ANY RESPECT, THE USER ASSUMES THE COST OF ANY NECESSARY SERVICING, REPAIR OR 13 * CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. 14 * NO USE OF ANY SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. 15 * DESY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, 16 * OR MODIFICATIONS. 17 * THE FULL LICENSE SPECIFYING FOR THE SOFTWARE THE REDISTRIBUTION, MODIFICATION, 18 * USAGE AND OTHER RIGHTS AND OBLIGATIONS IS INCLUDED WITH THE DISTRIBUTION OF THIS 19 * PROJECT IN THE FILE LICENSE.HTML. IF THE LICENSE IS NOT INCLUDED YOU MAY FIND A COPY 20 * AT HTTP://WWW.DESY.DE/LEGAL/LICENSE.HTM 21 */ 22 23 package de.desy.acop.launcher; 24 25 import java.awt.BorderLayout; 26 import java.awt.Component; 27 import java.awt.Container; 28 import java.awt.Dimension; 29 import java.awt.Frame; 30 import java.awt.Toolkit; 31 import java.io.BufferedReader; 32 import java.io.IOException; 33 import java.io.InputStream; 34 import java.io.InputStreamReader; 35 import java.io.OutputStream; 36 import java.io.PrintWriter; 37 import java.lang.reflect.Constructor; 38 39 import javax.swing.JDialog; 40 import javax.swing.JFrame; 41 import javax.swing.JInternalFrame; 42 import javax.swing.JPanel; 43 44 import com.cosylab.gui.components.util.ComponentPositioner; 45 46 47 /** 48 * 49 * <code>Launcher</code> is a utility class which can launch an application 50 * according to its type and supplied parameters. 51 * 52 * 53 * @author <a href="mailto:jaka.bobnar@cosylab.com">Jaka Bobnar</a> 54 * @version $Id: Templates.xml,v 1.10 2004/01/13 16:17:13 jbobnar Exp $ 55 * 56 */ 57 public class Launcher { 58 59 public static final String CAMNAME = "de.desy.mst.libs.framework.mstapp.util.camInstrumentation.CAMframeControl"; 60 61 /** 62 * 63 * <code>StreamGobbler</code> reads the data from the input stream 64 * and forwards it to the outputstream. 65 * 66 * @author <a href="mailto:jaka.bobnar@cosylab.com">Jaka Bobnar</a> 67 * 68 */ 69 private static class StreamGobbler extends Thread { 70 private InputStream is; 71 private OutputStream os; 72 73 74 StreamGobbler(InputStream is, OutputStream os) { 75 this.is = is; 76 this.os = os; 77 } 78 79 /** 80 * creates readers to handle the text created by the external program 81 */ 82 public void run() { 83 try { 84 BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 85 PrintWriter writer = new PrintWriter(os); 86 String line; 87 while ((line = reader.readLine()) != null) { 88 writer.println(line); 89 } 90 reader.close(); 91 } catch (IOException ioe) { 92 ioe.printStackTrace(); 93 } 94 } 95 } 96 97 /** 98 * Method will start a webstart application. The provided String should be 99 * the url to the jnlp file. This method will construct a webstart 100 * start command composed of all provided parameters and forward the call to 101 * {@link #runWebstartApplication(String, boolean)}. 102 * 103 * @param jnlpURL the url of the jnlp file to be started 104 * @param jvmParameters the virtual machine parameters (without the -D) 105 * @param waitForCompletion the flag indicating if the calling thread 106 * should be blocked until the execution is completed 107 * @throws IOException if execution of the command failed 108 */ 109 public static void runWebstartApplication(String jnlpURL, String[] jvmParameters, boolean waitForCompletion) throws IOException { 110 StringBuilder sb = new StringBuilder(); 111 for (String s : jvmParameters) { 112 sb.append("-J-D"); 113 sb.append(s); 114 sb.append(' '); 115 } 116 sb.append(jnlpURL); 117 runWebstartApplication(sb.toString(), waitForCompletion); 118 } 119 120 /** 121 * Method will start a webstart application. The provided String should be 122 * a complete url to the jnlp file. The provided url can also include 123 * several program or JVM arguments. They have to be supplied as prescribed 124 * by the Java API. This method will forward the call to 125 * {@link #runWebstartApplication(String, OutputStream, OutputStream, boolean)} 126 * where the streams are null. 127 * 128 * @see #runCommand(String, OutputStream, OutputStream, boolean) 129 * 130 * @param completeJNLP_URL the url which will be loaded using the java 131 * webstart command (javaws) 132 * @param waitForCompletion the flag indicating if the calling thread 133 * should be blocked until the execution is completed 134 * @throws IOException if execution of the command failed 135 */ 136 public static void runWebstartApplication(String completeJNLP_URL, boolean waitForCompletion) throws IOException { 137 runWebstartApplication(completeJNLP_URL, null, null, waitForCompletion); 138 } 139 140 /** 141 * Method will start a webstart application. The provided String should be 142 * a complete url to the jnlp file. The provided url can also include 143 * several program or JVM arguments. They have to be supplied as prescribed 144 * by the Java API. 145 * 146 * @see #runCommand(String, OutputStream, OutputStream, boolean) 147 * 148 * @param completeJNLP_URL the url which will be loaded using the java 149 * webstart command (javaws) 150 * @param outputStream the output stream where all the output from the 151 * input stream of the executed process will be forwarded 152 * @param errorOutput the output stream where all the output from the 153 * error stream of the executed process will be forwarded 154 * @param waitForCompletion the flag indicating if the calling thread 155 * should be blocked until the execution is completed 156 * @throws IOException if execution of the command failed 157 */ 158 public static void runWebstartApplication(String completeJNLP_URL, OutputStream outputStream, OutputStream errorOutput, boolean waitForCompletion) throws IOException { 159 runCommand("javaws " + completeJNLP_URL, outputStream, errorOutput, waitForCompletion); 160 } 161 162 /** 163 * Runs the specified command. This method will invoke the {@link Runtime#exec(String)}, 164 * where the provided string parameters is the command. The output generated by 165 * the execution of this command is forwarded to the provided {@link OutputStream}s. 166 * If any of these stream are null the output from that part of execution will 167 * be skipped. 168 * <p> 169 * The invoker has the possibility to block the current thread until the 170 * process created by this method is completed. The lifecycle of the process 171 * is specified by the command itself and the process which will be executed 172 * by that command. 173 * 174 * @param command the command to be executed 175 * @param outputStream the output stream where all the data from the {@link Process}'s 176 * input stream will be forwarded (null is allowed) 177 * @param errorOutput the output stream where all the data from the {@link Process}'s 178 * error stream will be forwarded (null is allowed) 179 * @param waitForCompletion a flag indicating if the calling thread should be blocked 180 * until the process is completed 181 * @throws IOException if execution of the command failed 182 */ 183 public static void runCommand(String command, OutputStream outputStream, OutputStream errorOutput, boolean waitForCompletion) throws IOException { 184 Process p = Runtime.getRuntime().exec(command); 185 if (outputStream != null) { 186 new StreamGobbler(p.getInputStream(), outputStream).start(); 187 } 188 if (errorOutput != null) { 189 new StreamGobbler(p.getErrorStream(), errorOutput).start(); 190 } 191 if (waitForCompletion) { 192 try { 193 p.waitFor(); 194 } catch (InterruptedException e) { 195 //ignore 196 } 197 } 198 } 199 200 201 private static String getClassName() { 202 return Launcher.class.getName(); 203 } 204 205 public static void main(String[] args) throws IOException { 206 String[] s = new String[2]; 207 s[0] = "-Dtine.transport=TCP"; 208 s[1] = "-Dav.machine=FLASH"; 209 Launcher.runWebstartApplication("http://mcalaunch.desy.de/webapps/Released/common/ser/ArchiveViewer.jnlp", System.out, System.out, false); 210 } 211 212 /** 213 * Launches an application using the input arguments. This call is forwarded 214 * to {@link #launch(Class, String[], boolean)}, where shouldExitOnClose is false. 215 * 216 * @param <T> the object which should be constructed and returned 217 * @param applicationClass the class decribing that object 218 * @param args program arguments 219 * @return the instance of the application 220 */ 221 public static <T> T launch(final Class<T> applicationClass, final String[] args) { 222 return launch(applicationClass, args, false); 223 } 224 225 /** 226 * Launches an application defined by the applicationClass. The method will try 227 * to create an object of the given instance using the constructor with String[] 228 * parameter. If this fails, it will call invoke the main method on the the 229 * give application class using the args arguments. If the main method doesn't 230 * exist an object's default constructor will be used (in this case args array is 231 * useless). 232 * <p> 233 * If the application is not a window type but it is at least a {@link Component}, 234 * a JFrame will be created and the component will be added to that frame. 235 * DefaultCloseOperation for the frame is EXIT or DISPOSE and it depends 236 * on the shouldExitOnClose parameter. 237 * </p> 238 * <p> 239 * This method is not thread safe and will block the current thread until the 240 * application is loaded. One should take of appropriate multithreading to avoid 241 * GUI freezing. 242 * </p> 243 * 244 * @param <T> the object which should be constructed and returned 245 * @param applicationClass the class describing that object 246 * @param args the program arguments 247 * @param shouldExitOnClose if true the frame will have EXIT_ON_CLOSE defaultCloseOperation 248 * @return the instance of application if it was started 249 */ 250 public static <T> T launch(final Class<T> applicationClass, final String[] args, boolean shouldExitOnClose) { 251 if (applicationClass == null) { 252 System.out.println("No class specified to launch in '"+getClassName()+".launch()' method"); 253 return null; 254 } 255 if (args == null) { 256 System.out.println("No arguments specified in '"+getClassName()+".launch()' method."); 257 return null; 258 } 259 260 Constructor<T> constructor = null; 261 T obj; 262 try { 263 constructor = applicationClass.getConstructor(new Class[]{String[].class}); 264 } catch (NoSuchMethodException e2) { 265 //ignore 266 } 267 268 if (constructor == null) { 269 try { 270 obj = applicationClass.newInstance(); 271 } catch (Exception e) { 272 e.printStackTrace(); 273 System.out.println("Cannot launch class '"+applicationClass + "'. \nIt does not provide a Constructor with parameters 'String[]' nor default constructor."); 274 return null; 275 } 276 277 /* 278 * This code below effecitvly prevents you to launch class from 279 * main method, it makes closed loop. 280 * This happens only if you launch it from the main method that invoked 281 * this Launcher call. 282 */ 283 284 /*//if no String[] constructor try main method 285 Method mainMethod = null; 286 try { 287 mainMethod = applicationClass.getMethod("main", new Class[]{String[].class}); 288 } catch (NoSuchMethodException e1) { 289 //ignore 290 } 291 292 if (mainMethod != null) { 293 try { 294 mainMethod.invoke(null, new Object[]{args}); 295 return null; 296 } catch (Exception e) { 297 //ignore 298 } 299 } 300 //if no main method try default constructor 301 try { 302 constructor = applicationClass.getConstructor(new Class[]{}); 303 } catch (NoSuchMethodException e2) { 304 //ignore 305 } 306 //if no default constructor abort 307 if (constructor == null) { 308 System.out.println("Cannot launch class '"+applicationClass + "'. \nIt does not provide a Constructor with parameters 'String[]' nor default constructo or the main method."); 309 return null; 310 } 311 312 //create new object 313 try { 314 obj = constructor.newInstance(new Object[]{}); 315 } catch (Exception e) { 316 e.printStackTrace(); 317 return null; 318 }*/ 319 320 } else { 321 322 try { 323 obj = constructor.newInstance(new Object[]{args}); 324 } catch (Exception e) { 325 e.printStackTrace(); 326 return null; 327 } 328 } 329 if (obj != null) { 330 int closeOperation = shouldExitOnClose ? JFrame.EXIT_ON_CLOSE : JFrame.DISPOSE_ON_CLOSE; 331 if (JFrame.class.isAssignableFrom(applicationClass)) { 332 JFrame frame = JFrame.class.cast(obj); 333 frame.setDefaultCloseOperation(closeOperation); 334 ComponentPositioner.centerOnScreen(frame); 335 frame.setVisible(true); 336 initializeFrameControl(frame); 337 } else if (JDialog.class.isAssignableFrom(applicationClass)) { 338 JDialog frame = JDialog.class.cast(obj); 339 frame.setDefaultCloseOperation(closeOperation); 340 ComponentPositioner.centerOnScreen(frame); 341 frame.setVisible(true); 342 } else if (JInternalFrame.class.isAssignableFrom(applicationClass)) { 343 JInternalFrame frame = JInternalFrame.class.cast(obj); 344 frame.setDefaultCloseOperation(closeOperation); 345 frame.setVisible(true); 346 } else if (Container.class.isAssignableFrom(applicationClass)) { 347 Container comp = Container.class.cast(obj); 348 JFrame frame = new JFrame(); 349 frame.setDefaultCloseOperation(closeOperation); 350 frame.setContentPane(comp); 351 Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); 352 frame.setSize((int)(d.width*0.8), (int)(d.height*0.8)); 353 ComponentPositioner.centerOnScreen(frame); 354 frame.setVisible(true); 355 initializeFrameControl(frame); 356 } else if (Component.class.isAssignableFrom(applicationClass)) { 357 Component comp = Component.class.cast(obj); 358 JFrame frame = new JFrame(); 359 frame.setDefaultCloseOperation(closeOperation); 360 JPanel p = new JPanel(new BorderLayout()); 361 p.add(comp, BorderLayout.CENTER); 362 frame.setContentPane(p); 363 Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); 364 frame.setSize((int)(d.width*0.8), (int)(d.height*0.8)); 365 ComponentPositioner.centerOnScreen(frame); 366 frame.setVisible(true); 367 initializeFrameControl(frame); 368 } 369 return obj; 370 } 371 return null; 372 } 373 374 /** 375 * Initializes Component Application Manager. This method uses reflection to initialize 376 * the class, and than instantiates the manager. 377 * 378 * @param frame the frame to connect it with the manager 379 * @see de.desy.mst.libs.framework.mstapp.util.camInstrumentation.CAMframeControl 380 */ 381 private static final void initializeFrameControl(Frame frame) { 382 try { 383 Class<?> c = Class.forName(CAMNAME); 384 Constructor<?> constructor = c.getConstructor(new Class[]{Frame.class}); 385 constructor.newInstance(frame); 386 } catch (Exception e) { 387 System.err.println("Cannot initialize CAM frame control."); 388 } 389 } 390 }