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

Last change on this file since 7980 was 7923, checked in by davidb, 20 years ago

GLI applet now only quits once the web page with the launch button
is replaced in the browser with a new page.

  • Property svn:keywords set to Author Date Id Revision
File size: 37.5 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//import com.l2fprod.gui.*;
30//import com.l2fprod.gui.plaf.skin.*;
31//import com.l2fprod.util.*;
32import java.awt.*;
33import java.awt.event.*;
34import java.io.*;
35import java.lang.*;
36import java.net.*;
37import java.util.*;
38import javax.swing.*;
39import javax.swing.plaf.*;
40import javax.swing.text.*;
41import org.greenstone.gatherer.Configuration;
42import org.greenstone.gatherer.GAuthenticator;
43import org.greenstone.gatherer.cdm.CommandTokenizer;
44import org.greenstone.gatherer.collection.CollectionManager;
45import org.greenstone.gatherer.feedback.ActionRecorderDialog;
46import org.greenstone.gatherer.file.FileManager;
47import org.greenstone.gatherer.file.FileAssociationManager;
48import org.greenstone.gatherer.gui.Coloring;
49import org.greenstone.gatherer.gui.GUIManager;
50import org.greenstone.gatherer.gui.ModalDialog;
51import org.greenstone.gatherer.gui.Splash;
52import org.greenstone.gatherer.gui.URLField;
53import org.greenstone.gatherer.gui.WarningDialog;
54import org.greenstone.gatherer.msm.MDSTest;
55import org.greenstone.gatherer.msm.MetadataSetManager;
56import org.greenstone.gatherer.util.ArrayTools;
57import org.greenstone.gatherer.util.GSDLSiteConfig;
58import org.greenstone.gatherer.util.StaticStrings;
59import org.greenstone.gatherer.util.Utility;
60import sun.misc.*;
61
62/** Containing the top-level "core" for the Gatherer, this class is the
63 * common core for the GLI application and applet. It first parses the
64 * command line arguments, preparing to update the configuration as
65 * required. Next it loads several important support classes such as the
66 * Configuration and Dictionary. Finally it creates the other important
67 * managers and sends them on their way.
68 * @author John Thompson, Greenstone Digital Library, University of Waikato
69 * @version 2.3
70 */
71
72// How to catch All Exceptions including those from the AWT Event thread.
73// Step 1: register a handler class in your main()
74//
75// System.setProperty("sun.awt.exception.handler", "YourHandler");
76//
77// Step 2: implement your handler class.
78//
79// public class YourHandler {
80// public void handle(Throwable thrown) {
81// eat the exception without dumping to the console.
82// }
83// }
84
85public class Gatherer
86{
87 static public Hashtable authentications = new Hashtable();
88
89 static final private String SKIN_DEFINITION_FILE = "lib/greenaqua/greenaqua.xml";
90
91 static public boolean always_show_exceptions = true;
92 /** Has the exit flag been set? <i>true</i> if so, <i>false</i> otherwise. */
93 public boolean exit = false;
94 /** The size of the Gatherer window. */
95 public Dimension frame_size = null;
96
97 /** All of the external applications that must exit before we close the Gatherer. */
98 public Vector apps = new Vector();
99 /** A public reference to the FileAssociationManager. */
100 static public FileAssociationManager assoc_man;
101 /** A public reference to the CollectionManager. */
102 static public CollectionManager c_man;
103 /** A public reference to the Gatherer's configuration. */
104 static public Configuration config;
105 /** a reference to the Servlet Configuration is GS3 */
106 static public ServletConfiguration servlet_config;
107 /** The current modal dialog being shown on screen, if any. */
108 static public ModalDialog current_modal = null;
109 /** A public reference to the Dictionary. */
110 static public Dictionary dictionary;
111 /** A public reference to the FileManager. */
112 static public FileManager f_man;
113 /** A public reference to the GUIManager. */
114 static public GUIManager g_man;
115 static public boolean g_man_built = false;
116
117 /** A static reference to ourselves. */
118 static public Gatherer self;
119 /** The debug print stream. */
120 static public PrintStream debug;
121 /** We are using the GLI for GS3 */
122 static public boolean GS3 = false;
123
124 static public boolean isGsdlRemote = false;
125 static public String cgiBase = "";
126
127 // feedback stuff
128 /** is the feedback feature enabled? */
129 static public boolean feedback_enabled = true;
130 /** the action recorder dialog */
131 static public ActionRecorderDialog feedback_dialog = null;
132 /** 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. */
133 static public String extra_env[] = null;
134 private GSDLSiteConfig gsdlsite_cfg = null;
135 private ExternalApplication server = null;
136
137 /** Magic to allow Enter to fire the default button. */
138 static {
139 KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
140 Keymap map = JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP);
141 map.removeKeyStrokeBinding(enter);
142 }
143
144 public Gatherer()
145 {
146 this.self = this;
147
148 init_images();
149 }
150
151 protected void init_images()
152 {
153 Class base = this.getClass();
154 Utility.BLANK_ICON = new ImageIcon(base.getResource("/images/blank.gif"));
155 Utility.ERROR_ICON = new ImageIcon(base.getResource("/images/error.gif"));
156 Utility.HELP_ICON = new ImageIcon(base.getResource("/images/help.gif"));
157 Utility.ON_ICON = new ImageIcon(base.getResource("/images/check.gif"));
158 Utility.OFF_ICON = new ImageIcon(base.getResource("/images/cross.gif"));
159
160 Utility.base = base;
161 }
162
163
164 public GUIManager init(Dimension size, String gsdl_path, String gsdl3_path,
165 String exec_path, boolean debug_enabled, String perl_path,
166 boolean no_load, String open_collection,
167 String site_name, String servlet_path,
168 boolean mirroring_enabled, String wget_version_str,
169 String wget_path)
170 {
171
172 // This will hopefully catch ctrl-c and terminate, and exit gracefully. However it is platform specific, and may not be supported by some JVMs.
173 /** It does, but I get bloody sick of it working when the Gatherer hangs.
174 CTRLCHandler handler = new CTRLCHandler();
175 Signal.handle(new Signal("INT"), handler);
176 Signal.handle(new Signal("TERM"), handler);
177 handler = null;
178 */
179 if (gsdl3_path != null && !gsdl3_path.equals("")) {
180 this.GS3 = true;
181 } else {
182 gsdl3_path = null;
183 }
184 // Create the debug stream only if required.
185 if(debug_enabled) {
186 try {
187 Calendar now = Calendar.getInstance();
188 StringBuffer name = new StringBuffer("debug");
189 name.append(now.get(Calendar.DATE));
190 name.append("-");
191 name.append(now.get(Calendar.MONTH));
192 name.append("-");
193 name.append(now.get(Calendar.YEAR));
194 name.append(".txt");
195 this.debug = new PrintStream(new FileOutputStream(name.toString()));
196 Properties props = System.getProperties();
197 props.list(debug);
198 }
199 catch(Exception error) {
200 ///ystem.err.println("Error in Gatherer.init(): " + error);
201 error.printStackTrace();
202 System.exit(1);
203 }
204 }
205 try {
206 // Load Config
207 loadConfig(gsdl_path, gsdl3_path, exec_path, perl_path, mirroring_enabled, site_name);
208
209 // the feedback dialog has been loaded with a default locale,
210 // now set the user specified one
211 if (feedback_enabled && feedback_dialog != null) {
212 feedback_dialog.setLocale(config.getLocale("general.locale", true));
213 }
214
215 // Read Dictionary
216 dictionary = new Dictionary(config.getLocale("general.locale", true), config.getFont("general.font", true));
217
218
219 if (gsdl_path == null) {
220 missingGSDL();
221 }
222
223 // If we were given a server run it if neccessary.
224 if (!GS3 && config.exec_file != null) {
225 startServerEXE();
226 }
227
228 // 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.
229 if (config.exec_file == null && config.exec_address == null) {
230 if (config.exec_file == null) {
231 Gatherer.println("config.exec_file is null");
232 }
233 if (config.exec_address == null) {
234 Gatherer.println("config.exec_address is null");
235 }
236 missingEXEC();
237 }
238
239 if (Gatherer.isGsdlRemote) {
240 Gatherer.println("Not checking for perl path/exe");
241 }
242 else {
243 // Perl path is a little different as it is perfectly ok to
244 // start the Gatherer without providing a perl path
245 boolean found_perl = false;
246 if (config.perl_path != null) {
247 // See if the file pointed to actually exists
248 File perl_file = new File(config.perl_path);
249 found_perl = perl_file.exists();
250 perl_file = null;
251 }
252 if (config.perl_path == null || !found_perl) {
253 // Run test to see if we can run perl as is.
254 PerlTest perl_test = new PerlTest();
255 if (perl_test.found()) {
256 // If so replace the perl path with the system
257 // default (or null for unix).
258 config.perl_path = perl_test.toString();
259 found_perl = true;
260 }
261 }
262 if (!found_perl) {
263 // Time for an error message.
264 missingPERL();
265 }
266 }
267
268 // also check for wget
269 boolean mirror_workflow = config.get(StaticStrings.WORKFLOW_MIRROR, false);
270 if (mirror_workflow) {
271 wget_version_str = StaticStrings.NO_WGET_STR;
272 // has the user specified a path?
273 if (wget_path != null && !wget_path.equals("")) {
274 File wget_file = new File(wget_path);
275 if (wget_file.exists()) {
276 // we assume its ok if its there
277 wget_version_str = StaticStrings.WGET_STR;
278 }
279 }
280
281 // it hasn't been set by the user so we use the greenstone one
282 if (wget_version_str.equals(StaticStrings.NO_WGET_STR)) {
283 // TODO fix for gs3
284 wget_path = Utility.getWGetPath(gsdl_path);
285 File wget_file = new File(wget_path);
286 if (!wget_file.exists()) {
287 // we give up trying to find one
288 missingWGET();
289 } else {
290 // we have found one
291 wget_version_str = StaticStrings.WGET_STR;
292 }
293 }
294
295 if (wget_version_str.equals(StaticStrings.WGET_STR)) {
296 // we have found one, should we check the version??
297 wget_version_str = testWGetVersion(wget_path);
298 if (wget_version_str.equals(StaticStrings.WGET_OLD_STR)) {
299 oldWGET();
300 }
301 }
302
303 // tell the config the new values
304 config.setWGetPath(wget_path);
305 config.setWGetVersion(wget_version_str);
306 }
307
308 // Set default font
309 setUIFont(config.getFont("general.font", true), config.getFont("general.tooltip_font", true));
310 // Set up proxy
311 setProxy();
312 // Now we set up an Authenticator
313 Authenticator.setDefault(new GAuthenticator());
314
315 assoc_man = new FileAssociationManager();
316 // Create File Manager
317 f_man = new FileManager();
318 // Create Collection Manager
319 c_man = new CollectionManager();
320
321 if (GS3) {
322 if (site_name==null) {
323 site_name = config.site_name;
324 servlet_path = null; // need to reset this
325 }
326 if (servlet_path == null) {
327 servlet_path = config.getServletPath();
328 }
329 }
330
331 // If there was an open collection last session, reopen it.
332 if (open_collection == null) {
333 open_collection = config.getString("general.open_collection", true);
334 }
335 if (!no_load && open_collection.length() > 0) {
336 c_man.loadCollection(open_collection);
337 }
338
339 }
340 catch (Exception error) {
341 error.printStackTrace();
342 }
343
344 // Create GUI Manager (last) or else suffer the death of a thousand NPE's
345 g_man = new GUIManager(size);
346
347 return g_man;
348 }
349
350
351
352 public void run(Dimension size, Splash splash, GUIManager g_man)
353 {
354 // Size and place the frame on the screen
355 Rectangle bounds = config.getBounds("general.bounds", true);
356 if (bounds == null) {
357 // Choose a sensible default value
358 bounds = new Rectangle(0, 0, 640, 480);
359 }
360
361 // Ensure width and height are reasonable
362 size = bounds.getSize();
363 if (size.width < 640) {
364 size.width = 640;
365 }
366 else if (size.width > config.screen_size.width) {
367 size.width = config.screen_size.width;
368 }
369 if (size.height < 480) {
370 size.height = 480;
371 }
372 else if (size.height > config.screen_size.height) {
373 size.height = config.screen_size.height;
374 }
375
376 if (splash != null) { splash.toFront(); }
377
378 if (!g_man_built) {
379
380 g_man.display();
381
382 // 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).
383 g_man.setLocation(bounds.x, bounds.y);
384 g_man.setVisible(true);
385
386 // After the window has been made visible, check that it is in the correct place
387 // sometimes java places a window not in the correct place,
388 // but with an offset. If so, we work out what the offset is
389 // and change the desired location to take that into account
390 Point location = g_man.getLocation();
391 int x_offset = bounds.x - location.x;
392 int y_offset = bounds.y - location.y;
393 // If not, offset the window to move it into the correct location
394 if (x_offset > 0 || y_offset > 0) {
395 ///ystem.err.println("changing the location to "+(bounds.x + x_offset)+" "+ (bounds.y + y_offset));
396 g_man.setLocation(bounds.x + x_offset, bounds.y + y_offset);
397 }
398
399 // 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.
400 g_man.afterDisplay();
401 g_man_built = true;
402 }
403 else {
404 g_man.setVisible(true);
405 }
406
407 if (splash != null) {
408 // Hide the splash screen
409 splash.hide();
410 splash.destroy();
411 splash = null;
412 }
413 }
414
415
416 /** Exits the Gatherer after ensuring that things needing saving are saved.
417 * @see java.io.FileOutputStream
418 * @see java.io.PrintStream
419 * @see java.lang.Exception
420 * @see javax.swing.JOptionPane
421 * @see org.greenstone.gatherer.Configuration
422 * @see org.greenstone.gatherer.collection.CollectionManager
423 * @see org.greenstone.gatherer.gui.GUIManager
424 */
425 public void exit() {
426 exit = true;
427 // If we have an open collection make note of it.
428 config.setString("general.open_collection", true, null);
429 if(c_man.ready()) {
430 ///ystem.err.println("Collection open.");
431 if(c_man.saved()) {
432 ///ystem.err.println("Collection has been recently saved, so I'll remember it for next time.");
433 config.setString("general.open_collection", true, c_man.getCollectionFilename());
434 }
435 c_man.closeCollection();
436 }
437 if(assoc_man != null) {
438 assoc_man.save();
439 assoc_man = null;
440 }
441
442 // Store the current position and size (if reasonable) of the Gatherer for next time
443 Rectangle bounds = g_man.getBounds();
444 config.setBounds("general.bounds", true, bounds);
445
446 // Save configuration.
447 saveConfig();
448
449 // Get the gui to deallocate
450 g_man.hide();
451 g_man.destroy();
452 g_man_built = false;
453
454
455 // Flush dictionary
456 // dictionary.destroy();
457
458 // Flush debug
459 if(debug != null) {
460 try {
461 debug.flush();
462 debug.close();
463 }
464 catch (Exception error) {
465 error.printStackTrace();
466 }
467 }
468
469 // If we started a server, we should try to stop it.
470 if(!GS3 && gsdlsite_cfg != null) {
471 stopServerEXE();
472 }
473
474 if(apps.size() == 0) {
475 if (!Gatherer.isGsdlRemote) {
476 System.exit(0);
477 }
478 }
479 else {
480 JOptionPane.showMessageDialog(g_man, Dictionary.get("General.Outstanding_Processes"), Dictionary.get("General.Outstanding_Processes_Title"), JOptionPane.ERROR_MESSAGE);
481 g_man.hide();
482 }
483 }
484
485 // Used to send messages to the local library
486 // Warning: this has a lot of potential for nasty race conditions
487 // The response code is returned immediately -- but this does not mean the local
488 // library action has finished!
489 public void configServer(String command)
490 {
491 try {
492 String raw_url = config.exec_address.toString() + command;
493 URL url = new URL(raw_url);
494 Gatherer.println("Action: " + raw_url);
495 HttpURLConnection library_connection = (HttpURLConnection) url.openConnection();
496 int response_code = library_connection.getResponseCode();
497 if(HttpURLConnection.HTTP_OK <= response_code && response_code < HttpURLConnection.HTTP_MULT_CHOICE) {
498 Gatherer.println("200 - Complete.");
499 }
500 else {
501 Gatherer.println("404 - Failed.");
502 }
503 url = null;
504 }
505 catch (Exception ex) {
506 ex.printStackTrace();
507 Gatherer.printStackTrace(ex);
508 }
509 }
510
511 // used to send reload coll messages to the tomcat server
512 public void configGS3Server(String site, String command) {
513
514 try {
515 // need to add the servlet name to the exec address
516 String raw_url = config.exec_address.toString() + config.getServletPath() + command;
517 URL url = new URL(raw_url);
518 Gatherer.println("Action: " + raw_url);
519 HttpURLConnection library_connection = (HttpURLConnection) url.openConnection();
520 int response_code = library_connection.getResponseCode();
521 if(HttpURLConnection.HTTP_OK <= response_code && response_code < HttpURLConnection.HTTP_MULT_CHOICE) {
522 Gatherer.println("200 - Complete.");
523 }
524 else {
525 Gatherer.println("404 - Failed.");
526 }
527 url = null;
528 }
529 catch(Exception exception) {
530 Gatherer.printStackTrace(exception);
531 ///ystem.err.println("Bad URL.");
532 }
533 }
534
535 /** Retrieve the metadata directory, as required by any MSMCaller implementation.
536 * @return The currently active collection metadata directory as a <strong>String</strong>.
537 * @see org.greenstone.gatherer.collection.CollectionManager
538 */
539 public String getCollectionMetadata() {
540 if (c_man != null && c_man.ready()) {
541 return c_man.getCollectionMetadata();
542 }
543 return "";
544 }
545
546 /** Used to 'spawn' a new child application when a file is double clicked.
547 * @param file The file to open
548 * @see org.greenstone.gatherer.Gatherer.ExternalApplication
549 */
550 public void spawnApplication(File file) {
551 String [] commands = assoc_man.getCommand(file);
552 if(commands != null) {
553 ExternalApplication app = new ExternalApplication(commands);
554 apps.add(app);
555 app.start();
556 }
557 else {
558 ///ystem.err.println("No open command available.");
559 }
560 }
561
562 /** Used to 'spawn' a new browser application or reset an existing one when the preview button is clicked
563 * @param url The url to open the browser at
564 * @see org.greenstone.gatherer.Gatherer.BrowserApplication
565 */
566 public void spawnBrowser(String url) {
567 String command = assoc_man.getBrowserCommand(url);
568 if (command != null) {
569 BrowserApplication app = new BrowserApplication(command, url);
570 apps.add(app);
571 app.start();
572 }
573 else {
574 ///ystem.err.println("No browser command available.");
575 }
576 }
577
578 /** Prints a warning message about a missing library path, which means the final collection cannot be previewed in the Gatherer.
579 */
580 static public void missingEXEC() {
581 WarningDialog dialog = new WarningDialog("warning.MissingEXEC", "general.exec_address");
582 dialog.setValueField(new URLField(Gatherer.config.getColor("coloring.editable_foreground", false), Gatherer.config.getColor("coloring.editable_background", false), Gatherer.config.getColor("coloring.error_foreground", false), Gatherer.config.getColor("coloring.error_background", false)));
583 dialog.display();
584 dialog.dispose();
585 dialog = null;
586
587 String library_path_string = Gatherer.config.getString("general.exec_address", true);
588 if (!library_path_string.equals("")) {
589 try {
590 Gatherer.config.exec_address = new URL(library_path_string);
591 }
592 catch (MalformedURLException error) {
593 ///ystem.err.println("Error: Bad address: " + exec_address_string);
594 }
595 }
596 }
597
598 /** Prints a warning message about a missing GSDL path, which although not fatal pretty much ensures nothing will work properly in the Gatherer.
599 */
600 static public void missingGSDL() {
601 WarningDialog dialog = new WarningDialog("warning.MissingGSDL", false);
602 dialog.display();
603 dialog.dispose();
604 dialog = null;
605 }
606
607 /** 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. */
608 static public void missingPERL() {
609 WarningDialog dialog = new WarningDialog("warning.MissingPERL", false);
610 dialog.display();
611 dialog.dispose();
612 dialog = null;
613 }
614
615 /** Prints a warning message about a missing a valid WGet path. not fatal, but mirroring won't work */
616 static public void missingWGET() {
617 WarningDialog dialog = new WarningDialog("warning.MissingWGET", false);
618 dialog.display();
619 dialog.dispose();
620 dialog = null;
621 }
622 /** Prints a warning message about having an old version of WGet. not fatal, but mirroring may not work properly */
623 static public void oldWGET() {
624 WarningDialog dialog = new WarningDialog("warning.OldWGET", false);
625 dialog.display();
626 dialog.dispose();
627 dialog = null;
628 }
629 /** Print a message to the debug stream. */
630 static synchronized public void print(String message) {
631 if(debug != null) {
632 debug.print(message);
633 System.err.print(message);
634 }
635 }
636 /** Print a message to the debug stream. */
637 static synchronized public void println(String message) {
638 if(debug != null) {
639 debug.println(message);
640 System.err.println(message);
641 }
642 }
643
644 /** Print a stack trace to the debug stream. */
645 static synchronized public void printStackTrace(Exception exception) {
646 if(debug != null) {
647 exception.printStackTrace(debug);
648 exception.printStackTrace();
649 }
650 }
651 /** Prints a usage message to screen.
652 */
653 static public void printUsage(Dictionary dictionary) {
654 System.out.println(Dictionary.get("General.Usage"));
655 }
656
657 /** Sets up the proxy connection by setting JVM Environment flags and creating a new Authenticator.
658 * @see java.lang.Exception
659 * @see java.lang.System
660 * @see java.net.Authenticator
661 * @see org.greenstone.gatherer.Configuration
662 * @see org.greenstone.gatherer.GAuthenticator
663 */
664 static public void setProxy() {
665 try {// Can throw several exceptions
666 if(Gatherer.config.get("general.use_proxy", true)) {
667 System.setProperty("http.proxyType", "4");
668 System.setProperty("http.proxyHost", Gatherer.config.getString("general.proxy_host", true));
669 System.setProperty("http.proxyPort", Gatherer.config.getString("general.proxy_port", true));
670 System.setProperty("http.proxySet", "true");
671 } else {
672 System.setProperty("http.proxySet", "false");
673 }
674 } catch (Exception error) {
675 Gatherer.println("Error in Gatherer.initProxy(): " + error);
676 Gatherer.printStackTrace(error);
677 }
678 }
679
680 /** Set all the default fonts of the program to a choosen font, except the tooltip font which should be some fixed width font.
681 * @param f The default font to use in the Gatherer as a <strong>FontUIResource</strong>.
682 * @param ttf The tooltip font to use also as a <strong>FontUIResource</strong>.
683 * @see java.util.Enumeration
684 * @see javax.swing.UIManager
685 */
686 static private void setUIFont(FontUIResource f, FontUIResource ttf) {
687 // sets the default font for all Swing components.
688 // ex.
689 // setUIFont (new FontUIResource("Serif",Font.ITALIC,12));
690 //
691 Enumeration keys = UIManager.getDefaults().keys();
692 while (keys.hasMoreElements()) {
693 Object key = keys.nextElement();
694 Object value = UIManager.get (key);
695 if (value instanceof FontUIResource)
696 UIManager.put (key, f);
697 }
698 // Now set the tooltip font to some fixed width font
699 UIManager.put("ToolTip.font", ttf);
700 }
701
702 /** Loads the configuration file if one exists. Otherwise it creates a new one. Currently uses serialization.
703 * @param gsdl_path The path to the gsdl directory, gathered from the startup arguments, and presented as a <strong>String</strong>.
704 * @param exec_path The path to the library executable, gathered from the startup arguments, and presented as a <strong>String</strong>.
705 * @param perl_path The path to the PERL compiler as a <strong>String</strong>. Necessary for windows platform versions.
706 * @param mirroring_enabled
707 * @param wget_version_str
708 * @param wget_path
709 * @see java.io.FileInputStream
710 * @see java.io.ObjectInputStream
711 * @see java.lang.Exception
712 * @see org.greenstone.gatherer.Configuration
713 */
714 private void loadConfig(String gsdl_path, String gsdl3_path, String exec_path, String perl_path, boolean mirroring_enabled, String site_name) {
715 try {
716 config = new Configuration(gsdl_path, gsdl3_path, exec_path, perl_path, mirroring_enabled, site_name);
717 }
718 catch (Exception error) {
719 Gatherer.println(Configuration.CONFIG_XML+" is not a well formed XML document.");
720 Gatherer.printStackTrace(error);
721 }
722 if (GS3) {
723 try {
724 servlet_config = new ServletConfiguration(gsdl3_path);
725 } catch (Exception error) {
726 error.printStackTrace();
727 }
728 }
729 }
730
731 /** 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). */
732 private void saveConfig() {
733 try {
734 config.save();
735 } catch (Exception error) {
736 Gatherer.printStackTrace(error);
737 }
738 }
739
740 private void startServerEXE() {
741 if(config.exec_file != null && config.exec_address == null && Utility.isWindows() && !GS3) {
742 // First of all we create a GSDLSiteCFG object and check if a URL is already present, in which case the server is already running.
743 gsdlsite_cfg = new GSDLSiteConfig(config.exec_file);
744 String url = gsdlsite_cfg.getURL();
745 // If its already running then set exec address.
746 if(url != null) {
747 try {
748 config.exec_address = new URL(url);
749 }
750 catch(Exception error) {
751 }
752 }
753 // Otherwise its time to run the server in a spawned process.
754 if(config.exec_address == null && config.exec_file.exists()) {
755 // Configure for immediate entry. Note that this only works if the gsdlsite.cfg file exists.
756 gsdlsite_cfg.set();
757 // Spawn server
758 String command = config.exec_file.getAbsolutePath() + " " + gsdlsite_cfg.getSiteConfigFilename();
759 server = new ExternalApplication(command);
760 server.start();
761 command = null;
762 // Now we have to wait until program has started. We do this by reloading and checking
763 ///ystem.err.print("Waiting until the local library has loaded");
764 try {
765 gsdlsite_cfg.load();
766
767 int try_again = JOptionPane.YES_OPTION;
768 int attempt_count = 0;
769 while(gsdlsite_cfg.getURL() == null && try_again == JOptionPane.YES_OPTION) {
770 ///ystem.err.print(".");
771 if(attempt_count == 60) {
772 attempt_count = 0;
773 try_again = JOptionPane.showConfirmDialog(Gatherer.g_man, Dictionary.get("Server.QuitTimeOut"), Dictionary.get("General.Warning"), JOptionPane.YES_NO_OPTION);
774 }
775 else {
776 synchronized(this) {
777 wait(1000); // Wait one second (give or take)
778 }
779 gsdlsite_cfg.load();
780 attempt_count++;
781 }
782 }
783
784 if((url = gsdlsite_cfg.getURL()) != null) {
785 // Ta-da. Now the url should be available.
786 config.exec_address = new URL(url);
787
788 // 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.
789 try {
790 Gatherer.println("Try connecting to server on config url: '" + config.exec_address + "'");
791 URLConnection connection = config.exec_address.openConnection();
792 connection.getContent();
793 }
794 catch(IOException bad_url_connection) {
795 try {
796 Gatherer.println("Try connecting to server on local host: '" + gsdlsite_cfg.getLocalHostURL() + "'");
797 config.exec_address = new URL(gsdlsite_cfg.getLocalHostURL ());
798 URLConnection connection = config.exec_address.openConnection();
799 connection.getContent();
800 }
801 catch(IOException worse_url_connection) {
802 Gatherer.println("Can't connect to server on either address.");
803 config.exec_address = null;
804 config.exec_file = null;
805 }
806 }
807 }
808 // Unable to start local library. Show appropriate message.
809 else {
810 missingEXEC();
811 }
812 }
813 catch (Exception error) {
814 error.printStackTrace();
815 }
816 }
817 // Can't do a damb thing.
818 }
819 Gatherer.println("Having started server.exe, exec_address is: " + config.exec_address);
820 }
821
822 private void stopServerEXE() {
823 if(server != null && config.exec_address != null) {
824 // See if its already exited for some reason.
825 gsdlsite_cfg.load();
826 if(gsdlsite_cfg.getURL() != null) {
827 // Send the command for it to exit.
828 configServer(GSDLSiteConfig.QUIT_COMMAND);
829 // Wait until it exits.
830 try {
831 gsdlsite_cfg.load();
832 int try_again = JOptionPane.YES_OPTION;
833 int attempt_count = 0;
834 while(gsdlsite_cfg.getURL() != null && try_again == JOptionPane.YES_OPTION) {
835 if(attempt_count == 60) {
836 attempt_count = 0;
837 try_again = JOptionPane.showConfirmDialog(Gatherer.g_man, Dictionary.get("Server.QuitTimeOut"), Dictionary.get("General.Warning"), JOptionPane.YES_NO_OPTION);
838 }
839 else {
840 synchronized(this) {
841 wait(1000); // Wait one second (give or take)
842 }
843 gsdlsite_cfg.load();
844 attempt_count++;
845 }
846 }
847 //if(gsdlsite_cfg.getURL() != null) {
848 //JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("Server.QuitManual"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
849 //}
850 }
851 catch (Exception error) {
852 error.printStackTrace();
853 }
854 }
855 // Restore the gsdlsite_cfg.
856 if(gsdlsite_cfg != null) {
857 gsdlsite_cfg.restore();
858 }
859 // If the local server is still running then our changed values will get overwritten.
860 if(gsdlsite_cfg.getURL() != null) {
861 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("Server.QuitFailed"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
862 }
863 gsdlsite_cfg = null;
864 server = null;
865 }
866 }
867
868 // TODO fill this in
869 private String testWGetVersion(String wget_path) {
870 return StaticStrings.WGET_STR;
871 }
872
873 /** 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. */
874 private class ExternalApplication
875 extends Thread {
876 private Process process = null;
877 /** The initial command string given to this sub-process. */
878 private String command = null;
879 private String[] commands = null;
880 /** Constructor.
881 * @param command The initial command <strong>String</strong>.
882 */
883 public ExternalApplication(String command) {
884 this.command = command;
885 }
886
887 public ExternalApplication(String[] commands) {
888 this.commands = commands;
889 }
890 /** We start the child process inside a new thread so it doesn't block the rest of Gatherer.
891 * @see java.lang.Exception
892 * @see java.lang.Process
893 * @see java.lang.Runtime
894 * @see java.lang.System
895 * @see java.util.Vector
896 */
897 public void run() {
898 // Call an external process using the args.
899 try {
900 if(commands != null) {
901 StringBuffer whole_command = new StringBuffer();
902 for(int i = 0; i < commands.length; i++) {
903 whole_command.append(commands[i]);
904 whole_command.append(" ");
905 }
906 println("Running " + whole_command.toString());
907 Runtime rt = Runtime.getRuntime();
908 process = rt.exec(commands);
909 process.waitFor();
910 }
911 else {
912 println("Running " + command);
913 Runtime rt = Runtime.getRuntime();
914 process = rt.exec(command);
915 process.waitFor();
916 }
917 }
918 catch (Exception error) {
919 println("Error in ExternalApplication.run(): " + error);
920 printStackTrace(error);
921 }
922 // Remove ourself from Gatherer list of threads.
923 apps.remove(this);
924 // Call exit if we were the last outstanding child process thread.
925 if(apps.size() == 0 && exit == true) {
926 stopServerEXE();
927 System.exit(0);
928 }
929 }
930 public void stopExternalApplication() {
931 if(process != null) {
932 process.destroy();
933 }
934 }
935 }
936 /** 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. */
937 private class BrowserApplication
938 extends Thread {
939 private Process process = null;
940 /** The initial command string given to this sub-process. */
941 private String command = null;
942 private String url = null;
943 private String[] commands = null;
944 /** Constructor.
945 * @param command The initial command <strong>String</strong>.
946 */
947// public BrowserApplication(String command) {
948// this.command = command;
949// }
950
951 public BrowserApplication(String command, String url) {
952 StringTokenizer st = new StringTokenizer(command);
953 int num_tokens = st.countTokens();
954 this.commands = new String [num_tokens];
955 int i=0;
956 while (st.hasMoreTokens()) {
957 commands[i] = st.nextToken();
958 i++;
959 }
960 //this.commands = commands;
961 this.url = url;
962 }
963 /** We start the child process inside a new thread so it doesn't block the rest of Gatherer.
964 * @see java.lang.Exception
965 * @see java.lang.Process
966 * @see java.lang.Runtime
967 * @see java.lang.System
968 * @see java.util.Vector
969 */
970 public void run() {
971 // Call an external process using the args.
972 if(commands == null) {
973 apps.remove(this);
974 return;
975 }
976 try {
977 String prog_name = commands[0];
978 String lower_name = prog_name.toLowerCase();
979 if (lower_name.indexOf("mozilla") != -1 || lower_name.indexOf("netscape") != -1) {
980 Gatherer.println("found mozilla or netscape, trying remote it");
981 // mozilla and netscape, try using a remote command to get things in the same window
982 String [] new_commands = new String[] {prog_name, "-raise", "-remote", "openURL("+url+",new-tab)"};
983 if(debug != null) {
984 printArray(new_commands);
985 }
986 Runtime rt = Runtime.getRuntime();
987 process = rt.exec(new_commands);
988 int exitCode = process.waitFor();
989 if (exitCode != 0) { // if Netscape or mozilla was not open
990 Gatherer.println("couldn't do remote, trying original command");
991 if(debug != null) {
992 printArray(commands);
993 }
994 process = rt.exec(commands); // try the original command
995 }
996 } else {
997 // just run what we have been given
998 StringBuffer whole_command = new StringBuffer();
999 for(int i = 0; i < commands.length; i++) {
1000 whole_command.append(commands[i]);
1001 whole_command.append(" ");
1002 }
1003 println("Running " + whole_command.toString());
1004 Runtime rt = Runtime.getRuntime();
1005 process = rt.exec(commands);
1006 process.waitFor();
1007 }
1008 }
1009
1010 catch (Exception error) {
1011 println("Error in BrowserApplication.run(): " + error);
1012 printStackTrace(error);
1013 }
1014 // Remove ourself from Gatherer list of threads.
1015 apps.remove(this);
1016 // Call exit if we were the last outstanding child process thread.
1017 if(apps.size() == 0 && exit == true) {
1018 stopServerEXE();
1019 System.exit(0);
1020 }
1021 }
1022 public void printArray(String [] array) {
1023 for(int i = 0; i < array.length; i++) {
1024 debug.print(array[i]+" ");
1025 System.err.println(array[i]+" ");
1026 }
1027 }
1028 public void stopBrowserApplication() {
1029 if(process != null) {
1030 process.destroy();
1031 }
1032 }
1033 }
1034
1035 /** 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. */
1036 private class CTRLCHandler
1037 implements SignalHandler {
1038 /** <i>true</i> if we ignore any other signals we receive, most likely because we are already dealing with one, <i>false</i> otherwise. */
1039 private boolean ignore = false;
1040 /** The method called by the system to inform us a signal has occured.
1041 * @param sig The <strong>Signal</strong> itself.
1042 * @see org.greenstone.gatherer.collection.CollectionManager
1043 */
1044 public void handle(Signal sig) {
1045 if(!ignore) {
1046 ignore = true;
1047 // handle SIGINT
1048 System.out.println("Caught Ctrl-C...");
1049 if(c_man != null && c_man.ready()) {
1050 c_man.closeCollection();
1051 }
1052 exit();
1053 ignore = false;
1054 }
1055 }
1056 }
1057
1058 private class PerlTest {
1059
1060 private String[] command = new String[2];
1061
1062 public PerlTest() {
1063 if(Utility.isWindows()) {
1064 command[0] = Utility.PERL_EXECUTABLE_WINDOWS;
1065 }
1066 else {
1067 command[0] = Utility.PERL_EXECUTABLE_UNIX;
1068 }
1069 command[1] = "-version";
1070 }
1071
1072 public boolean found() {
1073 boolean found = false;
1074 try {
1075 Process prcs = Runtime.getRuntime().exec(command);
1076 prcs.waitFor();
1077 found = (prcs.exitValue() == 0);
1078 prcs = null;
1079 }
1080 catch(Exception error) {
1081 }
1082 return found;
1083 }
1084
1085 public String toString() {
1086 return command[0];
1087 }
1088 }
1089}
Note: See TracBrowser for help on using the repository browser.