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

Last change on this file since 6622 was 6622, checked in by jmt12, 20 years ago

More modifications to mirroring including testing for a valid version of Wget (and complaining if its missing or it is old) and rearranging buttons on the GProgressBar

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