Index: /gli/trunk/src/org/greenstone/gatherer/Gatherer.java
===================================================================
--- /gli/trunk/src/org/greenstone/gatherer/Gatherer.java	(revision 18986)
+++ /gli/trunk/src/org/greenstone/gatherer/Gatherer.java	(revision 18987)
@@ -325,5 +325,5 @@
 	    }
 	    else { // local greenstone: Start up the local library server, if that's what we want
-		if (Utility.isWindows() && local_library_path != null && !GS3) {
+		if (local_library_path != null && !GS3) {
 		    LocalLibraryServer.start(gsdl_path, local_library_path);
 		}
@@ -795,4 +795,21 @@
 	apps.add(app);
 	app.start();
+    }
+
+    static public void spawnApplication(String command, String ID)
+    {
+	ExternalApplication app = new ExternalApplication(command, ID);
+	apps.add(app);
+	app.start();
+    }
+
+    static public void terminateApplication(String ID) {
+	for(int i = 0; i < apps.size(); i++) {
+	    ExternalApplication app = (ExternalApplication)apps.get(i);
+	    if(app.getID() != null && app.getID().equals(ID)) {
+		app.stopExternalApplication();
+		apps.remove(app);
+	    }
+	}
     }
 
@@ -1060,4 +1077,7 @@
 	private String command = null;
 	private String[] commands = null;
+
+	private String ID = null;
+
 	/** Constructor.
 	 * @param command The initial command <strong>String</strong>.
@@ -1070,4 +1090,14 @@
 	    this.commands = commands;
 	}
+
+	public ExternalApplication(String command, String ID) {
+	    this.command = command;
+	    this.ID = ID;
+	}
+	
+	public String getID() { 
+	    return ID; 
+	}
+
 	/** We start the child process inside a new thread so it doesn't block the rest of Gatherer.
 	 * @see java.lang.Exception
Index: /gli/trunk/src/org/greenstone/gatherer/greenstone/LocalLibraryServer.java
===================================================================
--- /gli/trunk/src/org/greenstone/gatherer/greenstone/LocalLibraryServer.java	(revision 18986)
+++ /gli/trunk/src/org/greenstone/gatherer/greenstone/LocalLibraryServer.java	(revision 18987)
@@ -37,4 +37,6 @@
 import org.greenstone.gatherer.Dictionary;
 import org.greenstone.gatherer.Gatherer;
+import org.greenstone.gatherer.util.PortFinder;
+import org.greenstone.gatherer.util.Utility;
 
 
@@ -49,8 +51,22 @@
 
     static private boolean running = false;
+    static private String ID = "greenstone-server"; // a sort of process ID
+
+    // Need to use sockets to tell the server program to terminate when on Linux
+    // The socket port number that we will use to communicate the termination
+    static private int port;
+
+    // The server is persistent if it does not have to reload all the values
+    // over and over again each time. Tomcat is persistent and fastcgi is too,
+    // but the Apache webserver is not persistent by default.
+    // Change the initialisation of this value depending on whether fastcgi is 
+    // on. At the moment, this does not apply to the Linux' local library server.
+    static private boolean isPersistentServer = Utility.isWindows();
 
     static public void addCollection(String collection_name)
     {
-	config(ADD_COMMAND + collection_name);
+	if (isPersistentServer) {
+	    config(ADD_COMMAND + collection_name);
+	}
     }
 
@@ -59,4 +75,7 @@
     static private void config(String command)
     {
+	if (!isPersistentServer) {
+	    return;
+	}
 	if (Configuration.library_url == null) {
 	    System.err.println("Error: Trying to configure local library with null Configuration.library_url!");
@@ -93,9 +112,32 @@
     }
 
+    // Used to send messages to the local server on Linux
+    static private boolean sendMessageToServer(String message) {
+	if(Utility.isWindows()) {
+	    return false;
+	}
+	
+	if(port == -1) {
+	    return false;
+	}
+	    
+	try {
+	    Socket clientSocket = new Socket("localhost", port);
+	    Writer writer = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
+	    writer.write(message);
+	    writer.close();
+	    writer = null;
+	} catch (Exception e) {
+	    System.err.println("An exception occurred when trying to send the message: " + message 
+			       + "\nto the LocalLibraryServer.\n" + e);
+	    return false;
+	} 
+	return true;
+    }
 
     static public boolean isRunning()
     {
 	if (!running) return false;
-	llssite_cfg_file.load();
+	llssite_cfg_file.load(true);
 	if (llssite_cfg_file.getURL() == null)  return false;
 	return true;
@@ -105,16 +147,19 @@
     static public void releaseCollection(String collection_name)
     {
-	config(RELEASE_COMMAND + collection_name);
-    }
-
+	if (isPersistentServer) {
+	    config(RELEASE_COMMAND + collection_name);
+	}
+    }
 
     static public void start(String gsdl_path, String local_library_server_file_path)
     {
-	// Check the local library server.exe file exists
+	// Check the local library server file (server.exe or gs2-server.sh) exists
 	local_library_server_file = new File(local_library_server_file_path);
+	
 	if (!local_library_server_file.exists()) {
 	    DebugStream.println("No local library at given file path.");
-
-	    local_library_server_file = new File(gsdl_path + "server.exe");
+	
+	    String defaultServerFilename = Utility.isWindows() ? "server.exe" : "gs2-server.sh";
+	    local_library_server_file = new File(gsdl_path + defaultServerFilename);
 	    if (!local_library_server_file.exists()) {
 		DebugStream.println("No local library at all.");
@@ -122,13 +167,23 @@
 	    }
 	}
+	llssite_cfg_file = new LLSSiteConfig(local_library_server_file);
+	
+	// Spawn local library server process
+	String local_library_server_command = local_library_server_file.getAbsolutePath() + getExtraLaunchArguments(llssite_cfg_file);
 
 	// Check if the server is already running
-	llssite_cfg_file = new LLSSiteConfig(local_library_server_file);
 	String url = llssite_cfg_file.getURL();
 	if (url != null) {
 	    // If it is already running then set the Greenstone web server address and we're done
+	    // E.g. if previously GLI was not properly shut down, the URL property (signifying 
+	    // the server is still running) would still be in the glisite_cfg file.
 	    try {
 		Configuration.library_url = new URL(url);
 		running = true;
+
+		// on Linux, run the server interface (will work with any server already running)
+		if(!Utility.isWindows()) {
+		    Gatherer.spawnApplication(local_library_server_command, ID);
+		}
 		return;
 	    }
@@ -136,5 +191,5 @@
 		DebugStream.printStackTrace(exception);
 	    }
-	}
+	}	
 
 	// Configure the server for immediate entry
@@ -142,13 +197,35 @@
 
 	// Spawn local library server process
-	String local_library_server_command = local_library_server_file.getAbsolutePath() + " " + llssite_cfg_file.getSiteConfigFilename();
-	Gatherer.spawnApplication(local_library_server_command);
-
-	// Wait until program has started, by reloading and checking the URL field
-	llssite_cfg_file.load();
+	Gatherer.spawnApplication(local_library_server_command, ID);
+	
+	// Wait until program has started
+	testServerRunning(); // will set running = true when the server is up and running successfully
+    }
+
+
+    static public void stop()
+    {
+	if (running == false) {
+	    return;
+	}
+
+	// Send the command for it to exit.
+	if (isPersistentServer) {
+	    config(QUIT_COMMAND);
+	} else {
+	    if(sendMessageToServer("QUIT")) {
+		Gatherer.terminateApplication(ID);
+	    } else {
+		System.err.println("Unable to stop the server, since there's no communication port to send the quit msg over."
+				   + "\nPlease stop the local Greenstone server manually.");	    
+	    }
+	}
+
+	// Wait until program has stopped, by reloading and checking the URL field
+	llssite_cfg_file.load(false);
 	int attempt_count = 0;
-	while (llssite_cfg_file.getURL() == null) {
+	while (llssite_cfg_file.getURL() != null) {
 	    new OneSecondWait();  // Wait one second (give or take)
-	    llssite_cfg_file.load();
+	    llssite_cfg_file.load(false);
 	    attempt_count++;
 
@@ -163,4 +240,56 @@
 	}
 
+	// Restore the llssite_cfg.
+	llssite_cfg_file.restore();
+
+	// If the local server is still running then our changed values will get overwritten.
+	if (llssite_cfg_file.getURL() != null) {
+	    JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("Server.QuitManual"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
+	}
+
+	running = false;
+    }
+    
+    static private String getExtraLaunchArguments(LLSSiteConfig site_cfg_file) {
+	String args = " " + site_cfg_file.getSiteConfigFilename();
+
+	if(Utility.isWindows()) {
+	    return args;
+	}
+	
+	// Else, when running the Local Library Server on Linux, need to provide a port argument
+	try {
+	    PortFinder portFinder = new PortFinder(50100, 100);
+	    port = portFinder.findPortInRange();
+	} catch(Exception e) {
+	    System.err.println("Exception when trying to find an available port: " + e);
+	    port = -1;
+	}
+	
+	return args + " --quitport=" + port;
+    }
+
+
+    // This method first tests whether there is a URL in the llssite_cfg_file 
+    // and after that appears, it tests whether the URL is functional.
+    static private void testServerRunning() {
+	// Wait until program has started, by reloading and checking the URL field
+	llssite_cfg_file.load(false);
+	int attempt_count = 0;
+	while (llssite_cfg_file.getURL() == null) {
+	    new OneSecondWait();  // Wait one second (give or take)
+	    llssite_cfg_file.load(false);
+	    attempt_count++;
+
+	    // After waiting a minute ask the user whether they want to wait another minute
+	    if (attempt_count == 60) {
+		int try_again = JOptionPane.showConfirmDialog(Gatherer.g_man, Dictionary.get("Server.QuitTimeOut"), Dictionary.get("General.Warning"), JOptionPane.YES_NO_OPTION);
+		if (try_again == JOptionPane.NO_OPTION) {
+		    return;
+		}
+		attempt_count = 0;
+	    }
+	}
+	
 	// Ta-da. Now the url should be available
 	try {
@@ -173,5 +302,5 @@
 	// A quick test involves opening a connection to get the home page for this collection
 	try {
-	    DebugStream.println("Try connecting to server on config url: '" + Configuration.library_url+ "'");
+	    DebugStream.println("Try connecting to server on config url: '" + Configuration.library_url + "'");
 	    URLConnection connection = Configuration.library_url.openConnection();
 	    connection.getContent();
@@ -184,4 +313,5 @@
 		URLConnection connection = Configuration.library_url.openConnection();
 		connection.getContent();
+		
 	    }
 	    catch (IOException worse_url_connection) {
@@ -195,56 +325,28 @@
     }
 
-
-    static public void stop()
-    {
-	if (running == false) {
-	    return;
-	}
-
-	// Send the command for it to exit.
-	config(QUIT_COMMAND);
-
-	// Wait until program has stopped, by reloading and checking the URL field
-	llssite_cfg_file.load();
-	int attempt_count = 0;
-	while (llssite_cfg_file.getURL() != null) {
-	    new OneSecondWait();  // Wait one second (give or take)
-	    llssite_cfg_file.load();
-	    attempt_count++;
-
-	    // After waiting a minute ask the user whether they want to wait another minute
-	    if (attempt_count == 60) {
-		int try_again = JOptionPane.showConfirmDialog(Gatherer.g_man, Dictionary.get("Server.QuitTimeOut"), Dictionary.get("General.Warning"), JOptionPane.YES_NO_OPTION);
-		if (try_again == JOptionPane.NO_OPTION) {
-		    return;
-		}
-		attempt_count = 0;
-	    }
-	}
-
-	// Restore the llssite_cfg.
-	llssite_cfg_file.restore();
-
-	// If the local server is still running then our changed values will get overwritten.
-	if (llssite_cfg_file.getURL() != null) {
-	    JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("Server.QuitManual"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
-	}
-
-	running = false;
-    }
-    
     static public void checkServerRunning() {
 	if (!running) return; // don't worry about it if its not supposed to be running 
-	llssite_cfg_file.load();
-	if (llssite_cfg_file.getURL() == null) {
+	llssite_cfg_file.load(true); // don't force reload, load only if modified
+
+	String url = llssite_cfg_file.getURL();
+	if(url != null) {
+	    try {
+		Configuration.library_url = new URL(url);
+	    }
+	    catch (MalformedURLException exception) {
+		DebugStream.printStackTrace(exception);
+	    }
+	} else {
 	    // need to restart the server again
 	    llssite_cfg_file.set();
 
 	    // Spawn local library server process
-	    String local_library_server_command = local_library_server_file.getAbsolutePath() + " " + llssite_cfg_file.getSiteConfigFilename();
-	    Gatherer.spawnApplication(local_library_server_command);
-
-	}
-    }
+	    String local_library_server_command = local_library_server_file.getAbsolutePath() + getExtraLaunchArguments(llssite_cfg_file);
+	    running = false;
+	    Gatherer.spawnApplication(local_library_server_command, ID);
+	    testServerRunning(); // don't return until the webserver is up and running
+	}
+    }
+
     static private class OneSecondWait
     {
@@ -268,4 +370,6 @@
 	private String autoenter_initial;
 	private String start_browser_initial;
+
+	private long lastModified = 0;
     
 	static final private String AUTOENTER = "autoenter";
@@ -274,5 +378,5 @@
 	static final private String FALSE = "0";
 	static final private String GLISITE_CFG = "glisite.cfg";
-	static final private String GSDL = "gsdl";
+	static final private String GSDL = "greenstone"; // httpprefix is no longer /gsdl but /greenstone
 	static final private String LLSSITE_CFG = "llssite.cfg";
 	static final private String LOCAL_HOST = "http://localhost"; 
@@ -326,4 +430,8 @@
 		}
 	    }
+
+	    if(glisite_cfg.exists()) {
+		lastModified = glisite_cfg.lastModified();
+	    }
 	}
 	
@@ -360,4 +468,9 @@
 	    // URL is made from url and portnumber
 	    String url = (String) get(URL);
+
+	    if(!Utility.isWindows()) {
+		return url;
+	    }
+
 	    if(url != null) {
 		StringBuffer temp = new StringBuffer(url);
@@ -383,5 +496,16 @@
 	}
 
-	public void load() {
+	public boolean isModified() {
+	    return (lastModified != glisite_cfg.lastModified());
+	}
+
+	public void load(boolean reloadOnlyIfModified) {	    
+
+	    if(isModified()) {
+		lastModified = glisite_cfg.lastModified();
+	    } else if(reloadOnlyIfModified) {
+		return; // asked to reload only if modified. Don't reload since not modified
+	    }
+
 	    if(glisite_cfg.exists()) {
 		debug("Load: " + glisite_cfg.getAbsolutePath());
Index: /gli/trunk/src/org/greenstone/gatherer/gui/PreviewButton.java
===================================================================
--- /gli/trunk/src/org/greenstone/gatherer/gui/PreviewButton.java	(revision 18986)
+++ /gli/trunk/src/org/greenstone/gatherer/gui/PreviewButton.java	(revision 18987)
@@ -80,4 +80,9 @@
 	    return;
 	}
+
+	// check that the local library server is still running  - doesn't do anything if it's not supposed to be running
+	// !! Don't like this here
+	LocalLibraryServer.checkServerRunning();
+	
 	// FLI or GLI for GS2 case (no library_url)
 	preview_address = Configuration.library_url.toString() + "?c=" + CollectionManager.getLoadedCollectionName() + "&l=" + Configuration.getLanguage();
@@ -124,7 +129,5 @@
 	    
 	    configureHomeURL();
-	    // check that the local library server is still running  - doesn't do anything if its not supposed to be running
-	    // !! Don't like this here
-	    LocalLibraryServer.checkServerRunning();
+	    
 	    if (Gatherer.GS3) {
 		// we need to rerun the convert in case the format stuff has changed
Index: /gli/trunk/src/org/greenstone/gatherer/util/PortFinder.java
===================================================================
--- /gli/trunk/src/org/greenstone/gatherer/util/PortFinder.java	(revision 18987)
+++ /gli/trunk/src/org/greenstone/gatherer/util/PortFinder.java	(revision 18987)
@@ -0,0 +1,133 @@
+package org.greenstone.gatherer.util;
+
+/** Verbatim copy of what's in GS3's org.greenstone.server */
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.BindException;
+
+// When needing to use sockets, this class can be used to find an available port
+public class PortFinder {
+
+    // Port numbers range from 0 to 65,535. RESERVED PORTS are below 1025
+    // see http://www.gnu.org/software/hello/manual/libc/Ports.html
+    public static final int MAX_PORT = 65535;
+    public static final int PORTS_RESERVED = 1024;
+
+    // Considering a limited range of ports that will be reused (circular buffer)
+    public final int PORT_BASE;
+    public final int PORT_BLOCK_SIZE;
+
+    // Keep track of what port numbers we have checked for availability
+    private int nextFreePort;
+
+    // The socket port number that we will use
+    private int port = -1;
+
+
+    public PortFinder() {
+	this( PORTS_RESERVED+1, (MAX_PORT - PORTS_RESERVED) );
+    }
+
+    public PortFinder(int base, int blocksize) {
+	PORT_BASE = base;
+	PORT_BLOCK_SIZE = blocksize;
+
+	nextFreePort = PORT_BASE;
+    }
+
+    /** Circular buffer. Modifies the value of nextFreePort (the buffer index). */
+    private void incrementNextFreePort() {
+	int offset = nextFreePort - PORT_BASE;
+	offset = (offset + 1) % PORT_BLOCK_SIZE;
+	nextFreePort = PORT_BASE + offset;
+    } 
+
+    /** Finds the first available port in the range specified during Constructor call.
+     *  @return the number of an available port.	
+     */
+    public int findPortInRange() throws Exception {	
+	try {
+	    boolean foundFreePort = false;
+	    for(int i = 0; i < PORT_BLOCK_SIZE; i++) {
+		
+		if(isPortAvailable(nextFreePort)) {
+		    foundFreePort = true;
+		    break;
+		    
+		} else {
+		    incrementNextFreePort();
+		}
+	    }
+	    
+	    if(foundFreePort) {
+		// Free port number currently found becomes the port number of the socket
+		// that will be used
+		this.port = nextFreePort;
+		incrementNextFreePort();
+		
+	    } else {
+		throw new Exception("Cannot find an available port in the range " 
+				    + PORT_BASE + "-" + (PORT_BASE+PORT_BLOCK_SIZE)
+				    + "\nwhich is necessary for forcibly terminating wget.");
+	    }
+	    
+	}catch(IOException e) {
+	    System.err.println("Error when trying to find an available port. In PortFinder.findPort() " + e);
+	}
+	return port;
+    }
+
+    /** @return true if the portnumber is in the valid range. */
+    public static boolean isValidPortNumber(int portNum) {
+	return (portNum >= 0 && portNum <= MAX_PORT);
+    }
+
+    /** @return true if the portnumber is in the useable range. */
+    public static boolean isAssignablePortNumber(int portNum) {
+	return (portNum > PORTS_RESERVED && portNum <= MAX_PORT);
+    }
+
+    /** Finds an available port.
+     * @return the number of an available port.
+     */
+    public static int findAnyFreePort() throws Exception {	
+	ServerSocket tmpSocket = null;
+	int portNumber = -1;
+	try {
+	    // Creates a server socket, bound to the specified port.
+	    // A port of 0 creates a socket on any free port. 
+	    tmpSocket = new ServerSocket(0);
+	    portNumber = tmpSocket.getLocalPort();
+	    tmpSocket.close();
+	} catch(Exception e) {
+	    System.err.println("Unable to find a free port or close it. Got Exception: " + e);
+	    tmpSocket = null;
+	}
+	return portNumber;
+    }
+
+    /** @return true if the portnum is available for use */
+    public static boolean isPortAvailable(int portnum) {
+	ServerSocket tmpSocket = null;
+	try {
+	    tmpSocket = new ServerSocket(portnum);
+	    tmpSocket.close();
+	    //System.err.println("Port " + portnum + " not yet in use.");
+	    return true;
+	    
+	} catch(BindException ex){
+	    // "Signals that an error occurred while attempting to bind a 
+	    // socket to a local address and port. Typically, the port is 
+	    // in use, or the requested local address could not be assigned."
+	    System.err.println("Port " + portnum + " already in use or can't be assigned.");
+	    tmpSocket = null;
+	    return false;	    
+	} catch(Exception ex) { 
+	    // Some other problem creating or closing the server socket
+	    System.err.println("Problem creating or closing server socket at port " + portnum);
+	    tmpSocket = null;
+	    return false;
+	}
+    }
+}
