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

Last change on this file since 24491 was 24491, checked in by ak19, 13 years ago

Now Server2's main method handles messy commandline arguments of the kind where parameters are split over multiple arguments, which can happen when gs2-webserver.bat calls Server2 now. The logic of dealing with spaces in filepath (and messy parameters) is therefore moved now from gs2-webserver.bat into Server2.java

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