KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > ruby > rubyproject > execution > ExecutionService


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.netbeans.modules.ruby.rubyproject.execution;
20
21 import java.awt.event.ActionEvent JavaDoc;
22 import java.io.File JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.lang.InterruptedException JavaDoc;
25 import java.util.HashSet JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Map JavaDoc;
28 import java.util.Set JavaDoc;
29 import java.util.WeakHashMap JavaDoc;
30 import javax.swing.AbstractAction JavaDoc;
31 import javax.swing.Action JavaDoc;
32 import org.netbeans.api.progress.ProgressHandle;
33 import org.netbeans.api.progress.ProgressHandleFactory;
34 import org.netbeans.modules.ruby.rubyproject.api.RubyInstallation;
35 import org.openide.ErrorManager;
36 import org.openide.execution.ExecutionEngine;
37 import org.openide.execution.ExecutorTask;
38 import org.openide.util.Cancellable;
39 import org.openide.util.Exceptions;
40 import org.openide.util.NbBundle;
41 import org.openide.util.RequestProcessor;
42 import org.openide.util.Task;
43 import org.openide.util.TaskListener;
44 import org.openide.util.Utilities;
45 import org.openide.windows.IOProvider;
46 import org.openide.windows.InputOutput;
47
48
49 /**
50  * <p>An ExecutionService takes an {@link ExecutionDescriptor} and executes it.
51  * It will execute the program with an associated I/O window, with stop and
52  * restart buttons. It wll also obey various descriptor properties such as
53  * whether or not to show a progress bar.
54  * <p>
55  * All launched processes will be killed on exit. Possibly I could make this
56  * optional or at least ask the user.
57  * </p>
58  * @todo There is one remaining JRubyism in here - setting JRUBY_HOME
59  *
60  * @author Tor Norbye
61  */

