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

Last change on this file since 4928 was 4676, checked in by jmt12, 21 years ago

Now debug will only be available if you run -debug.

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