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

Last change on this file since 32686 was 32686, checked in by ak19, 5 years ago

For GUI testing, it's far and away easier if only GUI components had names. Else it's a real pain to access components from the AssertJ Swing API. Introducing setNamesRecursively, a new method (run on the Event Dispatch Thread EDT as is required by the code) that uses reflection to automate setting the names of hopefully all if not all the most important for testing GUI components of GLI. It's called from Gatherer's openGUI which is performed on the EDT, so no need to call invokeLater() to force this method to take place on the Event Dispatch Thread. There are 2 caveats: not sure all the components are named and not all of them appear to have a unique name, so I may expand the code hereafter to add numbering after checking against a hash map for uniqueness if I find this to be necessary

  • Property svn:keywords set to Author Date Id Revision
File size: 69.8 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
29import java.awt.*;
30import java.awt.event.*;
31import java.io.*;
32import java.lang.*;
33import java.net.*;
34import java.util.*;
35import javax.swing.*;
36import javax.swing.plaf.*;
37import javax.swing.text.*;
38
39
40import org.greenstone.gatherer.Configuration;
41import org.greenstone.gatherer.GAuthenticator;
42import org.greenstone.gatherer.FedoraInfo;
43import org.greenstone.gatherer.collection.CollectionManager;
44import org.greenstone.gatherer.feedback.ActionRecorderDialog;
45import org.greenstone.gatherer.feedback.Base64;
46import org.greenstone.gatherer.file.FileManager;
47import org.greenstone.gatherer.file.FileAssociationManager;
48import org.greenstone.gatherer.file.RecycleBin;
49import org.greenstone.gatherer.greenstone.Classifiers;
50import org.greenstone.gatherer.greenstone.LocalGreenstone;
51import org.greenstone.gatherer.greenstone.LocalLibraryServer;
52import org.greenstone.gatherer.greenstone.Plugins;
53import org.greenstone.gatherer.greenstone3.ServletConfiguration;
54import org.greenstone.gatherer.gui.GUIManager;
55import org.greenstone.gatherer.gui.URLField;
56import org.greenstone.gatherer.gui.WarningDialog;
57import org.greenstone.gatherer.gui.FedoraLogin;
58import org.greenstone.gatherer.metadata.FilenameEncoding;
59import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
60import org.greenstone.gatherer.util.GS3ServerThread;
61import org.greenstone.gatherer.util.JarTools;
62import org.greenstone.gatherer.util.SafeProcess;
63import org.greenstone.gatherer.util.StaticStrings;
64import org.greenstone.gatherer.util.Utility;
65
66
67/** Containing the top-level "core" for the Gatherer, this class is the
68* common core for the GLI application and applet. It first parses the
69* command line arguments, preparing to update the configuration as
70* required. Next it loads several important support classes such as the
71* Configuration and Dictionary. Finally it creates the other important
72* managers and sends them on their way.
73* @author John Thompson, Greenstone Digital Library, University of Waikato
74* @version 2.3
75*/
76public class Gatherer
77{
78 /** The name of the GLI. */
79 static final public String PROGRAM_NAME = "Greenstone Librarian Interface";
80 /** The current version of the GLI.
81 * Note: the gs3-release-maker relies on this variable being declared
82 * in a line which matches this java regex:
83 * ^(.*)String\s*PROGRAM_VERSION\s*=\s*"trunk";
84 * If change the declaration and it no longer matches the regex, please
85 * change the regex in the gs3-release-maker code and in this message
86 */
87
88 static final public String PROGRAM_VERSION = "trunk";
89
90 static private Dimension size = new Dimension(800, 540);
91 static public RemoteGreenstoneServer remoteGreenstoneServer = null;
92
93 /** Has the exit flag been set? */
94 static final public int EXIT_THEN_RESTART= 2;
95 static public boolean exit = false;
96 static public int exit_status = 0;
97
98 static private String gli_directory_path = null;
99 static private String gli_user_directory_path = null;
100
101 static public String client_operating_system = null;
102
103 /** All of the external applications that must exit before we close the Gatherer. */
104 static private Vector apps = new Vector();
105 static private String non_standard_collect_directory_path = null;
106 static public String open_collection_file_path = null;
107 static public String gsdlsite_collecthome = "";
108 /** A public reference to the FileAssociationManager. */
109 static public FileAssociationManager assoc_man;
110 /** A public reference to the CollectionManager. */
111 static public CollectionManager c_man;
112 /** A public reference to the RecycleBin. */
113 static public RecycleBin recycle_bin;
114 /** a reference to the Servlet Configuration is GS3 */
115 static public ServletConfiguration servlet_config;
116 /** A public reference to the FileManager. */
117 static public FileManager f_man;
118 /** A public reference to the GUIManager. */
119 static public GUIManager g_man = null;
120 static private boolean g_man_built = false;
121
122 /** We are using the GLI for GS3 */
123 static public boolean GS3 = false;
124
125 static public boolean isApplet = false;
126 static public boolean isGsdlRemote = false;
127 static public boolean isLocalLibrary = false;
128
129 // for storing the original proxy settings on GLI startup
130 private static Properties startup_proxy_settings = new Properties();
131
132 /* TODO: If we're using local GLI, collections are built locally. If we're using client-GLI
133 * and it contains a gs2build folder in it, then localBuild will also be true (if this is not
134 * turned off in Preferences). If we're remote and this is turned off in Prefs, build remotely. */
135 /*static public boolean buildingLocally = true;*/
136 /** If we're using local GLI, we can always download. If we're using client-GLI, we can only
137 * download if we have a gs2build folder inside it. And if we don't turn off downloadEnabling
138 * in the preferences.
139 */
140 static public boolean isDownloadEnabled = true;
141
142 // feedback stuff
143 /** is the feedback feature enabled? */
144 static public boolean feedback_enabled = true;
145 /** the action recorder dialog */
146 static public ActionRecorderDialog feedback_dialog = null;
147
148 // Refresh reasons
149 static public final int COLLECTION_OPENED = 0;
150 static public final int COLLECTION_CLOSED = 1;
151 static public final int COLLECTION_REBUILT = 2;
152 static public final int PREFERENCES_CHANGED = 3;
153
154 //////kk added////////
155 static public String cgiBase="";
156 /////////////////
157
158 /** Magic to allow Enter to fire the default button. */
159 static {
160 KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
161 Keymap map = JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP);
162 map.removeKeyStrokeBinding(enter);
163 }
164
165 static private URL default_gliserver_url=null;
166
167 public Gatherer(String[] args)
168 {
169 // Display the version to make error reports a lot more useful
170 System.err.println("Version: " + PROGRAM_VERSION + "\n");
171
172 JarTools.initialise(this);
173
174 GetOpt go = new GetOpt(args);
175
176 // Remember the GSDLOS value
177 client_operating_system = go.client_operating_system;
178
179 // If feedback is enabled, set up the recorder dialog
180 if (go.feedback_enabled) {
181 // Use the default locale for now - this will be changed by the Gatherer run method
182 feedback_enabled = true;
183 feedback_dialog = new ActionRecorderDialog(Locale.getDefault());
184 }
185
186 // Are we using a remote Greenstone?
187 if (go.use_remote_greenstone) {
188 isGsdlRemote = true;
189
190 // We don't have a local Greenstone!
191 go.gsdl3_path=null;
192 go.gsdl3_src_path=null;
193
194 // Don't set go.gsdl_path to null, since gdsl_path may still be set
195 // if we have a client-gli containing gs2build folder.
196 // However, keep track of whether we can download.
197 if(go.gsdl_path == null) {
198 isDownloadEnabled = false;
199 }
200
201 // We have to use our own collect directory since we can't use the Greenstone one
202 setCollectDirectoryPath(getGLIUserDirectoryPath() + "collect" + File.separator);
203 }
204 // We have a local Greenstone. OR we have a gs2build folder inside
205 // the client-GLI folder (with which the Download panel becomes enabled)
206 if(isDownloadEnabled) {
207 LocalGreenstone.setDirectoryPath(go.gsdl_path);
208 }
209
210 // Users may specify a non-standard collect directory (eg. when running one GLI in a network environment)
211 // Processing of custom non-standard collect directory passed into gli now happens in init()
212 //if (go.collect_directory_path != null) { setCollectDirectoryPath(go.collect_directory_path); }
213
214 // More special code for running with a remote Greenstone
215 if (isGsdlRemote) {
216 if (go.fedora_info.isActive()) {
217 Configuration.TEMPLATE_CONFIG_XML = "xml/" + Configuration.FEDORA_CONFIG_PREFIX + "configRemote.xml";
218 }
219 else {
220 Configuration.TEMPLATE_CONFIG_XML = "xml/configRemote.xml";
221 }
222
223 Configuration.CONFIG_XML = "configRemote.xml";
224
225 File collect_directory = new File(Gatherer.getCollectDirectoryPath());
226 if (!collect_directory.exists() && !collect_directory.mkdir()) {
227 System.err.println("Warning: Unable to make directory: " + collect_directory);
228 }
229 }
230 else {
231 if (go.fedora_info.isActive()) {
232 Configuration.TEMPLATE_CONFIG_XML = "xml/" + Configuration.FEDORA_CONFIG_PREFIX + Configuration.CONFIG_XML;
233 }
234 // else, the CONFIG_XML uses the default config file, which is for the local GS server
235 }
236
237 init(go.gsdl_path, go.gsdl3_path, go.gsdl3_src_path,
238 go.fedora_info,
239 go.local_library_path, go.library_url_string,
240 go.gliserver_url_string, go.debug, go.perl_path, go.no_load, go.filename, go.site_name,
241 go.servlet_path, go.collect_directory_path);
242 }
243
244
245 public void init(String gsdl_path, String gsdl3_path, String gsdl3_src_path,
246 FedoraInfo fedora_info,
247 String local_library_path,
248 String library_url_string, String gliserver_url_string, boolean debug_enabled,
249 String perl_path, boolean no_load, String open_collection,
250 String site_name, String servlet_path, String collect_directory_path)
251 {
252 if (gsdl3_path != null && !gsdl3_path.equals("")) {
253 this.GS3 = true;
254 } else {
255 gsdl3_path = null;
256 gsdl3_src_path = null;
257 }
258
259 // Create the debug stream if required
260 if (debug_enabled) {
261 DebugStream.enableDebugging();
262
263 Calendar now = Calendar.getInstance();
264 String debug_file_path = "debug" + now.get(Calendar.DATE) + "-" + now.get(Calendar.MONTH) + "-" + now.get(Calendar.YEAR) + ".txt";
265
266 // Debug file is created in the user's GLI directory
267 debug_file_path = getGLIUserDirectoryPath() + debug_file_path;
268 DebugStream.println("Debug file path: " + debug_file_path);
269 DebugStream.setDebugFile(debug_file_path);
270 DebugStream.print(System.getProperties());
271 }
272
273 // Delete plugins.dat and classifiers.dat files from previous versions of the GLI (no longer used)
274 File plugins_dat_file = new File(Gatherer.getGLIUserDirectoryPath() + "plugins.dat");
275 if (plugins_dat_file.exists()) {
276 System.err.println("Deleting plugins.dat file...");
277 Utility.delete(plugins_dat_file);
278 }
279 File classifiers_dat_file = new File(Gatherer.getGLIUserDirectoryPath() + "classifiers.dat");
280 if (classifiers_dat_file.exists()) {
281 System.err.println("Deleting classifiers.dat file...");
282 Utility.delete(classifiers_dat_file);
283 }
284
285 try {
286 // Load GLI config file
287 new Configuration(getGLIUserDirectoryPath(), gsdl_path, gsdl3_path, gsdl3_src_path, site_name,
288 fedora_info);
289
290 // Check we know where Perl is
291 Configuration.perl_path = perl_path;
292 if (isGsdlRemote && !isDownloadEnabled && Utility.isWindows() && Configuration.perl_path != null) {
293 if (Configuration.perl_path.toLowerCase().endsWith("perl.exe")) {
294 Configuration.perl_path = Configuration.perl_path.substring(0, Configuration.perl_path.length() - "perl.exe".length());
295 }
296 if (Configuration.perl_path.endsWith(File.separator)) {
297 Configuration.perl_path = Configuration.perl_path.substring(0, Configuration.perl_path.length() - File.separator.length());
298 }
299 }
300
301 // the feedback dialog has been loaded with a default locale,
302 // now set the user specified one
303 if (feedback_enabled && feedback_dialog != null) {
304 feedback_dialog.setLocale(Configuration.getLocale("general.locale", true));
305 }
306
307 // Read Dictionary
308 new Dictionary(Configuration.getLocale("general.locale", true), Configuration.getFont("general.font", true));
309
310 // check that we are using Sun Java
311 String java_vendor = System.getProperty("java.vendor");
312 if (!java_vendor.equals("Sun Microsystems Inc.")
313 && !java_vendor.equals("Oracle Corporation")) {
314 System.err.println(Dictionary.get("General.NotSunJava", java_vendor));
315 }
316
317 // Unless we're using remote building, we need to know where the local Greenstone is
318 if (!isGsdlRemote && gsdl_path == null) {
319 missingGSDL();
320 }
321
322 if (fedora_info.isActive()) {
323
324 // if we have either a remote GS 2/3 server, or if we are dealing with a
325 // local GS2, or else if it's a local GS3 but with fedora installed elsewhere,
326 // we ask for fedora login details now
327 File fedora_webapp = new File(gsdl3_src_path+File.separator+"packages"+File.separator+"tomcat"+File.separator+"webapps"+File.separator+"fedora");
328
329 if(isGsdlRemote || !GS3 || !fedora_webapp.exists()) {
330 popupFedoraInfo();
331 }
332
333 // else if we have a local GS3 with fedora installed within it, we'll be starting
334 // up fedora further below, together with the local tomcat
335 }
336
337 // the no_load flag to GLI is processed at the end of handling open_collection_file_path
338 open_collection_file_path = open_collection;
339 if (open_collection_file_path == null) {
340 open_collection_file_path = Configuration.getString(
341 "general.open_collection"+Configuration.gliPropertyNameSuffix(), true);
342 }
343
344 initCollectDirectoryPath();
345
346 if (no_load || (isGsdlRemote && open_collection_file_path.equals(""))) {
347 open_collection_file_path = null;
348 }
349
350
351 // Before calling setProxy, store anything already present
352 // Downloading happens on the machine where GLI is running from, even with clientgli
353 // For downloading with wget, we use ftp/http(s)_proxy env vars, so check if they're
354 // already set
355 if(System.getenv("http_proxy") != null) System.err.println("http_proxy already set");
356 if(System.getenv("https_proxy") != null) System.err.println("https_proxy already set");
357 if(System.getenv("ftp_proxy") != null) System.err.println("ftp_proxy already set");
358
359
360 // Finally, we're ready to find out the version of the remote Greenstone server
361 if(isGsdlRemote) {
362 // instantiate the RemoteGreenstoneServer object first
363 remoteGreenstoneServer = new RemoteGreenstoneServer();
364
365 // Set up proxy
366 setProxy();
367 // Now we set up an Authenticator
368 Authenticator.setDefault(new GAuthenticator());
369
370 int greenstoneVersion = 2;
371 requestGLIServerURL();
372 gliserver_url_string = Configuration.gliserver_url.toString();
373
374 greenstoneVersion = remoteGreenstoneServer.getGreenstoneVersion();
375 // Display the version to make error reports a lot more useful
376 System.err.println("Remote Greenstone server version: " + greenstoneVersion);
377 if(greenstoneVersion == -1) { // remote server not running
378 Gatherer.exit();
379 }
380 if(greenstoneVersion >= 3) {
381 this.GS3 = true;
382 Configuration.prepareForGS3();
383 }
384
385 if(fedora_info.isActive()) {
386 // when GS server is remote, FEDORA_HOME resides on the remote server side,
387 // but we know the library URL from user-provided information
388 library_url_string = fedora_info.getLibraryURL();
389 } else {
390 library_url_string = remoteGreenstoneServer.getLibraryURL(Configuration.gliserver_url.toString());
391 }
392 // write it into the config file
393 Configuration.setString("general.library_url", true, library_url_string);
394 }
395 else { // local greenstone: add shutdown hooks to forcibly stop the server on irregular termination
396 // And start up the local library server, if that's what we want
397
398 // The Java virtual machine shuts down in response to two kinds of events:
399 // - The program exits normally, when the last non-daemon thread exits or when the exit (equivalently, System.exit) method is invoked, or
400 // - The virtual machine is terminated in response to a user interrupt, such as typing ^C, or a system-wide event, such as user logoff or system shutdown.
401 // https://coderanch.com/t/328888/java/Killing-process-spawned-Runtime-exec
402 // So here we add a shutdown hook to take care of Ctrl-C situations where GLI is irregularly terminated
403 // Sadly, the shutdownhook never gets called on Windows, whether GS2 or GS3,
404 // when a Ctrl-C is sent to the DOS prompt that launched GLI. Because GLI on Windows
405 // currently waits to respond to a Ctrl-C until AFTER GLI is already (properly) exited
406 Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
407 public void run() {
408
409 if(Gatherer.exit != true) { // unexpected termination, such as Ctrl-C won't set Gatherer.exit
410 // so still need to at least issue the call to stop any running GS server
411
412 System.err.println("ShutDownHook called...");
413
414 if (GS3) { // issue the ant call to stop any running GS3
415 // (tomcat and networked Derby Server) by calling GS3ServerThread.stopServer()
416 if(!GS3ServerThread.wasServerLaunchedOutsideGLI()) {
417 System.err.println("Attempting to forcibly terminate the GS server...");
418 GS3ServerThread.stopServer();
419 } else {
420 System.err.println("@@@ Tomcat was launched outside GLI. Leaving it running...");
421 }
422 } else { // issue the call to stop any running GS2 local library server
423 if (LocalLibraryServer.isRunning() == true) {
424 System.err.println("Attempting to forcibly terminate the GS server...");
425 LocalLibraryServer.forceStopServer();
426 }
427 }
428 }
429 }
430 }));
431
432
433 if (!GS3) {
434 isLocalLibrary = LocalLibraryServer.start(gsdl_path, local_library_path);
435
436 }
437 else { // local GS3, start the local tomcat
438
439 if(!GS3ServerThread.wasServerLaunchedOutsideGLI()) {
440 //System.err.println("@@@@ Launching tomcat from GLI");
441 GS3ServerThread thread = new GS3ServerThread(gsdl3_src_path, "restart");
442 thread.start();
443 }
444
445 // If fedora installed inside this local GS3, then ask for fedora login details now.
446 if (fedora_info.isActive()) {
447 File fedora_webapp = new File(gsdl3_src_path+File.separator+"packages"+File.separator+"tomcat"+File.separator+"webapps"+File.separator+"fedora");
448
449 if(fedora_webapp.exists()) {
450 System.err.println("**** Waiting for the local Greenstone server to start up fedora...");
451 popupFedoraInfo();
452 }
453 }
454 }
455 // else web library: GS server is local, but doesn't use the webserver included with GS2
456 }
457
458 // The "-library_url" option overwrites anything in the config files
459 if (library_url_string != null && library_url_string.length() > 0) {
460 try {
461 System.err.println("Setting library_url to " + library_url_string + "...");
462 Configuration.library_url = new URL(library_url_string);
463 }
464 catch (MalformedURLException error) {
465 DebugStream.printStackTrace(error);
466 }
467 }
468
469
470 // Check that we now know the Greenstone library URL, since we need this for previewing collections
471 // It is not necessary if an independent GSI was launched and the user hasn't pressed Enter Library yet
472 DebugStream.println("Configuration.library_url = " + Configuration.library_url);
473 if (Configuration.library_url == null) {
474 if(isLocalLibrary) {
475 if(!LocalLibraryServer.isURLPending()) {
476 missingEXEC();
477 }
478 // else LocalLibraryServer is expecting a URL soon, so don't need to display the dialog
479 } else { // GS2 webLibrary or GS3 or remote Greenstone
480 missingEXEC();
481 }
482 }
483
484 // The "-gliserver_url" option overwrites anything in the config files
485 if (gliserver_url_string != null && gliserver_url_string.length() > 0) {
486 try {
487 System.err.println("Setting gliserver_url to " + gliserver_url_string + "...");
488 Configuration.gliserver_url = new URL(gliserver_url_string);
489 }
490 catch (MalformedURLException error) {
491 DebugStream.printStackTrace(error);
492 }
493 }
494
495 // If we're using a remote Greenstone we need to know where the gliserver script is
496 DebugStream.println("Configuration.gliserver_url = " + Configuration.gliserver_url);
497
498 if (GS3) {
499 // Load Greenstone 3 servlet configuration
500 if (isGsdlRemote){
501 servlet_config = new ServletConfiguration(Configuration.gli_user_directory_path);
502 }else{
503 servlet_config= new ServletConfiguration(gsdl3_path);
504 }
505 }
506
507 if (GS3 && Configuration.servlet_path == null) {
508 Configuration.servlet_path = servlet_config.getServletPath(Configuration.site_name);
509 }
510
511 // ensure that a directory called 'cache' exists in the GLI user directory
512 File user_cache_dir = new File(Gatherer.getGLIUserCacheDirectoryPath());
513 System.err.println("User cache dir: " + Gatherer.getGLIUserCacheDirectoryPath());
514 if (!user_cache_dir.exists() && !user_cache_dir.mkdir()) {
515 System.err.println("Warning: Unable to make directory: " + user_cache_dir);
516 }
517
518
519 if (Gatherer.isGsdlRemote) {
520 DebugStream.println("Not checking for perl path/exe");
521 }
522 else {
523 // Perl path is a little different as it is perfectly ok to
524 // start the GLI without providing a perl path
525 boolean found_perl = false;
526 if (Configuration.perl_path != null) {
527 // See if the file pointed to actually exists
528 File perl_file = new File(Configuration.perl_path);
529 found_perl = perl_file.exists();
530 perl_file = null;
531 }
532 if (Configuration.perl_path == null || !found_perl) {
533 // Run test to see if we can run perl as is.
534 PerlTest perl_test = new PerlTest();
535 if (perl_test.found()) {
536 // If so replace the perl path with the system
537 // default (or null for unix).
538 Configuration.perl_path = perl_test.toString();
539 found_perl = true;
540 }
541 }
542 if (!found_perl) {
543 // Time for an error message.
544 missingPERL();
545 }
546 }
547
548
549 // Check for ImageMagick - dependent on perl_path
550 if (Gatherer.isGsdlRemote) {
551 DebugStream.println("Not checking for ImageMagick.");
552 }
553 else if (!(new ImageMagickTest()).found()) {
554 // Time for a warning message
555 missingImageMagick();
556 }
557
558 // Check for PDFBox
559 if (Gatherer.isGsdlRemote) {
560 DebugStream.println("Not checking for PDFBox.");
561 }
562 else {
563 String gs_dir = gsdl_path; // for GS3, pdf-box is in gs2build/ext (not in GS3/ext), for GS2 it's also in GS2/ext
564 File pdfboxExtensionFolder = new File(gs_dir+File.separator+"ext"+File.separator+"pdf-box");
565 if (!(pdfboxExtensionFolder.exists() && pdfboxExtensionFolder.isDirectory())) {
566 // The user doesn't have PDFBox, inform them of it
567 String zipExtension = Utility.isWindows() ? "zip" : "tar.gz";
568 missingPDFBox(zipExtension, pdfboxExtensionFolder.getParent());
569 }
570 }
571
572 // Check that the local can support multiple filename encodings
573 //System.err.println("#### Java identifies current Locale as (file.encoding): "
574 // + System.getProperty("file.encoding"));
575 if(System.getProperty("file.encoding").equals("UTF-8")){
576 // If the locale is UTF-8, Java will interpret all filename bytes as UTF-8,
577 // which is a destructive process as it will convert characters not recognised
578 // by UTF-8 into the invalid character, rather than preserving the bytecodes.
579 // This has the effect that non-UTF8 encoded filenames on a system set to a
580 // UTF-8 locale are not 'seen' by Java (if they contain non-ASCII characters).
581 multipleFilenameEncodingsNotSupported();
582 FilenameEncoding.MULTIPLE_FILENAME_ENCODINGS_SUPPORTED = false;
583 FilenameEncoding.URL_FILE_SEPARATOR = File.separator;
584 } else {
585 FilenameEncoding.MULTIPLE_FILENAME_ENCODINGS_SUPPORTED = true;
586 FilenameEncoding.URL_FILE_SEPARATOR = "/"; // URL file separator is always "/"
587 }
588
589 // Set the default font for all Swing components.
590 FontUIResource default_font = Configuration.getFont("general.font", true);
591 Enumeration keys = UIManager.getDefaults().keys();
592 while (keys.hasMoreElements()) {
593 Object key = keys.nextElement();
594 Object value = UIManager.get(key);
595 if (value instanceof FontUIResource) {
596 UIManager.put(key, default_font);
597 }
598 }
599
600 // At this point (which is where this code originally used to be), we can set up the proxy for the
601 // non-remote case. The remote Greenstone server would already have setup its proxy when required.
602 if(!isGsdlRemote) {
603 setProxy();
604 // Now we set up an Authenticator
605 Authenticator.setDefault(new GAuthenticator());
606 }
607 assoc_man = new FileAssociationManager();
608 // Create File Manager
609 f_man = new FileManager();
610 // Create Collection Manager
611 c_man = new CollectionManager();
612 // Create Recycle Bin
613 recycle_bin = new RecycleBin();
614
615 if (GS3) {
616 if (site_name==null) {
617 site_name = Configuration.site_name;
618 servlet_path = null; // need to reset this
619 }
620 if (servlet_path == null) {
621 servlet_path = Configuration.getServletPath();
622 }
623 }
624
625
626 } catch (Exception exception) {
627 DebugStream.printStackTrace(exception);
628 }
629
630
631 // Create GUI Manager (last) or else suffer the death of a thousand NPE's
632 g_man = new GUIManager(size);
633
634 // Get a list of the core Greenstone classifiers and plugins
635 Classifiers.loadClassifiersList(null);
636 Plugins.loadPluginsList(null);
637
638 // Users may specify a non-standard collect directory (eg. when running one GLI in a network environment)
639 // Set to any custom collection directory provided to gli. This happens if gli was run as:
640 // ./gli.sh -collectdir </full/path/to/custom/collect>
641 if(collect_directory_path != null) {
642
643 // create a version of the collect_dir_path without a file-separator at end
644 String collectDir = collect_directory_path;
645 if(collectDir.endsWith(File.separator)) {
646 collectDir = collectDir.substring(0, collectDir.length()-1); // remove file separator at end
647 }
648
649 // update .gli/config.xml to contain the version of the path without file-separator at end
650 if(collect_directory_path.equals(getDefaultGSCollectDirectoryPath(false))) {
651 Configuration.setString("general.open_collection"+Configuration.gliPropertyNameSuffix(),
652 true, "");
653 } else {
654 Configuration.setString("general.open_collection"+Configuration.gliPropertyNameSuffix(),
655 true, collectDir);
656 }
657
658 // set non_standard_collect_directory_path variable. Ensures file_separator at end
659 Gatherer.setCollectDirectoryPath(collect_directory_path);
660
661 // Use version of path without file-separator at end to set collecthome in gsdlsite(3).cfg
662 if(collectDir != null) {
663 collectDir = "\""+collectDir+"\"";
664 }
665 Utility.updatePropertyConfigFile(getGsdlSiteConfigFile(), "collecthome", collectDir);
666 // if gsdlsite.cfg does not exist (if using server.exe for instance), the above method will just return
667 }
668
669 // If using a remote Greenstone we need to download the collection configurations now
670 if (Gatherer.isGsdlRemote) {
671 if (remoteGreenstoneServer.downloadCollectionConfigurations().equals("")) {
672 // !! Something went wrong downloading the collection configurations
673 System.err.println("Error: Could not download collection configurations.");
674 if(!Gatherer.isApplet) { // don't close the browser if it is an applet!
675 System.exit(0);
676 }
677 }
678 }
679 }
680
681
682 /** Returns the correct version of the (local or remote) Greenstone server if init() has already been called. */
683 public static int serverVersionNumber() {
684 return GS3 ? 3 : 2;
685 }
686
687 /** Returns "Server: version number" if init() has already been called. */
688 public static String getServerVersionAsString() {
689 return "Server: v" + serverVersionNumber();
690 }
691
692 public void openGUI()
693 {
694 // Size and place the frame on the screen
695 Rectangle bounds = Configuration.getBounds("general.bounds", true);
696 if (bounds == null) {
697 // Choose a sensible default value
698 bounds = new Rectangle(0, 0, 640, 480);
699 }
700
701 // Ensure width and height are reasonable
702 size = bounds.getSize();
703 if (size.width < 640) {
704 size.width = 640;
705 }
706 else if (size.width > Configuration.screen_size.width && Configuration.screen_size.width > 0) {
707 size.width = Configuration.screen_size.width;
708 }
709 if (size.height < 480) {
710 size.height = 480;
711 }
712 else if (size.height > Configuration.screen_size.height && Configuration.screen_size.height > 0) {
713 size.height = Configuration.screen_size.height;
714 }
715
716 if (!g_man_built) {
717
718 g_man.display();
719
720 // 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).
721 g_man.setLocation(bounds.x, bounds.y);
722 g_man.setVisible(true);
723
724 // After the window has been made visible, check that it is in the correct place
725 // sometimes java places a window not in the correct place,
726 // but with an offset. If so, we work out what the offset is
727 // and change the desired location to take that into account
728 Point location = g_man.getLocation();
729 int x_offset = bounds.x - location.x;
730 int y_offset = bounds.y - location.y;
731 // If not, offset the window to move it into the correct location
732 if (x_offset > 0 || y_offset > 0) {
733 ///ystem.err.println("changing the location to "+(bounds.x + x_offset)+" "+ (bounds.y + y_offset));
734 g_man.setLocation(bounds.x + x_offset, bounds.y + y_offset);
735 }
736
737 // 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.
738 g_man.afterDisplay();
739 g_man_built = true;
740 }
741 else {
742 g_man.setVisible(true);
743 }
744
745 // Get a list of the core Greenstone classifiers and plugins
746 /*Classifiers.loadClassifiersList(null);
747 Plugins.loadPluginsList(null);
748
749 // If using a remote Greenstone we need to download the collection configurations now
750 if (Gatherer.isGsdlRemote) {
751 if (remoteGreenstoneServer.downloadCollectionConfigurations().equals("")) {
752 // !! Something went wrong downloading the collection configurations
753 System.err.println("Error: Could not download collection configurations.");
754 System.exit(0);
755 }
756 }*/
757 g_man.setName("GUIManager"); // g_man has a name "frame0" assigned automatically
758 // setNamesRecursively won't assign a name if one is already set
759 // Setting explicitly since GUIManager is preferred for its name
760 GUIManager.setNamesRecursively("", g_man);
761 GUIManager.printComponentNames(g_man, "");
762
763 // If there was a collection left open last time, reopen it
764 if (open_collection_file_path == null || new File(Gatherer.open_collection_file_path).isDirectory()) {
765
766 //the menu bar items, file and edit, are disabled from the moment of their creation. if there is no left-over collection from the last session, enable them; otherwise it's disabled until the collection finishes loading. They will be enabled in collectionManager.java
767 setMenuBarEnabled(true);
768 } else {
769
770 // If there was a collection left open last time, reopen it
771 c_man.openCollectionFromLastTime();
772 }
773 }
774
775 public static void setMenuBarEnabled(boolean enabled) {
776 g_man.menu_bar.file.setEnabled(enabled);
777 g_man.menu_bar.edit.setEnabled(enabled);
778 }
779
780 /** Exits the Gatherer after ensuring that things needing saving are saved.
781 * @see java.io.FileOutputStream
782 * @see java.io.PrintStream
783 * @see java.lang.Exception
784 * @see javax.swing.JOptionPane
785 * @see org.greenstone.gatherer.Configuration
786 * @see org.greenstone.gatherer.collection.CollectionManager
787 * @see org.greenstone.gatherer.gui.GUIManager
788 */
789 static public void exit(int new_exit_status)
790 {
791 DebugStream.println("In Gatherer.exit()...");
792 exit = true;
793 if (new_exit_status != 0) {
794 // default exit_status is already 0
795 // only remember a new exit status if it is non-trivial
796 exit_status = new_exit_status;
797 }
798
799 // Save the file associations
800 if (assoc_man != null) {
801 assoc_man.save();
802 assoc_man = null;
803 }
804
805 // Get the gui to deallocate
806 if(g_man != null) {
807 g_man.destroy();
808 g_man_built = false;
809 }
810
811 // Flush debug
812 DebugStream.closeDebugStream();
813
814 // If we started a server, we should try to stop it.
815 if (LocalLibraryServer.isRunning() == true) {
816 LocalLibraryServer.stop();
817 }
818
819 // If we're using a remote Greenstone server we need to make sure that all jobs have completed first
820 if (isGsdlRemote) {
821 remoteGreenstoneServer.exit();
822 } else if (GS3) { // stop the local tomcat web server when running GS3
823 // can't call ant stop from its own thread - what if GLI has exited by then?
824 // issue call to ant stop from the main GLI thread
825 //GS3ServerThread thread = new GS3ServerThread(Configuration.gsdl_path, "stop");
826 //thread.start();
827 if(!GS3ServerThread.wasServerLaunchedOutsideGLI()) {
828 GS3ServerThread.stopServer();
829 } else {
830 System.err.println("@@@ Tomcat was launched outside GLI. Leaving it running...");
831 }
832
833 }
834
835 // Make sure we haven't started up any processes that are still running
836 if (apps.size() == 0) {
837 // If we're running as an applet we don't actually quit (just hide the main GLI window)
838 if (!Gatherer.isApplet) {
839 // This is the end...
840 System.exit(exit_status);
841 }
842 }
843 else {
844 JOptionPane.showMessageDialog(g_man, Dictionary.get("General.Outstanding_Processes"), Dictionary.get("General.Outstanding_Processes_Title"), JOptionPane.ERROR_MESSAGE);
845 g_man.setVisible(false);
846 }
847 }
848
849 static public void exit()
850 {
851 exit(0);
852 }
853
854 /** Returns the path of the current collect directory. */
855 static public String getCollectDirectoryPath()
856 {
857 if (non_standard_collect_directory_path != null) {
858 return non_standard_collect_directory_path;
859 }
860
861 return getDefaultGSCollectDirectoryPath(true); // file separator appended
862
863 }
864
865 // if we need to know whether the local server we are running is server.exe vs apache web server
866 static public boolean isPersistentServer() {
867 return (!isGsdlRemote && LocalLibraryServer.isPersistentServer());
868 }
869
870 /** Returns the path of the Greenstone "collect" directory. */
871 static public String getDefaultGSCollectDirectoryPath(boolean appendSeparator) {
872 String colDir;
873 if (GS3) {
874 colDir = getSitesDirectoryPath() + Configuration.site_name + File.separator + "collect";
875 }
876 else {
877 colDir = Configuration.gsdl_path + "collect";
878 }
879
880 if(appendSeparator) {
881 colDir += File.separator;
882 }
883 return colDir;
884 }
885
886 /** Returns the path of the GLI directory. */
887 static public String getGLIDirectoryPath()
888 {
889 return gli_directory_path;
890 }
891
892
893 /** Returns the path of the GLI "metadata" directory. */
894 static public String getGLIMetadataDirectoryPath()
895 {
896 return getGLIDirectoryPath() + "metadata" + File.separator;
897 }
898
899
900 /** Returns the path of the GLI user directory. */
901 static public String getGLIUserDirectoryPath()
902 {
903 return gli_user_directory_path;
904 }
905
906
907 /** Returns the path of the GLI user "cache" directory. */
908 static public String getGLIUserCacheDirectoryPath()
909 {
910 return getGLIUserDirectoryPath() + "cache" + File.separator;
911 }
912
913
914 /** Returns the path of the GLI user "log" directory. */
915 static public String getGLIUserLogDirectoryPath()
916 {
917 return getGLIUserDirectoryPath() + "log" + File.separator;
918 }
919
920
921 static public String getSitesDirectoryPath()
922 {
923 return Configuration.gsdl3_path + "sites" + File.separator;
924 }
925
926
927 static public void setCollectDirectoryPath(String collect_directory_path)
928 {
929 non_standard_collect_directory_path = collect_directory_path;
930 if (!non_standard_collect_directory_path.endsWith(File.separator)) {
931 non_standard_collect_directory_path = non_standard_collect_directory_path + File.separator;
932 }
933 }
934
935
936 static public void setGLIDirectoryPath(String gli_directory_path_arg)
937 {
938 gli_directory_path = gli_directory_path_arg;
939 }
940
941
942 static public void setGLIUserDirectoryPath(String gli_user_directory_path_arg)
943 {
944 gli_user_directory_path = gli_user_directory_path_arg;
945
946 // Ensure the GLI user directory exists
947 File gli_user_directory = new File(gli_user_directory_path);
948 if (!gli_user_directory.exists() && !gli_user_directory.mkdirs()) {
949 System.err.println("Error: Unable to make directory: " + gli_user_directory);
950 }
951 }
952
953
954 public static void initCollectDirectoryPath() {
955 String defaultColdir = getDefaultGSCollectDirectoryPath(false); // no file separator at end
956 String coldir = defaultColdir;
957 // If local GS and opening a collection outside the standard GS collect folder,
958 // need to open the non-standard collect folder that the collection resides in
959 if (!isGsdlRemote
960 && !open_collection_file_path.startsWith(defaultColdir))
961 {
962 File collectFolder = null;
963
964 if(!open_collection_file_path.equals("")) {
965 if(!open_collection_file_path.endsWith("gli.col")) { // then it's a collect folder
966 collectFolder = new File(open_collection_file_path);
967 } else {
968 // the filepath is a gli.col file. To get the collect folder: the 1st level up
969 // is the collection folder, 2 two levels up is the containing collect folder
970 collectFolder = new File(open_collection_file_path).getParentFile().getParentFile();
971 }
972
973 // Need to deal with colgroups as well: while there's an etc/collect.cfg
974 // in the current collectFolder, move one level up
975 String cfg_file = (Gatherer.GS3)? Utility.CONFIG_GS3_FILE : Utility.CONFIG_FILE;
976 if(new File(collectFolder.getAbsolutePath()+File.separator+cfg_file).exists()) { // colgroup
977 collectFolder = collectFolder.getParentFile();
978 }
979
980 // Inform the user that their collecthome is non-standard (not inside GS installation)
981 nonStandardCollectHomeMessage(collectFolder.getAbsolutePath(), defaultColdir); // display message
982 }
983
984 if(collectFolder == null || !collectFolder.exists()) {
985 // if GLI config file specified no collectDir (open_collection_file_path is "")
986 // OR if dealing with a local server but the collectdir no longer exists,
987 // use the default greenstone collect directory, and write that to affected files
988
989 open_collection_file_path = defaultColdir; // default GS collect dir
990 // Configuration.setString("general.open_collection"+Configuration.gliPropertyNameSuffix(), true, "");
991 } else { // use the coldir value specified in the flags to GLI or from the last GLI session
992 coldir = collectFolder.getAbsolutePath();
993 }
994 // set it as the current folder
995 setCollectDirectoryPath(coldir); // will ensure the required file separator at end
996 }
997
998 if(!isGsdlRemote) {
999 // LocalLibraryServer would already have set glisite.cfg to the correct collecthome for server.exe
1000 // Here we set collecthome in gsdl(3)site.cfg for the GS2 apache web server and GS3 tomcat server
1001 String gsdlsitecfg = getGsdlSiteConfigFile();
1002 // update the gsdlsite config file and store the old value for use when we exit GLI
1003 if(coldir.equals(defaultColdir)) {
1004 gsdlsite_collecthome = Utility.updatePropertyConfigFile(
1005 gsdlsitecfg, "collecthome", null);
1006 } else {
1007 gsdlsite_collecthome = Utility.updatePropertyConfigFile(
1008 gsdlsitecfg, "collecthome", "\""+coldir+"\""); // no file separator
1009 // if gsdlsite.cfg does not exist (if using server.exe for instance), the above method will just return
1010 }
1011 }
1012 }
1013
1014 /** depending on the version of GS being run, return the path to the current GS' installation's gsdl(3)site.cfg */
1015 public static String getGsdlSiteConfigFile() {
1016 if(Gatherer.GS3) { // web/WEB-INF/cgi/gsdl3site.cfg
1017 return Configuration.gsdl3_path + File.separator + "WEB-INF"
1018 + File.separator + "cgi" + File.separator + "gsdl3site.cfg";
1019 } else { // cgi-bin/gsdlsite.cfg
1020 String gsdlarch = System.getenv("GSDLARCH");
1021 if(gsdlarch == null) {
1022 gsdlarch = "";
1023 }
1024 return Configuration.gsdl_path /* + File.separator */
1025 + "cgi-bin" + File.separator + client_operating_system+gsdlarch + File.separator + "gsdlsite.cfg";
1026 }
1027 }
1028
1029 public static void collectDirectoryHasChanged(
1030 String oldCollectPath, String newCollectPath, final Component container)
1031 {
1032 if(oldCollectPath.equals(newCollectPath)) {
1033 return; // nothing to be done
1034 }
1035
1036 // Will use a busy cursor if the process of changing the collect directory takes more
1037 // than half a second/500ms. See http://www.catalysoft.com/articles/busyCursor.html
1038 Cursor originalCursor = container.getCursor();
1039 java.util.TimerTask timerTask = new java.util.TimerTask() {
1040 public void run() {
1041 // set the cursor on the container:
1042 container.setCursor(new Cursor(Cursor.WAIT_CURSOR));
1043 }
1044 };
1045 java.util.Timer timer = new java.util.Timer();
1046
1047 try {
1048 timer.schedule(timerTask, 500);
1049
1050 // first save any open collection in the old location, then close it
1051 if(Gatherer.c_man.getCollection() != null) {
1052 Gatherer.g_man.saveThenCloseCurrentCollection(); // close the current collection first
1053 }
1054
1055 // change to new collect path
1056 if(newCollectPath.equals(getDefaultGSCollectDirectoryPath(true))) {
1057 Configuration.setString("general.open_collection"+Configuration.gliPropertyNameSuffix(),
1058 true, "");
1059 } else {
1060 Configuration.setString("general.open_collection"+Configuration.gliPropertyNameSuffix(),
1061 true, newCollectPath);
1062 }
1063 Gatherer.setCollectDirectoryPath(newCollectPath);
1064
1065
1066 // refresh the Documents in Greenstone Collections
1067 //WorkspaceTreeModel.refreshGreenstoneCollectionsNode();
1068 Gatherer.g_man.refreshWorkspaceTreeGreenstoneCollections();
1069
1070 // The web server needs to be told where a new (non-standard) collecthome home is.
1071 // The web server reads collecthome from cgi-bin/<OS>/gsdlsite.cfg, where the property
1072 // collecthome can be specified if a non-standard collecthome is to be used. If no
1073 // such property is specified in the file, then it assumes the standard GS collecthome.
1074 // This method does nothing for a remote Greenstone.
1075 if(Gatherer.isGsdlRemote) {
1076 return;
1077 }
1078
1079 // non-destructive update of gsdl(3)site.cfg (comments preserved)
1080 String collectDir = Gatherer.getCollectDirectoryPath();
1081 //collectDir = "\"" + collectDir.substring(0, collectDir.length()-1) + "\""; // remove file separator at end
1082 collectDir = collectDir.substring(0, collectDir.length()-1); // remove file separator at end
1083 if(collectDir != null) {
1084 collectDir = "\""+collectDir+"\"";
1085 }
1086 Utility.updatePropertyConfigFile(getGsdlSiteConfigFile(), "collecthome", collectDir);
1087 // if gsdlsite.cfg does not exist (if using server.exe for instance), the above method will just return
1088
1089 if(!Gatherer.GS3 && Gatherer.isLocalLibrary) {
1090 // for Images in the collection to work, the apache web server
1091 // configuration's COLLECTHOME should be updated on collectdir change.
1092 // Does nothing for server.exe at the moment
1093
1094 LocalLibraryServer.reconfigure();
1095 }
1096 } finally { // Note try-finally section without catch:
1097 // "Java's finally clause is guaranteed to be executed even when
1098 // an exception is thrown and not caught in the current scope."
1099 // See http://www.catalysoft.com/articles/busyCursor.html
1100 // the following code fragment is guaranteed to restore the original
1101 // cursor now the custom actionPerformed() processing is complete, regardless
1102 // of whether the processing method terminates normally or throws an exception
1103 // and regardless of where in the call stack the exception is caught.
1104
1105 timer.cancel();
1106 container.setCursor(originalCursor);
1107 }
1108 }
1109
1110
1111 static public void refresh(int refresh_reason)
1112 {
1113 if (g_man != null) {
1114
1115 g_man.refresh(refresh_reason, c_man.ready());
1116 }
1117
1118 // Now is a good time to force a garbage collect
1119 System.gc();
1120 }
1121
1122
1123 // used to send reload coll messages to the tomcat server
1124 static public void configGS3Server(String site, String command) {
1125
1126 // Do not do configGS3Server for a GS3 solr collection. Not at present at least, when we're stopping
1127 // and starting the GS3 server from GLI for solr cols, since this function clears the solr.xml file.
1128 /*if(Gatherer.c_man.isSolrCollection()) {
1129 return;
1130 }*/
1131
1132 if (Configuration.library_url == null){
1133 System.out.println("Error: you have not provided the Greenstone Library address.");
1134 return;
1135
1136 }
1137
1138 try {
1139 // need to add the servlet name to the exec address
1140 String raw_url = Configuration.library_url.toString() + Configuration.getServletPath() + command;
1141 URL url = new URL(raw_url);
1142 DebugStream.println("Action: " + raw_url);
1143 HttpURLConnection library_connection = (HttpURLConnection) url.openConnection();
1144 int response_code = library_connection.getResponseCode();
1145 if(HttpURLConnection.HTTP_OK <= response_code && response_code < HttpURLConnection.HTTP_MULT_CHOICE) {
1146 DebugStream.println("200 - Complete.");
1147 }
1148 else {
1149 DebugStream.println("404 - Failed.");
1150 }
1151 url = null;
1152 }
1153 catch(java.net.ConnectException connectException) {
1154 JOptionPane.showMessageDialog(g_man, Dictionary.get("Preferences.Connection.Library_Path_Connection_Failure", Configuration.library_url.toString()), Dictionary.get("General.Warning"), JOptionPane.WARNING_MESSAGE);
1155 DebugStream.println(connectException.getMessage());
1156 }
1157 catch (Exception exception) {
1158 DebugStream.printStackTrace(exception);
1159 }
1160 }
1161
1162
1163 /** Used to 'spawn' a new child application when a file is double clicked.
1164 * @param file The file to open
1165 * @see org.greenstone.gatherer.Gatherer.ExternalApplication
1166 */
1167 static public void spawnApplication(File file) {
1168 String [] commands = assoc_man.getCommand(file);
1169 if(commands != null) {
1170 ExternalApplication app = new ExternalApplication(commands);
1171 apps.add(app);
1172 app.start();
1173 }
1174 else {
1175 ///ystem.err.println("No open command available.");
1176 }
1177 }
1178
1179
1180 static public void spawnApplication(String command)
1181 {
1182 ExternalApplication app = new ExternalApplication(command);
1183 apps.add(app);
1184 app.start();
1185 }
1186
1187 static public void spawnApplication(String command, String ID)
1188 {
1189 ExternalApplication app = new ExternalApplication(command, ID);
1190 apps.add(app);
1191 app.start();
1192 }
1193
1194 static public void spawnApplication(String[] commands, String ID)
1195 {
1196 ExternalApplication app = new ExternalApplication(commands, ID);
1197 apps.add(app);
1198 app.start();
1199 }
1200
1201 static public void terminateApplication(String ID) {
1202 for(int i = 0; i < apps.size(); i++) {
1203 ExternalApplication app = (ExternalApplication)apps.get(i);
1204 if(app.getID() != null && app.getID().equals(ID)) {
1205 app.stopExternalApplication();
1206 apps.remove(app);
1207 }
1208 }
1209 }
1210
1211
1212 /** Used to 'spawn' a new browser application or reset an existing one when the preview button is clicked
1213 * @param url The url to open the browser at
1214 * @see org.greenstone.gatherer.Gatherer.BrowserApplication
1215 */
1216 static public void spawnBrowser(String url) {
1217 String command = assoc_man.getBrowserCommand(url);
1218 if (command != null) {
1219 BrowserApplication app = new BrowserApplication(command, url);
1220 apps.add(app);
1221 app.start();
1222 }
1223 else {
1224 ///ystem.err.println("No browser command available.");
1225 }
1226 }
1227
1228
1229 /** Prints a warning message about a missing library path, which means the final collection cannot be previewed in the Gatherer.
1230 */
1231 static public void missingEXEC() {
1232 WarningDialog dialog;
1233 String configPropertyName = "general.library_url"+Configuration.gliPropertyNameSuffix();
1234
1235 if (GS3) {
1236 // Warning dialog with no cancel button and no "turn off warning" checkbox
1237 dialog = new WarningDialog("warning.MissingEXEC", Dictionary.get("MissingEXEC_GS3.Title"), Dictionary.get("MissingEXEC_GS3.Message"), configPropertyName, false, false);
1238 } else { // local case
1239 dialog = new WarningDialog("warning.MissingEXEC", Dictionary.get("MissingEXEC.Title"), Dictionary.get("MissingEXEC.Message"), configPropertyName, false);
1240 }
1241
1242 JTextField field = new URLField.Text(Configuration.getColor("coloring.editable_foreground", false), Configuration.getColor("coloring.editable_background", false));
1243
1244 // Set the default library URL to the tomcat server and port number
1245 // specified in the build.properties located in the gsdl3_src_path
1246 if (GS3) {
1247 String host = "localhost";
1248 String port = "8383";
1249
1250 File buildPropsFile = new File(Configuration.gsdl3_src_path + File.separator + "build.properties");
1251 if(buildPropsFile.exists()) {
1252 Properties props = new Properties();
1253 try{
1254 props.load(new FileInputStream(buildPropsFile));
1255 host = props.getProperty("tomcat.server", host);
1256 port = props.getProperty("tomcat.port", port);
1257 }catch(Exception e){
1258 DebugStream.println("Could not load build.properties file");
1259 System.err.println("Could not load build.properties file");
1260 }
1261 props = null;
1262 }
1263 String defaultURL = "http://"+host+":"+port+"/"+"greenstone3";
1264 field.setText(defaultURL);
1265 field.selectAll();
1266 }
1267 dialog.setValueField(field);
1268 dialog.display();
1269 dialog.dispose();
1270 dialog = null;
1271
1272 String library_url_string = Configuration.getString(configPropertyName, true);
1273 if (!library_url_string.equals("")) {
1274 try {
1275 // WarningDialog does not allow invalid URLs, so the following is ignored:
1276 // make sure the URL the user provided contains the http:// prefix
1277 // and then save the corrected URL
1278 if(!library_url_string.startsWith("http://")
1279 && !library_url_string.startsWith("https://")) {
1280 library_url_string = "http://"+library_url_string;
1281 Configuration.setString(configPropertyName, true, configPropertyName);
1282 }
1283 Configuration.library_url = new URL(library_url_string);
1284 }
1285 catch (MalformedURLException exception) {
1286 DebugStream.printStackTrace(exception);
1287 }
1288 }
1289 }
1290
1291
1292
1293 /** Prints a warning message about a missing library path, which means the final collection cannot be previewed in the Gatherer.
1294 */
1295 static private void popupFedoraInfo() {
1296
1297 FedoraLogin dialog = new FedoraLogin("Fedora Login", false);
1298
1299 if (Configuration.library_url == null) {
1300
1301 String library_url_string = dialog.getLibraryURL();
1302 if (!library_url_string.equals("")) {
1303 try {
1304 Configuration.library_url = new URL(library_url_string);
1305 }
1306 catch (MalformedURLException exception) {
1307 DebugStream.printStackTrace(exception);
1308 }
1309 }
1310 }
1311
1312 boolean showLogin = true;
1313 do {
1314 if(!dialog.loginRequested()) { // user pressed cancel to exit the FedoraLogin dialog
1315 System.exit(0);
1316 } else {
1317 showLogin = dialog.loginRequested();
1318 String hostname = dialog.getHostname();
1319 String port = dialog.getPort();
1320 String username = dialog.getUsername();
1321 String password = dialog.getPassword();
1322 String protocol = dialog.getProtocol();
1323
1324 Configuration.fedora_info.setHostname(hostname);
1325 Configuration.fedora_info.setPort(port);
1326 Configuration.fedora_info.setUsername(username);
1327 Configuration.fedora_info.setPassword(password);
1328 Configuration.fedora_info.setProtocol(protocol);
1329
1330 String ping_url_str = protocol + "://" + hostname + ":" + port + "/fedora";
1331 String login_str = username + ":" + password;
1332
1333 String login_encoding = Base64.encodeBytes(login_str.getBytes());
1334
1335 try {
1336 URL ping_url = new URL(ping_url_str);
1337 URLConnection uc = ping_url.openConnection();
1338 uc.setRequestProperty ("Authorization", "Basic " + login_encoding);
1339 // Attempt to access some content ...
1340 InputStream content = (InputStream)uc.getInputStream();
1341
1342 // if no exception occurred in the above, we would have come here:
1343 showLogin = false;
1344 dialog.dispose();
1345 }
1346 catch (Exception exception) {
1347 // TODO: move into dictionary
1348 String[] errorMessage = {"Failed to connect to the Fedora server.", "It might not be running, or",
1349 "incorrect username and/or password."};
1350 dialog.setErrorMessage(errorMessage);
1351 //DebugStream.printStackTrace(exception);
1352 // exception occurred, show the dialog again (do this after printing to
1353 // debugStream, else the above does not get done for some reason).
1354 dialog.setVisible(true);
1355 }
1356 }
1357 } while(showLogin);
1358
1359 dialog = null; // no more need of the dialog
1360
1361 // Now we are connected.
1362 }
1363
1364
1365
1366 static private void requestGLIServerURL()
1367 {
1368 WarningDialog dialog;
1369 String[] defaultURLs = {
1370 "http://localhost:8080/greenstone3/cgi-bin/gliserver.pl",
1371 "http://localhost:8080/gsdl/cgi-bin/gliserver.pl"
1372 };
1373
1374 // Warning dialog with no cancel button and no "turn off warning" checkbox
1375 // (since user-input of the gliserver script is mandatory)
1376 dialog = new WarningDialog("warning.MissingGLIServer", Dictionary.get("MissingGLIServer.Title"), Dictionary.get("MissingGLIServer.Message"), "general.gliserver_url", false, false);
1377
1378 dialog.setValueField(new URLField.DropDown(Configuration.getColor("coloring.editable_foreground", false),
1379 Configuration.getColor("coloring.editable_background", false),
1380 defaultURLs, "general.gliserver_url",
1381 "general.open_collection"+Configuration.gliPropertyNameSuffix(),
1382 "gliserver.pl"));
1383
1384 if (Gatherer.default_gliserver_url!=null){
1385 dialog.setValueField(Gatherer.default_gliserver_url.toString());
1386 }
1387
1388 // A WarningDialog cannot always be made to respond (let alone to exit the program) on close. We
1389 // handle the response of this particular WarningDialog here: a URL for gliserver.pl is a crucial
1390 // piece of user-provided data. Therefore, if no URL was entered for gliserver.pl, it'll exit safely.
1391 dialog.addWindowListener(new WindowAdapter() {
1392 public void windowClosing(WindowEvent e) {
1393 Gatherer.exit();
1394 }
1395 });
1396
1397 dialog.display();
1398 dialog.dispose();
1399 dialog = null;
1400
1401
1402 String gliserver_url_string = Configuration.getString("general.gliserver_url", true);
1403 if (!gliserver_url_string.equals("")) {
1404 try {
1405 Configuration.gliserver_url = new URL(gliserver_url_string);
1406 Configuration.setString("general.gliserver_url", true, gliserver_url_string);
1407 }
1408 catch (MalformedURLException exception) {
1409 DebugStream.printStackTrace(exception);
1410 }
1411 }
1412 }
1413
1414
1415 /** Prints a warning message about a missing GSDL path, which although not fatal pretty much ensures nothing will work properly in the GLI.
1416 */
1417 static private void missingGSDL() {
1418 WarningDialog dialog = new WarningDialog("warning.MissingGSDL", Dictionary.get("MissingGSDL.Title"), Dictionary.get("MissingGSDL.Message"), null, false);
1419 dialog.display();
1420 dialog.dispose();
1421 dialog = null;
1422 }
1423
1424 /** Prints a warning message about missing a valid ImageMagick path, which although not fatal means building image collections won't work */
1425 static private void missingImageMagick() {
1426 WarningDialog dialog = new WarningDialog("warning.MissingImageMagick", Dictionary.get("MissingImageMagick.Title"), Dictionary.get("MissingImageMagick.Message"), null, false);
1427 dialog.display();
1428 dialog.dispose();
1429 dialog = null;
1430 }
1431
1432 /** Prints a message informing the user where they can get PDFBox from to process PDF files of v1.5 and greater */
1433 static private void missingPDFBox(String zipExtension, String extFolder) {
1434 // point to the correct version of the PDFBox extension for this Greenstone release
1435 String releaseTag = "";
1436 if(!PROGRAM_VERSION.equals("trunk")) { // assume it's a release version
1437 releaseTag = "main/tags/"+PROGRAM_VERSION+"/";
1438 }
1439
1440 WarningDialog dialog = new WarningDialog("warning.MissingPDFBox", Dictionary.get("MissingPDFBox.Title"), Dictionary.get("MissingPDFBox.Message", new String[]{releaseTag, zipExtension, extFolder}), null, false);
1441 dialog.display();
1442 dialog.dispose();
1443 dialog = null;
1444 }
1445
1446 /** Prints a warning message about missing a valid PERL path, which although not fatal pretty much ensures no collection creation/building will work properly in the GLI. */
1447 static private void missingPERL() {
1448 WarningDialog dialog = new WarningDialog("warning.MissingPERL", Dictionary.get("MissingPERL.Title"), Dictionary.get("MissingPERL.Message"), null, false);
1449 dialog.display();
1450 dialog.dispose();
1451 dialog = null;
1452 }
1453
1454 /** Prints a message informing the user that their collecthome is non-standard (not inside GS installation) */
1455 static private void nonStandardCollectHomeMessage(String open_collection_file_path, String defaultColDir) {
1456 WarningDialog dialog = new WarningDialog("warning.NonStandardCollectHome", Dictionary.get("NonStandardCollectHome.Title"), Dictionary.get("NonStandardCollectHome.Message", new String[]{open_collection_file_path, defaultColDir}), null, false);
1457 dialog.display();
1458 dialog.dispose();
1459 dialog = null;
1460 }
1461
1462 /** Prints a warning message about the OS not supporting multiple filename encodings. */
1463 static private void multipleFilenameEncodingsNotSupported() {
1464 WarningDialog dialog = new WarningDialog("warning.NoEncodingSupport",
1465 Dictionary.get("NoEncodingSupport.Title"),
1466 Dictionary.get("NoEncodingSupport.Message"), null, false);
1467 dialog.display();
1468 dialog.dispose();
1469 dialog = null;
1470 }
1471
1472 /** Sets up the proxy connection by setting JVM Environment flags and creating a new Authenticator.
1473 * @see java.lang.Exception
1474 * @see java.lang.System
1475 * @see java.net.Authenticator
1476 * @see org.greenstone.gatherer.Configuration
1477 * @see org.greenstone.gatherer.GAuthenticator
1478 */
1479 static public void setProxy() {
1480 try {// Can throw several exceptions
1481 boolean use_proxy = Configuration.get("general.use_proxy", true);
1482
1483 setProxyForProtocol("http", use_proxy);
1484 setProxyForProtocol("https", use_proxy);
1485 setProxyForProtocol("ftp", use_proxy);
1486
1487 } catch (Exception error) {
1488 DebugStream.println("Error in Gatherer.initProxy(): " + error);
1489 DebugStream.printStackTrace(error);
1490 }
1491 }
1492
1493 private static void setProxyForProtocol(String protocol, boolean use_proxy) throws Exception {
1494 // These are Java properties, therefore see
1495 // https://docs.oracle.com/javase/7/docs/api/java/net/doc-files/net-properties.html
1496 // https://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html
1497 // https://stackoverflow.com/questions/14243590/proxy-settings-in-java
1498
1499 // Just need to warn the user that we're overriding proxy settings using custom defined proxy settings
1500 // and returning to using the original proxy settings when custom defined proxy settings are turned off
1501 // We don't actually need to store/restore the original settings, as Java system vars like http.proxyHost
1502 // http.proxyPort don't seem to be set even if on GLI startup the env vars like http(s)_proxy were already set.
1503
1504
1505 if(use_proxy) {
1506 if(System.getenv(protocol+"_proxy") != null) {
1507 System.err.println("Overriding original "+protocol+" proxy settings");
1508 }
1509
1510 // set the custom proxy defined through GLI for this protocol
1511 System.setProperty(protocol+".proxyHost", Configuration.getString("general."+protocol.toUpperCase()+"_proxy_host", true));
1512 System.setProperty(protocol+".proxyPort", Configuration.getString("general."+protocol.toUpperCase()+"_proxy_port", true));
1513
1514 // Not sure what proxyType is. And proxySet ceased to exist since JDK 6 or before. See links above.
1515 // But we used to set them both for HTTP before, so still doing so for proxyType, since it may or may not exist
1516 // But proxySet doesn't exist anymore, so not continuing with that.
1517 if(protocol.equals("http")) {
1518 System.setProperty(protocol+".proxyType", "4");
1519 //System.setProperty(protocol+".proxySet", "true");
1520 }
1521
1522 }
1523
1524 else { // use_proxy=false, not using proxy defined through GLI, so
1525 // either unset proxy vars for this protocol, or restore any original settings for them
1526
1527 if(System.getenv(protocol+"_proxy") != null) {
1528 System.err.println("Restoring original "+protocol+" proxy settings");
1529 }
1530
1531 System.setProperty(protocol+".proxyHost", "");
1532 System.setProperty(protocol+".proxyPort", "");
1533 //if(protocol.equals("http")) {
1534 //System.setProperty(protocol+".proxySet", "false");
1535 //}
1536 }
1537 }
1538
1539
1540 /** 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 voluntarily 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. */
1541 static private class ExternalApplication
1542 extends Thread {
1543 private SafeProcess process = null;
1544 /** The initial command string given to this sub-process. */
1545 private String command = null;
1546 private String[] commands = null;
1547
1548 private String ID = null;
1549
1550 /** Constructor.
1551 * @param command The initial command <strong>String</strong>.
1552 */
1553 public ExternalApplication(String command) {
1554 this.command = command;
1555 }
1556
1557 public ExternalApplication(String[] commands) {
1558 this.commands = commands;
1559 }
1560
1561 public ExternalApplication(String command, String ID) {
1562 this.command = command;
1563 this.ID = ID;
1564 }
1565
1566 public ExternalApplication(String[] commands, String ID) {
1567 this.commands = commands;
1568 this.ID = ID;
1569 }
1570
1571 public String getID() {
1572 return ID;
1573 }
1574
1575 /** We start the child process inside a new thread so it doesn't block the rest of Gatherer.
1576 * @see java.lang.Exception
1577 * @see java.lang.Process
1578 * @see java.lang.Runtime
1579 * @see java.lang.System
1580 * @see java.util.Vector
1581 */
1582 public void run() {
1583 // Call an external process using the args.
1584 try {
1585 if(commands != null) {
1586 StringBuffer whole_command = new StringBuffer();
1587 for(int i = 0; i < commands.length; i++) {
1588 // get rid of any quotes around parameters in file associations
1589 if(commands[i].startsWith("\"") || commands[i].startsWith("\'")) {
1590 commands[i] = commands[i].substring(1);
1591 }
1592 if(commands[i].endsWith("\"") || commands[i].endsWith("\'")) {
1593 commands[i] = commands[i].substring(0, commands[i].length()-1);
1594 }
1595
1596 if (i>0) {
1597 whole_command.append(" ");
1598 }
1599 whole_command.append(commands[i]);
1600 }
1601 DebugStream.println("Running " + whole_command.toString());
1602 process = new SafeProcess(commands);
1603 }
1604 else {
1605 DebugStream.println("Running " + command);
1606 process = new SafeProcess(command);
1607 }
1608 process.runProcess();
1609 }
1610 catch (Exception exception) {
1611 DebugStream.printStackTrace(exception);
1612 }
1613 // Remove ourself from Gatherer list of threads.
1614 apps.remove(this);
1615 // Call exit if we were the last outstanding child process thread.
1616 if (apps.size() == 0 && exit == true) {
1617 // In my opinion (DB) there is no need to exit here,
1618 // the 'run' method ending naturally brings this
1619 // thread to an end. In fact it is potentially
1620 // dangerous to exit here, as the main thread in the
1621 // Gatherer class may be stopped prematurely. As it so
1622 // happens the point at which the ExternalApplication thread
1623 // is asked to stop (Back in the main Gatherer thread) is after
1624 // various configuration files have been saved.
1625 //
1626 // A similar argument holds for BrowserApplication thread below.
1627 System.exit(exit_status);
1628 }
1629 }
1630 public void stopExternalApplication() {
1631 if(process != null) {
1632 SafeProcess.log("*** stopExternalApplication called.");
1633 process.cancelRunningProcess();
1634 }
1635 }
1636 }
1637 /** 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. */
1638 static private class BrowserApplication
1639 extends Thread {
1640 private SafeProcess process = null;
1641 /** The initial command string given to this sub-process. */
1642 private String command = null;
1643 private String url = null;
1644 private String[] commands = null;
1645
1646 public BrowserApplication(String command, String url) {
1647 StringTokenizer st = new StringTokenizer(command);
1648 int num_tokens = st.countTokens();
1649 this.commands = new String [num_tokens];
1650 int i=0;
1651 while (st.hasMoreTokens()) {
1652 commands[i] = st.nextToken();
1653 i++;
1654 }
1655 //this.commands = commands;
1656 this.url = url;
1657 }
1658 /** We start the child process inside a new thread so it doesn't block the rest of Gatherer.
1659 * @see java.lang.Exception
1660 * @see java.lang.Process
1661 * @see java.lang.Runtime
1662 * @see java.lang.System
1663 * @see java.util.Vector
1664 */
1665 public void run() {
1666 // Call an external process using the args.
1667 if(commands == null) {
1668 apps.remove(this);
1669 return;
1670 }
1671 try {
1672 String prog_name = commands[0];
1673 String lower_name = prog_name.toLowerCase();
1674 if (lower_name.indexOf("mozilla") != -1 || lower_name.indexOf("netscape") != -1) {
1675 DebugStream.println("found mozilla or netscape, trying remote it");
1676 // mozilla and netscape, try using a remote command to get things in the same window
1677 String [] new_commands = new String[] {prog_name, "-raise", "-remote", "openURL("+url+",new-tab)"};
1678 printArray(new_commands);
1679
1680 process = new SafeProcess(new_commands);
1681 int exitCode = process.runProcess();
1682 if (exitCode != 0) { // if Netscape or mozilla was not open
1683 DebugStream.println("couldn't do remote, trying original command");
1684 printArray(commands);
1685 process = null;
1686 process = new SafeProcess(commands); // try the original command
1687 process.runProcess();
1688 }
1689 } else {
1690 // just run what we have been given
1691 StringBuffer whole_command = new StringBuffer();
1692 for(int i = 0; i < commands.length; i++) {
1693 whole_command.append(commands[i]);
1694 whole_command.append(" ");
1695 }
1696 DebugStream.println("Running " + whole_command.toString());
1697 process = new SafeProcess(commands);
1698 process.runProcess();
1699 }
1700 }
1701
1702 catch (Exception exception) {
1703 DebugStream.printStackTrace(exception);
1704 }
1705 // Remove ourself from Gatherer list of threads.
1706 apps.remove(this);
1707 // Call exit if we were the last outstanding child process thread.
1708 if (apps.size() == 0 && exit == true) {
1709 System.exit(exit_status);
1710 }
1711 }
1712 public void printArray(String [] array) {
1713 for(int i = 0; i < array.length; i++) {
1714 DebugStream.print(array[i]+" ");
1715 System.err.println(array[i]+" ");
1716 }
1717 }
1718 public void stopBrowserApplication() {
1719 if(process != null) {
1720 SafeProcess.log("*** stopBrowserApplication called.");
1721 process.cancelRunningProcess();
1722 }
1723 }
1724 }
1725
1726
1727 private class ImageMagickTest
1728 {
1729 public boolean found()
1730 {
1731 // at this stage, GLI has already sourced setup.bash, and the necessary
1732 // env variables will be available to the perl process we're about to launch
1733 boolean found = false;
1734
1735
1736 // run the command `/path/to/perl -S gs-magick.pl identify -version`
1737 ArrayList cmd_list = new ArrayList();
1738 if (!Gatherer.isGsdlRemote) {
1739 if(Configuration.perl_path != null) {
1740 cmd_list.add(Configuration.perl_path);
1741 } else {
1742 System.err.println("***** ImageMagickTest Warning: Perl_path not set, calling 'perl' instead.");
1743 cmd_list.add("perl");
1744 }
1745 cmd_list.add("-S");
1746 }
1747 cmd_list.add("gs-magick.pl");
1748 if(Utility.isWindows()) {
1749 cmd_list.add("identify.exe");
1750 } else {
1751 cmd_list.add("identify");
1752 }
1753 cmd_list.add("-version");
1754
1755 String[] command_parts = (String[]) cmd_list.toArray(new String[0]);
1756
1757 String cmd_str = "";
1758 for(int i = 0; i < command_parts.length; i++) {
1759 cmd_str += command_parts[i] + " ";
1760 }
1761 DebugStream.println("***** Running ImageMagickTest command: " + cmd_str);
1762
1763 SafeProcess image_magick_process = new SafeProcess(command_parts);
1764 int exitValue = image_magick_process.runProcess(); // default process iostream handling
1765 //new way of detection of ImageMagick
1766 // Inspect the standard output of the process and seach for two particular occurrences: Version and ImageMagick.
1767 String output = image_magick_process.getStdOutput().toLowerCase();
1768 if (output.indexOf("version") != -1 || output.indexOf("imagemagick") != -1) {
1769 found = true;
1770 } // else found var remains false
1771
1772 return found;
1773 //return (image_magick_process.exitValue() == 0);
1774
1775 }
1776 }
1777
1778
1779 private class PerlTest
1780 {
1781 private String[] command = new String[2];
1782
1783 public PerlTest()
1784 {
1785 command[0] = (Utility.isWindows() ? Utility.PERL_EXECUTABLE_WINDOWS : Utility.PERL_EXECUTABLE_UNIX);
1786 command[1] = "-version";
1787 }
1788
1789 public boolean found()
1790 {
1791 try {
1792 SafeProcess perl_process = new SafeProcess(command);
1793 int exitValue = perl_process.runBasicProcess();
1794 return (exitValue == 0);
1795 }
1796 catch (Exception exception) {
1797 return false;
1798 }
1799 }
1800
1801 public String toString() {
1802 return command[0];
1803 }
1804 }
1805
1806}
Note: See TracBrowser for help on using the repository browser.