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

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

Improved the window positioning code. With some window managers, setting the location of the window causes it to be offset by a certain amount. This is now detected and compensated for.

  • Property svn:keywords set to Author Date Id Revision
File size: 34.2 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 /** Writes a message to the debug filestream.
278 * @param message The message as a <strong>String</strong>.
279 */
280 public void debug(String message) {
281 debug(null, message);
282 }
283 /** Writes a message to the debug filestream.
284 * @param error The <strong>Exception</strong> associated with this message, or <i>null</i> for no exception.
285 * @param message The message as a <strong>String</strong>.
286 * @see java.io.FileOutputStream
287 * @see java.io.PrintStream
288 * @see java.lang.Exception
289 */
290 public void debug(Exception exception, String message) {
291 if(message != null) {
292 Gatherer.println(message);
293 }
294 if(exception != null) {
295 Gatherer.printStackTrace(exception);
296 }
297 }
298 /** Exits the Gatherer after ensuring that things needing saving are saved.
299 * @see java.io.FileOutputStream
300 * @see java.io.PrintStream
301 * @see java.lang.Exception
302 * @see javax.swing.JOptionPane
303 * @see org.greenstone.gatherer.Configuration
304 * @see org.greenstone.gatherer.collection.CollectionManager
305 * @see org.greenstone.gatherer.gui.GUIManager
306 */
307 public void exit() {
308 exit = true;
309 // If we have an open collection make note of it.
310 config.setString("general.open_collection", true, null);
311 if(c_man.ready()) {
312 ///ystem.err.println("Collection open.");
313 if(c_man.saved()) {
314 ///ystem.err.println("Collection has been recently saved, so I'll remember it for next time.");
315 config.setString("general.open_collection", true, c_man.getCollectionFilename());
316 }
317 c_man.closeCollection();
318 }
319 if(assoc_man != null) {
320 assoc_man.destroy();
321 assoc_man = null;
322 }
323
324 // Store the current position and size (if reasonable) of the Gatherer for next time
325 Rectangle bounds = g_man.getBounds();
326 config.setBounds("general.bounds", true, bounds);
327
328 // Save configuration.
329 saveConfig();
330 // Flush debug
331 if(debug != null) {
332 try {
333 debug.flush();
334 debug.close();
335 }
336 catch (Exception error) {
337 error.printStackTrace();
338 }
339 }
340
341 // If we started a server, we should try to stop it.
342 if(gsdlsite_cfg != null) {
343 stopServerEXE();
344 }
345
346 if(apps.size() == 0) {
347 System.exit(0);
348 }
349 else {
350 JOptionPane.showMessageDialog(g_man, get("General.Outstanding_Processes"), get("General.Outstanding_Processes_Title"), JOptionPane.ERROR_MESSAGE);
351 g_man.hide();
352 }
353 }
354 /** Overloaded to call get with both a key and an empty argument array.
355 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
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 automatiically populated with formatting Strings of with argument String provided in the get call.
357 */
358 public String get(String key) {
359 return dictionary.get(key, (String[])null);
360 }
361 /** Overloaded to call get with both a key and an argument array with one element.
362 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
363 * @param arg A single argument as a <strong>String</strong>.
364 * @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.
365 */
366 public String get(String key, String arg) {
367 String args[] = new String[1];
368 args[0] = arg;
369 return dictionary.get(key, args);
370 }
371 /** 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>
372 * 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>.
373 * @param key A <strong>String</strong> which is mapped to a initial String within the ResourceBundle.
374 * @param args A <strong>String[]</strong> used to populate argument fields within the complete String.
375 * @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.
376 * @see org.greenstone.gatherer.Gatherer
377 * @see org.greenstone.gatherer.Dictionary
378 */
379 public String get(String key, String args[]) {
380 return dictionary.get(key, args);
381 }
382 /** Retrieve the metadata directory, as required by any MSMCaller implementation.
383 * @return The currently active collection metadata directory as a <strong>String</strong>.
384 * @see org.greenstone.gatherer.collection.CollectionManager
385 */
386 public String getCollectionMetadata() {
387 if(c_man != null && c_man.ready()) {
388 return c_man.getCollectionMetadata();
389 }
390 return "";
391 }
392 /** Retrieve a reference to the frame that any dialog boxes will appear relative to, as required by any MSMCaller or CDMCaller implementation.
393 * @return A <strong>JFrame</strong>.
394 * @see org.greenstone.gatherer.gui.GUIManager
395 */
396 public JFrame getFrame() {
397 return g_man;
398 }
399 /** 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.
400 * @return A reference to the <Strong>MetadataSetManager</strong>.
401 * @see org.greenstone.gatherer.collection.CollectionManager
402 */
403 public MetadataSetManager getMSM() {
404 if(c_man != null && c_man.getCollection() != null && c_man.getCollection().msm != null) {
405 return c_man.getCollection().msm;
406 }
407 return null;
408 }
409 /** Used to 'spawn' a new child application when a file is double clicked.
410 * @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>.
411 * @see java.util.Vector
412 * @see org.greenstone.gatherer.Gatherer.ExternalApplication
413 */
414 public void spawnApplication(File file) {
415 String command = assoc_man.getCommand(file);
416 if(command != null) {
417 ExternalApplication app = new ExternalApplication(command);
418 apps.add(app);
419 app.start();
420 }
421 else {
422 ///ystem.err.println("No open command available.");
423 }
424 }
425
426 /** 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.
427 * @param encoded An encoded <strong>String</strong>.
428 * @return The decoded <strong>String</strong>.
429 */
430 static public String decode(String encoded) {
431 return encoded.replace('%', ' ');
432 }
433 /** The entry point into the Gatherer. Parses arguments.
434 * @param args A collection of arguments that may include: initial screen size, dictionary, path to the GSDL etc.
435 * @see java.io.File
436 * @see java.io.FileInputStream
437 * @see java.lang.Exception
438 * @see java.util.Properties
439 * @see org.greenstone.gatherer.Dictionary
440 * @see org.greenstone.gatherer.Gatherer
441 * @see org.greenstone.gatherer.gui.Splash
442 */
443 static public void main(String[] args) {
444 // A serious hack, but its good enough to stop crappy 'Could not lock user prefs' error messages.
445 // Thanks to Walter Schatz from the java forums.
446 System.setProperty("java.util.prefs.syncInterval","2000000"); // One message every 600 hours!
447
448 Gatherer gatherer = new Gatherer();
449
450 boolean debug = false;
451 boolean no_load = false;
452 Dictionary dictionary = new Dictionary(null, null); // Default dictionary. Only used for starting error messages.
453 Dimension size = new Dimension(800, 540);
454 String exec_path = null;
455 String extra = null;
456 String filename = null;
457 String gsdl_path = null;
458 String perl_path = null;
459 // Parse arguments
460 for(int i = 0; i < args.length; i++) {
461 if(args[i].equals("-gsdl")) {
462 gsdl_path = decode(args[i+1]);
463 if(!gsdl_path.endsWith(File.separator)) {
464 gsdl_path = gsdl_path + File.separator;
465 }
466 }
467 if(args[i].equals("-load")) {
468 filename = decode(args[i+1]);
469 }
470 else if(args[i].equals("-library") && (i + 1) < args.length) {
471 exec_path = args[i+1];
472 // If we are on a non-windows system, and thus the local server is unavailable, we can append http:// if no protocol found.
473 if(exec_path.lastIndexOf(":", 5) == -1) {
474 exec_path = "http://" + exec_path;
475 }
476 // If the user has given us an address, but it ends with a '/' we assume we're using the greenstone library.cgi
477 if(exec_path.startsWith("http://") && exec_path.endsWith("/")) {
478 exec_path = exec_path + "library";
479 }
480 }
481 else if(args[i].equals("-perl")) {
482 perl_path = decode(args[i+1]);
483 // Test whether this points to the Perl bin directory or the Perl executable itself.
484 File perl_file = new File(perl_path);
485 if(perl_file.isDirectory()) {
486 // If this is windows we create a child file perl.exe, otherwise we create perl
487 if(Utility.isWindows()) {
488 perl_file = new File(perl_file, Utility.PERL_EXECUTABLE_WINDOWS);
489 }
490 else {
491 perl_file = new File(perl_file, Utility.PERL_EXECUTABLE_UNIX);
492 }
493 // And store this new path.
494 perl_path = perl_file.getAbsolutePath();
495 perl_file = null;
496 }
497 // Otherwise its fine as it is
498 ///ystem.err.println("Perl executable is: " + perl_path);
499 }
500 else if(args[i].equals("--help") || args[i].equals("-help") || args[i].equals("?") || args[i].equals("/?") || args[i].equals("/help")) {
501 printUsage(dictionary);
502 System.exit(0);
503 }
504 else if(args[i].equals("--debug") || args[i].equals("-debug")) {
505 debug = true;
506 }
507 // Don't load any previous collection. Convenient for me
508 else if(args[i].equals("-no_load")) {
509 no_load = true;
510 }
511 // Check if they want it skinned.
512 else if(args[i].equals("-skinlf")) {
513 // SkinLF
514 try {
515 SkinLookAndFeel.setSkin(SkinLookAndFeel.loadThemePackDefinition(SkinUtils.toURL(new File("lib/greenaqua/greenaqua.xml"))));
516 SkinLookAndFeel.enable();
517 }
518 catch (Exception error) {
519 ///ystem.err.println("Error: " + error);
520 error.printStackTrace();
521 }
522 }
523 }
524
525 // Splash screen.
526 Splash splash = new Splash();
527
528 // We take appropriate action when an empty gsdl_path is detected.
529 if(gsdl_path == null) {
530 // Check for the presence of the path.cfg file.
531 File path_file = new File("path.cfg");
532 if(path_file.exists()) {
533 try {
534 // Read in then add each property to the Java Environment.
535 FileInputStream prop_file = new FileInputStream(path_file);
536 Properties p = new Properties();
537 p.load(prop_file);
538 extra = p.getProperty(KEY);
539 }
540 catch (Exception error) {
541 System.out.println("Error in main():");
542 error.printStackTrace();
543 System.exit(1);
544 }
545 }
546 else {
547 missingGSDL(dictionary);
548 }
549 }
550 // We take appropriate action when an empty bin_path is detected.
551 gatherer.run(size, gsdl_path, exec_path, debug, perl_path, no_load, splash, filename);
552 }
553 /** Prints a warning message about a missing library path, which means the final collection cannot be previewed in the Gatherer.
554 */
555 static public void missingEXEC(Dictionary dictionary) {
556 WarningDialog dialog = new WarningDialog("warning.MissingEXEC", false);
557 dialog.display();
558 dialog.dispose();
559 dialog = null;
560 ///ystem.out.println(dictionary.get("General.Missing_EXEC"));
561 }
562 /** Prints a warning message about a missing GSDL path, which although not fatal pretty much ensures nothing will work properly in the Gatherer.
563 */
564 static public void missingGSDL(Dictionary dictionary) {
565 WarningDialog dialog = new WarningDialog("warning.MissingGSDL", false);
566 dialog.display();
567 dialog.dispose();
568 dialog = null;
569 ///ystem.out.println(dictionary.get("General.Missing_GSDL"));
570 }
571 /** 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. */
572 static public void missingPERL(Dictionary dictionary) {
573 WarningDialog dialog = new WarningDialog("warning.MissingPERL", false);
574 dialog.display();
575 dialog.dispose();
576 dialog = null;
577 ///ystem.out.println(dictionary.get("General.Missing_PERL"));
578 }
579 /** Print a message to the debug stream. */
580 static synchronized public void println(String message) {
581 if(debug != null) {
582 debug.println(message);
583 }
584 else {
585 System.err.println(message);
586 }
587 }
588 /** Print a stack trace to the debug stream. */
589 static synchronized public void printStackTrace(Exception exception) {
590 if(debug != null) {
591 exception.printStackTrace(debug);
592 }
593 else {
594 exception.printStackTrace();
595 }
596 }
597 /** Prints a usage message to screen.
598 */
599 static public void printUsage(Dictionary dictionary) {
600 System.out.println(dictionary.get("General.Usage"));
601 }
602
603 /** Sets up the proxy connection by setting JVM Environment flags and creating a new Authenticator.
604 * @see java.lang.Exception
605 * @see java.lang.System
606 * @see java.net.Authenticator
607 * @see org.greenstone.gatherer.Configuration
608 * @see org.greenstone.gatherer.GAuthenticator
609 */
610 static public void setProxy() {
611 try {// Can throw several exceptions
612 if(Gatherer.config.get("general.use_proxy", true)) {
613 System.setProperty("http.proxyType", "4");
614 System.setProperty("http.proxyHost", Gatherer.config.getString("general.proxy_host", true));
615 System.setProperty("http.proxyPort", Gatherer.config.getString("general.proxy_port", true));
616 System.setProperty("http.proxySet", "true");
617 } else {
618 System.setProperty("http.proxySet", "false");
619 }
620 } catch (Exception error) {
621 Gatherer.println("Error in Gatherer.initProxy(): " + error);
622 Gatherer.printStackTrace(error);
623 }
624 }
625
626 /** Set all the default fonts of the program to a choosen font, except the tooltip font which should be some fixed width font.
627 * @param f The default font to use in the Gatherer as a <strong>FontUIResource</strong>.
628 * @param ttf The tooltip font to use also as a <strong>FontUIResource</strong>.
629 * @see java.util.Enumeration
630 * @see javax.swing.UIManager
631 */
632 static public void setUIFont (FontUIResource f, FontUIResource ttf){
633 // sets the default font for all Swing components.
634 // ex.
635 // setUIFont (new FontUIResource("Serif",Font.ITALIC,12));
636 //
637 Enumeration keys = UIManager.getDefaults().keys();
638 while (keys.hasMoreElements()) {
639 Object key = keys.nextElement();
640 Object value = UIManager.get (key);
641 if (value instanceof FontUIResource)
642 UIManager.put (key, f);
643 }
644 // Now set the tooltip font to some fixed width font
645 UIManager.put("ToolTip.font", ttf);
646 }
647
648 /** Loads the configuration file if one exists. Otherwise it creates a new one. Currently uses serialization.
649 * @param size The desired size of the Gatherer window as a <strong>Dimension</strong>.
650 * @param gsdl_path The path to the gsdl directory, gathered from the startup arguments, and presented as a <strong>String</strong>.
651 * @param exec_path The path to the library executable, gathered from the startup arguments, and presented as a <strong>String</strong>.
652 * @param perl_path The path to the PERL compiler as a <strong>String</strong>. Necessary for windows platform versions.
653 * @see java.io.FileInputStream
654 * @see java.io.ObjectInputStream
655 * @see java.lang.Exception
656 * @see org.greenstone.gatherer.Configuration
657 */
658 private void loadConfig(String gsdl_path, String exec_path, String perl_path) {
659 try {
660 config = new Configuration(gsdl_path, exec_path, perl_path);
661 }
662 catch (Exception error) {
663 Gatherer.println("config.xml is not a well formed XML document.");
664 Gatherer.printStackTrace(error);
665 }
666 }
667
668 /** Creates and dispatches a message given the initial details.
669 * @param level An <i>int</i> indicating the message level for this message.
670 * @param message A <strong>String</strong> which contains the payload of this message.
671 * @see org.greenstone.gatherer.Message
672 * @see org.greenstone.gatherer.Log
673 */
674 private void message(int level, String message) {
675 Message msg = new Message(Message.GENERAL, level, message);
676 log.add(msg);
677 }
678
679 /** 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). */
680 private void saveConfig() {
681 try {
682 config.save();
683 } catch (Exception error) {
684 Gatherer.printStackTrace(error);
685 }
686 }
687
688 private void startServerEXE() {
689 if(config.exec_file != null && config.exec_address == null && Utility.isWindows()) {
690 // First of all we create a GSDLSiteCFG object and check if a URL is already present, in which case the server is already running.
691 gsdlsite_cfg = new GSDLSiteConfig(config.exec_file);
692 String url = gsdlsite_cfg.getURL();
693 // If its already running then set exec address.
694 if(url != null) {
695 try {
696 config.exec_address = new URL(url);
697 }
698 catch(Exception error) {
699 }
700 }
701 // Otherwise its time to run the server in a spawned process.
702 if(config.exec_address == null && config.exec_file.exists()) {
703 // Configure for immediate entry. Note that this only works if the gsdlsite.cfg file exists.
704 gsdlsite_cfg.set();
705 // Spawn server
706 server = new ExternalApplication(config.exec_file.getAbsolutePath());
707 server.start();
708 // Now we have to wait until program has started. We do this by reloading and checking
709 try {
710 gsdlsite_cfg.load();
711 while((url = gsdlsite_cfg.getURL()) == null) {
712 synchronized(this) {
713 wait(1000);
714 }
715 gsdlsite_cfg.load();
716 }
717 // Ta-da. Now the url should be available.
718 config.exec_address = new URL(url);
719 }
720 catch (Exception error) {
721 error.printStackTrace();
722 }
723 }
724 // Can't do a damb thing.
725 }
726 System.err.println("Having started server.exe, exec_address is: " + config.exec_address);
727 }
728
729 private void stopServerEXE() {
730 if(server != null) {
731 // See if its already exited for some reason.
732 gsdlsite_cfg.load();
733 if(gsdlsite_cfg.getURL() != null) {
734 // Send the command for it to exit.
735 Gatherer.g_man.preview_pane.configServer(GSDLSiteConfig.QUIT_COMMAND);
736 // Wait until it exits.
737 try {
738 gsdlsite_cfg.load();
739 int try_again = JOptionPane.YES_OPTION;
740 while(try_again == JOptionPane.YES_OPTION) {
741 int attempt_count = 0;
742 while(gsdlsite_cfg.getURL() != null && attempt_count < 60) {
743 synchronized(this) {
744 wait(1000); // Wait one second (give or take)
745 }
746 gsdlsite_cfg.load();
747 attempt_count++;
748 }
749 if(attempt_count == 60) {
750 try_again = JOptionPane.showConfirmDialog(Gatherer.g_man, dictionary.get("Server.QuitTimeOut"), dictionary.get("General.Warning"), JOptionPane.YES_NO_OPTION);
751 }
752 else {
753 try_again = JOptionPane.NO_OPTION;
754 }
755 }
756 if(gsdlsite_cfg.getURL() != null) {
757 JOptionPane.showMessageDialog(Gatherer.g_man, dictionary.get("Server.QuitManual"), dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
758 }
759 }
760 catch (Exception error) {
761 error.printStackTrace();
762 }
763 }
764 // Restore the gsdlsite_cfg.
765 if(gsdlsite_cfg != null) {
766 gsdlsite_cfg.restore();
767 }
768 // If the local server is still running then our changed values will get overwritten.
769 if(gsdlsite_cfg.getURL() != null) {
770 JOptionPane.showMessageDialog(Gatherer.g_man, dictionary.get("Server.QuitFailed"), dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
771 }
772 gsdlsite_cfg = null;
773 server = null;
774 }
775 }
776
777 /** 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. */
778 private class ExternalApplication
779 extends Thread {
780 private Process process = null;
781 /** The initial command string given to this sub-process. */
782 private String command = null;
783 /** Constructor.
784 * @param command The initial command <strong>String</strong>.
785 */
786 public ExternalApplication(String command) {
787 this.command = command;
788 }
789 /** We start the child process inside a new thread so it doesn't block the rest of Gatherer.
790 * @see java.lang.Exception
791 * @see java.lang.Process
792 * @see java.lang.Runtime
793 * @see java.lang.System
794 * @see java.util.Vector
795 */
796 public void run() {
797 // Call an external process using the args.
798 try {
799 debug("Running " + command);
800 Runtime rt = Runtime.getRuntime();
801 process = rt.exec(command);
802 process.waitFor();
803 }
804 catch (Exception error) {
805 debug(error, "Error in ExternalApplication.run(): " + error);
806 }
807 // Remove ourself from Gatherer list of threads.
808 apps.remove(this);
809 // Call exit if we were the last outstanding child process thread.
810 if(apps.size() == 0 && exit == true) {
811 stopServerEXE();
812 System.exit(0);
813 }
814 }
815 public void stopExternalApplication() {
816 if(process != null) {
817 process.destroy();
818 }
819 }
820 }
821 /** 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. */
822 private class CTRLCHandler
823 implements SignalHandler {
824 /** <i>true</i> if we ignore any other signals we receive, most likely because we are already dealing with one, <i>false</i> otherwise. */
825 private boolean ignore = false;
826 /** The method called by the system to inform us a signal has occured.
827 * @param sig The <strong>Signal</strong> itself.
828 * @see org.greenstone.gatherer.collection.CollectionManager
829 */
830 public void handle(Signal sig) {
831 if(!ignore) {
832 ignore = true;
833 // handle SIGINT
834 System.out.println("Caught Ctrl-C...");
835 if(c_man != null && c_man.ready()) {
836 c_man.closeCollection();
837 }
838 exit();
839 ignore = false;
840 }
841 }
842 }
843
844 private class PerlTest {
845
846 private String[] command = new String[2];
847
848 public PerlTest() {
849 if(Utility.isWindows()) {
850 command[0] = Utility.PERL_EXECUTABLE_WINDOWS;
851 }
852 else {
853 command[0] = Utility.PERL_EXECUTABLE_UNIX;
854 }
855 command[1] = "-version";
856 }
857
858 public boolean found() {
859 boolean found = false;
860 try {
861 Process prcs = Runtime.getRuntime().exec(command);
862 prcs.waitFor();
863 found = (prcs.exitValue() == 0);
864 prcs = null;
865 }
866 catch(Exception error) {
867 }
868 return found;
869 }
870
871 public String toString() {
872 return command[0];
873 }
874 }
875}
876
877
878
879
880
Note: See TracBrowser for help on using the repository browser.