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

Last change on this file since 5593 was 5593, checked in by mdewsnip, 21 years ago

Changed calls to the Dictionary.

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