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

Last change on this file since 4631 was 4620, checked in by jmt12, 21 years ago

2030134: The code is now in place for GLI to create its own site config file so as to avoid the 'GLI crashes leaving gsdlsite.cfg in inconsistant state which subsequently causes problems in GSDL and GLI' bug.

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