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

Last change on this file since 7161 was 6885, checked in by mdewsnip, 20 years ago

Commented out exception handler -- it is very annoying to not see exceptions when they happen.

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