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

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

Better way to ensure that the port is not arbitrarily modified. This bypasses the need to turn on the Do Not Modify Port server setting option by default, which then bypasses the need to make 8282 the default port for GS2 (which would have been necessary since the old GS2 default port of 80 would not have coped well with the Do Not Modify Port switch turned on by default).

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