source: trunk/gli/src/org/greenstone/gatherer/Gatherer.java@ 8622

Last change on this file since 8622 was 8622, checked in by mdewsnip, 19 years ago

Tidied up some more local library stuff.

  • Property svn:keywords set to Author Date Id Revision
File size: 30.2 KB
Line 
1/**
2 *#########################################################################
3 *
4 * A component of the Gatherer application, part of the Greenstone digital
5 * library suite from the New Zealand Digital Library Project at the
6 * University of Waikato, New Zealand.
7 *
8 * Author: John Thompson, Greenstone Digital Library, University of Waikato
9 *
10 * Copyright (C) 1999 New Zealand Digital Library Project
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 *########################################################################
26 */
27package org.greenstone.gatherer;
28
29//import com.l2fprod.gui.*;
30//import com.l2fprod.gui.plaf.skin.*;
31//import com.l2fprod.util.*;
32import java.awt.*;
33import java.awt.event.*;
34import java.io.*;
35import java.lang.*;
36import java.net.*;
37import java.util.*;
38import javax.swing.*;
39import javax.swing.plaf.*;
40import javax.swing.text.*;
41import org.greenstone.gatherer.Configuration;
42import org.greenstone.gatherer.GAuthenticator;
43import org.greenstone.gatherer.collection.CollectionManager;
44import org.greenstone.gatherer.feedback.ActionRecorderDialog;
45import org.greenstone.gatherer.file.FileManager;
46import org.greenstone.gatherer.file.FileAssociationManager;
47import org.greenstone.gatherer.gui.GUIManager;
48import org.greenstone.gatherer.gui.Splash;
49import org.greenstone.gatherer.gui.URLField;
50import org.greenstone.gatherer.gui.WarningDialog;
51import org.greenstone.gatherer.util.StaticStrings;
52import org.greenstone.gatherer.util.Utility;
53
54
55/** Containing the top-level "core" for the Gatherer, this class is the
56 * common core for the GLI application and applet. It first parses the
57 * command line arguments, preparing to update the configuration as
58 * required. Next it loads several important support classes such as the
59 * Configuration and Dictionary. Finally it creates the other important
60 * managers and sends them on their way.
61 * @author John Thompson, Greenstone Digital Library, University of Waikato
62 * @version 2.3
63 */
64public class Gatherer
65{
66 static public Hashtable authentications = new Hashtable();
67
68 static final private String SKIN_DEFINITION_FILE = "lib/greenaqua/greenaqua.xml";
69
70 /** Has the exit flag been set? <i>true</i> if so, <i>false</i> otherwise. */
71 public boolean exit = false;
72 /** The size of the Gatherer window. */
73 public Dimension frame_size = null;
74
75 /** All of the external applications that must exit before we close the Gatherer. */
76 public Vector apps = new Vector();
77 /** A public reference to the FileAssociationManager. */
78 static public FileAssociationManager assoc_man;
79 /** A public reference to the CollectionManager. */
80 static public CollectionManager c_man;
81 /** a reference to the Servlet Configuration is GS3 */
82 static public ServletConfiguration servlet_config;
83 /** A public reference to the FileManager. */
84 static public FileManager f_man;
85 /** A public reference to the GUIManager. */
86 static public GUIManager g_man;
87 static private boolean g_man_built = false;
88
89 /** A static reference to ourselves. */
90 static public Gatherer self;
91 /** We are using the GLI for GS3 */
92 static public boolean GS3 = false;
93
94 static public boolean isGsdlRemote = false;
95 static public String cgiBase = "";
96
97 // feedback stuff
98 /** is the feedback feature enabled? */
99 static public boolean feedback_enabled = true;
100 /** the action recorder dialog */
101 static public ActionRecorderDialog feedback_dialog = null;
102
103
104 /** Magic to allow Enter to fire the default button. */
105 static {
106 KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
107 Keymap map = JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP);
108 map.removeKeyStrokeBinding(enter);
109 }
110
111 public Gatherer()
112 {
113 this.self = this;
114
115 Utility.initImages(this);
116 }
117
118
119 public GUIManager init(Dimension size, String gsdl_path, String gsdl3_path, String local_library_path,
120 String exec_path, boolean debug_enabled, String perl_path,
121 boolean no_load, String open_collection,
122 String site_name, String servlet_path,
123 boolean mirroring_enabled, String wget_version_str,
124 String wget_path)
125 {
126 // This will hopefully catch ctrl-c and terminate, and exit gracefully. However it is platform specific, and may not be supported by some JVMs.
127 /** It does, but I get bloody sick of it working when the Gatherer hangs.
128 CTRLCHandler handler = new CTRLCHandler();
129 Signal.handle(new Signal("INT"), handler);
130 Signal.handle(new Signal("TERM"), handler);
131 handler = null;
132 */
133 if (gsdl3_path != null && !gsdl3_path.equals("")) {
134 this.GS3 = true;
135 } else {
136 gsdl3_path = null;
137 }
138 // Create the debug stream only if required.
139 if (debug_enabled) {
140 DebugStream.enableDebugging();
141
142 Calendar now = Calendar.getInstance();
143 String debug_file_path = "debug" + now.get(Calendar.DATE) + "-" + now.get(Calendar.MONTH) + "-" + now.get(Calendar.YEAR) + ".txt";
144
145 // Debug file is created in the GLI directory, unless the GLI is running as an applet
146 if (isGsdlRemote) {
147 debug_file_path = Utility.getGLIUserFolder().toString() + File.separator + debug_file_path;
148 }
149 DebugStream.println("Debug file path: " + debug_file_path);
150 DebugStream.setDebugFile(debug_file_path);
151 DebugStream.print(System.getProperties());
152 }
153 try {
154 // Load Config
155 loadConfig(gsdl_path, gsdl3_path, exec_path, perl_path, mirroring_enabled, site_name);
156
157 // I don't really know what this is for but I've moved it here from Configuration
158 if (isGsdlRemote && Utility.isWindows() && Configuration.perl_path != null) {
159 if (Configuration.perl_path.toLowerCase().endsWith("perl.exe")) {
160 Configuration.perl_path = Configuration.perl_path.substring(0, Configuration.perl_path.length() - "perl.exe".length());
161 }
162 if (Configuration.perl_path.endsWith(File.separator)) {
163 Configuration.perl_path = Configuration.perl_path.substring(0, Configuration.perl_path.length() - File.separator.length());
164 }
165 }
166
167 if (GS3 && Configuration.servlet_path == null) {
168 Configuration.servlet_path = servlet_config.getServletPath(Configuration.site_name);
169 }
170
171 // the feedback dialog has been loaded with a default locale,
172 // now set the user specified one
173 if (feedback_enabled && feedback_dialog != null) {
174 feedback_dialog.setLocale(Configuration.getLocale("general.locale", true));
175 }
176
177 // Read Dictionary
178 new Dictionary(Configuration.getLocale("general.locale", true), Configuration.getFont("general.font", true));
179
180 if (gsdl_path == null) {
181 missingGSDL();
182 }
183
184 // Start up the local library server, if that's what we want
185 if (Utility.isWindows() && local_library_path != null && !GS3) {
186 LocalLibraryServer.start(gsdl_path, local_library_path);
187 DebugStream.println("Having started server.exe, exec_address is: " + Configuration.exec_address);
188 }
189
190 // Having loaded the configuration (necessary to determine if certain warnings have been disabled) and dictionary, we now check if the necessary path variables have been provided.
191 if (Configuration.exec_address == null) {
192 DebugStream.println("config.exec_address is null");
193 missingEXEC();
194 }
195
196 if (Gatherer.isGsdlRemote) {
197 DebugStream.println("Not checking for perl path/exe");
198 }
199 else {
200 // Perl path is a little different as it is perfectly ok to
201 // start the Gatherer without providing a perl path
202 boolean found_perl = false;
203 if (Configuration.perl_path != null) {
204 // See if the file pointed to actually exists
205 File perl_file = new File(Configuration.perl_path);
206 found_perl = perl_file.exists();
207 perl_file = null;
208 }
209 if (Configuration.perl_path == null || !found_perl) {
210 // Run test to see if we can run perl as is.
211 PerlTest perl_test = new PerlTest();
212 if (perl_test.found()) {
213 // If so replace the perl path with the system
214 // default (or null for unix).
215 Configuration.perl_path = perl_test.toString();
216 found_perl = true;
217 }
218 }
219 if (!found_perl) {
220 // Time for an error message.
221 missingPERL();
222 }
223 }
224
225 // also check for wget
226 boolean mirror_workflow = Configuration.get(StaticStrings.WORKFLOW_MIRROR, false);
227 if (mirror_workflow) {
228 wget_version_str = StaticStrings.NO_WGET_STR;
229 // has the user specified a path?
230 if (wget_path != null && !wget_path.equals("")) {
231 File wget_file = new File(wget_path);
232 if (wget_file.exists()) {
233 // we assume its ok if its there
234 wget_version_str = StaticStrings.WGET_STR;
235 }
236 }
237
238 // it hasn't been set by the user so we use the greenstone one
239 if (wget_version_str.equals(StaticStrings.NO_WGET_STR)) {
240 // TODO fix for gs3
241 wget_path = Utility.getWGetPath(gsdl_path);
242 File wget_file = new File(wget_path);
243 if (!wget_file.exists()) {
244 // we give up trying to find one
245 missingWGET();
246 } else {
247 // we have found one
248 wget_version_str = StaticStrings.WGET_STR;
249 }
250 }
251
252 if (wget_version_str.equals(StaticStrings.WGET_STR)) {
253 // we have found one, should we check the version??
254 wget_version_str = testWGetVersion(wget_path);
255 if (wget_version_str.equals(StaticStrings.WGET_OLD_STR)) {
256 oldWGET();
257 }
258 }
259
260 // tell the config the new values
261 Configuration.setWGetPath(wget_path);
262 Configuration.setWGetVersion(wget_version_str);
263 }
264
265 // Set default font
266 setUIFont(Configuration.getFont("general.font", true), Configuration.getFont("general.tooltip_font", true));
267 // Set up proxy
268 setProxy();
269 // Now we set up an Authenticator
270 Authenticator.setDefault(new GAuthenticator());
271
272 assoc_man = new FileAssociationManager();
273 // Create File Manager
274 f_man = new FileManager();
275 // Create Collection Manager
276 c_man = new CollectionManager();
277
278 if (GS3) {
279 if (site_name==null) {
280 site_name = Configuration.site_name;
281 servlet_path = null; // need to reset this
282 }
283 if (servlet_path == null) {
284 servlet_path = Configuration.getServletPath();
285 }
286 }
287
288 // If there was an open collection last session, reopen it.
289 if (open_collection == null) {
290 open_collection = Configuration.getString("general.open_collection", true);
291 }
292 if (!no_load && open_collection.length() > 0) {
293 c_man.loadCollection(open_collection);
294 }
295
296 }
297 catch (Exception exception) {
298 DebugStream.printStackTrace(exception);
299 }
300
301 // Create GUI Manager (last) or else suffer the death of a thousand NPE's
302 g_man = new GUIManager(size);
303
304 return g_man;
305 }
306
307
308
309 public void run(Dimension size, Splash splash, GUIManager g_man)
310 {
311 // Size and place the frame on the screen
312 Rectangle bounds = Configuration.getBounds("general.bounds", true);
313 if (bounds == null) {
314 // Choose a sensible default value
315 bounds = new Rectangle(0, 0, 640, 480);
316 }
317
318 // Ensure width and height are reasonable
319 size = bounds.getSize();
320 if (size.width < 640) {
321 size.width = 640;
322 }
323 else if (size.width > Configuration.screen_size.width) {
324 size.width = Configuration.screen_size.width;
325 }
326 if (size.height < 480) {
327 size.height = 480;
328 }
329 else if (size.height > Configuration.screen_size.height) {
330 size.height = Configuration.screen_size.height;
331 }
332
333 if (splash != null) { splash.toFront(); }
334
335 if (!g_man_built) {
336
337 g_man.display();
338
339 // Place the window in the desired location on the screen, if this is do-able (not under most linux window managers apparently. In fact you're lucky if they listen to any of your screen size requests).
340 g_man.setLocation(bounds.x, bounds.y);
341 g_man.setVisible(true);
342
343 // After the window has been made visible, check that it is in the correct place
344 // sometimes java places a window not in the correct place,
345 // but with an offset. If so, we work out what the offset is
346 // and change the desired location to take that into account
347 Point location = g_man.getLocation();
348 int x_offset = bounds.x - location.x;
349 int y_offset = bounds.y - location.y;
350 // If not, offset the window to move it into the correct location
351 if (x_offset > 0 || y_offset > 0) {
352 ///ystem.err.println("changing the location to "+(bounds.x + x_offset)+" "+ (bounds.y + y_offset));
353 g_man.setLocation(bounds.x + x_offset, bounds.y + y_offset);
354 }
355
356 // The 'after-display' triggers several events which don't occur until after the visual components are actually available on screen. Examples of these would be the various html renderings, as they can't happen offscreen.
357 g_man.afterDisplay();
358 g_man_built = true;
359 }
360 else {
361 g_man.setVisible(true);
362 }
363
364 if (splash != null) {
365 // Hide the splash screen
366 splash.setVisible(false);
367 splash.destroy();
368 splash = null;
369 }
370 }
371
372
373 /** Exits the Gatherer after ensuring that things needing saving are saved.
374 * @see java.io.FileOutputStream
375 * @see java.io.PrintStream
376 * @see java.lang.Exception
377 * @see javax.swing.JOptionPane
378 * @see org.greenstone.gatherer.Configuration
379 * @see org.greenstone.gatherer.collection.CollectionManager
380 * @see org.greenstone.gatherer.gui.GUIManager
381 */
382 public void exit() {
383 exit = true;
384 // If we have an open collection make note of it.
385 Configuration.setString("general.open_collection", true, null);
386 if(c_man.ready()) {
387 ///ystem.err.println("Collection open.");
388 if(c_man.saved()) {
389 ///ystem.err.println("Collection has been recently saved, so I'll remember it for next time.");
390 Configuration.setString("general.open_collection", true, c_man.getCollectionFilename());
391 }
392 c_man.closeCollection();
393 }
394 if(assoc_man != null) {
395 assoc_man.save();
396 assoc_man = null;
397 }
398
399 // Store the current position and size (if reasonable) of the Gatherer for next time
400 Rectangle bounds = g_man.getBounds();
401 Configuration.setBounds("general.bounds", true, bounds);
402
403 // Save configuration.
404 saveConfig();
405
406 // Get the gui to deallocate
407 g_man.setVisible(false);
408 g_man.destroy();
409 g_man_built = false;
410
411 // Flush debug
412 DebugStream.closeDebugStream();
413
414 // If we started a server, we should try to stop it.
415 if (LocalLibraryServer.isRunning() == true) {
416 LocalLibraryServer.stop();
417 }
418
419 if(apps.size() == 0) {
420 if (!Gatherer.isGsdlRemote) {
421 System.exit(0);
422 }
423 }
424 else {
425 JOptionPane.showMessageDialog(g_man, Dictionary.get("General.Outstanding_Processes"), Dictionary.get("General.Outstanding_Processes_Title"), JOptionPane.ERROR_MESSAGE);
426 g_man.setVisible(false);
427 }
428 }
429
430 // used to send reload coll messages to the tomcat server
431 static public void configGS3Server(String site, String command) {
432
433 try {
434 // need to add the servlet name to the exec address
435 String raw_url = Configuration.exec_address.toString() + Configuration.getServletPath() + command;
436 URL url = new URL(raw_url);
437 DebugStream.println("Action: " + raw_url);
438 HttpURLConnection library_connection = (HttpURLConnection) url.openConnection();
439 int response_code = library_connection.getResponseCode();
440 if(HttpURLConnection.HTTP_OK <= response_code && response_code < HttpURLConnection.HTTP_MULT_CHOICE) {
441 DebugStream.println("200 - Complete.");
442 }
443 else {
444 DebugStream.println("404 - Failed.");
445 }
446 url = null;
447 }
448 catch (Exception exception) {
449 DebugStream.printStackTrace(exception);
450 }
451 }
452
453 /** Retrieve the metadata directory, as required by any MSMCaller implementation.
454 * @return The currently active collection metadata directory as a <strong>String</strong>.
455 * @see org.greenstone.gatherer.collection.CollectionManager
456 */
457 public String getCollectionMetadata() {
458 if (c_man != null && c_man.ready()) {
459 return c_man.getCollectionMetadata();
460 }
461 return "";
462 }
463
464 /** Used to 'spawn' a new child application when a file is double clicked.
465 * @param file The file to open
466 * @see org.greenstone.gatherer.Gatherer.ExternalApplication
467 */
468 public void spawnApplication(File file) {
469 String [] commands = assoc_man.getCommand(file);
470 if(commands != null) {
471 ExternalApplication app = new ExternalApplication(commands);
472 apps.add(app);
473 app.start();
474 }
475 else {
476 ///ystem.err.println("No open command available.");
477 }
478 }
479
480
481 public void spawnApplication(String command)
482 {
483 ExternalApplication app = new ExternalApplication(command);
484 apps.add(app);
485 app.start();
486 }
487
488
489 /** Used to 'spawn' a new browser application or reset an existing one when the preview button is clicked
490 * @param url The url to open the browser at
491 * @see org.greenstone.gatherer.Gatherer.BrowserApplication
492 */
493 public void spawnBrowser(String url) {
494 String command = assoc_man.getBrowserCommand(url);
495 if (command != null) {
496 BrowserApplication app = new BrowserApplication(command, url);
497 apps.add(app);
498 app.start();
499 }
500 else {
501 ///ystem.err.println("No browser command available.");
502 }
503 }
504
505 /** Prints a warning message about a missing library path, which means the final collection cannot be previewed in the Gatherer.
506 */
507 static public void missingEXEC() {
508 String message_name;
509 if (GS3) {
510 message_name = "warning.MissingEXEC_GS3";
511 } else {
512 message_name = "warning.MissingEXEC";
513 }
514 WarningDialog dialog = new WarningDialog(message_name, "general.exec_address", false);
515 dialog.setValueField(new URLField(Configuration.getColor("coloring.editable_foreground", false), Configuration.getColor("coloring.editable_background", false), Configuration.getColor("coloring.error_foreground", false), Configuration.getColor("coloring.error_background", false)));
516 dialog.display();
517 dialog.dispose();
518 dialog = null;
519
520 String library_path_string = Configuration.getString("general.exec_address", true);
521 if (!library_path_string.equals("")) {
522 try {
523 Configuration.exec_address = new URL(library_path_string);
524 }
525 catch (MalformedURLException error) {
526 ///ystem.err.println("Error: Bad address: " + exec_address_string);
527 }
528 }
529 }
530
531 /** Prints a warning message about a missing GSDL path, which although not fatal pretty much ensures nothing will work properly in the Gatherer.
532 */
533 static public void missingGSDL() {
534 WarningDialog dialog = new WarningDialog("warning.MissingGSDL", null, false);
535 dialog.display();
536 dialog.dispose();
537 dialog = null;
538 }
539
540 /** Prints a warning message about a missing PERL path, which although not fatal pretty much ensures no collection creation/building will work properly in the Gatherer. */
541 static public void missingPERL() {
542 WarningDialog dialog = new WarningDialog("warning.MissingPERL", null, false);
543 dialog.display();
544 dialog.dispose();
545 dialog = null;
546 }
547
548 /** Prints a warning message about a missing a valid WGet path. not fatal, but mirroring won't work */
549 static public void missingWGET() {
550 WarningDialog dialog = new WarningDialog("warning.MissingWGET", null, false);
551 dialog.display();
552 dialog.dispose();
553 dialog = null;
554 }
555 /** Prints a warning message about having an old version of WGet. not fatal, but mirroring may not work properly */
556 static public void oldWGET() {
557 WarningDialog dialog = new WarningDialog("warning.OldWGET", null, false);
558 dialog.display();
559 dialog.dispose();
560 dialog = null;
561 }
562
563
564 /** Sets up the proxy connection by setting JVM Environment flags and creating a new Authenticator.
565 * @see java.lang.Exception
566 * @see java.lang.System
567 * @see java.net.Authenticator
568 * @see org.greenstone.gatherer.Configuration
569 * @see org.greenstone.gatherer.GAuthenticator
570 */
571 static public void setProxy() {
572 try {// Can throw several exceptions
573 if(Configuration.get("general.use_proxy", true)) {
574 System.setProperty("http.proxyType", "4");
575 System.setProperty("http.proxyHost", Configuration.getString("general.proxy_host", true));
576 System.setProperty("http.proxyPort", Configuration.getString("general.proxy_port", true));
577 System.setProperty("http.proxySet", "true");
578 } else {
579 System.setProperty("http.proxySet", "false");
580 }
581 } catch (Exception error) {
582 DebugStream.println("Error in Gatherer.initProxy(): " + error);
583 DebugStream.printStackTrace(error);
584 }
585 }
586
587 /** Set all the default fonts of the program to a choosen font, except the tooltip font which should be some fixed width font.
588 * @param f The default font to use in the Gatherer as a <strong>FontUIResource</strong>.
589 * @param ttf The tooltip font to use also as a <strong>FontUIResource</strong>.
590 * @see java.util.Enumeration
591 * @see javax.swing.UIManager
592 */
593 static private void setUIFont(FontUIResource f, FontUIResource ttf) {
594 // sets the default font for all Swing components.
595 // ex.
596 // setUIFont (new FontUIResource("Serif",Font.ITALIC,12));
597 //
598 Enumeration keys = UIManager.getDefaults().keys();
599 while (keys.hasMoreElements()) {
600 Object key = keys.nextElement();
601 Object value = UIManager.get (key);
602 if (value instanceof FontUIResource)
603 UIManager.put (key, f);
604 }
605 // Now set the tooltip font to some fixed width font
606 UIManager.put("ToolTip.font", ttf);
607 }
608
609 /** Loads the configuration file if one exists. Otherwise it creates a new one. Currently uses serialization.
610 * @param gsdl_path The path to the gsdl directory, gathered from the startup arguments, and presented as a <strong>String</strong>.
611 * @param exec_path The path to the library executable, gathered from the startup arguments, and presented as a <strong>String</strong>.
612 * @param perl_path The path to the PERL compiler as a <strong>String</strong>. Necessary for windows platform versions.
613 * @param mirroring_enabled
614 * @param wget_version_str
615 * @param wget_path
616 * @see java.io.FileInputStream
617 * @see java.io.ObjectInputStream
618 * @see java.lang.Exception
619 * @see org.greenstone.gatherer.Configuration
620 */
621 private void loadConfig(String gsdl_path, String gsdl3_path, String exec_path, String perl_path, boolean mirroring_enabled, String site_name) {
622 try {
623 new Configuration(gsdl_path, gsdl3_path, exec_path, perl_path, mirroring_enabled, site_name);
624 }
625 catch (Exception error) {
626 DebugStream.println(Configuration.CONFIG_XML+" is not a well formed XML document.");
627 DebugStream.printStackTrace(error);
628 }
629 if (GS3) {
630 try {
631 servlet_config = new ServletConfiguration(gsdl3_path);
632 } catch (Exception exception) {
633 DebugStream.printStackTrace(exception);
634 }
635 }
636 }
637
638 /** Causes the general configuration file to export itself to xml. Doesn't effect any remaining collection configuration, as its up to the collection manager to handle them (especially since we have no idea where they are going). */
639 private void saveConfig() {
640 try {
641 Configuration.save();
642 } catch (Exception exception) {
643 DebugStream.printStackTrace(exception);
644 }
645 }
646
647
648 // TODO fill this in
649 private String testWGetVersion(String wget_path) {
650 return StaticStrings.WGET_STR;
651 }
652
653 /** This private class contains an instance of an external application running within a JVM shell. It is important that this process sits in its own thread, but its more important that when we exit the Gatherer we don't actually System.exit(0) the Gatherer object until the user has volunteerily ended all of these child processes. Otherwise when we quit the Gatherer any changes the users may have made in external programs will be lost and the child processes are automatically deallocated. */
654 private class ExternalApplication
655 extends Thread {
656 private Process process = null;
657 /** The initial command string given to this sub-process. */
658 private String command = null;
659 private String[] commands = null;
660 /** Constructor.
661 * @param command The initial command <strong>String</strong>.
662 */
663 public ExternalApplication(String command) {
664 this.command = command;
665 }
666
667 public ExternalApplication(String[] commands) {
668 this.commands = commands;
669 }
670 /** We start the child process inside a new thread so it doesn't block the rest of Gatherer.
671 * @see java.lang.Exception
672 * @see java.lang.Process
673 * @see java.lang.Runtime
674 * @see java.lang.System
675 * @see java.util.Vector
676 */
677 public void run() {
678 // Call an external process using the args.
679 try {
680 if(commands != null) {
681 StringBuffer whole_command = new StringBuffer();
682 for(int i = 0; i < commands.length; i++) {
683 whole_command.append(commands[i]);
684 whole_command.append(" ");
685 }
686 DebugStream.println("Running " + whole_command.toString());
687 Runtime rt = Runtime.getRuntime();
688 process = rt.exec(commands);
689 process.waitFor();
690 }
691 else {
692 DebugStream.println("Running " + command);
693 Runtime rt = Runtime.getRuntime();
694 process = rt.exec(command);
695 process.waitFor();
696 }
697 }
698 catch (Exception exception) {
699 DebugStream.printStackTrace(exception);
700 }
701 // Remove ourself from Gatherer list of threads.
702 apps.remove(this);
703 // Call exit if we were the last outstanding child process thread.
704 if (apps.size() == 0 && exit == true) {
705 LocalLibraryServer.stop();
706 System.exit(0);
707 }
708 }
709 public void stopExternalApplication() {
710 if(process != null) {
711 process.destroy();
712 }
713 }
714 }
715 /** This private class contains an instance of an external application running within a JVM shell. It is important that this process sits in its own thread, but its more important that when we exit the Gatherer we don't actually System.exit(0) the Gatherer object until the user has volunteerily ended all of these child processes. Otherwise when we quit the Gatherer any changes the users may have made in external programs will be lost and the child processes are automatically deallocated. */
716 private class BrowserApplication
717 extends Thread {
718 private Process process = null;
719 /** The initial command string given to this sub-process. */
720 private String command = null;
721 private String url = null;
722 private String[] commands = null;
723
724 public BrowserApplication(String command, String url) {
725 StringTokenizer st = new StringTokenizer(command);
726 int num_tokens = st.countTokens();
727 this.commands = new String [num_tokens];
728 int i=0;
729 while (st.hasMoreTokens()) {
730 commands[i] = st.nextToken();
731 i++;
732 }
733 //this.commands = commands;
734 this.url = url;
735 }
736 /** We start the child process inside a new thread so it doesn't block the rest of Gatherer.
737 * @see java.lang.Exception
738 * @see java.lang.Process
739 * @see java.lang.Runtime
740 * @see java.lang.System
741 * @see java.util.Vector
742 */
743 public void run() {
744 // Call an external process using the args.
745 if(commands == null) {
746 apps.remove(this);
747 return;
748 }
749 try {
750 String prog_name = commands[0];
751 String lower_name = prog_name.toLowerCase();
752 if (lower_name.indexOf("mozilla") != -1 || lower_name.indexOf("netscape") != -1) {
753 DebugStream.println("found mozilla or netscape, trying remote it");
754 // mozilla and netscape, try using a remote command to get things in the same window
755 String [] new_commands = new String[] {prog_name, "-raise", "-remote", "openURL("+url+",new-tab)"};
756 printArray(new_commands);
757
758 Runtime rt = Runtime.getRuntime();
759 process = rt.exec(new_commands);
760 int exitCode = process.waitFor();
761 if (exitCode != 0) { // if Netscape or mozilla was not open
762 DebugStream.println("couldn't do remote, trying original command");
763 printArray(commands);
764 process = rt.exec(commands); // try the original command
765 }
766 } else {
767 // just run what we have been given
768 StringBuffer whole_command = new StringBuffer();
769 for(int i = 0; i < commands.length; i++) {
770 whole_command.append(commands[i]);
771 whole_command.append(" ");
772 }
773 DebugStream.println("Running " + whole_command.toString());
774 Runtime rt = Runtime.getRuntime();
775 process = rt.exec(commands);
776 process.waitFor();
777 }
778 }
779
780 catch (Exception exception) {
781 DebugStream.printStackTrace(exception);
782 }
783 // Remove ourself from Gatherer list of threads.
784 apps.remove(this);
785 // Call exit if we were the last outstanding child process thread.
786 if (apps.size() == 0 && exit == true) {
787 LocalLibraryServer.stop();
788 System.exit(0);
789 }
790 }
791 public void printArray(String [] array) {
792 for(int i = 0; i < array.length; i++) {
793 DebugStream.print(array[i]+" ");
794 System.err.println(array[i]+" ");
795 }
796 }
797 public void stopBrowserApplication() {
798 if(process != null) {
799 process.destroy();
800 }
801 }
802 }
803
804 /** This class is intented to detect a specific SIGNAL, in this case SIGINT, and exit properly, rather than letting the Gatherer be interrupted which has the potential to leave erroneous lock files. */
805// private class CTRLCHandler
806// implements SignalHandler {
807// /** <i>true</i> if we ignore any other signals we receive, most likely because we are already dealing with one, <i>false</i> otherwise. */
808// private boolean ignore = false;
809// /** The method called by the system to inform us a signal has occured.
810// * @param sig The <strong>Signal</strong> itself.
811// * @see org.greenstone.gatherer.collection.CollectionManager
812// */
813// public void handle(Signal sig) {
814// if(!ignore) {
815// ignore = true;
816// // handle SIGINT
817// System.out.println("Caught Ctrl-C...");
818// if(c_man != null && c_man.ready()) {
819// c_man.closeCollection();
820// }
821// exit();
822// ignore = false;
823// }
824// }
825// }
826
827 private class PerlTest {
828
829 private String[] command = new String[2];
830
831 public PerlTest() {
832 if(Utility.isWindows()) {
833 command[0] = Utility.PERL_EXECUTABLE_WINDOWS;
834 }
835 else {
836 command[0] = Utility.PERL_EXECUTABLE_UNIX;
837 }
838 command[1] = "-version";
839 }
840
841 public boolean found() {
842 boolean found = false;
843 try {
844 Process prcs = Runtime.getRuntime().exec(command);
845 prcs.waitFor();
846 found = (prcs.exitValue() == 0);
847 prcs = null;
848 }
849 catch(Exception error) {
850 }
851 return found;
852 }
853
854 public String toString() {
855 return command[0];
856 }
857 }
858}
Note: See TracBrowser for help on using the repository browser.