62 public class ExecutionService {
63     static {
64         Thread JavaDoc t =
65             new Thread JavaDoc() {
66                 public void run() {
67                     ExecutionService.killAll();
68                 }
69             };
70
71         Runtime.getRuntime().addShutdownHook(t);
72     }
73     /**
74      * Display names of currently active processes.
75      */

76     private static final Set JavaDoc<String JavaDoc> activeDisplayNames = new HashSet JavaDoc<String JavaDoc>();
77
78     /**
79      * All tabs which were used for some process which has now ended.
80      * These are closed when you start a fresh process.
81      * Map from tab to tab display name.
82      * @see "#43001"
83      */

84     private static final Map JavaDoc<ExecutionService, String JavaDoc> freeTabs =
85         new WeakHashMap JavaDoc<ExecutionService, String JavaDoc>();
86     private static Set JavaDoc<ExecutionService> runningProcesses = new HashSet JavaDoc<ExecutionService>();
87
88     //private ExecutorTask currentTask;
89
private Task currentTask;
90     private InputOutput io;
91     private StopAction stopAction;
92     private RerunAction rerunAction;
93     private ExecutionDescriptor descriptor;
94     private String JavaDoc displayName; // May be tweaked from descriptor to deal with duplicate running same-name processes
95

96     public ExecutionService(ExecutionDescriptor descriptor) {
97         this.descriptor = descriptor;
98         
99     }
100
101     /** Add settings in the environment appropriate for running JRuby:
102         add the given directory into the path, and set up JRUBY_HOME
103      */

104     public static void setupEnvironment(ProcessBuilder JavaDoc pb, String JavaDoc path) {
105         // Get source folders
106
Map JavaDoc<String JavaDoc, String JavaDoc> env = pb.environment();
107
108         // Find PATH environment variable - on Windows it can be some other
109
// case and we should use whatever it has.
110
String JavaDoc pathName = "PATH"; // NOI18N
111

112         if (Utilities.isWindows()) {
113             pathName = "Path"; // NOI18N
114

115             for (String JavaDoc key : env.keySet()) {
116                 if ("PATH".equals(key.toUpperCase())) { // NOI18N
117
pathName = key;
118
119                     break;
120                 }
121             }
122         }
123
124         String JavaDoc currentPath = env.get(pathName);
125
126         if (currentPath == null) {
127             currentPath = "";
128         }
129
130         if (!Utilities.isWindows()) {
131             path = path.replace(" ", "\\ "); // NOI18N
132
}
133
134         currentPath = path + File.pathSeparator + currentPath;
135         env.put(pathName, currentPath); // NOI18N
136

137         // In case we're launching JRuby:
138
String JavaDoc jrubyHome = RubyInstallation.getInstance().getJRubyHome();
139         env.put("JRUBY_HOME", jrubyHome); // NOI18N
140
env.put("JRUBY_BASE", jrubyHome); // NOI18N
141
env.put("JAVA_HOME", System.getProperty("java.home")); // NOI18N
142
}
143
144     public static void killAll() {
145         for (ExecutionService service : runningProcesses) {
146             StopAction sa = service.stopAction;
147
148             if (sa != null) {
149                 sa.actionPerformed(null);
150
151                 if (sa.process != null) {
152                     sa.process.destroy();
153                 }
154             }
155         }
156     }
157     
158     Task rerun() {
159         io = null;
160         stopAction = null;
161         rerunAction = null;
162         return run();
163     }
164
165     public Task run() {
166         String JavaDoc dn = descriptor.displayName;
167
168         if (activeDisplayNames.contains(dn)) {
169             // Uniquify: "prj (targ) #2", "prj (targ) #3", etc.
170
int i = 2;
171             String JavaDoc testdn;
172
173             do {
174                 testdn = NbBundle.getMessage(ExecutionService.class, "Uniquified", dn, i++);
175             } while (activeDisplayNames.contains(testdn));
176
177             dn = testdn;
178         }
179         assert !activeDisplayNames.contains(dn);
180         displayName = dn;
181         activeDisplayNames.add(displayName);
182
183         //final ExecutorTask task;
184
synchronized (this) {
185             // OutputWindow
186
//if (AntSettings.getAutoCloseTabs()) { // #47753
187
synchronized (freeTabs) {
188                 for (Map.Entry JavaDoc<ExecutionService, String JavaDoc> entry : freeTabs.entrySet()) {
189                     ExecutionService free = entry.getKey();
190
191                     //InputOutput free = entry.getKey();
192
String JavaDoc freeName = entry.getValue();
193
194                     if (free != this && (io == null) && freeName.equals(displayName)) {
195                         // Reuse it.
196
io = free.io;
197                         stopAction = free.stopAction;
198                         rerunAction = free.rerunAction;
199
200                         try {
201                             io.getOut().reset();
202                         } catch (IOException JavaDoc ioe) {
203                             Exceptions.printStackTrace(ioe);
204                         }
205
206                         io.setInputVisible(descriptor.inputVisible);
207
208                         // Apparently useless and just prints warning: io.getErr().reset();
209
// useless: io.flushReader();
210
io.select();
211                     } else {
212                         // Discard it.
213
//free.closeInputOutput();
214
//stopActions.remove(free);
215
//rerunActions.remove(free);
216
}
217                 }
218
219                 // TODO - how do I clear out the tabs - on close?
220
freeTabs.clear();
221             }
222
223             //}
224
if ((io == null) || (stopAction == null)) {
225                 stopAction = new StopAction();
226                 rerunAction = new RerunAction(this, descriptor.fileObject);
227
228                 if (io == null) {
229                     io = IOProvider.getDefault()
230                                    .getIO(displayName, new Action JavaDoc[] { rerunAction, stopAction });
231
232                     try {
233                         io.getOut().reset();
234                     } catch (IOException JavaDoc exc) {
235                         ErrorManager.getDefault().notify(exc);
236                     }
237
238                     // Note - do this AFTER the reset() call above; if not, weird bugs occur
239
io.setInputVisible(descriptor.inputVisible);
240                     io.setErrSeparated(false);
241
242                     // Open I/O window now. This should probably be configurable.
243
io.select();
244                 }
245             }
246         }
247
248         //io.getErr().println(NbBundle.getMessage(RubyExecutionService.class, "RunStarting"));
249
Runnable JavaDoc runnable =
250             new Runnable JavaDoc() {
251                 public void run() {
252                     File JavaDoc cmd = descriptor.cmd;
253                     if (cmd == null) {
254                         cmd = new File JavaDoc(RubyInstallation.getInstance().getRuby());
255                     }
256
257                     String JavaDoc[] applicationArgs = null;
258
259                     if (descriptor.additionalArgs != null) {
260                         applicationArgs = org.openide.util.Utilities.parseParameters(descriptor.additionalArgs);
261                     }
262
263                     String JavaDoc[] applicationFlags = null;
264
265                     if (descriptor.initialArgs != null) {
266                         applicationFlags = org.openide.util.Utilities.parseParameters(descriptor.initialArgs);
267                     }
268
269                     int argvSize =
270                         1 + ((descriptor.args != null) ? descriptor.args.length : 0) +
271                         ((applicationArgs != null) ? applicationArgs.length : 0) +
272                         ((applicationFlags != null) ? applicationFlags.length : 0);
273                     String JavaDoc[] argv = new String JavaDoc[argvSize];
274                     argv[0] = cmd.getPath();
275
276                     int destPos = 1;
277
278                     if (applicationFlags != null) {
279                         System.arraycopy(applicationFlags, 0, argv, destPos, applicationFlags.length);
280                         destPos += applicationFlags.length;
281                     }
282
283                     if ((descriptor.args != null) && (descriptor.args.length > 0)) {
284                         System.arraycopy(descriptor.args, 0, argv, destPos, descriptor.args.length);
285                         destPos += descriptor.args.length;
286                     }
287
288                     if (applicationArgs != null) {
289                         System.arraycopy(applicationArgs, 0, argv, destPos, applicationArgs.length);
290                         destPos += applicationArgs.length;
291                     }
292
293                     if ((argv != null) && Utilities.isWindows()) {
294                         for (int i = 0; i < argv.length; i++) {
295                             if ((argv[i] != null) && (argv[i].indexOf(' ') != -1) &&
296                                     (argv[i].indexOf('"') == -1)) {
297                                 argv[i] = '"' + argv[i] + '"';
298                             }
299                         }
300                     }
301
302                     ProcessBuilder JavaDoc pb = new ProcessBuilder JavaDoc(argv);
303                     pb.directory(descriptor.pwd);
304
305                     setupEnvironment(pb, cmd.getParent());
306
307                     try {
308                         Process JavaDoc process = pb.start();
309                         runningProcesses.add(ExecutionService.this);
310                         stopAction.process = process;
311                         runIO(stopAction, process, io, descriptor.fileLocator, descriptor.outputRecognizers);
312
313                         int retcode = process.waitFor();
314                     } catch (IOException JavaDoc ex) {
315                         ErrorManager.getDefault().notify(ex);
316                     } catch (InterruptedException JavaDoc ex) {
317                         ErrorManager.getDefault().notify(ex);
318                     }
319                 }
320             };
321
322         final ProgressHandle handle;
323
324         if (descriptor.showProgress || descriptor.showSuspended) {
325             handle =
326                 ProgressHandleFactory.createHandle(displayName,
327                     new Cancellable() {
328                         public boolean cancel() {
329                             stopAction.actionPerformed(null);
330
331                             return true;
332                         }
333                     },
334                     new AbstractAction JavaDoc() {
335                         public void actionPerformed(ActionEvent JavaDoc e) {
336                             io.select();
337                         }
338                     });
339             handle.start();
340             handle.switchToIndeterminate();
341
342             if (descriptor.showSuspended) {
343                 handle.suspend(NbBundle.getMessage(ExecutionService.class, "Running"));
344             }
345         } else {
346             handle = null;
347         }
348
349         stopAction.setEnabled(true);
350         rerunAction.setEnabled(false);
351
352         ExecutorTask task = ExecutionEngine.getDefault().execute(null, runnable, InputOutput.NULL);
353         //ExecutorTask task = ExecutionEngine.getDefault().execute(displayName, runnable, io);
354

355         //ExecutorTask task = ExecutionEngine.getDefault().execute(displayName, runnable, io);
356
currentTask = task;
357         task.addTaskListener(new TaskListener() {
358                 public void taskFinished(Task task) {
359                     int result = 0;
360
361                     runningProcesses.remove(ExecutionService.this);
362
363                     if (io != null) {
364                         synchronized (freeTabs) {
365                             freeTabs.put(ExecutionService.this, displayName);
366                         }
367                     }
368
369                     activeDisplayNames.remove(displayName);
370
371                     if (task instanceof ExecutorTask) {
372                         result = ((ExecutorTask)task).result();
373                     }
374
375                     // if (result == 0) {
376
// System.err.println(NbBundle.getMessage(RubyExecutionService.class,
377
// "RunCompleted"));
378
// } else {
379
// System.err.println(NbBundle.getMessage(RubyExecutionService.class,
380
// "RunFailed", result));
381
// }
382
if (descriptor.postBuildAction != null) {
383                         descriptor.postBuildAction.run();
384                     }
385
386                     if (handle != null) {
387                         handle.finish();
388                     }
389
390                     stopAction.setEnabled(false);
391                     rerunAction.setEnabled(true);
392
393                     if (stopAction.process != null) {
394                         stopAction.process.destroy();
395                         stopAction.process = null;
396                     }
397
398                     currentTask = null;
399                 }
400             });
401
402         return task;
403     }
404
405     private void runIO(final StopAction sa, Process JavaDoc process, InputOutput ioput,
406         FileLocator fileLocator, List JavaDoc<OutputRecognizer> recognizers) {
407         try {
408             InputForwarder in = new InputForwarder(process.getOutputStream(), ioput.getIn());
409             OutputForwarder out =
410                 new OutputForwarder(process.getInputStream(), ioput.getOut(), fileLocator,
411                     recognizers, sa, "Output"); // NOI18N
412
OutputForwarder err =
413                 new OutputForwarder(process.getErrorStream(), ioput.getErr(), fileLocator,
414                     recognizers, sa, "Error"); // NOI18N
415

416             RequestProcessor PROCESSOR = new RequestProcessor("Process Execution Stream Handler", 3, true); // NOI18N
417

418             TaskListener tl =
419                 new TaskListener() {
420                     public void taskFinished(Task task) {
421                         sa.notifyDone((RequestProcessor.Task)task);
422                     }
423                 };
424
425             RequestProcessor.Task outTask = PROCESSOR.post(out);
426             RequestProcessor.Task errTask = PROCESSOR.post(err);
427             RequestProcessor.Task inTask = PROCESSOR.post(in);
428
429             outTask.addTaskListener(tl);
430             errTask.addTaskListener(tl);
431             inTask.addTaskListener(tl);
432
433             sa.processorTasks.add(outTask);
434             sa.processorTasks.add(errTask);
435             sa.processorTasks.add(inTask);
436
437             process.waitFor();
438             sa.process = null;
439
440             in.cancel();
441             outTask.waitFinished();
442             errTask.waitFinished();
443             inTask.waitFinished();
444
445             PROCESSOR.stop();
446         } catch (InterruptedException JavaDoc exc) {
447             // XXX Uhm... why do we log this? Isn't this a good thing?
448
// This happens if we try to cancel the process for example
449
ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, exc);
450         }
451     }
452 }
453
Popular Tags