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

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

Change that goes with change in PortFinder.java: now it passes a verbose (or silent) boolean flag when calling findPortInRange().

  • 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 = 60; // 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.