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

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

More small updates and tooltips added.

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