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 }