source: gli/trunk/src/org/greenstone/gatherer/greenstone/LocalLibraryServer.java@ 19513

Last change on this file since 19513 was 19513, checked in by ak19, 15 years ago

Reverting to before recent commits for working with independently launched GSI (which wrote out URL_pending to llssite.cfg file)

  • Property svn:keywords set to Author Date Id Revision
File size: 21.3 KB
Line 
1/**
2 *############################################################################
3 * A component of the Greenstone Librarian Interface, part of the Greenstone
4 * digital library suite from the New Zealand Digital Library Project at the
5 * University of Waikato, New Zealand.
6 *
7 * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
8 *
9 * Copyright (C) 2004 New Zealand Digital Library Project
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *############################################################################
25 */
26
27package org.greenstone.gatherer.greenstone;
28
29
30import java.io.*;
31import java.lang.*;
32import java.net.*;
33import java.util.*;
34import javax.swing.*;
35import org.greenstone.gatherer.Configuration;
36import org.greenstone.gatherer.DebugStream;
37import org.greenstone.gatherer.Dictionary;
38import org.greenstone.gatherer.Gatherer;
39import org.greenstone.gatherer.util.PortFinder;
40import org.greenstone.gatherer.util.Utility;
41
42
43public class LocalLibraryServer
44{
45 static final private int WAITING_TIME = 20; // number of seconds to wait for the server to start and stop
46
47 static final private String ADD_COMMAND = "?a=config&cmd=add-collection&c=";
48 static final private String RELEASE_COMMAND = "?a=config&cmd=release-collection&c=";
49 static final private String QUIT_COMMAND = "?a=config&cmd=kill";
50
51 static private LLSSiteConfig llssite_cfg_file = null;
52 static private File local_library_server_file = null;
53
54 static private boolean running = false;
55 static private String ID = "greenstone-server"; // a sort of process ID
56
57 // Need to use sockets to tell the server program to terminate when on Linux
58 // The socket port number that we will use to communicate the termination
59 static private int port;
60
61 // The server is persistent if it does not have to reload all the values
62 // over and over again each time. Tomcat is persistent and fastcgi is too,
63 // but the Apache webserver is not persistent by default.
64 // Change the initialisation of this value depending on whether fastcgi is
65 // on. At the moment, this does not apply to the Linux' local library server.
66 static private boolean isPersistentServer = Utility.isWindows();
67
68 static public void addCollection(String collection_name)
69 {
70 if (isPersistentServer) {
71 config(ADD_COMMAND + collection_name);
72 }
73 }
74
75
76 // Used to send messages to the local library
77 static private void config(String command)
78 {
79 if (!isPersistentServer) {
80 return;
81 }
82 if (Configuration.library_url == null) {
83 System.err.println("Error: Trying to configure local library with null Configuration.library_url!");
84 return;
85 }
86
87 try {
88 URL url = new URL(Configuration.library_url.toString() + command);
89 HttpURLConnection library_connection = (HttpURLConnection) url.openConnection();
90
91 // It's very important that we read the output of the command
92 // This ensures that the command has actually finished
93 // (The response code is returned immediately)
94 InputStream library_is = library_connection.getInputStream();
95 BufferedReader library_in = new BufferedReader(new InputStreamReader(library_is, "UTF-8"));
96 String library_output_line = library_in.readLine();
97 while (library_output_line != null) {
98 DebugStream.println("Local library server output: " + library_output_line);
99 library_output_line = library_in.readLine();
100 }
101 library_in.close();
102
103 int response_code = library_connection.getResponseCode();
104 if (response_code >= HttpURLConnection.HTTP_OK && response_code < HttpURLConnection.HTTP_MULT_CHOICE) {
105 DebugStream.println("200 - Complete.");
106 }
107 else {
108 DebugStream.println("404 - Failed.");
109 }
110 }
111 catch (Exception exception) {
112 DebugStream.printStackTrace(exception);
113 }
114 }
115
116 // Used to send messages to the local server on Linux
117 static private boolean sendMessageToServer(String message) {
118 if(Utility.isWindows()) {
119 return false;
120 }
121
122 if(port == -1) {
123 return false;
124 }
125
126 try {
127 Socket clientSocket = new Socket("localhost", port);
128 Writer writer = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
129 writer.write(message);
130 writer.close();
131 writer = null;
132 } catch (Exception e) {
133 System.err.println("An exception occurred when trying to send the message: " + message
134 + "\nto the LocalLibraryServer.\n" + e);
135 return false;
136 }
137 return true;
138 }
139
140 static public boolean isRunning()
141 {
142 if (!running) return false;
143 llssite_cfg_file.load(true);
144 if (llssite_cfg_file.getURL() == null) return false;
145 return true;
146 }
147
148
149 static public void releaseCollection(String collection_name)
150 {
151 if (isPersistentServer) {
152 config(RELEASE_COMMAND + collection_name);
153 }
154 }
155
156 static public void start(String gsdl_path, String local_library_server_file_path)
157 {
158 // Check the local library server file (server.exe or gs2-server.sh) exists
159 local_library_server_file = new File(local_library_server_file_path);
160
161 if (!local_library_server_file.exists()) {
162 DebugStream.println("No local library at given file path.");
163
164 String defaultServerFilename = Utility.isWindows() ? "server.exe" : "gs2-server.sh";
165 local_library_server_file = new File(gsdl_path + defaultServerFilename);
166 if (!local_library_server_file.exists()) {
167 DebugStream.println("No local library at all.");
168 return;
169 }
170 }
171
172 // In the case of the Local Library Server on Linux, we need to do an extra test:
173 // If GS2 was not configured with --enable-apache-httpd, then there is no apache webserver folder,
174 // even though the gs2-server.sh file would still be there. That means if the folder is absent
175 // we still have no local library server.
176 if(!Utility.isWindows()) {
177 File localLinuxServerFolder = new File(gsdl_path, "apache-httpd");
178 if (!localLinuxServerFolder.exists() && !localLinuxServerFolder.isDirectory()) {
179 DebugStream.println("The web server does not exist at "
180 + localLinuxServerFolder.getAbsolutePath() + "\nNo local library at all. Trying web library");
181 return;
182 }
183 }
184
185 llssite_cfg_file = new LLSSiteConfig(local_library_server_file);
186
187 // Spawn local library server process
188 String local_library_server_command = local_library_server_file.getAbsolutePath() + getExtraLaunchArguments(llssite_cfg_file);
189
190 // Check if the server is already running
191 String url = llssite_cfg_file.getURL();
192 if (url != null) {
193 // If it is already running then set the Greenstone web server address and we're done
194 // E.g. if previously GLI was not properly shut down, the URL property (signifying
195 // the server is still running) would still be in the glisite_cfg file.
196 try {
197 Configuration.library_url = new URL(url);
198 running = true;
199
200 // Run the server interface
201 Gatherer.spawnApplication(local_library_server_command, ID);
202 return;
203 }
204 catch (MalformedURLException exception) {
205 DebugStream.printStackTrace(exception);
206 }
207 }
208
209 // Configure the server for immediate entry
210 //llssite_cfg_file.set();
211
212 // Spawn local library server process
213 Gatherer.spawnApplication(local_library_server_command, ID);
214
215 // Wait until program has started
216 try {
217 testServerRunning(); // will set running = true when the server is up and running successfully
218 } catch (IOException bad_url_connection) {
219 try {
220 // If this fails then we try changing the url to be localhost
221 Configuration.library_url = new URL(llssite_cfg_file.getLocalHostURL());
222 DebugStream.println("Try connecting to server on local host: '" + Configuration.library_url + "'");
223 URLConnection connection = Configuration.library_url.openConnection();
224 connection.getContent();
225 running = true;
226
227 } catch (IOException worse_url_connection) {
228 DebugStream.println("Can't connect to server on either address.");
229 Configuration.library_url = null;
230 running = false;
231 }
232 }
233 }
234
235
236 static public void stop()
237 {
238 if (running == false) {
239 return;
240 }
241
242 // Send the command for it to exit.
243 if (isPersistentServer) {
244 config(QUIT_COMMAND);
245 } else {
246 if(sendMessageToServer("QUIT")) {
247 Gatherer.terminateApplication(ID);
248 } else {
249 System.err.println("Unable to stop the server, since there's no communication port to send the quit msg over."
250 + "\nPlease stop the local Greenstone server manually.");
251 }
252 }
253
254 // Wait until program has stopped, by reloading and checking the URL field
255 llssite_cfg_file.load(false);
256 int attempt_count = 0;
257 while (llssite_cfg_file.getURL() != null) {
258 new OneSecondWait(); // Wait one second (give or take)
259 llssite_cfg_file.load(false);
260 attempt_count++;
261
262 // After waiting for the specified time, ask the user whether they want to wait for that long again
263 if (attempt_count == WAITING_TIME) {
264 int try_again = JOptionPane.showConfirmDialog(Gatherer.g_man, Dictionary.get("Server.QuitTimeOut", Integer.toString(WAITING_TIME)),
265 Dictionary.get("General.Warning"), JOptionPane.YES_NO_OPTION);
266 if (try_again == JOptionPane.NO_OPTION) {
267 return;
268 }
269 attempt_count = 0;
270 }
271 }
272
273 // Restore the llssite_cfg.
274 llssite_cfg_file.restore();
275
276 // If the local server is still running then our changed values will get overwritten.
277 if (llssite_cfg_file.getURL() != null) {
278 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("Server.QuitManual"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
279 }
280
281 running = false;
282 }
283
284 static private String getExtraLaunchArguments(LLSSiteConfig site_cfg_file) {
285 String args = " " + site_cfg_file.getSiteConfigFilename();
286
287 if(Utility.isWindows()) {
288 return args;
289 }
290
291 // Else, when running the Local Library Server on Linux, need to provide a port argument
292 try {
293 PortFinder portFinder = new PortFinder(50100, 100);
294 port = portFinder.findPortInRange(false); // silent mode
295 } catch(Exception e) {
296 System.err.println("Exception when trying to find an available port: " + e);
297 port = -1;
298 }
299
300 return args + " --quitport=" + port;
301 }
302
303
304 // This method first tests whether there is a URL in the llssite_cfg_file
305 // and after that appears, it tests whether the URL is functional.
306 static private void testServerRunning() throws IOException {
307 // Wait until program has started, by reloading and checking the URL field
308 llssite_cfg_file.load(false);
309 int attempt_count = 0;
310 while (llssite_cfg_file.getURL() == null) {
311 new OneSecondWait(); // Wait one second (give or take)
312 llssite_cfg_file.load(false);
313 attempt_count++;
314
315 // After waiting for the specified time, ask the user whether they want to wait for that long again
316 if (attempt_count == WAITING_TIME) {
317 int try_again = JOptionPane.showConfirmDialog(Gatherer.g_man, Dictionary.get("Server.StartUpTimeOut", Integer.toString(WAITING_TIME)),
318 Dictionary.get("General.Warning"), JOptionPane.YES_NO_OPTION);
319 if (try_again == JOptionPane.NO_OPTION) {
320 return;
321 }
322 attempt_count = 0;
323 }
324 }
325
326 // Ta-da. Now the url should be available
327 try {
328 Configuration.library_url = new URL(llssite_cfg_file.getURL());
329 }
330 catch (MalformedURLException exception) {
331 DebugStream.printStackTrace(exception);
332 }
333
334 // A quick test involves opening a connection to get the home page for this collection
335 try {
336 DebugStream.println("Try connecting to server on config url: '" + Configuration.library_url + "'");
337 URLConnection connection = Configuration.library_url.openConnection();
338 connection.getContent();
339 running = true;
340 }
341 catch (IOException bad_url_connection) {
342 throw bad_url_connection;
343 }
344
345
346 }
347
348 static public void checkServerRunning() {
349 if (!running) return; // don't worry about it if its not supposed to be running
350 llssite_cfg_file.load(true); // don't force reload, load only if modified
351
352 String url = llssite_cfg_file.getURL();
353 if(url != null) {
354 try {
355 Configuration.library_url = new URL(url);
356 }
357 catch (MalformedURLException exception) {
358 DebugStream.printStackTrace(exception);
359 }
360 } else {
361 // need to restart the server again
362 llssite_cfg_file.set();
363
364 // Spawn local library server process
365 String local_library_server_command = local_library_server_file.getAbsolutePath() + getExtraLaunchArguments(llssite_cfg_file);
366 running = false;
367 Gatherer.spawnApplication(local_library_server_command, ID);
368 try {
369 testServerRunning(); // don't return until the webserver is up and running
370 } catch (IOException bad_url_connection) {
371 DebugStream.println("Can't connect to server on address " + Configuration.library_url);
372 running = false;
373 }
374 }
375 }
376
377 static private class OneSecondWait
378 {
379 public OneSecondWait()
380 {
381 synchronized(this) {
382 try {
383 wait(1000);
384 }
385 catch (InterruptedException exception) {
386 }
387 }
388 }
389 }
390
391
392 static public class LLSSiteConfig
393 extends LinkedHashMap {
394 private File llssite_cfg;
395 private File glisite_cfg;
396 private String autoenter_initial;
397 private String start_browser_initial;
398
399 private long lastModified = 0;
400
401 static final private String AUTOENTER = "autoenter";
402 static final private String COLON = ":";
403 static final private String ENTERLIB = "enterlib";
404 static final private String FALSE = "0";
405 static final private String GLISITE_CFG = "glisite.cfg";
406 static final private String GSDL = "greenstone"; // httpprefix is no longer /gsdl but /greenstone
407 static final private String LLSSITE_CFG = "llssite.cfg";
408 static final private String LOCAL_HOST = "http://localhost";
409 static final private String PORTNUMBER = "portnumber";
410 static final private String SEPARATOR = "/";
411 static final private String SPECIFIC_CONFIG = "--config=";
412 static final private String STARTBROWSER = "start_browser";
413 static final private String TRUE = "1";
414 static final private String URL = "url";
415
416 public LLSSiteConfig(File server_exe) {
417 debug("New LLSSiteConfig for: " + server_exe.getAbsolutePath());
418
419 llssite_cfg = new File(server_exe.getParentFile(), LLSSITE_CFG);
420 glisite_cfg = new File(server_exe.getParentFile(), GLISITE_CFG);
421
422 File configFile = null;
423 if(!glisite_cfg.exists()) { // create it from the templates or the llssite.cfg file
424
425 File llssite_cfg_in = new File(server_exe.getParentFile(), LLSSITE_CFG+".in");
426 File glisite_cfg_in = new File(server_exe.getParentFile(), GLISITE_CFG+".in");
427
428 // need to generate glisite_cfg from glisite_cfg_in, llssite_cfg or llssite.cfg.in
429 if(glisite_cfg_in.exists()) {
430 copyConfigFile(glisite_cfg_in, glisite_cfg, false);
431 }
432 else if(llssite_cfg_in.exists()) {
433 copyConfigFile(llssite_cfg_in, glisite_cfg_in, true); // adjust for glisite.cfg
434 copyConfigFile(glisite_cfg_in, glisite_cfg, false);
435 }
436 else if(llssite_cfg.exists()) {
437 copyConfigFile(llssite_cfg, glisite_cfg, true); // adjust for glisite.cfg
438 }
439 else {
440 debug("Neither the file glisite.cfg nor llssite.cfg can be found!");
441 }
442 }
443
444 if(glisite_cfg.exists()) {
445 configFile = glisite_cfg;
446 }
447
448 autoenter_initial = null;
449 start_browser_initial = null;
450 if(configFile != null) {
451 debug("Load: " + configFile.getAbsolutePath());
452 clear();
453 try {
454 BufferedReader in = new BufferedReader(new FileReader(configFile));
455 String line = null;
456 while((line = in.readLine()) != null) {
457 String key = null;
458 String value = null;
459 int index = -1;
460 if((index = line.indexOf("=")) != -1 && line.length() >= index + 1) {
461 key = line.substring(0, index);
462 value = line.substring(index + 1);
463 }
464 else {
465 key = line;
466 }
467 put(key, value);
468 }
469 in.close();
470 }
471 catch (Exception error) {
472 error.printStackTrace();
473 }
474 }
475
476 if(glisite_cfg.exists()) {
477 lastModified = glisite_cfg.lastModified();
478 }
479 }
480
481 public boolean exists() {
482 return llssite_cfg.exists();
483 }
484
485 public String getLocalHostURL() {
486 StringBuffer url = new StringBuffer(LOCAL_HOST);
487 url.append(COLON);
488 url.append((String)get(PORTNUMBER));
489 String enterlib = (String)get(ENTERLIB);
490 if(enterlib == null || enterlib.length() == 0) {
491 // Use the default /gsdl and hope for the best.
492 url.append(SEPARATOR);
493 url.append(GSDL);
494 }
495 else {
496 if(!enterlib.startsWith(SEPARATOR)) {
497 url.append(SEPARATOR);
498 }
499 url.append(enterlib);
500 }
501 enterlib = null;
502 debug("Found Local Library Address: " + url.toString());
503 return url.toString();
504 }
505
506 public String getSiteConfigFilename() {
507 return SPECIFIC_CONFIG + glisite_cfg.getAbsolutePath();
508 }
509
510 public String getURL() {
511 // URL is made from url and portnumber
512 String url = (String) get(URL);
513
514 if(!Utility.isWindows()) {
515 return url;
516 }
517
518 if(url != null) {
519 StringBuffer temp = new StringBuffer(url);
520 temp.append(COLON);
521 temp.append((String)get(PORTNUMBER));
522 String enterlib = (String)get(ENTERLIB);
523 if(enterlib == null || enterlib.length() == 0) {
524 // Use the default /gsdl and hope for the best.
525 temp.append(SEPARATOR);
526 temp.append(GSDL);
527 }
528 else {
529 if(!enterlib.startsWith(SEPARATOR)) {
530 temp.append(SEPARATOR);
531 }
532 temp.append(enterlib);
533 }
534 enterlib = null;
535 url = temp.toString();
536 }
537 debug("Found Local Library Address: " + url);
538 return url;
539 }
540
541 public boolean isModified() {
542 return (lastModified != glisite_cfg.lastModified());
543 }
544
545 public void load(boolean reloadOnlyIfModified) {
546
547 if(isModified()) {
548 lastModified = glisite_cfg.lastModified();
549 } else if(reloadOnlyIfModified) {
550 return; // asked to reload only if modified. Don't reload since not modified
551 }
552
553 if(glisite_cfg.exists()) {
554 debug("Load: " + glisite_cfg.getAbsolutePath());
555 clear();
556 try {
557 BufferedReader in = new BufferedReader(new FileReader(glisite_cfg));
558 String line = null;
559 while((line = in.readLine()) != null) {
560 String key = null;
561 String value = null;
562 int index = -1;
563 if((index = line.indexOf("=")) != -1 && line.length() >= index + 1) {
564 key = line.substring(0, index);
565 value = line.substring(index + 1);
566 }
567 else {
568 key = line;
569 }
570 put(key, value);
571 }
572 in.close();
573 }
574 catch (Exception error) {
575 error.printStackTrace();
576 }
577 }
578 else {
579 debug("No glisite.cfg file can be found!");
580 }
581 }
582
583 /** Restore the autoenter value to its initial value, and remove url if present. */
584 public void restore() {
585 if(glisite_cfg != null) {
586 // Delete the file
587 glisite_cfg.delete();
588 }
589 else {
590 debug("Restore Initial Settings");
591 put(AUTOENTER, autoenter_initial);
592 put(STARTBROWSER, start_browser_initial);
593 remove(URL);
594 save();
595 }
596 }
597
598 public void set() {
599 debug("Set Session Settings");
600 if(autoenter_initial == null) {
601 autoenter_initial = (String) get(AUTOENTER);
602 debug("Remember autoenter was: " + autoenter_initial);
603 }
604 put(AUTOENTER, TRUE);
605 if(start_browser_initial == null) {
606 start_browser_initial = (String) get(STARTBROWSER);
607 debug("Remember start_browser was: " + start_browser_initial);
608 }
609 put(STARTBROWSER, FALSE);
610 save();
611 }
612
613 private void debug(String message) {
614 ///ystem.err.println(message);
615 }
616
617 private void save() {
618 //debug("Save: " + llssite_cfg.getAbsolutePath());
619 debug("Save: " + glisite_cfg.getAbsolutePath());
620 try {
621 //BufferedWriter out = new BufferedWriter(new FileWriter(llssite_cfg, false));
622 BufferedWriter out = new BufferedWriter(new FileWriter(glisite_cfg, false));
623 for(Iterator keys = keySet().iterator(); keys.hasNext(); ) {
624 String key = (String) keys.next();
625 String value = (String) get(key);
626 out.write(key, 0, key.length());
627 if(value != null) {
628 out.write('=');
629 out.write(value, 0, value.length());
630 }
631 out.newLine();
632 }
633 out.flush();
634 out.close();
635 }
636 catch (Exception error) {
637 error.printStackTrace();
638 }
639 }
640
641 private static void copyConfigFile(File source_cfg, File dest_cfg, boolean setToGliSiteDefaults) {
642 // source_cfg file should exist
643 // dest_cfg file should not yet exist
644 // If setToGliSiteDefaults is true, then GLIsite.cfg's default configuration
645 // is applied to concerned lines: autoenter=1, and startbrowser=0
646
647 try {
648 BufferedReader in = new BufferedReader(new FileReader(source_cfg));
649 BufferedWriter out = new BufferedWriter(new FileWriter(dest_cfg, false));
650
651 String line = null;
652 while((line = in.readLine()) != null) {
653
654 if(setToGliSiteDefaults) {
655 if(line.startsWith(AUTOENTER)) {
656 line = AUTOENTER+"=1";
657 }
658 else if(line.startsWith(STARTBROWSER)) {
659 line = STARTBROWSER+"=0";
660 }
661 }
662
663 // write out the line
664 out.write(line + "\n");
665 }
666
667 out.flush();
668 in.close();
669 out.close();
670 } catch(Exception e) {
671 System.err.println("Exception occurred when trying to copy the config file "
672 + source_cfg.getName() + " to " + dest_cfg.getName() + ": " + e);
673 e.printStackTrace();
674 }
675 }
676 }
677}
Note: See TracBrowser for help on using the repository browser.