source: main/trunk/greenstone3/src/java/org/greenstone/server/Server2.java@ 24911

Last change on this file since 24911 was 24911, checked in by ak19, 12 years ago

Number of required arguments changed recently with the introduction of the GSDLOS_GSDLARCH parameter, so the test for validating the number of input parameters needed to be updated as well.

File size: 20.0 KB
Line 
1
2package org.greenstone.server;
3
4import java.io.BufferedReader;
5import java.io.File;
6import java.io.FileInputStream;
7import java.io.FileOutputStream;
8import java.io.InputStreamReader;
9import java.io.IOException;
10import java.net.InetAddress;
11import java.net.ServerSocket;
12import java.net.Socket;
13import java.net.UnknownHostException;
14import java.net.URL;
15//import java.net.URLConnection;
16import java.util.ArrayList;
17import java.util.Properties;
18import java.util.StringTokenizer;
19
20import org.apache.log4j.*;
21
22import org.greenstone.util.PortFinder;
23import org.greenstone.util.ScriptReadWrite;
24import org.greenstone.util.RunMake;
25import org.greenstone.util.RunAnt;
26
27import org.greenstone.server.BaseServer;
28import org.greenstone.server.BaseProperty;
29
30
31public class Server2 extends BaseServer
32{
33 private static final int WAITING_TIME = 10; // time to wait and check for whether the server is running
34 private static final String URL_PENDING="URL_pending";
35
36 protected String libraryURL;
37 protected String property_prefix;
38 protected String gsdlos_gsdlarch;
39
40 private class QuitListener extends Thread
41 {
42 int quitPort = -1;
43 ServerSocket serverSocket = null;
44
45 public QuitListener(int quitport) throws Exception {
46 ///Server2.this.recordSuccess("In QuitListener constructor");
47 this.quitPort = quitport;
48 serverSocket = new ServerSocket(quitPort);
49 }
50
51 public void run() {
52 Socket connection = null;
53
54 try {
55 // wait for a connection
56 connection = serverSocket.accept();
57 boolean stop = false;
58
59 // read input
60 try {
61 BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
62 String line = null;
63 while((line = reader.readLine()) != null) {
64 if(line.equals("QUIT")) {
65 stop = true;
66 // Server2.this.recordSuccess("In QuitListener - line is QUIT");
67 reader.close();
68 reader = null;
69 serverSocket.close();
70 serverSocket = null;
71 break;
72 } else if(line.equals("RECONFIGURE")) {
73 server_control_.displayMessage(dictionary.get("ServerControl.Configuring"));
74 reconfigRequired();
75 } else if(line.equals("RESTART")) {
76
77 // If the GSI is set to NOT autoenter/autostart the server, then write url=URL_PENDING out to the file.
78 // When the user finally presses the Enter Library button and so has started up the server, the correct
79 // url will be written out to the configfile.
80 String url = property_prefix+"url";
81 if(config_properties.getProperty(BaseServer.Property.AUTOSTART, "").equals("0")) {
82 if(config_properties.getProperty(url) == null) {
83 config_properties.setProperty(url, URL_PENDING);
84 ScriptReadWrite scriptReadWrite = new ScriptReadWrite();
85 ArrayList fileLines = scriptReadWrite.readInFile(BaseServer.config_properties_file);
86 scriptReadWrite.replaceOrAddLine(fileLines, url, URL_PENDING, true);
87 scriptReadWrite.writeOutFile(config_properties_file, fileLines);
88 }
89 }
90
91 autoStart();
92 }
93 }
94 } catch(Exception e) {
95 Server2.this.recordError("Exception in QuitListener thread.");
96 } finally {
97 if(stop) {
98 Server2.this.stop();
99 System.exit(0);
100 }
101 }
102 } catch(IOException ioe) {
103 Server2.this.recordError("Server2.QuitListener: Unable to make the connection with the client socket." + ioe);
104 }
105 }
106 }
107
108
109 public Server2(String gsdl2_home, String gsdlos_arch, String lang, String configfile, int quitPort, String mode)
110 {
111 super(gsdl2_home, lang, configfile, "etc"+File.separator+"logs-gsi");
112 // configfile can be either glisite.cfg or llssite.cfg
113
114 //logger_.error("gsdlhome: " + gsdl2_home + " | lang: " + lang + " | configfile: "
115 //+ configfile + " | gsdlos_gsdlarch: " + gsdlos_arch + " | mode: " + property_prefix + " | quitport: " + quitPort);
116
117 // to get to the OS specific subdirectory of cgi-bin
118 gsdlos_gsdlarch = gsdlos_arch;
119
120 // property_prefix is the mode we're running in (gli or empty) and contains the prefix
121 // string to look for in config file for auto_enter, start_browser and url properties
122 property_prefix = mode;
123 if(!property_prefix.equals("") && !property_prefix.endsWith(".")) { // ensure a '.' is suffixed if non-empty
124 property_prefix += ".";
125 }
126 Property = new Server2Property(property_prefix);
127
128
129 String frame_title = dictionary.get("ServerControl.Frame_Title");
130 server_control_ = new Server2Control(this,frame_title);
131
132 /* Make command targets for managing Web server */
133 START_CMD = "web-start";
134 RESTART_CMD = "web-restart";
135 CONFIGURE_CMD = "configure-web \"" + configfile + "\"";
136 STOP_CMD = "web-stop";
137
138 // now we can monitor for the quit command if applicable
139 if(quitPort != -1) {
140 // First check if given port is within the range of allowed ports
141 if(PortFinder.isAssignablePortNumber(quitPort)) {
142 try {
143 new QuitListener(quitPort).start();
144 } catch(Exception e) {
145 Server2.this.recordError("Exception constructing the QuitListener thread.");
146 }
147 }
148 else {
149 recordError("QuitPort provided is not within acceptable range: ("
150 + PortFinder.PORTS_RESERVED + " - " + PortFinder.MAX_PORT + "]" );
151 quitPort = -1;
152 }
153 }
154
155 // For some machines, localhost is not sufficient,
156 // need hostname defined as well (e.g. Ubuntu 10.10)
157 InetAddress inetAddress = null;
158 try {
159 inetAddress = InetAddress.getLocalHost();
160 String hosts = inetAddress.getHostName();
161 ScriptReadWrite scriptReadWrite = new ScriptReadWrite();
162 ArrayList fileLines = scriptReadWrite.readInFile(BaseServer.config_properties_file);
163 scriptReadWrite.replaceOrAddLine(fileLines, "hosts", hosts, true);
164 scriptReadWrite.writeOutFile(config_properties_file, fileLines);
165 } catch(UnknownHostException e) {
166 // Unable to get hostname, need to try for the default localhost without it
167 }
168
169 // If the GSI is set to NOT autoenter/autostart the server, then write url=URL_PENDING out to the file.
170 // When the user finally presses the Enter Library button and so has started up the server, the correct
171 // url will be written out to the configfile.
172 if(config_properties.getProperty(BaseServer.Property.AUTOSTART, "").equals("0")) {//if(configfile.endsWith("llssite.cfg")) {
173 String url = property_prefix+"url";
174 if(config_properties.getProperty(url) == null) {
175 config_properties.setProperty(url, URL_PENDING);
176 ScriptReadWrite scriptReadWrite = new ScriptReadWrite();
177 ArrayList fileLines = scriptReadWrite.readInFile(BaseServer.config_properties_file);
178 scriptReadWrite.replaceOrAddLine(fileLines, url, URL_PENDING, true);
179 scriptReadWrite.writeOutFile(config_properties_file, fileLines);
180 }
181 }
182
183 autoStart();
184 }
185
186 // Prepare the log4j.properties for GS2
187 protected void initLogger() {
188
189 String libjavaFolder = gsdl_home+File.separator+"lib"+File.separator+"java"+File.separator;
190 File propsFile = new File(libjavaFolder+"log4j.properties");
191
192 // create it from the template file log4j.properties.in
193 if(!propsFile.exists()) {
194 try {
195 // need to set gsdl2.home property's value to be gsdl_home,
196 // so that the location of the log files gets resolved correctly
197
198 // load the template log4j.properties.in file into logProps
199 FileInputStream infile = new FileInputStream(new File(libjavaFolder+"log4j.properties.in"));
200 if(infile != null) {
201 Properties logProps = new Properties();
202 logProps.load(infile);
203 infile.close();
204
205 // set gsdl3.home to gsdl_home
206 logProps.setProperty("gsdl2.home", gsdl_home);
207
208 // write the customised properties out to a custom log4j.properties file
209 FileOutputStream outfile = new FileOutputStream(propsFile);
210 if(outfile != null) {
211 logProps.store(outfile, "Customised log4j.properties file");
212 outfile.close();
213 } else {
214 System.err.println("Could not store properties file " + propsFile + " for Server2.");
215 }
216 }
217 } catch(Exception e) {
218 System.err.println("Exception occurred when custom-configuring the logger for Server2.\n" + e);
219 }
220 }
221
222 // now configure the logger with the custom log4j.properties file
223 if(propsFile.exists()) {
224 PropertyConfigurator.configure(propsFile.getAbsolutePath());
225 } else {
226 System.err.println("Could not create properties file " + propsFile + " for Server2.");
227 }
228 }
229
230
231 protected int runTarget(String cmd)
232 {
233 RunMake runMake = new RunMake();
234 runMake.setTargetCmd(cmd);
235 runMake.run();
236 return runMake.getTargetState();
237 }
238
239 public String getBrowserURL() {
240 return libraryURL;
241 }
242
243 // works out the library URL again
244 public void reload() {
245 // default values, to be replaced with what's in gsdlsite.cfg
246 String host = "localhost";
247 String port = "80";
248 String gwcgi;
249 String httpprefix = "/greenstone";
250 String suffix = "/cgi-bin/library.cgi";
251
252 // get the prefix from the gsdlsite.cfg and build.properties files (port number and servername)
253 try{
254 File gsdlsite_cfg = new File(gsdl_home + File.separator + "cgi-bin" + File.separator
255 + gsdlos_gsdlarch + File.separator + "gsdlsite.cfg");
256 FileInputStream fin = new FileInputStream(gsdlsite_cfg);
257 Properties gsdlProperties = new Properties();
258 if(fin != null) {
259 gsdlProperties.load(fin);
260
261 gwcgi = gsdlProperties.getProperty("gwcgi");
262 if(gwcgi != null) {
263 suffix = gwcgi;
264 } else {
265 httpprefix = gsdlProperties.getProperty("httpprefix", httpprefix);
266 suffix = httpprefix + suffix;
267 }
268 fin.close();
269 } else {
270 recordError("Could not open gsdlsite_cfg for reading, using default library prefix.");
271 }
272 //reloadConfigProperties();
273 port = config_properties.getProperty("portnumber", port);
274
275 // The "hosts" property in the config file contains more than one allowed host
276 // Need to work out the particular host chosen from the address_resolution_method
277 // Default is address_resolution_method 2: localhost
278 String addressResolutionMethod = config_properties.getProperty("address_resolution_method");
279 int address_resolution_method = (addressResolutionMethod == null) ? 2 : Integer.parseInt(addressResolutionMethod);
280 InetAddress inetAddress = null;
281 try {
282 inetAddress = InetAddress.getLocalHost();
283 } catch(UnknownHostException e) {
284 logger_.error(e);
285 logger_.info("Defaulting host IP to "+ host); // use the default
286 address_resolution_method = 2;
287 inetAddress = null;
288 }
289 switch(address_resolution_method) {
290 case 0:
291 host = inetAddress.getHostName();
292 break;
293 case 1:
294 host = inetAddress.getHostAddress();
295 break;
296 case 2:
297 host = "localhost";
298 break;
299 case 3:
300 host = "127.0.0.1";
301 break;
302 default:
303 host = "localhost";
304 }
305 } catch(Exception e) {
306 recordError("Exception trying to load properties from gsdlsite_cfg. Using default library prefix.", e);
307 suffix = httpprefix + suffix;
308 }
309
310 libraryURL = "http://" + host + ":" + port + suffix;
311 }
312
313 public boolean reloadConfigProperties(boolean port_has_changed) {
314 super.reloadConfigProperties(port_has_changed);
315
316 // make sure the port is okay, otherwise find another port
317 // first choice is port 80, second choice starts at 8282
318 String port = config_properties.getProperty("portnumber", "80");
319 String keepport = config_properties.getProperty("keepport", "0"); // default is to not try to force the same port if in use by other servers
320
321 int portDefault = 8282;
322 try {
323 int portNum = Integer.parseInt(port);
324 boolean verbose = true;
325 if(port_has_changed) { // this is the test that prevents the server from arbitrarily shifting the port.
326 // only check at configured port if it's not the current port (the port we
327 // are still running on), because that will always be in use and unavailable.
328 if(!PortFinder.isPortAvailable(portNum, verbose)) { // first time, print any Port Unavailable messages
329 if(keepport.equals("1")) {
330 server_control_.errorMessage(dictionary.get("ServerSettings.SettingsUnchangedPortOccupied", new String[]{port}));
331 String errorMsg = "Unable to run the Greenstone server on port " + port + ". It appears to already be in use.";
332 System.err.println("\n******************");
333 logger_.error(errorMsg);
334 System.err.println("If you wish to try another port, go to File > Settings of the Greenstone Server interface and either change the port number or untick the \"Do Not Modify Port\" option there. Then press the \"Enter Library\" button.");
335 System.err.println("******************\n");
336
337 return false; // property change is unsuccessful
338
339 } else { // can modify port, try to find a new port
340
341 PortFinder portFinder = new PortFinder(portDefault, 101);
342 // Search for a free port silently from now on--don't want more
343 // messages saying that a port could not be found...
344 portNum = portFinder.findPortInRange(!verbose);
345
346 if (portNum == -1) {
347 // If we've still not found a free port, do we try the default port again?
348 System.err.println("No free port found. Going to try on " + portDefault + " anyway.");
349 port = Integer.toString(portDefault);
350 } else {
351 port = Integer.toString(portNum);
352 }
353 config_properties.setProperty("portnumber", port); // store the correct port
354
355 // write this updated port to the config file, since the configure target uses the file to run
356 ScriptReadWrite scriptReadWrite = new ScriptReadWrite();
357 ArrayList fileLines = scriptReadWrite.readInFile(BaseServer.config_properties_file);
358 scriptReadWrite.replaceOrAddLine(fileLines, "portnumber", port, false); // write the correct port
359 scriptReadWrite.writeOutFile(config_properties_file, fileLines);
360
361 configure_required_ = true;
362 System.err.println("Running server on port " + port + ".");
363 }
364 }
365 }
366 } catch (Exception e) {
367 recordError("Exception in Server2.reload(): " + e.getMessage());
368 port = Integer.toString(portDefault);
369 }
370
371 return true;
372 }
373
374
375 // About to stop the webserver
376 // Custom GS2 action: remove the url property from the config file
377 protected void preStop() {
378 ScriptReadWrite scriptReadWrite = new ScriptReadWrite();
379 ArrayList fileLines = scriptReadWrite.readInFile(BaseServer.config_properties_file);
380
381 // Remove the url=... line, start searching from the end
382 boolean done = false;
383 for (int i = fileLines.size()-1; i >= 0 && !done; i--) {
384 String line = ((String) fileLines.get(i)).trim();
385 if(line.startsWith(property_prefix+"url=")) {
386 fileLines.remove(i);
387 done = true;
388 }
389 }
390 scriptReadWrite.writeOutFile(config_properties_file, fileLines);
391 }
392
393 // Called when the URL has been changed (called after a reload() and starting the server).
394 // By the time we get here, reload() would already have been called and have set
395 // both the port and worked out libraryURL
396 // This method needs to write the URL to the configfile since things should work
397 // like GS2's Local Lib Server for Windows
398 protected void postStart() {
399
400 URL libURL = null;
401 try {
402 libURL = new URL(libraryURL);
403 } catch (Exception e) {
404 recordError("Unable to convert library URL string into a valid URL, Server2.java." + e);
405 }
406
407 // 1. Test that the server is running at the libraryURL before writing it out to glisite.cfg/configfile
408 // A quick test involves opening a connection to get the home page for this collection
409 if(libURL != null && !libraryURL.equals(URL_PENDING)) {
410
411 boolean ready = false;
412 for(int i = 0; i < WAITING_TIME && !ready; i++) {
413 try {
414 libURL.openConnection();
415 //URLConnection connection = new URL(libraryURL).openConnection();
416 //connection.getContent();
417 ready = true;
418 recordSuccess("Try connecting to server on url: '" + libraryURL + "'");
419 } catch (IOException bad_url_connection) {
420 // keep looping
421 recordSuccess("NOT YET CONNECTED. Waiting to try again...");
422 try {
423 Thread.sleep(1000);
424 } catch (InterruptedException ie) {
425 ready = true;
426 recordError("Unexpected: got an InterruptedException in sleeping thread, Server2.java." + ie);
427 }
428 } catch (Exception e) {
429 ready = true;
430 recordError("Got an Exception while waiting for the connection to become live, Server2.java." + e);
431 }
432 }
433 }
434
435 // 2. Now write the URL to the config file
436 String port = config_properties.getProperty("portnumber");
437
438 ScriptReadWrite scriptReadWrite = new ScriptReadWrite();
439 ArrayList fileLines = scriptReadWrite.readInFile(BaseServer.config_properties_file);
440 scriptReadWrite.replaceOrAddLine(fileLines, property_prefix+"url", libraryURL, true);
441 scriptReadWrite.replaceOrAddLine(fileLines, "portnumber", port, false); // write the correct port
442 scriptReadWrite.writeOutFile(config_properties_file, fileLines);
443 }
444
445
446 public static void main (String[] args)
447 {
448 if ((args.length < 2) || (args.length > 6)) {
449 System.err.println(
450 "Usage: java org.greenstone.server.Server2 <gsdl2-home-dir> <GSDLOS_GSDLARCH> [lang] [--mode=\"gli\"] [--config=configfile] [--quitport=portNum]");
451 System.exit(1);
452 }
453
454 String gsdl2_home = args[0];
455 File gsdl2_dir = new File(gsdl2_home);
456 if (!gsdl2_dir.isDirectory()) {
457 System.err.println("gsdl-home-dir directory does not exist!");
458 System.exit(1);
459 }
460
461 String gsdlos_arch = args[1];
462
463 //for(int i = 0; i < args.length; i++) { System.err.println("Arg[" + i + "]: |" + args[i] + "|"); }
464
465 // for every subsequent argument, check whether we're dealing with amalgamated
466 // parameters in that argument. If so, split on whitespace and reconstruct arguments
467 for(int i = 2; i < args.length; i++) {
468
469 // if the *last* occurrence of a flag parameter is not at the start of
470 // this argument then we're dealing with amalgamated parameters in this
471 // argument. Need to split on whitespace and reconstitute the args list
472 int lastIndex = args[i].lastIndexOf("--");
473 if(lastIndex > 0) {
474
475 StringTokenizer tokenizer = new StringTokenizer(args[i]); // splits on whitespace
476 String[] tokens = new String[tokenizer.countTokens()+i];
477 int j = 0;
478 for(; j < i; j++) { // copy over previous arguments
479 tokens[j] = args[j];
480 }
481
482 // the remainder comes from the tokens
483 for(; tokenizer.hasMoreTokens(); j++) {
484 tokens[j] = tokenizer.nextToken();
485 }
486 args = null;
487 args = tokens;
488 }
489
490 }
491
492 // Defaults for optional arguments
493
494 // if no config file is given, then it defaults to llssite.cfg
495 // (embedded in quotes to preserve spaces in the filepath)
496 File defaultConfigFile = new File(gsdl2_dir, "llssite.cfg");
497 String configfile = defaultConfigFile.getAbsolutePath();
498 int port = -1;
499 String mode = "";
500 String lang = "en";
501
502 int index = 2; // move onto any subsequent arguments (past 1st GSDLHOME arg and 2nd GSDLOS_GSDLARCH arg)
503 if (args.length > index && !args[index].startsWith("--")) {
504 lang = args[index]; // 2nd arg is not a flag option, so must be language
505 index++;
506 }
507
508 // Cycle through arguments, parsing and storing them
509 // note that the batch file could have split arguments in the config-
510 // filepath over several parameters. Here we join them back up again.
511 while(args.length > index) {
512
513 if(args[index].startsWith("--config=")) {
514 configfile = args[index].substring(args[index].indexOf('=')+1); // get value after '=' sign
515 gsdl2_dir = null;
516 defaultConfigFile = null;
517 }
518
519 else if(args[index].startsWith("--quitport=")) {
520 String quitport = args[index].substring(args[index].indexOf('=')+1);
521 try {
522 port = Integer.parseInt(quitport);
523 } catch(Exception e) { // parse fails
524 System.err.println("Port must be numeric. Continuing without it.");
525 }
526 }
527
528 // mode can be: "gli" if launched by GLI or unspecified. If unspecified, then
529 // the gs2-server was launched independently.
530 else if(args[index].startsWith("--mode=")) {
531 mode = args[index].substring(args[index].indexOf('=')+1);
532 }
533
534 else if(!args[index].startsWith("--")) { // assume it's part of the config file name (that this path had spaces in it)
535 configfile = configfile + " " + args[index]; // reinstate space in path
536 }
537
538 index++;
539 }
540
541 configfile = configfile.trim(); // remove any trailing whitespace
542 //System.err.println("\n\n*******\n\ngsdlhome: " + gsdl2_home + " | lang: " + lang + " |configfile:"
543 // + configfile + "| mode: " + mode + " | quitport: " + port + "\n\n*************\n");
544 new Server2(gsdl2_home, gsdlos_arch, lang, configfile, port, mode);
545 }
546}
Note: See TracBrowser for help on using the repository browser.