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

Last change on this file since 7326 was 7326, checked in by kjdon, 20 years ago

the intial stage of making gli work with gs3 - still uses gs2 building, but uses colls in gs3 setup

  • Property svn:keywords set to Author Date Id Revision
File size: 47.8 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/**************************************************************************************
30 * Written: ??/??/02
31 * Revised: ??/??/02 - Commented
32 * ??/??/03 - Added support for local library server
33 * 20/07/03 - Rewrote argument parsing so that spaces no longer cause GLI to die. Also added time out when starting local library.
34 **************************************************************************************/
35
36//import com.l2fprod.gui.*;
37//import com.l2fprod.gui.plaf.skin.*;
38//import com.l2fprod.util.*;
39import java.awt.*;
40import java.awt.event.*;
41import java.io.*;
42import java.lang.*;
43import java.net.*;
44import java.util.*;
45import javax.swing.*;
46import javax.swing.plaf.*;
47import javax.swing.text.*;
48import org.greenstone.gatherer.Configuration;
49import org.greenstone.gatherer.GAuthenticator;
50import org.greenstone.gatherer.cdm.CommandTokenizer;
51import org.greenstone.gatherer.collection.CollectionManager;
52import org.greenstone.gatherer.feedback.ActionRecorderDialog;
53import org.greenstone.gatherer.file.FileManager;
54import org.greenstone.gatherer.file.FileAssociationManager;
55import org.greenstone.gatherer.gui.Coloring;
56import org.greenstone.gatherer.gui.GUIManager;
57import org.greenstone.gatherer.gui.ModalDialog;
58import org.greenstone.gatherer.gui.Splash;
59import org.greenstone.gatherer.gui.URLField;
60import org.greenstone.gatherer.gui.WarningDialog;
61import org.greenstone.gatherer.msm.MDSTest;
62import org.greenstone.gatherer.msm.MetadataSetManager;
63import org.greenstone.gatherer.util.ArrayTools;
64import org.greenstone.gatherer.util.GSDLSiteConfig;
65import org.greenstone.gatherer.util.StaticStrings;
66import org.greenstone.gatherer.util.Utility;
67import sun.misc.*;
68
69/** Containing the main() method for the Gatherer, this class is the starting point for the rest of the application. It first parses the command line arguments, preparing to update the configuration as required. Next it loads several important support classes such as the Configuration and Dictionary. Finally it creates the other important managers and sends them on their way.
70 * @author John Thompson, Greenstone Digital Library, University of Waikato
71 * @version 2.3
72 */
73
74// How to catch All Exceptions including those from the AWT Event thread.
75// Step 1: register a handler class in your main()
76//
77// System.setProperty("sun.awt.exception.handler", "YourHandler");
78//
79// Step 2: implement your handler class.
80//
81// public class YourHandler {
82// public void handle(Throwable thrown) {
83// eat the exception without dumping to the console.
84// }
85// }
86
87public class Gatherer {
88
89 static public Hashtable authentications = new Hashtable();
90
91 static final private String SKIN_DEFINITION_FILE = "lib/greenaqua/greenaqua.xml";
92
93 static public boolean always_show_exceptions = true;
94 /** Has the exit flag been set? <i>true</i> if so, <i>false</i> otherwise. */
95 public boolean exit = false;
96 /** The size of the Gatherer window. */
97 public Dimension frame_size = null;
98
99 /** A temporary shared memory area to store HIndexes to speed up metadata.xml writing. */
100 public Hashtable known_indexes = null;
101 /** Legacy copy of the debug_ps. */
102 public PrintStream debug_ps;
103 /** All of the external applications that must exit before we close the Gatherer. */
104 public Vector apps = new Vector();
105 /** Messages that have been issued before we have anyway to show them, ie prior to Log initialization. */
106 public Vector waiting_messages = new Vector();
107 /** The manager in charge of remembering what file extension gets opened with what program. */
108 static public FileAssociationManager assoc_man;
109 /** A public reference to the CollectionManager. */
110 static public CollectionManager c_man;
111 /** A public reference to the Gatherer's configuration. */
112 static public Configuration config;
113 /** a reference to the Servlet Configuration is GS3 */
114 static public ServletConfiguration servlet_config;
115 /** The current modal dialog being shown on screen, if any. */
116 static public ModalDialog current_modal = null;
117 /** A public reference to the Dictionary. */
118 static public Dictionary dictionary;
119 /** A public reference to the FileManager. */
120 static public FileManager f_man;
121 /** A public reference to the GUIManager. */
122 static public GUIManager g_man;
123 /** A static reference to ourselves. */
124 static public Gatherer self;
125 /** The debug print stream. */
126 static public PrintStream debug;
127 /** We are using the GLI for GS3 */
128 static public boolean GS3 = false;
129 /** The name of the necessary environment variable to check for in the programs environment. */
130 static public String KEY = "GSDLPATH";
131
132 // feedback stuff
133 /** is the feedback feature enabled? */
134 static public boolean feedback_enabled = true;
135 /** the action recorder dialog */
136 static public ActionRecorderDialog feedback_dialog = null;
137 /** Extra environment information which must be set before shell processes will run properly. Should always be null if the startup script/program has done its job properly. */
138 static public String extra_env[] = null;
139 private GSDLSiteConfig gsdlsite_cfg = null;
140 private ExternalApplication server = null;
141
142 /** The name of the Gatherers configuration file. */
143 static private String CONFIG_FILE_NAME = "gatherer.cfg";
144
145 /** Magic to allow Enter to fire the default button. */
146 static {
147 KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
148 Keymap map = JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP);
149 map.removeKeyStrokeBinding(enter);
150 }
151
152 public Gatherer() {
153 this.self = this;
154 }
155
156 /** Run method. Make the three main modules, c_man, f_man and g_man, and any other necessary classes such as Dictionary.
157 * @param size The desired size of the Gatherer window as a <strong>Dimension</strong>.
158 * @param gsdl_path The path to the gsdl directory, gathered from the startup arguments, and presented as a <strong>String</strong>.
159 * @param gsdl3_path The path to the gsdl3 directory, gathered from the startup arguments, and presented as a <strong>String</strong>.
160 * @param exec_path The path to the library executable, gathered from the startup arguments, and presented as a <strong>String</strong>.
161 * @param debug_enabled <i>true</i> to print verbose debug messages to "debug.txt", <i>false</i> otherwise.
162 * @param perl_path The path to the PERL compiler as a <strong>String</strong>. Necessary for windows platform versions.
163 * @param no_load <i>true</i> to prevent the previously opened collection from reopening.
164 * @param splash A reference to the splash screen.
165 * @param open_collection
166 * @param mirroring_enabled
167 * @param wget_version_str
168 * @param wget_path
169 * @see java.io.FileOutputStream
170 * @see java.io.PrintStream
171 * @see java.lang.Exception
172 * @see java.lang.StringBuffer
173 * @see java.util.Calendar
174 * @see org.greenstone.gatherer.Configuration
175 * @see org.greenstone.gatherer.Dictionary
176 * @see org.greenstone.gatherer.Gatherer.CTRLCHandler
177 * @see org.greenstone.gatherer.GAuthenticator
178 * @see org.greenstone.gatherer.collection.CollectionManager
179 * @see org.greenstone.gatherer.file.FileManager
180 * @see org.greenstone.gatherer.gui.GUIManager
181 * @see org.greenstone.gatherer.gui.Splash
182 */
183 public void run(Dimension size, String gsdl_path, String gsdl3_path, String exec_path, boolean debug_enabled, String perl_path, boolean no_load, Splash splash, String open_collection, String site_name, String servlet_path, boolean mirroring_enabled, String wget_version_str, String wget_path) {
184
185 // This will hopefully catch ctrl-c and terminate, and exit gracefully. However it is platform specific, and may not be supported by some JVMs.
186 /** It does, but I get bloody sick of it working when the Gatherer hangs.
187 CTRLCHandler handler = new CTRLCHandler();
188 Signal.handle(new Signal("INT"), handler);
189 Signal.handle(new Signal("TERM"), handler);
190 handler = null;
191 */
192
193 if (gsdl3_path != null && !gsdl3_path.equals("")) {
194 this.GS3 = true;
195 } else {
196 gsdl3_path = null;
197 }
198 // Create the debug stream only if required.
199 if(debug_enabled) {
200 try {
201 Calendar now = Calendar.getInstance();
202 StringBuffer name = new StringBuffer("debug");
203 name.append(now.get(Calendar.DATE));
204 name.append("-");
205 name.append(now.get(Calendar.MONTH));
206 name.append("-");
207 name.append(now.get(Calendar.YEAR));
208 name.append(".txt");
209 this.debug = new PrintStream(new FileOutputStream(name.toString()));
210 Properties props = System.getProperties();
211 props.list(debug);
212 // Legacy
213 debug_ps = debug;
214 }
215 catch(Exception error) {
216 ///ystem.err.println("Error in Gatherer.init(): " + error);
217 error.printStackTrace();
218 System.exit(1);
219 }
220 }
221 try {
222 // Load Config
223 loadConfig(gsdl_path, gsdl3_path, exec_path, perl_path, mirroring_enabled, wget_version_str, wget_path, site_name);
224
225 // the feedback dialog has been loaded with a default locale,
226 // now set the user specified one
227 if (feedback_enabled && feedback_dialog != null) {
228 feedback_dialog.setLocale(config.getLocale("general.locale", true));
229 }
230
231 // Read Dictionary
232 dictionary = new Dictionary(config.getLocale("general.locale", true), config.getFont("general.font", true));
233
234
235 if (gsdl_path == null) {
236 missingGSDL();
237 }
238
239 // If we were given a server run it if neccessary.
240 if (!GS3 && config.exec_file != null) {
241 startServerEXE();
242 }
243
244 // 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.
245 if (config.exec_file == null && config.exec_address == null) {
246 if (config.exec_file == null) {
247 Gatherer.println("config.exec_file is null");
248 }
249 if (config.exec_address == null) {
250 Gatherer.println("config.exec_address is null");
251 }
252 missingEXEC();
253 }
254
255 // Perl path is a little different as it is perfectly ok to start the Gatherer without providing a perl path
256 boolean found_perl = false;
257 if (config.perl_path != null) {
258 // See if the file pointed to actually exists
259 File perl_file = new File(config.perl_path);
260 found_perl = perl_file.exists();
261 perl_file = null;
262 }
263 if (config.perl_path == null || !found_perl) {
264 // Run test to see if we can run perl as is.
265 PerlTest perl_test = new PerlTest();
266 if (perl_test.found()) {
267 // If so replace the perl path with the system default (or null for unix).
268 config.perl_path = perl_test.toString();
269 found_perl = true;
270 }
271 }
272 if (!found_perl) {
273 // Time for an error message.
274 missingPERL();
275 }
276
277 // Size and place the frame on the screen
278 Rectangle bounds = config.getBounds("general.bounds", true);
279 if (bounds == null) {
280 // Choose a sensible default value
281 bounds = new Rectangle(0, 0, 640, 480);
282 }
283
284 // Ensure width and height are reasonable
285 size = bounds.getSize();
286 if (size.width < 640) {
287 size.width = 640;
288 }
289 else if (size.width > config.screen_size.width) {
290 size.width = config.screen_size.width;
291 }
292 if (size.height < 480) {
293 size.height = 480;
294 }
295 else if (size.height > config.screen_size.height) {
296 size.height = config.screen_size.height;
297 }
298 // Set default font
299 setUIFont(config.getFont("general.font", true), config.getFont("general.tooltip_font", true));
300 // Set up proxy
301 setProxy();
302 // Now we set up an Authenticator
303 Authenticator.setDefault(new GAuthenticator());
304
305 assoc_man = new FileAssociationManager();
306 // Create File Manager
307 f_man = new FileManager();
308 // Create Collection Manager
309 c_man = new CollectionManager();
310 // If there was an open collection last session, reopen it.
311
312 if (GS3) {
313 if (site_name==null) {
314 site_name = config.site_name;
315 servlet_path = null; // need to reset this
316 }
317 if (servlet_path == null) {
318 servlet_path = config.getServletPath();
319 }
320 }
321 if(open_collection == null) {
322 open_collection = config.getString("general.open_collection", true);
323 }
324 if(!no_load && open_collection.length() > 0) {
325 c_man.loadCollection(open_collection);
326 }
327 // Create GUI Manager (last) or else suffer the death of a thousand NPE's
328 splash.toFront();
329 g_man = new GUIManager(size);
330 g_man.display();
331
332 // 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).
333 g_man.setLocation(bounds.x, bounds.y);
334 g_man.setVisible(true);
335
336 // After the window has been made visible, check that it is in the correct place
337 // sometimes java places a window not in the correct place,
338 // but with an offset. If so, we work out what the offset is ]
339 // and change the desired location to take that into account
340 Point location = g_man.getLocation();
341 int x_offset = bounds.x - location.x;
342 int y_offset = bounds.y - location.y;
343 // If not, offset the window to move it into the correct location
344 if (x_offset > 0 || y_offset > 0) {
345 ///ystem.err.println("changing the location to "+(bounds.x + x_offset)+" "+ (bounds.y + y_offset));
346 g_man.setLocation(bounds.x + x_offset, bounds.y + y_offset);
347 }
348
349 // 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.
350 g_man.afterDisplay();
351 // Hide the splash.
352 splash.hide();
353 splash.destroy();
354 splash = null;
355 }
356 catch (Exception error) {
357 error.printStackTrace();
358 }
359 }
360
361 /** Exits the Gatherer after ensuring that things needing saving are saved.
362 * @see java.io.FileOutputStream
363 * @see java.io.PrintStream
364 * @see java.lang.Exception
365 * @see javax.swing.JOptionPane
366 * @see org.greenstone.gatherer.Configuration
367 * @see org.greenstone.gatherer.collection.CollectionManager
368 * @see org.greenstone.gatherer.gui.GUIManager
369 */
370 public void exit() {
371 exit = true;
372 // If we have an open collection make note of it.
373 config.setString("general.open_collection", true, null);
374 config.setString("general.site_name", true, null);
375 if(c_man.ready()) {
376 ///ystem.err.println("Collection open.");
377 if(c_man.saved()) {
378 ///ystem.err.println("Collection has been recently saved, so I'll remember it for next time.");
379 config.setString("general.open_collection", true, c_man.getCollectionFilename());
380 }
381 c_man.closeCollection();
382 }
383 if(assoc_man != null) {
384 assoc_man.save();
385 assoc_man = null;
386 }
387
388 // Get the gui to deallocate
389 g_man.hide();
390 g_man.destroy();
391
392 // Store the current position and size (if reasonable) of the Gatherer for next time
393 Rectangle bounds = g_man.getBounds();
394 config.setBounds("general.bounds", true, bounds);
395
396 // Save configuration.
397 saveConfig();
398
399 // Flush dictionary
400 // dictionary.destroy();
401
402 // Flush debug
403 if(debug != null) {
404 try {
405 debug.flush();
406 debug.close();
407 }
408 catch (Exception error) {
409 error.printStackTrace();
410 }
411 }
412
413 // If we started a server, we should try to stop it.
414 if(!GS3 && gsdlsite_cfg != null) {
415 stopServerEXE();
416 }
417
418 if(apps.size() == 0) {
419 System.exit(0);
420 }
421 else {
422 JOptionPane.showMessageDialog(g_man, Dictionary.get("General.Outstanding_Processes"), Dictionary.get("General.Outstanding_Processes_Title"), JOptionPane.ERROR_MESSAGE);
423 g_man.hide();
424 }
425 }
426
427 // used to send new coll messages to the local library
428 public void configServer(String command) {
429
430 try {
431 String raw_url = config.exec_address.toString() + command;
432 URL url = new URL(raw_url);
433 Gatherer.println("Action: " + raw_url);
434 HttpURLConnection library_connection = (HttpURLConnection) url.openConnection();
435 int response_code = library_connection.getResponseCode();
436 if(HttpURLConnection.HTTP_OK <= response_code && response_code < HttpURLConnection.HTTP_MULT_CHOICE) {
437 Gatherer.println("200 - Complete.");
438 }
439 else {
440 Gatherer.println("404 - Failed.");
441 }
442 url = null;
443 }
444 catch(Exception exception) {
445 Gatherer.printStackTrace(exception);
446 ///ystem.err.println("Bad URL.");
447 }
448
449 }
450
451 // used to send reload coll messages to the tomcat server
452 public void configGS3Server(String site, String command) {
453
454 try {
455 // need to add the servlet name to the exec address
456 String raw_url = config.exec_address.toString() + config.getServletPath() + command;
457 URL url = new URL(raw_url);
458 Gatherer.println("Action: " + raw_url);
459 HttpURLConnection library_connection = (HttpURLConnection) url.openConnection();
460 int response_code = library_connection.getResponseCode();
461 if(HttpURLConnection.HTTP_OK <= response_code && response_code < HttpURLConnection.HTTP_MULT_CHOICE) {
462 Gatherer.println("200 - Complete.");
463 }
464 else {
465 Gatherer.println("404 - Failed.");
466 }
467 url = null;
468 }
469 catch(Exception exception) {
470 Gatherer.printStackTrace(exception);
471 ///ystem.err.println("Bad URL.");
472 }
473 }
474
475 /** Retrieve the metadata directory, as required by any MSMCaller implementation.
476 * @return The currently active collection metadata directory as a <strong>String</strong>.
477 * @see org.greenstone.gatherer.collection.CollectionManager
478 */
479 public String getCollectionMetadata() {
480 if (c_man != null && c_man.ready()) {
481 return c_man.getCollectionMetadata();
482 }
483 return "";
484 }
485
486 /** Retrieve a reference to the frame that any dialog boxes will appear relative to, as required by any MSMCaller or CDMCaller implementation.
487 * @return A <strong>JFrame</strong>.
488 * @see org.greenstone.gatherer.gui.GUIManager
489 */
490 /* private JFrame getFrame() {
491 return g_man;
492 } */
493
494 /** Method to retrieve a reference to the metadata set manager class. This is then used to create the 'metadataset' commands in the collection configuration file.
495 * @return A reference to the <Strong>MetadataSetManager</strong>.
496 * @see org.greenstone.gatherer.collection.CollectionManager
497 */
498 /* private MetadataSetManager getMSM() {
499 if(c_man != null && c_man.getCollection() != null && c_man.getCollection().msm != null) {
500 return c_man.getCollection().msm;
501 }
502 return null;
503 } */
504
505 /** Used to 'spawn' a new child application when a file is double clicked.
506 * @param file The file to open
507 * @see org.greenstone.gatherer.Gatherer.ExternalApplication
508 */
509 public void spawnApplication(File file) {
510 String [] commands = assoc_man.getCommand(file);
511 if(commands != null) {
512 ExternalApplication app = new ExternalApplication(commands);
513 apps.add(app);
514 app.start();
515 }
516 else {
517 ///ystem.err.println("No open command available.");
518 }
519 }
520
521 /** Used to 'spawn' a new browser application or reset an existing one when the preview button is clicked
522 * @param url The url to open the browser at
523 * @see org.greenstone.gatherer.Gatherer.BrowserApplication
524 */
525 public void spawnBrowser(String url) {
526 String command = assoc_man.getBrowserCommand(url);
527 if (command != null) {
528 BrowserApplication app = new BrowserApplication(command, url);
529 apps.add(app);
530 app.start();
531 }
532 else {
533 ///ystem.err.println("No browser command available.");
534 }
535 }
536
537 /** The entry point into the Gatherer. Parses arguments.
538 * @param args A collection of arguments that may include: initial screen size, dictionary, path to the GSDL etc.
539 * @see java.io.File
540 * @see java.io.FileInputStream
541 * @see java.lang.Exception
542 * @see java.util.Properties
543 * @see org.greenstone.gatherer.Dictionary
544 * @see org.greenstone.gatherer.Gatherer
545 * @see org.greenstone.gatherer.gui.Splash
546 * @see org.greenstone.gatherer.util.StaticStrings
547 */
548 static public void main(String[] args) {
549 // A serious hack, but its good enough to stop crappy 'Could not lock user prefs' error messages.
550 // Thanks to Walter Schatz from the java forums.
551 System.setProperty("java.util.prefs.syncInterval","2000000"); // One message every 600 hours!
552
553
554 // Override the exception handler with a new one which we can easily quiet the exceptions from.
555 // System.setProperty("sun.awt.exception.handler", "GLIExceptionHandler");
556
557 // Ensure platform specific LAF
558 try {
559 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
560 }
561 catch(Exception exception) {
562 exception.printStackTrace();
563 }
564
565 Gatherer gatherer = new Gatherer();
566 boolean debug = false;
567 boolean feedback_enabled = false;
568 boolean mirroring_enabled = false;
569 boolean no_load = false;
570 Dictionary dictionary = new Dictionary(null, null); // Default dictionary. Only used for starting error messages.
571 Dimension size = new Dimension(800, 540);
572 String exec_path = null;
573 String extra = null;
574 String filename = null;
575 String gsdl_path = null;
576 String gsdl3_path = null;
577 String perl_path = null;
578 String site_name = null; // for GS3
579 String servlet_path = null;
580 String wget_path = null;
581 String wget_version_str = StaticStrings.NO_WGET_STR;
582 // Parse arguments
583 int argument_index = 0;
584 String next_token = null;
585 while(argument_index < args.length || next_token != null) {
586 // 1. We start by attempting to parse an argument name. An argument must start with a '-', and should not contain spaces. If anything else is encountered it is ignored.
587 String argument_name = null;
588 if(next_token == null) {
589 next_token = args[argument_index];
590 argument_index++;
591 }
592 if(next_token.startsWith(StaticStrings.MINUS_CHARACTER)) {
593 // Trim second '-' just to be kind to Unixy-type people
594 if(next_token.startsWith(StaticStrings.MINUS_CHARACTER + StaticStrings.MINUS_CHARACTER)) {
595 argument_name = next_token.substring(1);
596 }
597 else {
598 argument_name = next_token;
599 }
600 }
601 next_token = null;
602 // 2. If we now have an argument name we continue by attempting to parse a value. A value is taken to be the sequence of space seperated Strings between the last argument name and up to but not including the next argument name. Of course an argument needn't have any value (ie -debug, -help), in which case value will be null.
603 if(argument_name != null) {
604 String argument_value = null;
605 StringBuffer argument_value_buffer = new StringBuffer("");
606 while(argument_index < args.length && next_token == null) {
607 next_token = args[argument_index];
608 argument_index++;
609 // If we just parsed an arbitary String then append it to value, followed by a single space
610 if(!next_token.startsWith(StaticStrings.MINUS_CHARACTER)) {
611 argument_value_buffer.append(next_token);
612 argument_value_buffer.append(StaticStrings.SPACE_CHARACTER);
613 next_token = null;
614 }
615 // If the argument token retrieved is an argument name, then leave it in next_token, which will cause the argument parsing process to move onto the next step.
616 }
617 // If a value now exists in argument buffer, retrieve it. Remove the last character as it will be an erroneous space.
618 if(argument_value_buffer.length() > 0) {
619 argument_value = argument_value_buffer.substring(0, argument_value_buffer.length() - 1);
620 }
621
622 // 3. We now have the argument name, and any associated value. We are ready to store the data in the appropriate variables.
623 Gatherer.println("Parsed Argument: name=" + argument_name + (argument_value != null ? (", value=" + argument_value) : ", no value"));
624 // 3a. First those arguments that have no associated value
625 if(argument_value == null) {
626 if(argument_name.equals(StaticStrings.HELP_ARGUMENT)) {
627 printUsage(dictionary);
628 System.exit(0);
629 }
630 // Run GLI in debug mode. Produces debug log plus extra messages.
631 else if(argument_name.equals(StaticStrings.DEBUG_ARGUMENT)) {
632 debug = true;
633 }
634 // Run GLI with feedback enabled.
635 else if(argument_name.equals(StaticStrings.FEEDBACK_ARGUMENT)) {
636 feedback_enabled = true;
637 }
638 // Forces no loading on previous collection.
639 else if(argument_name.equals(StaticStrings.NO_LOAD_ARGUMENT)) {
640 no_load = true;
641 filename = null;
642 }
643 // Run the GLI with mirroring enabled
644 else if(argument_name.equals(StaticStrings.MIRROR_ARGUMENT)) {
645 mirroring_enabled = true;
646 }
647 /* I've got a sneak suspicion Aqua look and feel is not free domain
648 // Specify the use of Greenstone LAF.
649 else if(argument_name.equals(StaticStrings.SKIN_ARGUMENT)) {
650 // SkinLF
651 try {
652 SkinLookAndFeel.setSkin(SkinLookAndFeel.loadThemePackDefinition(SkinUtils.toURL(new File(SKIN_DEFINITION_FILE))));
653 SkinLookAndFeel.enable();
654 }
655 catch (Exception error) {
656 ///ystem.err.println("Error: " + error);
657 error.printStackTrace();
658 }
659 }
660 */
661 }
662 // 3b. Now for those that do
663 else {
664 // Parse the path to the GSDL. Required argument.
665 if(argument_name.equals(StaticStrings.GSDL_ARGUMENT)) {
666 if(argument_value.endsWith(File.separator)) {
667 gsdl_path = argument_value;
668 }
669 else {
670 gsdl_path = argument_value + File.separator;
671 }
672 }
673 // GSDL3 path
674 if(argument_name.equals(StaticStrings.GSDL3_ARGUMENT)) {
675 if(argument_value.endsWith(File.separator)) {
676 gsdl3_path = argument_value;
677 }
678 else {
679 gsdl3_path = argument_value + File.separator;
680 }
681 }
682 else if (argument_name.equals(StaticStrings.SITE_ARGUMENT)) {
683 site_name = argument_value;
684 }
685 else if (argument_name.equals(StaticStrings.SERVLET_ARGUMENT)) {
686 servlet_path = argument_value;
687 }
688 // Specify a collection to load initially. Could be used for file associations.
689 else if(argument_name.equals(StaticStrings.LOAD_ARGUMENT)) {
690 filename = argument_value;
691 no_load = false;
692 }
693 // Parse the url to a running web server, or a file path to the local library server.
694 else if(argument_name.equals(StaticStrings.LIBRARY_ARGUMENT)) {
695 exec_path = argument_value;
696 // If there is no colon in first five characters of the exec_path (which would either be an existing protocol, or a windows file path to say a local library), we can append the protocol http://.
697 if(argument_value.lastIndexOf(StaticStrings.COLON_CHARACTER, 5) == -1) {
698 exec_path = StaticStrings.HTTP_PROTOCOL_STR + argument_value;
699 }
700 else {
701 exec_path = argument_value;
702 }
703 // If the user has given us an address, but it ends with a '/' we assume we're using the greenstone library.cgi
704 if(exec_path.startsWith(StaticStrings.HTTP_PROTOCOL_STR) && exec_path.endsWith(StaticStrings.URL_SEPARATOR_CHARACTER)) {
705 exec_path = exec_path + StaticStrings.LIBRARY_STR;
706 }
707 }
708 // Parse the path to PERL. If not provided its assumes perl should be availble on the PATH.
709 else if(argument_name.equals(StaticStrings.PERL_ARGUMENT)) {
710 perl_path = argument_value;
711 // Test whether this points to the Perl bin directory or the Perl executable itself.
712 File perl_file = new File(perl_path);
713 if(perl_file.isDirectory()) {
714 // If this is windows we create a child file perl.exe, otherwise we create perl
715 if(Utility.isWindows()) {
716 perl_file = new File(perl_file, Utility.PERL_EXECUTABLE_WINDOWS);
717 }
718 else {
719 perl_file = new File(perl_file, Utility.PERL_EXECUTABLE_UNIX);
720 }
721 // And store this new path.
722 perl_path = perl_file.getAbsolutePath();
723 perl_file = null;
724 }
725 // Otherwise its fine as it is
726 }
727 // Test for the presence of a WGet version. This is only useful if the user has enabled mirroring. Note that mirroring can be enabled by running the GLI with -mirror, editing the config.xml for GLI, or through a new option on the connections page of the preferences.
728 else if(argument_name.equals(StaticStrings.WGET_ARGUMENT)) {
729 if(argument_value.startsWith(StaticStrings.WGET_STR)) {
730 wget_version_str = StaticStrings.WGET_STR;
731 wget_path = argument_value.substring(StaticStrings.WGET_STR.length());
732 }
733 else if(argument_value.startsWith(StaticStrings.WGET_OLD_STR)) {
734 wget_version_str = StaticStrings.WGET_OLD_STR;
735 wget_path = argument_value.substring(StaticStrings.WGET_OLD_STR.length());
736 }
737 }
738 }
739 }
740 // Argument name was null, nothing to be done.
741 }
742 next_token = null;
743 // Arguments all parsed.
744
745 if (feedback_enabled) {
746 // if feedback is enabled, set up the recorder dialog
747 Locale currLocale = Locale.getDefault(); // use teh default locale for now - this will be changed by the Gatherer run method
748 ActionRecorderDialog dlg = new ActionRecorderDialog (currLocale);
749 gatherer.feedback_enabled = true;
750 gatherer.feedback_dialog = dlg;
751 }
752
753 // Splash screen.
754 Splash splash = new Splash();
755 // dictionary.destroy();
756 gatherer.run(size, gsdl_path, gsdl3_path, exec_path, debug, perl_path, no_load, splash, filename, site_name, servlet_path, mirroring_enabled, wget_version_str, wget_path);
757 }
758
759 /** Prints a warning message about a missing library path, which means the final collection cannot be previewed in the Gatherer.
760 */
761 static public void missingEXEC() {
762 WarningDialog dialog = new WarningDialog("warning.MissingEXEC", "general.exec_address");
763 dialog.setValueField(new URLField(Gatherer.config.getColor("coloring.editable_foreground", false), Gatherer.config.getColor("coloring.editable_background", false), Gatherer.config.getColor("coloring.error_foreground", false), Gatherer.config.getColor("coloring.error_background", false)));
764 dialog.display();
765 dialog.dispose();
766 dialog = null;
767
768 String library_path_string = Gatherer.config.getString("general.exec_address", true);
769 if (!library_path_string.equals("")) {
770 try {
771 Gatherer.config.exec_address = new URL(library_path_string);
772 }
773 catch (MalformedURLException error) {
774 ///ystem.err.println("Error: Bad address: " + exec_address_string);
775 }
776 }
777 }
778
779 /** Prints a warning message about a missing GSDL path, which although not fatal pretty much ensures nothing will work properly in the Gatherer.
780 */
781 static public void missingGSDL() {
782 WarningDialog dialog = new WarningDialog("warning.MissingGSDL", false);
783 dialog.display();
784 dialog.dispose();
785 dialog = null;
786 }
787
788 /** 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. */
789 static public void missingPERL() {
790 WarningDialog dialog = new WarningDialog("warning.MissingPERL", false);
791 dialog.display();
792 dialog.dispose();
793 dialog = null;
794 }
795
796 /** Print a message to the debug stream. */
797 static synchronized public void print(String message) {
798 if(debug != null) {
799 debug.print(message);
800 System.err.print(message);
801 }
802 }
803 /** Print a message to the debug stream. */
804 static synchronized public void println(String message) {
805 if(debug != null) {
806 debug.println(message);
807 System.err.println(message);
808 }
809 }
810
811 /** Print a stack trace to the debug stream. */
812 static synchronized public void printStackTrace(Exception exception) {
813 if(debug != null) {
814 exception.printStackTrace(debug);
815 exception.printStackTrace();
816 }
817 }
818 /** Prints a usage message to screen.
819 */
820 static public void printUsage(Dictionary dictionary) {
821 System.out.println(Dictionary.get("General.Usage"));
822 }
823
824 /** Sets up the proxy connection by setting JVM Environment flags and creating a new Authenticator.
825 * @see java.lang.Exception
826 * @see java.lang.System
827 * @see java.net.Authenticator
828 * @see org.greenstone.gatherer.Configuration
829 * @see org.greenstone.gatherer.GAuthenticator
830 */
831 static public void setProxy() {
832 try {// Can throw several exceptions
833 if(Gatherer.config.get("general.use_proxy", true)) {
834 System.setProperty("http.proxyType", "4");
835 System.setProperty("http.proxyHost", Gatherer.config.getString("general.proxy_host", true));
836 System.setProperty("http.proxyPort", Gatherer.config.getString("general.proxy_port", true));
837 System.setProperty("http.proxySet", "true");
838 } else {
839 System.setProperty("http.proxySet", "false");
840 }
841 } catch (Exception error) {
842 Gatherer.println("Error in Gatherer.initProxy(): " + error);
843 Gatherer.printStackTrace(error);
844 }
845 }
846
847 /** Set all the default fonts of the program to a choosen font, except the tooltip font which should be some fixed width font.
848 * @param f The default font to use in the Gatherer as a <strong>FontUIResource</strong>.
849 * @param ttf The tooltip font to use also as a <strong>FontUIResource</strong>.
850 * @see java.util.Enumeration
851 * @see javax.swing.UIManager
852 */
853 static private void setUIFont(FontUIResource f, FontUIResource ttf) {
854 // sets the default font for all Swing components.
855 // ex.
856 // setUIFont (new FontUIResource("Serif",Font.ITALIC,12));
857 //
858 Enumeration keys = UIManager.getDefaults().keys();
859 while (keys.hasMoreElements()) {
860 Object key = keys.nextElement();
861 Object value = UIManager.get (key);
862 if (value instanceof FontUIResource)
863 UIManager.put (key, f);
864 }
865 // Now set the tooltip font to some fixed width font
866 UIManager.put("ToolTip.font", ttf);
867 }
868
869 /** Loads the configuration file if one exists. Otherwise it creates a new one. Currently uses serialization.
870 * @param gsdl_path The path to the gsdl directory, gathered from the startup arguments, and presented as a <strong>String</strong>.
871 * @param exec_path The path to the library executable, gathered from the startup arguments, and presented as a <strong>String</strong>.
872 * @param perl_path The path to the PERL compiler as a <strong>String</strong>. Necessary for windows platform versions.
873 * @param mirroring_enabled
874 * @param wget_version_str
875 * @param wget_path
876 * @see java.io.FileInputStream
877 * @see java.io.ObjectInputStream
878 * @see java.lang.Exception
879 * @see org.greenstone.gatherer.Configuration
880 */
881 private void loadConfig(String gsdl_path, String gsdl3_path, String exec_path, String perl_path, boolean mirroring_enabled, String wget_version_str, String wget_path, String site_name) {
882 try {
883 config = new Configuration(gsdl_path, gsdl3_path, exec_path, perl_path, mirroring_enabled, wget_version_str, wget_path, site_name);
884 }
885 catch (Exception error) {
886 Gatherer.println("config.xml is not a well formed XML document.");
887 Gatherer.printStackTrace(error);
888 }
889 if (GS3) {
890 try {
891 servlet_config = new ServletConfiguration(gsdl3_path);
892 } catch (Exception error) {
893 error.printStackTrace();
894 }
895 }
896
897 }
898
899 /** 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). */
900 private void saveConfig() {
901 try {
902 config.save();
903 } catch (Exception error) {
904 Gatherer.printStackTrace(error);
905 }
906 }
907
908 private void startServerEXE() {
909 if(config.exec_file != null && config.exec_address == null && Utility.isWindows() && !GS3) {
910 // First of all we create a GSDLSiteCFG object and check if a URL is already present, in which case the server is already running.
911 gsdlsite_cfg = new GSDLSiteConfig(config.exec_file);
912 String url = gsdlsite_cfg.getURL();
913 // If its already running then set exec address.
914 if(url != null) {
915 try {
916 config.exec_address = new URL(url);
917 }
918 catch(Exception error) {
919 }
920 }
921 // Otherwise its time to run the server in a spawned process.
922 if(config.exec_address == null && config.exec_file.exists()) {
923 // Configure for immediate entry. Note that this only works if the gsdlsite.cfg file exists.
924 gsdlsite_cfg.set();
925 // Spawn server
926 String command = config.exec_file.getAbsolutePath() + " " + gsdlsite_cfg.getSiteConfigFilename();
927 server = new ExternalApplication(command);
928 server.start();
929 command = null;
930 // Now we have to wait until program has started. We do this by reloading and checking
931 ///ystem.err.print("Waiting until the local library has loaded");
932 try {
933 gsdlsite_cfg.load();
934
935 int try_again = JOptionPane.YES_OPTION;
936 int attempt_count = 0;
937 while(gsdlsite_cfg.getURL() == null && try_again == JOptionPane.YES_OPTION) {
938 ///ystem.err.print(".");
939 if(attempt_count == 60) {
940 attempt_count = 0;
941 try_again = JOptionPane.showConfirmDialog(Gatherer.g_man, Dictionary.get("Server.QuitTimeOut"), Dictionary.get("General.Warning"), JOptionPane.YES_NO_OPTION);
942 }
943 else {
944 synchronized(this) {
945 wait(1000); // Wait one second (give or take)
946 }
947 gsdlsite_cfg.load();
948 attempt_count++;
949 }
950 }
951
952 if((url = gsdlsite_cfg.getURL()) != null) {
953 // Ta-da. Now the url should be available.
954 config.exec_address = new URL(url);
955
956 // A quick test involves opening a connection to get the home page for this collection. If this fails then we try changing the url to be localhost.
957 try {
958 Gatherer.println("Try connecting to server on config url: '" + config.exec_address + "'");
959 URLConnection connection = config.exec_address.openConnection();
960 connection.getContent();
961 }
962 catch(IOException bad_url_connection) {
963 try {
964 Gatherer.println("Try connecting to server on local host: '" + gsdlsite_cfg.getLocalHostURL() + "'");
965 config.exec_address = new URL(gsdlsite_cfg.getLocalHostURL ());
966 URLConnection connection = config.exec_address.openConnection();
967 connection.getContent();
968 }
969 catch(IOException worse_url_connection) {
970 Gatherer.println("Can't connect to server on either address.");
971 config.exec_address = null;
972 config.exec_file = null;
973 }
974 }
975 }
976 // Unable to start local library. Show appropriate message.
977 else {
978 missingEXEC();
979 }
980 }
981 catch (Exception error) {
982 error.printStackTrace();
983 }
984 }
985 // Can't do a damb thing.
986 }
987 Gatherer.println("Having started server.exe, exec_address is: " + config.exec_address);
988 }
989
990 private void stopServerEXE() {
991 if(server != null && config.exec_address != null) {
992 // See if its already exited for some reason.
993 gsdlsite_cfg.load();
994 if(gsdlsite_cfg.getURL() != null) {
995 // Send the command for it to exit.
996 configServer(GSDLSiteConfig.QUIT_COMMAND);
997 // Wait until it exits.
998 try {
999 gsdlsite_cfg.load();
1000 int try_again = JOptionPane.YES_OPTION;
1001 int attempt_count = 0;
1002 while(gsdlsite_cfg.getURL() != null && try_again == JOptionPane.YES_OPTION) {
1003 if(attempt_count == 60) {
1004 attempt_count = 0;
1005 try_again = JOptionPane.showConfirmDialog(Gatherer.g_man, Dictionary.get("Server.QuitTimeOut"), Dictionary.get("General.Warning"), JOptionPane.YES_NO_OPTION);
1006 }
1007 else {
1008 synchronized(this) {
1009 wait(1000); // Wait one second (give or take)
1010 }
1011 gsdlsite_cfg.load();
1012 attempt_count++;
1013 }
1014 }
1015 //if(gsdlsite_cfg.getURL() != null) {
1016 //JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("Server.QuitManual"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1017 //}
1018 }
1019 catch (Exception error) {
1020 error.printStackTrace();
1021 }
1022 }
1023 // Restore the gsdlsite_cfg.
1024 if(gsdlsite_cfg != null) {
1025 gsdlsite_cfg.restore();
1026 }
1027 // If the local server is still running then our changed values will get overwritten.
1028 if(gsdlsite_cfg.getURL() != null) {
1029 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("Server.QuitFailed"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1030 }
1031 gsdlsite_cfg = null;
1032 server = null;
1033 }
1034 }
1035
1036 /** 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. */
1037 private class ExternalApplication
1038 extends Thread {
1039 private Process process = null;
1040 /** The initial command string given to this sub-process. */
1041 private String command = null;
1042 private String[] commands = null;
1043 /** Constructor.
1044 * @param command The initial command <strong>String</strong>.
1045 */
1046 public ExternalApplication(String command) {
1047 this.command = command;
1048 }
1049
1050 public ExternalApplication(String[] commands) {
1051 this.commands = commands;
1052 }
1053 /** We start the child process inside a new thread so it doesn't block the rest of Gatherer.
1054 * @see java.lang.Exception
1055 * @see java.lang.Process
1056 * @see java.lang.Runtime
1057 * @see java.lang.System
1058 * @see java.util.Vector
1059 */
1060 public void run() {
1061 // Call an external process using the args.
1062 try {
1063 if(commands != null) {
1064 StringBuffer whole_command = new StringBuffer();
1065 for(int i = 0; i < commands.length; i++) {
1066 whole_command.append(commands[i]);
1067 whole_command.append(" ");
1068 }
1069 println("Running " + whole_command.toString());
1070 Runtime rt = Runtime.getRuntime();
1071 process = rt.exec(commands);
1072 process.waitFor();
1073 }
1074 else {
1075 println("Running " + command);
1076 Runtime rt = Runtime.getRuntime();
1077 process = rt.exec(command);
1078 process.waitFor();
1079 }
1080 }
1081 catch (Exception error) {
1082 println("Error in ExternalApplication.run(): " + error);
1083 printStackTrace(error);
1084 }
1085 // Remove ourself from Gatherer list of threads.
1086 apps.remove(this);
1087 // Call exit if we were the last outstanding child process thread.
1088 if(apps.size() == 0 && exit == true) {
1089 stopServerEXE();
1090 System.exit(0);
1091 }
1092 }
1093 public void stopExternalApplication() {
1094 if(process != null) {
1095 process.destroy();
1096 }
1097 }
1098 }
1099 /** 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. */
1100 private class BrowserApplication
1101 extends Thread {
1102 private Process process = null;
1103 /** The initial command string given to this sub-process. */
1104 private String command = null;
1105 private String url = null;
1106 private String[] commands = null;
1107 /** Constructor.
1108 * @param command The initial command <strong>String</strong>.
1109 */
1110// public BrowserApplication(String command) {
1111// this.command = command;
1112// }
1113
1114 public BrowserApplication(String command, String url) {
1115 StringTokenizer st = new StringTokenizer(command);
1116 int num_tokens = st.countTokens();
1117 this.commands = new String [num_tokens];
1118 int i=0;
1119 while (st.hasMoreTokens()) {
1120 commands[i] = st.nextToken();
1121 i++;
1122 }
1123 //this.commands = commands;
1124 this.url = url;
1125 }
1126 /** We start the child process inside a new thread so it doesn't block the rest of Gatherer.
1127 * @see java.lang.Exception
1128 * @see java.lang.Process
1129 * @see java.lang.Runtime
1130 * @see java.lang.System
1131 * @see java.util.Vector
1132 */
1133 public void run() {
1134 // Call an external process using the args.
1135 if(commands == null) {
1136 apps.remove(this);
1137 return;
1138 }
1139 try {
1140 String prog_name = commands[0];
1141 String lower_name = prog_name.toLowerCase();
1142 if (lower_name.indexOf("mozilla") != -1 || lower_name.indexOf("netscape") != -1) {
1143 Gatherer.println("found mozilla or netscape, trying remote it");
1144 // mozilla and netscape, try using a remote command to get things in the same window
1145 String [] new_commands = new String[] {prog_name, "-raise", "-remote", "openURL("+url+")"};
1146 if(debug != null) {
1147 printArray(new_commands);
1148 }
1149 Runtime rt = Runtime.getRuntime();
1150 process = rt.exec(new_commands);
1151 int exitCode = process.waitFor();
1152 if (exitCode != 0) { // if Netscape or mozilla was not open
1153 Gatherer.println("couldn't do remote, trying original command");
1154 if(debug != null) {
1155 printArray(commands);
1156 }
1157 process = rt.exec(commands); // try the original command
1158 }
1159 } else {
1160 // just run what we have been given
1161 StringBuffer whole_command = new StringBuffer();
1162 for(int i = 0; i < commands.length; i++) {
1163 whole_command.append(commands[i]);
1164 whole_command.append(" ");
1165 }
1166 println("Running " + whole_command.toString());
1167 Runtime rt = Runtime.getRuntime();
1168 process = rt.exec(commands);
1169 process.waitFor();
1170 }
1171 }
1172
1173 catch (Exception error) {
1174 println("Error in BrowserApplication.run(): " + error);
1175 printStackTrace(error);
1176 }
1177 // Remove ourself from Gatherer list of threads.
1178 apps.remove(this);
1179 // Call exit if we were the last outstanding child process thread.
1180 if(apps.size() == 0 && exit == true) {
1181 stopServerEXE();
1182 System.exit(0);
1183 }
1184 }
1185 public void printArray(String [] array) {
1186 for(int i = 0; i < array.length; i++) {
1187 debug.print(array[i]+" ");
1188 System.err.println(array[i]+" ");
1189 }
1190 }
1191 public void stopBrowserApplication() {
1192 if(process != null) {
1193 process.destroy();
1194 }
1195 }
1196 }
1197
1198 /** 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. */
1199 private class CTRLCHandler
1200 implements SignalHandler {
1201 /** <i>true</i> if we ignore any other signals we receive, most likely because we are already dealing with one, <i>false</i> otherwise. */
1202 private boolean ignore = false;
1203 /** The method called by the system to inform us a signal has occured.
1204 * @param sig The <strong>Signal</strong> itself.
1205 * @see org.greenstone.gatherer.collection.CollectionManager
1206 */
1207 public void handle(Signal sig) {
1208 if(!ignore) {
1209 ignore = true;
1210 // handle SIGINT
1211 System.out.println("Caught Ctrl-C...");
1212 if(c_man != null && c_man.ready()) {
1213 c_man.closeCollection();
1214 }
1215 exit();
1216 ignore = false;
1217 }
1218 }
1219 }
1220
1221 private class PerlTest {
1222
1223 private String[] command = new String[2];
1224
1225 public PerlTest() {
1226 if(Utility.isWindows()) {
1227 command[0] = Utility.PERL_EXECUTABLE_WINDOWS;
1228 }
1229 else {
1230 command[0] = Utility.PERL_EXECUTABLE_UNIX;
1231 }
1232 command[1] = "-version";
1233 }
1234
1235 public boolean found() {
1236 boolean found = false;
1237 try {
1238 Process prcs = Runtime.getRuntime().exec(command);
1239 prcs.waitFor();
1240 found = (prcs.exitValue() == 0);
1241 prcs = null;
1242 }
1243 catch(Exception error) {
1244 }
1245 return found;
1246 }
1247
1248 public String toString() {
1249 return command[0];
1250 }
1251 }
1252
1253 static public class GLIExceptionHandler {
1254 public void handle(Throwable thrown) {
1255 if(always_show_exceptions || debug != null) {
1256 thrown.printStackTrace();
1257 }
1258 }
1259 }
1260}
Note: See TracBrowser for help on using the repository browser.