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

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

Fixed problems with supplying config argument to server.exe - changes at line no. 735 and replaced the entirity of ExternalApplication

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