View Javadoc

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 }