Index: other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/Manifest.MF
===================================================================
--- other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/Manifest.MF	(revision 15736)
+++ other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/Manifest.MF	(revision 15736)
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: org.greenstone.fedora.installer.GSearchInstaller
+
Index: other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/demoBrowseIndexToHtml.xslt
===================================================================
--- other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/demoBrowseIndexToHtml.xslt	(revision 15736)
+++ other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/demoBrowseIndexToHtml.xslt	(revision 15736)
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8"?> 
+<xsl:stylesheet version="1.0"
+		xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+		
+<!-- This xslt stylesheet presents a browseIndex page.
+-->
+	<xsl:output method="html" indent="yes" encoding="UTF-8"/>
+
+	<xsl:param name="ERRORMESSAGE" select="''"/>
+	
+	<xsl:variable name="FIELDNAME" select="/resultPage/browseIndex/@fieldName"/>
+	<xsl:variable name="INDEXNAME" select="/resultPage/@indexName"/>
+	<xsl:variable name="STARTTERM" select="/resultPage/browseIndex/@startTerm"/>
+	<xsl:variable name="TERMPAGESIZE" select="/resultPage/browseIndex/@termPageSize"/>
+	<xsl:variable name="TERMTOTAL" select="/resultPage/browseIndex/@termTotal"/>
+	<xsl:variable name="PAGELASTNO" select="/resultPage/browseIndex/terms/term[position()=last()]/@no"/>
+	<xsl:variable name="PAGELASTTERM" select="/resultPage/browseIndex/terms/term[position()=last()]/text()"/>
+
+	<xsl:include href="WEBSERVERPATH/webapps/fedoragsearch/WEB-INF/classes/config/rest/demoCommon.xslt"/>
+
+	<xsl:variable name="EQCHAR">
+		<xsl:choose>
+			<xsl:when test="$INDEXNAME = 'DemoOnZebra'">=</xsl:when>
+			<xsl:otherwise>:</xsl:otherwise>
+		</xsl:choose>
+	</xsl:variable>
+							
+	<xsl:template name="opSpecifics">
+		
+		<h2>browseIndex</h2>
+			<form method="get" action="rest">
+				<table border="3" cellpadding="5" cellspacing="0">
+					<tr>
+						<td>
+							<input type="hidden" name="operation" value="browseIndex"/>
+							Start term: <input type="text" name="startTerm" size="30" value="{$STARTTERM}"/> 
+							Field name: <select name="fieldName">
+					            <xsl:apply-templates select="browseIndex/fields"/>
+							</select>
+							<xsl:text> </xsl:text>Term page size: <input type="text" name="termPageSize" size="4" value="{$TERMPAGESIZE}"/> 
+							<xsl:text> </xsl:text><input type="submit" value="Browse"/>
+						</td>
+					</tr>
+					<tr>
+						<td>
+							<xsl:text> </xsl:text>Index name: 
+								<select name="indexName">
+									<xsl:choose>
+		<xsl:when test="$INDEXNAME='INDEX_NAME'">
+			<option value="INDEX_NAME" selected="true">INDEX_NAME</option>
+		</xsl:when>
+		<xsl:otherwise>
+			<option value="INDEX_NAME" selected="true">INDEX_NAME</option>
+		</xsl:otherwise>
+	</xsl:choose>
+								</select>
+							<xsl:text> </xsl:text>restXslt: 
+								<select name="restXslt">
+									<option value="demoBrowseIndexToHtml">demoBrowseIndexToHtml</option>
+									<option value="copyXml">no transformation</option>
+								</select>
+							<xsl:text> </xsl:text>resultPageXslt: 
+								<select name="resultPageXslt">
+									<option value="browseIndexToResultPage">browseIndexToResultPage</option>
+									<option value="copyXml">no transformation</option>
+								</select>
+							<xsl:text> </xsl:text>
+						</td>
+					</tr>
+				</table>
+			</form>
+			<p/>
+			<xsl:if test="$TERMTOTAL = 0 and $STARTTERM and $STARTTERM != '' ">
+				<p>No terms!</p>
+	 		</xsl:if>
+			<xsl:if test="$TERMTOTAL > 0">
+	 			<table border="0" cellpadding="5" cellspacing="0">
+					<tr>
+						<xsl:if test="99999999 > $TERMTOTAL">
+							<td><xsl:value-of select="$TERMTOTAL"/> terms found.
+							</td>
+	 					</xsl:if>
+					  <xsl:if test="$PAGELASTNO='' or $PAGELASTNO=' ' or $TERMTOTAL > $PAGELASTNO">
+	 					<td>
+						<form method="get" action="rest">
+							<input type="hidden" name="operation" value="browseIndex"/>
+							<input type="hidden" name="fieldName" value="{$FIELDNAME}"/>
+							<input type="hidden" name="indexName" value="{$INDEXNAME}"/>
+							<input type="hidden" name="startTerm" value="{$PAGELASTTERM}"/>
+							<input type="hidden" name="termPageSize" value="{$TERMPAGESIZE}"/>
+							<input type="submit" value="Next term page"/>
+						</form>
+	 					</td>
+	 				  </xsl:if>
+					</tr>
+				</table>
+				<table border="3" cellpadding="5" cellspacing="0" bgcolor="silver">
+					<xsl:apply-templates select="browseIndex/terms"/>
+				</table>
+	 		</xsl:if>
+	</xsl:template>
+
+	<xsl:template match="field">
+		<xsl:variable name="THISFIELDNAME" select="text()"/>
+		<xsl:choose>
+			<xsl:when test="$FIELDNAME=$THISFIELDNAME">
+				<option selected="true">
+				    <xsl:attribute name="value">
+				        <xsl:value-of select="$THISFIELDNAME"/>
+					</xsl:attribute>
+				    <xsl:value-of select="$THISFIELDNAME"/>
+				</option>
+			</xsl:when>
+			<xsl:otherwise>
+				<option>
+				    <xsl:attribute name="value">
+				        <xsl:value-of select="$THISFIELDNAME"/>
+					</xsl:attribute>
+				    <xsl:value-of select="$THISFIELDNAME"/>
+				</option>
+			</xsl:otherwise>
+		</xsl:choose>
+	</xsl:template>
+
+	<xsl:template match="term">
+		<tr>
+			<td>
+				<xsl:value-of select="@no"/>.
+				<a>
+					<xsl:variable name="TERM" select="text()"/>
+					<xsl:variable name="QUERYSTRING" select="concat('?operation=gfindObjects', '&amp;', 'indexName=', $INDEXNAME, '&amp;', 'query=', $FIELDNAME, $EQCHAR, '&#034;', $TERM, '&#034;')"/>
+					<xsl:attribute name="href"><xsl:value-of select="$QUERYSTRING"/>
+					</xsl:attribute>
+					<xsl:value-of select="$TERM"/>
+					[<xsl:value-of select="@fieldtermhittotal"/>]
+				</a>
+			</td>
+		</tr>
+	</xsl:template>
+	
+</xsl:stylesheet>	
+
+
+
+
+
+				
+
+
+
+
Index: other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/demoFoxmlToLucene.xslt
===================================================================
--- other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/demoFoxmlToLucene.xslt	(revision 15736)
+++ other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/demoFoxmlToLucene.xslt	(revision 15736)
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- $Id: demoFoxmlToLucene.xslt,v 1.3 2006/11/28 11:19:42 gertsp Exp $ -->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exts="xalan://dk.defxws.fedoragsearch.server.XsltExtensions" exclude-result-prefixes="exts" xmlns:zs="http://www.loc.gov/zing/srw/" xmlns:foxml="info:fedora/fedora-system:def/foxml#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:mods="http://www.loc.gov/mods/v3" xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/" xmlns:uvalibdesc="http://dl.lib.virginia.edu/bin/dtd/descmeta/descmeta.dtd" xmlns:uvalibadmin="http://dl.lib.virginia.edu/bin/admin/admin.dtd/"
+xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rel="info:fedora/fedora-system:def/relations-external#">
+        <xsl:output method="xml" indent="yes" encoding="UTF-8"/>
+        <xsl:param name="REPOSITORYNAME" select="repositoryName"/>
+        <xsl:variable name="PID" select="/foxml:digitalObject/@PID"/>
+        <xsl:variable name="docBoost" select="1.4*2.5"/>
+        <!-- or any other calculation, default boost is 1.0 -->
+
+        <xsl:template match="/">
+                <IndexDocument>
+                        <!-- The PID attribute is mandatory for indexing to work -->
+                        <xsl:attribute name="PID"><xsl:value-of select="$PID"/></xsl:attribute>
+                        <xsl:attribute name="boost"><xsl:value-of select="$docBoost"/></xsl:attribute>
+                        <!-- The following allows only active demo FedoraObjects to be indexed. -->
+                        <!-- <xsl:if test="foxml:digitalObject/foxml:objectProperties/foxml:property[@NAME='info:fedora/fedora-system:def/model#state' and @VALUE='Active']"> -->
+                        <xsl:if test="foxml:digitalObject/foxml:objectProperties/foxml:property[@NAME='info:fedora/fedora-system:def/model#state' and (@VALUE='Active' or @VALUE='Inactive')]">
+
+                                <xsl:if test="foxml:digitalObject/foxml:objectProperties/foxml:property[@NAME='http://www.w3.org/1999/02/22-rdf-syntax-ns#type' and @VALUE='FedoraObject']">
+                                        <xsl:if test="starts-with($PID,'greenstone')">
+                                        <!--    filter out annotations: -->
+                                        <!--
+                                        <xsl:choose>
+                                                <xsl:when test="foxml:digitalObject/foxml:datastream/foxml:datastreamVersion[last()]/foxml:xmlContent/rdf:RDF/rdf:Description/rel:isMemberOf[@rdf:resource='info:fedora/mura:annotations']">
+                                                </xsl:when>
+                                                <xsl:otherwise>
+                                                -->
+                                                        <xsl:apply-templates mode="activeDemoFedoraObject"/>
+                                                <!--
+                                                </xsl:otherwise>
+                                        </xsl:choose>
+                                        -->
+                                        </xsl:if>
+                                </xsl:if>
+
+                                </xsl:if>
+                </IndexDocument>
+        </xsl:template>
+
+        <xsl:template match="/foxml:digitalObject" mode="activeDemoFedoraObject">
+
+                <IndexField IFname="PID" index="UN_TOKENIZED" store="YES" termVector="NO" boost="2.5">
+                        <xsl:value-of select="$PID"/>
+                </IndexField>
+                <IndexField IFname="repositoryName" index="UN_TOKENIZED" store="YES" termVector="NO">
+                        <xsl:value-of select="$REPOSITORYNAME"/>
+                </IndexField>
+                <xsl:for-each select="foxml:objectProperties/foxml:property">
+                        <IndexField index="UN_TOKENIZED" store="YES" termVector="NO">
+                                <xsl:attribute name="IFname"><xsl:value-of select="concat('object.', substring-after(@NAME,'#'))"/></xsl:attribute>
+                                <xsl:value-of select="@VALUE"/>
+                        </IndexField>
+                </xsl:for-each>
+
+                <!--index all datastreamVersions: -->
+                <!--xsl:for-each select="foxml:datastream/foxml:datastreamVersion/foxml:xmlContent/dc:dc/*">
+                        <IndexField index="TOKENIZED" store="YES" termVector="YES">
+                                <xsl:attribute name="IFname"><xsl:value-of select="concat('dc.', substring-after(name(),':'))"/></xsl:attribute>
+                                <xsl:value-of select="text()"/>
+                        </IndexField>
+                </xsl:for-each-->
+
+                <!--index only last datastreamVersion: -->
+                <xsl:for-each select="foxml:datastream/foxml:datastreamVersion[last()]/foxml:xmlContent/oai_dc:dc/*">
+                        <IndexField index="TOKENIZED" store="YES" termVector="YES">
+                                <xsl:attribute name="IFname"><xsl:value-of select="concat('dc.', substring-after(name(),':'))"/></xsl:attribute>
+                                <xsl:value-of select="text()"/>
+                        </IndexField>
+                </xsl:for-each>
+
+                <xsl:for-each select="foxml:datastream/foxml:datastreamVersion[last()]/foxml:xmlContent/dc:dc/*">
+                        <IndexField index="TOKENIZED" store="YES" termVector="YES">
+                                <xsl:attribute name="IFname"><xsl:value-of select="concat('dc.', substring-after(name(),':'))"/></xsl:attribute>
+                                <xsl:value-of select="text()"/>
+                        </IndexField>
+                </xsl:for-each>
+
+                <xsl:for-each select="foxml:datastream/foxml:datastreamVersion[last()]/foxml:xmlContent/oai_dc:dc/*">
+                        <IndexField index="UN_TOKENIZED" store="YES" termVector="YES">
+                                <xsl:attribute name="IFname"><xsl:value-of select="concat('dc2.', substring-after(name(),':'))"/></xsl:attribute>
+                                <xsl:value-of select="text()"/>
+                        </IndexField>
+                </xsl:for-each>
+
+                <xsl:for-each select="foxml:datastream/foxml:datastreamVersion[last()]/foxml:xmlContent/dc:dc/*">
+                        <IndexField index="UN_TOKENIZED" store="YES" termVector="YES">
+                                <xsl:attribute name="IFname"><xsl:value-of select="concat('dc2.', substring-after(name(),':'))"/></xsl:attribute>
+                                <xsl:value-of select="text()"/>
+                        </IndexField>
+                </xsl:for-each>
+
+                <xsl:for-each select="foxml:datastream/foxml:datastreamVersion[last()]/foxml:xmlContent/mods:mods//*">
+                        <IndexField index="TOKENIZED" store="YES" termVector="NO">
+                                <xsl:attribute name="IFname"><xsl:value-of select="concat('mods.', substring-after(name(),':'))"/></xsl:attribute>
+                                <xsl:value-of select="text()"/>
+                        </IndexField>
+                </xsl:for-each>
+
+
+                <xsl:for-each select="foxml:datastream[@ID='RELS-EXT']/foxml:datastreamVersion[last()]/foxml:xmlContent/rdf:RDF/rdf:Description/*">
+                        <IndexField index="UN_TOKENIZED" store="YES" termVector="YES">
+                                <xsl:attribute name="IFname"><xsl:value-of select="concat('rdf.', translate(name(), ':', '.'))"/></xsl:attribute>
+                                        <xsl:value-of select="substring-after(@rdf:resource, 'info:fedora/')"/>
+                                        <xsl:value-of select="text()"/>
+                        </IndexField>
+                </xsl:for-each>
+
+
+                <xsl:for-each select="foxml:datastream">
+                        <IndexField>
+                                <xsl:attribute name="IFname">ds.fulltext</xsl:attribute>
+                                <xsl:attribute name="dsId"><xsl:value-of select="@ID"/></xsl:attribute>
+                                <xsl:attribute name="index">TOKENIZED</xsl:attribute>
+                                <xsl:attribute name="store">YES</xsl:attribute>
+                                <xsl:attribute name="termVector">NO</xsl:attribute>
+                        </IndexField>
+
+                        <IndexField IFname="ds.label" index="TOKENIZED" store="YES" termVector="YES">
+                                <xsl:value-of select="foxml:datastreamVersion[last()]/@LABEL"/>
+                        </IndexField>
+                </xsl:for-each>
+
+        </xsl:template>
+
+</xsl:stylesheet>
Index: other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/fedoragsearch.properties
===================================================================
--- other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/fedoragsearch.properties	(revision 15736)
+++ other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/fedoragsearch.properties	(revision 15736)
@@ -0,0 +1,35 @@
+# $Id: fedoragsearch.properties,v 1.2 2006/10/13 14:17:53 gertsp Exp $
+
+#fedoragsearch.soapBase	  			= PROTOCOL://SOAPHOSTPORT/WEBAPPNAME/AXISSERVLETNAME #from web.xml
+fedoragsearch.soapBase	    		= http://HOST:PORT/fedoragsearch/services
+
+fedoragsearch.deployFile = CATALINA_HOME/webapps/fedoragsearch/WEB-INF/classes/config/deploy.wsdd
+
+#rest
+fedoragsearch.defaultNoXslt							= copyXml
+fedoragsearch.defaultUpdateIndexRestXslt			= demoUpdateIndexToHtml
+fedoragsearch.defaultGfindObjectsRestXslt			= demoGfindObjectsToHtml
+fedoragsearch.defaultBrowseIndexRestXslt			= demoBrowseIndexToHtml
+fedoragsearch.defaultGetRepositoryInfoRestXslt		= demoGetRepositoryInfoToHtml
+fedoragsearch.defaultGetIndexInfoRestXslt			= demoGetIndexInfoToHtml
+
+#resultPage
+fedoragsearch.maxPageSize									= 50
+fedoragsearch.defaultBrowseIndexTermPageSize					= 20
+fedoragsearch.defaultGfindObjectsHitPageSize				= 10
+#fedoragsearch.defaultGfindObjectsSnippetsMax				= 0 means no snippet generation
+fedoragsearch.defaultGfindObjectsSnippetsMax				= 3
+#fedoragsearch.defaultGfindObjectsFieldMaxLength			= 0 means no max length
+fedoragsearch.defaultGfindObjectsFieldMaxLength				= 100
+fedoragsearch.defaultGfindObjectsSort = score desc
+
+#fedoragsearch.mimeTypes	= text/plain text/html application/pdf -potential
+#fedoragsearch.mimeTypes	= text/plain text/html application/pdf -supported
+fedoragsearch.mimeTypes	= text/plain text/html application/pdf
+
+#fedoragsearch.repositoryNames = MyRep1 MyRepN configured in config/repository/MyRepI
+fedoragsearch.repositoryNames = REPOSITORY_NAME
+
+#fedoragsearch.indexNames = MyIndex1 MyIndexN configured in config/index/MyIndexI
+#fedoragsearch.indexNames = DemoOnLucene
+fedoragsearch.indexNames = INDEX_NAME
Index: other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/gsearch.properties
===================================================================
--- other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/gsearch.properties	(revision 15736)
+++ other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/gsearch.properties	(revision 15736)
@@ -0,0 +1,20 @@
+#fixed properties
+
+#paths
+gsearch.base=http://HOST:PORT/fedoragsearch
+
+fedoragsearch.dir=CATALINA_HOME/webapps/fedoragsearch
+classes.dir=CATALINA_HOME/webapps/fedoragsearch/WEB-INF/classes
+config.dir=CATALINA_HOME/webapps/fedoragsearch/WEB-INF/classes/config/
+index.dir=CATALINA_HOME/webapps/fedoragsearch/WEB-INF/classes/config/index
+repository.dir=CATALINA_HOME/webapps/fedoragsearch/WEB-INF/classes/config/repository
+rest.dir=CATALINA_HOME/webapps/fedoragsearch/WEB-INF/classes/config/rest
+soap.client.dir=CATALINA_HOME/webapps/fedoragsearch/client
+
+fgsrepository.fedoraObjectDir=FEDORA_HOME/data/objects
+lucene.indexes.dir=FEDORA_HOME/data/luceneIndexes
+fedora.config.dir=FEDORA_HOME/server/config
+
+# processes (without the OS-specific script extensions .sh/.bat)
+fedora.start=CATALINA_HOME/bin/startup
+fedora.stop=CATALINA_HOME/bin/shutdown
Index: other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/index.properties
===================================================================
--- other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/index.properties	(revision 15736)
+++ other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/index.properties	(revision 15736)
@@ -0,0 +1,19 @@
+# $Id: index.properties,v 1.2 2006/10/13 14:17:53 gertsp Exp $
+
+# Properties for the FedoraIndex (formerly DemoOnLucene) index
+
+fgsindex.indexName					= INDEX_NAME
+
+fgsindex.operationsImpl				= dk.defxws.fgslucene.OperationsImpl
+
+fgsindex.defaultUpdateIndexDocXslt		= demoFoxmlToLucene
+fgsindex.defaultUpdateIndexResultXslt	= updateIndexToResultPage
+fgsindex.defaultGfindObjectsResultXslt	= gfindObjectsToResultPage
+fgsindex.defaultBrowseIndexResultXslt	= browseIndexToResultPage
+fgsindex.defaultGetIndexInfoResultXslt	= copyXml
+
+#fgsindex.indexDir				= <...> The directory must exist
+fgsindex.indexDir = FEDORA_HOME/data/luceneIndexes/INDEX_NAME
+
+fgsindex.analyzer = org.apache.lucene.analysis.standard.StandardAnalyzer
+fgsindex.defaultQueryFields = dc.description dc.title dc.creator dc.identifier ds.label ds.fulltext
Index: other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/indexInfo.xml
===================================================================
--- other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/indexInfo.xml	(revision 15736)
+++ other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/indexInfo.xml	(revision 15736)
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resultPage indexName="INDEX_NAME">
+ <indexInfo>
+  <IndexShortName>INDEX_NAME</IndexShortName>
+  <IndexLongName>Fedora index on Lucene for ingested Greenstone documents</IndexLongName>
+  <EngineUrl>http://lucene.apache.org/</EngineUrl>
+  <EngineShortName>Lucene</EngineShortName>
+  <EngineLongName>Apache Lucene project</EngineLongName>
+  <EngineDescription>The Apache Lucene project develops open-source search software.</EngineDescription>
+  <EngineTags>lucene apache open-source search software</EngineTags>
+  <EngineImage>http://lucene.apache.org/lucene_green_300.gif</EngineImage>
+  <QueryLanguage>See e.g. http://lucene.apache.org/java/docs/queryparsersyntax.html</QueryLanguage>
+  <SampleSearch>dc.title:fedora AND dc.creator:"thornton staples"</SampleSearch>
+  <IndexFieldNameList>PID, repositoryName,<BR/> 
+                      property.label, property.contentModel, property.createdDate,<BR/>
+                      property.lastModifiedDate, property.state, property.type,<BR/>
+                      dc.creator, dc.date, dc.description, dc.format, dc.identifier,<BR/>
+                      dc.publisher, dc.relations, dc.right, dc.source,<BR/>
+                      dc.subject, dc.title,<BR/>
+                      DS2.text,<BR/>
+                      others depending on the indexing stylesheet.
+  </IndexFieldNameList>
+  <EngineDeveloper>Apache Lucene project</EngineDeveloper>
+  <EngineContact>info@lucene.apache.org</EngineContact>
+  <EngineAttribution>The Apache Lucene project &#169; 2005, The Apache Lucene project,
+   All Rights Reserved</EngineAttribution>
+ </indexInfo>
+</resultPage>
Index: other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/log4j.xml
===================================================================
--- other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/log4j.xml	(revision 15736)
+++ other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/log4j.xml	(revision 15736)
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- $Id: log4j.xml,v 1.1 2006/08/09 15:36:08 cwilper Exp $ -->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+        
+  <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
+    <layout class="org.apache.log4j.PatternLayout">
+      <param name="ConversionPattern" value="%p %d (%c{1}) %m%n"/>
+    </layout>
+  </appender>
+  
+  <appender name="FILEOUT" class="org.apache.log4j.FileAppender">
+    <param name="File" value="LOGPATH/fedoragsearch.log"/>
+    <layout class="org.apache.log4j.PatternLayout">
+      <param name="ConversionPattern" value="%p %d (%c{1}) %m%n"/>
+    </layout>
+  </appender>
+  
+  <logger name="dk.defxws.fedoragsearch" additivity="false">
+    <level value="INFO" />
+    <appender-ref ref="FILEOUT"/>
+  </logger>
+  
+  <logger name="dk.defxws.fgszebra" additivity="false">
+    <level value="INFO" />
+    <appender-ref ref="FILEOUT"/>
+  </logger>
+  
+  <logger name="dk.defxws.fgslucene" additivity="false">
+    <level value="INFO" />
+    <appender-ref ref="FILEOUT"/>
+  </logger>
+
+  <root>
+    <level value="WARN" />
+    <appender-ref ref="STDOUT"/>
+  </root>
+  
+</log4j:configuration>
Index: other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/org/greenstone/fedora/installer/GSearchInstaller.java
===================================================================
--- other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/org/greenstone/fedora/installer/GSearchInstaller.java	(revision 15736)
+++ other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/org/greenstone/fedora/installer/GSearchInstaller.java	(revision 15736)
@@ -0,0 +1,1493 @@
+package org.greenstone.fedora.installer;
+
+import java.util.*;
+import java.util.Map.Entry;
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ByteArrayInputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import java.io.StringWriter;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.DocumentBuilder;
+import org.w3c.dom.Element;
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Node;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.EntityResolver;
+
+import javax.swing.*;
+import java.awt.*;
+
+/** 
+ * This class essentially follows the instructions at 
+ * http://drama.ramp.org.au/cgi-bin/trac.cgi/wiki/InstallingFedoraGSearch
+ * in order to install Fedora Generic Search from their optimised 
+ * fedoragsearch.war file. (I've also tested it on the original war file 
+ * fedoragsearch.war available from http://defxws2006.cvt.dk/fedoragsearch/
+ * and it works.)
+ * It then does a few minor extra things in order to make Fedora Generic  
+ * Search work specifically with a Fedora repository of Greenstone documents.
+ * @author ak19
+*/
+public class GSearchInstaller {
+	
+	/** This EntityResolver allows the XML parser to ignore validating
+	 * against the DTD specified in the XML since it is pointing to the
+	 * wrong location. After parsing, we will put the DTD back in.
+	 * (Package class, only used here.)
+	 * See http://forum.java.sun.com/thread.jspa?threadID=284209&forumID=34
+	*/
+	static class IgnoreDTDEntityResolver implements EntityResolver {
+		public final String systemID; 
+		
+		/** Constructor that 'resolves' (by ignoring) DTDs for the given 
+		 * systemID.
+		 * @param systemID is the DTD path to be ignored, given
+		 * in the XML file as a SYSTEM property. */
+		public IgnoreDTDEntityResolver(String systemID) {
+			this.systemID = systemID;
+		}
+		
+		/** If the systemId matches the one this IgnoreDTDEntityResolver
+		 * was created for, then the specified DTD is skipped. 
+		 * @param publicId the public ID in the DOCTYPE (not used here).  
+		 * @param systemId is the SYSTEM id in the DOCTYPE that specifies
+		 * the DTD which is to be checked against this 
+		 * IgnoreDTDEntityResolver object's systemID. */
+        public InputSource resolveEntity(String publicId, String systemId)
+               throws SAXException, java.io.IOException
+	    {
+        	// The systemId passed in is the entire local file path, which is 
+        	// wrong. So we extract just the filename first and then make the 
+        	// comparison
+		int index = systemId.lastIndexOf(File.separator);
+		if(index == -1) {
+			index = systemId.lastIndexOf("/"); // tends to be / even on Windows
+		}
+        	systemId = systemId.substring(index+1, systemId.length());
+        	
+        	if(systemId.equals(this.systemID)) {
+        		// this deactivates the DTD
+        		return new InputSource(new ByteArrayInputStream(
+        			"<?xml version='1.0' encoding='UTF-8'?>".getBytes()));
+        	}
+        	else return null;
+	    }
+	}
+	
+	// Some String class constants
+	public static final String FEDORAGSEARCH = "fedoragsearch";
+	public static final String TOMCAT = "tomcat";
+	
+	// Custom constants for important and much-used environment variables
+	public final String FEDORA_HOME;
+	public final String CATALINA_HOME;
+	public final String scriptExtension; // .bat for Windows and .sh for Linux
+	
+	/** Reads from gsearch.properties file which contains default 
+	 * directory paths (using variables like FEDORA_HOME and 
+	 * CATALINA_HOME) and HOST and PORT, and sets these to custom values.
+	 * Some of the keys in the properties file include executable
+	 * processes (such as for starting and stopping tomcat).
+	*/ 
+	protected Properties gSearchProperties;
+
+	// custom values
+	public final String indexName;
+	public final String repositoryName;
+	public final String host;
+	public final String port;
+	public final String fedoraUsername; // username of fedoraServer
+	public final String fedoraPassword; // password of fedoraServer
+	public final String gsearchWarFileName; // the path to the fedorgsearch warfile
+	
+	// some internal class constants
+	protected static String PROP_REPOS = "repository";
+	protected static String PROP_INDEX = "index";
+	protected static String PROP_UNAME = "username";
+	protected static String PROP_PASSW = "password";
+	protected static String PROP_HOST = "host";
+	protected static String PROP_PORT = "port";
+	protected static String PROP_FILE = "file";
+	/** Default initialisation/customisation values. In case the Installer
+	 * was not provided all parameters, these are used as fallback values */
+	protected static final Properties defaults = new Properties();
+	static {
+		defaults.setProperty(PROP_REPOS, "Fedora");
+		defaults.setProperty(PROP_INDEX, "FedoraIndex");
+		defaults.setProperty(PROP_UNAME, "fedoraAdmin");
+		defaults.setProperty(PROP_PASSW, "");
+		defaults.setProperty(PROP_HOST, "localhost");
+		defaults.setProperty(PROP_PORT, "8080");
+		defaults.setProperty(PROP_FILE, "fedoragsearch.war"); 
+	}	
+	
+	/** GSearchInstaller constructor sets all custom values to defaults */
+	public GSearchInstaller() throws Exception {
+		this(defaults); // use the default options
+	}
+	
+	/** GSearchInstaller constructor sets the custom values */
+	public GSearchInstaller(Properties options) throws Exception 
+	{			
+		System.out.println(
+			"Preparing for installation of Fedora Generic Search...");
+		
+		// for those options not set, we use the fallbacks in 
+		// the defaults Properties
+		this.repositoryName = options.getProperty(PROP_REPOS, 
+				defaults.getProperty(PROP_REPOS));
+		this.indexName = options.getProperty(PROP_INDEX, 
+				defaults.getProperty(PROP_INDEX));
+		this.fedoraUsername = options.getProperty(PROP_UNAME,
+				defaults.getProperty(PROP_UNAME));
+		this.fedoraPassword = options.getProperty(PROP_PASSW,
+				defaults.getProperty(PROP_PASSW));
+		this.host = options.getProperty(PROP_HOST,
+				defaults.getProperty(PROP_HOST));
+		this.port = options.getProperty(PROP_PORT,
+				defaults.getProperty(PROP_PORT));
+		this.gsearchWarFileName = options.getProperty(PROP_FILE,
+				defaults.getProperty(PROP_FILE));
+		
+		// don't need options anymore
+		options.clear();
+		options = null;
+		
+		// Set the script extension to .sh or .bat depending on the OS
+		if(System.getProperty("os.name").toLowerCase().contains("windows")) {
+			// can be "Windows XP" or something
+			scriptExtension = ".bat";
+		} else { // "Linux", which would also be for Mac
+			scriptExtension = ".sh";
+		}
+		
+		// Set the environment variables
+		FEDORA_HOME = System.getenv("FEDORA_HOME");
+		if(FEDORA_HOME == null) {
+			throw new Exception("Environment variable FEDORA_HOME not set."
+					+ " Unable to proceed");
+		}
+		
+		String catalina = System.getenv("CATALINA_HOME");
+		if(catalina != null) {
+			CATALINA_HOME = catalina;
+		} else { 
+			// set it to FEDORA_HOME/tomcat
+			CATALINA_HOME = FEDORA_HOME+System.getProperty("file.separator")+TOMCAT;
+			System.err.println("Environment variable CATALINA_HOME " 
+				+ "not set...\nSetting it to FEDORA_HOME/tomcat: " + CATALINA_HOME);
+		}
+		
+		// User information
+		System.out.println("\tFEDORA_HOME:\t" + this.FEDORA_HOME);
+		System.out.println("\tCATALINA_HOME:\t" + this.CATALINA_HOME);
+		System.out.println("\tFedora host:\t" + this.host);
+		System.out.println("\tFedora port:\t" + this.port);
+		System.out.println("\tRepository name:\t" + this.repositoryName);
+		System.out.println("\tIndex name:\t" + this.indexName);
+		
+		// Load the all important gsearchProperties first and 
+		// customise them using the environment variables just set
+		System.out.println("Customising properties for installation:");
+		gSearchProperties = customiseProperties("gsearch.properties", true);
+	}
+	
+	public void install() throws Exception {
+		System.out.println(
+			"Beginning installation of Fedora Generic Search...");
+		// (1) Now we will try moving the fedoragsearch.war file into 
+		// fedora tomcat's webapps folder and unpack it there
+		// TODO: check gsearchWarFileName's filename is fedoragsearch.war
+		File gsearchWarFile = new File(gsearchWarFileName);
+		moveUnpackWarFile(gsearchWarFile);
+		
+		// (2) Ensure that lucene.indexes directory exists inside FEDORA_HOME
+		// (not inside FedoraGSearch)
+		createLuceneIndexDir();
+		
+		System.out.println("Customising files for installation...");
+		// (3) Editing $CATALINA_HOME/webapps/fedoragsearch/WEB-INF/classes/log4j.xml
+		// Changing tag <param name="File" value="LOGPATH/fedoragsearch.log"/>
+		// to <param name="File" value="FEDORA_HOME/server/logs/fedoragsearch.log"/>
+		String xmlFileName="log4j.xml";
+		String dtdFileName="log4j.dtd";
+		InputStream xmlSource = ClassLoader.getSystemResourceAsStream(
+				xmlFileName);
+		// We want to read the XML file but as there is a doctype
+		// referring to a dtd file located elsewhere, we need
+		// to temporarily ignore the dtd while reading the XML
+		Document doc = readXML(xmlSource, dtdFileName, xmlFileName); 
+		
+		// Now make the necessary changes to the file:
+		// Find the element <param value="xxxLOGPATHxxx"/>
+		// and replace just the LOGPATH bit with FEDORA_HOME
+		String logPath = FEDORA_HOME + "/server/logs/fedoragsearch.log";
+		logPath = logPath.replace(File.separator, "/"); // xml requires the "/"
+		if(!this.replaceElementWithAttrValue(doc, "param", "value", 
+						     "LOGPATH", logPath, true, false)) {
+		    // try again, the element may not contain variable LOGPATH
+		    // but it will contain "fedoragsearch.log"
+		    this.replaceElementWithAttrValue(doc, "param", "value", 
+				"fedoragsearch.log", logPath+"/fedoragsearch.log", true, true);
+		}
+
+		// Finally write out the file to the correct location, and put the 
+		// doctype containing the dtd back in.
+		File xmlFile = new File(gSearchProperties.getProperty("classes.dir"), 
+				xmlFileName);
+		writeXML(doc, xmlFile, dtdFileName);
+		
+		// (4) Copy and customise default property file fedoragsearch.properties
+		// Read from properties file with defaults, replacing placeholders 
+		// in the default properties with the custom (environment) variables 
+		// specified
+		String propFileName = "fedoragsearch.properties"; 
+		Properties properties = customiseProperties(propFileName, false);
+		copyPropFile(properties,	// the customised properties
+			propFileName, 			// defaults file to customise
+			new File(gSearchProperties.getProperty("config.dir")) // output path
+		);
+		
+		// (5) Rename $CATALINA_HOME/webapps/fedoragsearch/WEB-INF/classes/config/repository/REPOSNAME 
+		// as "repositoryName", since we want to reuse some of the files
+		if(!replaceDir(gSearchProperties.getProperty("repository.dir"),
+				"REPOSNAME", repositoryName)) 
+		{
+		    if(!replaceDir(gSearchProperties.getProperty("repository.dir"),
+			       "DemoAtDtu", repositoryName)) {
+		    	replaceDir(gSearchProperties.getProperty("repository.dir"),
+		    			"Fedora", repositoryName);
+		    }
+		}
+			
+		// (6) Copy and customise repositoryProperties
+		propFileName = "repository.properties";
+		properties = customiseProperties(propFileName, false);
+		// add fedora server username and password properties
+		properties.setProperty("fgsrepository.fedoraUser", this.fedoraUsername);
+		properties.setProperty("fgsrepository.fedoraPass", this.fedoraPassword);
+		copyPropFile(properties, // the customised properties
+			propFileName, 		 // the output file name (same as input file)
+			new File(gSearchProperties.getProperty("repository.dir"), 
+					repositoryName) // output file path 
+		); 
+		
+		// (7) Initially, make an as-is copy of the repositoryInfo file
+		// Later can get user input on customising it
+		File reposInfoXML = new File(gSearchProperties.getProperty("repository.dir")
+				+File.separator+repositoryName, "repositoryInfo.xml");
+		// We need to replace the existing reposInfoXML with the customised one
+		copyTemplateFile("repositoryInfo.xml", reposInfoXML, true); 
+				// true="replace"
+		
+		// (8) Rename $CATALINA_HOME/webapps/fedoragsearch/WEB-INF/classes/config/index/DemoOnLucene 
+		// to "indexName", since it contains other files that are to be used
+		// and will remain unchanged
+		if(!replaceDir(gSearchProperties.getProperty("index.dir"), 
+				"DemoOnLucene", indexName)) {
+			replaceDir(gSearchProperties.getProperty("index.dir"), 
+					"FedoraIndex", indexName); 
+		}
+		
+		// (9) Copy and customise indexProperties
+		propFileName = "index.properties";
+		properties = customiseProperties(propFileName, false);
+		// add fedora server username and password properties
+		copyPropFile(properties, // the customised properties
+			propFileName, 		 // the output file name (same as input file)
+			new File(gSearchProperties.getProperty("index.dir"), 
+					indexName) // output file path 
+		);
+		
+		// (10) Editing $CATALINA_HOME/webapps/fedoragsearch/WEB-INF/
+		// classes/config/index/FedoraIndex/indexInfo.xml
+		// Customising tags <resultPage indexName="INDEX_NAME"> 
+		// and <IndexShortName>INDEX_NAME</IndexShortName>
+		xmlFileName="indexInfo.xml";
+		xmlSource = ClassLoader.getSystemResourceAsStream(
+				xmlFileName);
+		// Read the XML file. There is no doctype 
+		// referring to any dtd file in indexInfo.xml
+		doc = readXML(xmlSource, "", xmlFileName); 
+		// Now make the necessary changes to the file:
+		// Find the element <resultPage indexName="xxxINDEX_NAMExxx"/>
+		// and replace just the INDEX_NAME bit with indexName
+		replaceElementWithAttrValue(doc, "resultPage", "indexName", 
+				"INDEX_NAME", indexName, true, false);
+		// Find the element <IndexShortName>xxxINDEX_NAMExxx</IndexShortName>
+		// and replace just the INDEX_NAME bit with indexName
+		replaceElementWithValue(doc, 
+				"IndexShortName", "INDEX_NAME", indexName, true, false);
+				
+		// Finally write out the file to the correct location.
+		// Output location should include indexName:
+		xmlFile = new File(
+			gSearchProperties.getProperty("index.dir")+File.separator+indexName,
+			xmlFileName);
+		writeXML(doc, xmlFile, ""); // no dtd to add back in this time.
+				
+		// (11) Enter $CATALINA_HOME/webapps/fedoragsearch/WEB-INF/classes/config/rest. 
+		// (12) Edit the stylesheets in here to set the path to your fedora/tomcat.
+		String restPath = gSearchProperties.getProperty("rest.dir");
+		String[] xsltFiles = { "demoBrowseIndexToHtml.xslt", 
+			"demoGetIndexInfoToHtml.xslt", "demoGetRepositoryInfoToHtml.xslt",
+			"demoGfindObjectsToHtml.xslt", "demoUpdateIndexToHtml.xslt" };
+		for(int i = 0; i < xsltFiles.length; i++) {
+			File outputFile = null; 
+			// File demoBrowseIndexToHtml.xslt will be customised from
+			// a template. Otherwise, the input file we read the DOM from 
+			// will later be the file we write it out to
+			if(xsltFiles[i].equals("demoBrowseIndexToHtml.xslt")) { //if(i == 0) { 
+				// Then don't read from restPath, read local template
+				xmlSource = ClassLoader.getSystemResourceAsStream(
+						xsltFiles[i]); // get template demoBrowseIndexToHtml
+				// Write to restPath
+				outputFile = new File(restPath, xsltFiles[i]);
+				// Read the XML stream (no dtd to worry about)
+				doc = readXML(xmlSource, "", xsltFiles[i]);
+			} else {
+				// Output file written to the same location
+				xmlFile = new File(restPath, xsltFiles[i]);
+				outputFile = xmlFile;
+				// Read the XML file (no dtd to worry about)
+				doc = readXML(xmlFile, "", xmlFile.getAbsolutePath());
+			} 
+			
+			// Now make the necessary changes to the file:
+			// Find the element <xsl:include href="xxxWEBSERVERPATHxxx"/>
+			// and replace just the WEBSERVERPATH bit with CATALINA_HOME
+			if(!replaceElementWithAttrValue(doc, "xsl:include", "href", 
+				"WEBSERVERPATH", CATALINA_HOME, true, false)) {
+			    // try again, the element may not contain variable LOGPATH
+			    // but it will contain "fedoragsearch.log"
+			    replaceElementWithAttrValue(doc, "xsl:include", "href", 
+				"demoCommon.xslt", 
+				CATALINA_HOME+"/webapps/fedoragsearch/WEB-INF/classes/config/rest/demoCommon.xslt", 
+				true, true);
+			}
+
+			// (13) Edit the file demoBrowseIndexToHtml.xslt and replace the
+			// occurrences of indexName in <select name="index">...</select> 
+			if(xsltFiles[i].equals("demoBrowseIndexToHtml.xslt")) { //if(i == 0) {
+				replaceElementWithAttrValue(doc, 
+					"xsl:when", "test", "INDEX_NAME", indexName, true, false);
+				// two occurrences of the following, so pass false
+				// to ensure it's not a onceOnly replacement, but 
+				// replace as often as it occurs.
+				replaceElementWithAttrValue(doc, "option", 
+					"value", "INDEX_NAME", indexName, false, false); 
+				// again, two occurrences, so replace multiple times
+				replaceElementWithValue(doc, "option", 
+					"INDEX_NAME", indexName, false, false);
+			}
+			
+			// Finally write out the XML to the specified output file
+			this.writeXML(doc, outputFile, ""); // no dtd to add back in
+			outputFile = null;
+		}
+		
+		// (14) Edit the fedora.fcfg file to configure Fedora for automatic 
+		// updates to notify notify Fedora GSearch of object changes
+		// for reindexing. Also checks that "greenstone" is in the list of
+		// Fedora-recognised PIDs
+		customiseFedoraConfigFile();
+				
+		// (New) Now finish off the installation with the steps described in 
+		// gs3-webservices-democlient/docs/HowToFiles/4InstallingFedoraGSearch.html
+		// We're further customising it for greenstone here: only PIDS=greenstone
+		// get indexed
+		// Copy the demoFoxmlToLucene.xslt template into the indexName dir.			
+		String demoFoxmlToLucene = "demoFoxmlToLucene.xslt";
+		// output file path is in the indexName directory:
+		File dest = new File(
+			gSearchProperties.getProperty("index.dir")+File.separator+indexName, 
+			demoFoxmlToLucene); 
+		// copy the template and replace the existing file:
+		this.copyTemplateFile(demoFoxmlToLucene, dest, true);
+		dest = null;
+		
+		System.out.println("Customising done.");
+		
+		// (15) Restart the tomcat server
+		final String stopFedora 
+			= gSearchProperties.getProperty("fedora.stop")+scriptExtension;
+		final String startFedora 
+			= gSearchProperties.getProperty("fedora.start")+scriptExtension;
+		
+		this.runProcess(new String[]{stopFedora}, false);
+		// wait for fedora to stop before restarting
+		waitForServerToStop();
+		this.runProcess(new String[]{startFedora}, false);
+			
+		
+		// This time, no way to test whether fedora server (and gsearch) are ready
+		System.out.print("Waiting for the fedora server to become ready...");
+		waitForFedoraServer();
+		
+	     // Finished installation
+		System.out.println("\nFinished installing Fedora Generic Search.");
+	}
+	
+	/** Indexes the contents of the repository(name) specified during Fedora
+	 * Generic Search installation. To do so, it runs the FedoraGenericSearch's 
+	 * runSOAPClient.sh with "host:port updateIndex fromFoxmlFiles". 
+	 * @param emptyFirst means the index will be created from scratch by first
+	 * executing runSOAPClient.sh with the arguments 
+	 * "host:port updateIndex createEmpty" before updating from FOXML files.*/
+	public void indexGreenstoneContents(boolean emptyFirst) throws Exception {
+		System.out.println("\nBeginning indexing of repository \"" 
+			+ this.repositoryName + "\"...");
+		
+		// There is a problem running the runSOAPClient script that is part of 
+		// the fedoragsearch installation: it has relative paths in its classpaths. 
+		// This means we have to cd into the folder and execute the script from 
+		// within the folder containing the script. cd would be a process, then
+		// executing the script would be another, independent process by which time
+		// we're no longer in the correct folder again.
+		// Therefore, this method works with a template file for the script: the 
+		// place-holder path will be replaced with the absolute path to the LIB 
+		// files needed by the script. And then the script can be executed from
+		// wherever.
+		
+		try {
+			// We're going to replace the place-holders in the template script, 
+			// and put a modified copy of the script in folder soap.client.dir 
+			java.io.BufferedReader in = new java.io.BufferedReader(
+	        	new java.io.InputStreamReader(
+	        		ClassLoader.getSystemResourceAsStream("runSOAPClient"
+	        			+this.scriptExtension)));
+	        			
+			File soapClientScript = new File(
+				gSearchProperties.getProperty("soap.client.dir"), 
+				"runSOAPClient" +this.scriptExtension);
+			OutputStream out = new FileOutputStream(soapClientScript);
+			
+			// Placeholder's replacement value: denotes the classpath  
+			// directory for the LIB folder mentioned in the script.
+			final String FEDORA_GSEARCH 
+			    = gSearchProperties.getProperty("fedoragsearch.dir");
+			// Read the contents of the template file line by line. 
+			// Do the replacements and write the lines out to the file.
+			String line = null;
+			while((line = in.readLine()) != null) {
+			    line = line.replace("FEDORA_GSEARCH", FEDORA_GSEARCH);
+			    line += "\n"; // append new line at the end of each line
+			    out.write(line.getBytes());
+			}
+			in.close();
+			out.close();
+			in = null;
+			out = null;
+			
+			// need to make the script executable on Linux
+			if(System.getProperty("os.name").equalsIgnoreCase("Linux")) {
+				this.runProcess(new String[]{"chmod", "u+x", 
+					soapClientScript.getAbsolutePath()}, true);
+			}
+			
+			// Now run the two steps of the script: 
+			// "updateIndex createEmpty" 
+			if(emptyFirst) {
+				this.runProcess(new String[]{ soapClientScript.getAbsolutePath(), 
+					host+":"+port, "updateIndex", "createEmpty" }, true);
+			}
+			
+			// "updateIndex fromFoxmlFiles"
+			this.runProcess(new String[]{ soapClientScript.getAbsolutePath(), 
+				  host+":"+port, "updateIndex", "fromFoxmlFiles" }, false);
+			
+			soapClientScript = null;
+			
+			System.out.println("Finished indexing repository \"" 
+				+ this.repositoryName + "\".");
+			System.out.println("Try visiting the Fedora Generic Search REST url" 
+				+ "\n\t(http://"+host+":"+port+"/fedoragsearch/rest"
+				+ "\n\tor https://"+host+":"+port+"/fedoragsearch/rest)");
+		} catch(IOException e){
+			System.out.println("Error when copying runSOAPClient template file.");
+			throw(e);	
+		} catch(Exception e){
+			System.out.println("Error when executing updateIndex.");
+			throw(e);	
+		}
+	}
+	
+	
+	//****************************HELPER METHODS****************************/
+	/** Method that will run the process associated with the gSearchProperties key. 
+	 * Waits until the process is executed.
+	 * @param args signify the executable process and its arguments. The first element 
+	 * must be the key into gSearchProperties whose value denotes the executable process 
+	 * that is to be run. Subsequent elements are the actual arguments to that process. 
+	 * @param ignoreWindows if true will not plug the cmd /c start "" at the start of
+	 * the arguments. If false, and only of the OS is windows, then these additional 
+	 * arguments get prepended to those already in the args array. */
+	protected int runProcess(String[] args, boolean ignoreWindows) throws Exception {
+	    // for Windows, we need to prepend extra arguments to launch a process
+	    // (Question remains: how to get rid of cmd consoles of child processes?)
+	    if(!ignoreWindows && System.getProperty("os.name").toLowerCase().contains("windows")) {
+		String[] tmp = args;
+		args = new String[args.length+4];
+		args[0] = "cmd"; 
+		args[1] = "/c";
+		args[2] = "start";
+		args[3] = "/MIN"; // launches console minimised
+		int j = 4;
+		for(int i = 0; i < tmp.length; i++) {
+		    args[j++] = tmp[i];
+		}
+		tmp = null;
+	    }
+	    System.out.print("Running processCommand: ");
+	    for(int i = 0; i < args.length; i++) {
+		System.out.print(args[i] + " ");
+	    }
+	    
+	    Process process = Runtime.getRuntime().exec(args);
+	    int exitValue = process.waitFor();
+	    System.out.println("\n\tExit value is: " + exitValue);
+	    // set local reference to null in case args was dynamically allocated
+	    args = null; 
+	    
+	    process.destroy();
+	    return exitValue;
+	}
+	
+	/** 
+	 * Loads properties from the property file denoted by propFileName
+	 * and replaces all place-holders (such as FEDORA_HOME, CATALINA_HOME,
+	 * HOST, PORT) with the custom values specified for this installation.
+	 * These customised properties are returned in the Properties map.
+	 * Never overwrite the property file given by propFileName! 
+	 * They are defaults, meant to be customised elsewhere.
+	 * 
+	 * @param propFileName is the name of the template properties file 
+	 * to be opened and read from. (Keep it read-only!)
+	 * @param display - if true, then prints the contents of the properties
+	 * if false, does not.
+	 * @return the properties in the template properties file 
+	 * customised with the values provided during installation.
+	 */
+	protected Properties customiseProperties(
+		String propFileName, boolean display) throws Exception
+	{
+		Properties defaultProps = new Properties();
+		InputStream in 
+			= GSearchInstaller.class.getClassLoader().getResourceAsStream(
+				propFileName); 
+		if(in == null) {
+			throw new Exception(
+				"Unable to locate " + propFileName
+				+".properties file. Cannot proceed.");
+		}
+		defaultProps.load(in);
+		// Create properties that will contain customised values based on 
+		// default values
+		Properties properties = new Properties(); 
+		Set entries = defaultProps.entrySet();
+		Iterator i = entries.iterator();
+		while (i.hasNext()) {
+			Entry e = (Entry)i.next();
+			String key = (String)e.getKey();
+			String value = (String)e.getValue();
+			if(value.startsWith("FEDORA_HOME")) {
+				value = value.replace("FEDORA_HOME", FEDORA_HOME);
+				value = value.replace("/", File.separator);
+			} else if(value.startsWith("CATALINA_HOME")) {
+				value = value.replace("CATALINA_HOME", CATALINA_HOME);
+				value = value.replace("/", File.separator);
+			}
+			
+			// now check for any property *containing* placeholder strings
+			// that need to be replaced. Separate if stmts: check each
+			if(value.contains("HOST:PORT")) {
+				value = value.replace("HOST:PORT", host+":"+port);
+			} 
+			if(value.contains("REPOSITORY_NAME")) {
+				value = value.replace("REPOSITORY_NAME", repositoryName);
+			}
+			if(value.contains("INDEX_NAME")) {
+				value = value.replace("INDEX_NAME", indexName);
+			}	
+			// except for URLs, File path separators have to be system independent
+			/*if(replacePaths && !value.startsWith("http")) {
+				value = value.replace("/", File.separator);
+			}*/
+
+			// Now overwrite the property
+			properties.setProperty(key, value);
+			
+			
+			// if instructed to print some information:
+			if(display) {
+				System.out.println("\tkey: " + key + "\tvalue: " + value);
+			}
+		}
+		// we're not going to use the default properties anymore
+		defaultProps.clear();
+		defaultProps = null;
+		
+		return properties;
+	} 
+	
+    
+    /** Waits for the fedora server to stop */
+    public void waitForServerToStop() throws Exception {
+		URL fedoraServerURL = new URL(gSearchProperties.getProperty("gsearch.base"));
+		boolean running = true;
+		// try the server for 60s tops or until it finally stops running
+		for(int i = 1; running && i <= 60; i++) {
+		    try{
+			// now try to connect - if this fails, we know the server has stopped 
+			fedoraServerURL.getContent();
+			System.out.print(".");
+			Thread.sleep(1000);
+		    }catch(IOException e) {
+			// means we have stopped running, good
+			System.out.println(" Server stopped.");
+			running = false;
+		    }
+		}
+		System.out.println();
+    }
+
+    /** Waits for the fedora server to be ready after a server start. */
+    public void waitForFedoraServer() throws Exception {
+		URL fedoraServerURL = new URL(gSearchProperties.getProperty("gsearch.base"));
+		boolean ready = false;
+		for(int i = 1; !ready && i <= 60; i++) { // try the server for 60s tops
+		    try{
+			// now try to connect, if it succeeds, then we know the server is ready
+			fedoraServerURL.getContent();
+			ready = true;
+			System.out.println(" Server ready.");
+		    }catch(IOException e) {
+			// if an IOException occurs, then it means the server is not yet ready
+			System.out.print(".");
+			Thread.sleep(1000);
+		    }	    
+		}
+		fedoraServerURL = null;
+		System.out.println();
+    }
+
+	/** Method that renames folder src in outputPath to dest.
+	 * @param outputPath is the directory in which src resides.
+	 * @param src is the name of the folder in outputPath that is 
+	 * to be renamed.
+	 * @param dest is what src is to be renamed to. */
+    protected boolean replaceDir(String outputPath, String src, String dest) 
+    {
+		// renaming folder located in outputPath from src to dest
+		File srcDir = new File(outputPath, src);
+		File destDir = new File(outputPath, dest);
+		
+		srcDir.renameTo(destDir);
+		if(!destDir.exists()) {
+		    System.out.println("Unable to create directory: "
+				       + destDir.getAbsolutePath());
+		    srcDir = destDir = null;
+		    return false;
+		}
+	
+		srcDir = destDir = null;
+		return true;
+    }
+	
+	/** Stores the given properties in the file outputPath/propFileName.
+	 * @param properties is the Properties map to be written out to a file.
+	 * @param propFileName is the name of the output properties file.
+	 * @param outputPath is the directory into which the properties file  
+	 * is to be written. 
+	 * */
+	protected void copyPropFile(Properties properties, 
+			String propFileName, File outputPath) 
+		throws Exception 
+	{
+		// Write the customised properties to an output file of the same  
+		// name but in the given outputPath location
+		FileOutputStream out = new FileOutputStream(
+				new File(outputPath, propFileName)); 
+		properties.store(out, propFileName + " based on "
+			+ "Muradora's \"Installing Fedora GSearch for Full Text Search\"");
+		// make sure we close it, since Properties.store() does not close it
+		out.close();
+		outputPath = null; // if no one else has a ref to it, will end up in gc
+	}
+	
+	/** Copies internal template src file (in executable jar) to dest file. 
+	 * If replace is true, then if the dest file exists, it will be overwritten.
+	 * From http://exampledepot.com/egs/java.io/CopyFile.html
+	 * @param src is the internal file (internal to the jar) to be copied
+	 * its inputStream is obtained and copied.
+	 * @param dest is the file into which the contents of src are to be copied
+	 * @param replace indicates whether dest is to be replaced if it already
+	 * exists. If replace is true, then any existing dest is replaced with the
+	 * copied output file of the same name. If false, the copy operation does
+	 * not take place
+	 * @throws IOException if the copying failed.
+	 */
+    protected void copyTemplateFile(String src, File dest, boolean replace) 
+    	throws IOException 
+    {
+    	if(!replace && dest.exists()) {
+    		System.err.println(src + " already exists, not overwritten.");
+    		return;
+    	}
+        //FileInputStream in = new FileInputStream(src);
+    	InputStream in = ClassLoader.getSystemResourceAsStream(src);
+		FileOutputStream out = new FileOutputStream(dest);
+        
+        // Transfer bytes from in to out
+        byte[] buf = new byte[1024];
+        int len;
+        try {
+        	while ((len = in.read(buf)) > 0) {
+        		out.write(buf, 0, len);
+        	}
+        } catch(IOException e) {
+        	throw e;
+        } finally { // just make sure the file streams are closed
+        	in.close();
+        	out.close();
+        	dest = null;
+        	in = null;
+        	out = null;
+        }
+    }
+    
+    /** Reads from an xmlFile. If dtd_SystemId is not an empty String,
+	 * then the XMLFile is not validated against the doctype statement it
+	 * contains (the dtd_SystemId entity in the xmlFile is ignored) so
+	 * that parsing still succeeds. However, if the DOM Structure returned
+	 * by this method is written back out to a file, then make sure that
+	 * this doctype is added back into the output file. 
+	 * @param xmlSource is either an xmlFile or xml InputStream (of a jarred
+	 * file, for example) to be read into a DOM structure.
+	 * @param dtd_SystemId - if specified, validation against the given DTD 
+	 * is ignored. The DOM structure returned will not contain the DOCTYPE
+	 * entity with the given dtd file reference. If writing the DOM out to
+	 * a file later on, then it is advised that this DOC_TYPE is added back
+	 * in. If there is no dtd to be validated or whose validation is to be
+	 * ignored, pass the empty string for dtd_SystemId.
+	 * @param sourceFileName is the name of the (possibly internal) file 
+	 * that is to be read.  
+	 * @throws Exception if an error occurred during parsing. 
+	 * */
+	protected Document readXML(Object xmlSource, String dtd_SystemId,
+			String sourceFileName) throws Exception 
+	{
+		Document doc = null;
+		 try {
+			 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+			 factory.setValidating(false); // doesn't help
+			 DocumentBuilder builder = factory.newDocumentBuilder();
+			 
+			 // If dtd is specified, then to ignore the dtd which is/may be 
+			 // pointing  to the wrong location and will therefore invalidate the 
+			 // parsing effort, we ignore the DTD for the moment. We will add 
+			 // this back in later when we write the customised xmlFile back out.
+			 // (e.g. in <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+			 // the template log4j.xml has log4j.dtd pointing to local filesystem
+			 // and that won't work. In this example, log4j.dtd is the dtd_SystemId)
+			 // See http://forum.java.sun.com/thread.jspa?threadID=284209&forumID=34
+			 if(!dtd_SystemId.equals("")) {
+				 builder.setEntityResolver(
+						new IgnoreDTDEntityResolver(dtd_SystemId));
+			 }
+			 if(xmlSource instanceof java.io.InputStream) {
+				 doc = builder.parse((InputStream)xmlSource);
+			 } else { // xml source is a file
+				 doc = builder.parse((File)xmlSource);
+			 }
+			
+			 // normalize text representation, so that the saved-and-
+			 // reloaded version is the same as the DOM constructed
+			 doc.getDocumentElement().normalize();
+			 
+			 //System.out.println("xmlFile contents:\n" 
+				//+ this.elementToFormattedString(doc.getDocumentElement(), ""));
+		 } catch (javax.xml.parsers.ParserConfigurationException e) {
+			 throw new Exception ("Error reading XML document " 
+					 + sourceFileName + " into DOM structure\n" + e);
+		 } catch (IOException e) {
+			 throw new Exception ("Error parsing XML document " 
+					 + sourceFileName + " into DOM structure\n" + e);
+		 }
+		 xmlSource = null;
+		 return doc;
+	}
+	
+	/** 
+	 * Writes out a DOM structure to a file.
+	 * @param doc is the DOM to be written out to a file
+	 * @param xmlFile is the file to write the XML out to
+	 * @param dtdToAddBackIn is "" if there's no special DTD to add
+	 * back into the DOCTYPE. If not "", it specifies the dtd file to
+	 * be added in the DOCTYPE of the XML output file.
+	 */
+	protected void writeXML(Document doc, File xmlFile, 
+			String dtdToAddBackIn) throws Exception
+	{
+		try {
+			OutputStream out = new FileOutputStream(xmlFile);
+			out.write(elementToFormattedString(
+				doc.getDocumentElement(), dtdToAddBackIn).getBytes());
+			out.close();
+			out = null;
+			xmlFile = null; // local reference not used anymore
+		} catch(Exception e){
+			throw new Exception("Exception when writing out XML to file "
+				+ xmlFile.getAbsolutePath() + "\n" + e.getMessage());
+		}
+	}
+	
+	/** Given a DOM document, finds the first element where nodeName=tagName
+	 * where one of the attributes has the name attrName and whose value
+	 * contains attrValueContent. The <b>portion</b> of the attribute value 
+	 * that matches is then replaced by replacementContent. 
+	 * If onceOnly is true, the first replacement is made and the method 
+	 * returns. If false, all matching replacements are made. 
+	 * @param doc is the DOM Document object in which to search for
+	 * the element to be replaced
+	 * @param tagName is the name of the element to search for
+	 * @param attrName is the name of the attribute of the element to search for
+	 * @param attrValueContent is the portion of the attribute value that will 
+	 * be replaced by replacementContent.
+	 * @param replacementContent is the replacement string that will overwrite
+	 * the part of the attrName attribute's value that matched attrValueContent
+	 * @param onceOnly - if true will look for the first match and perform the
+	 * replacement once. If false, it will replace all matches found.
+	 * @param wholeItem - if true, the entire string containing attrValueContent 
+	 * will be replaced by the string replacementContent. If false, only the 
+	 * attrValueContent portion of the original string will be replaced.
+	 * @return true if any replacements have been made.
+	*/
+	protected boolean replaceElementWithAttrValue(Document doc, String tagName,
+		String attrName, String attrValueContent, String replacementContent,
+		boolean onceOnly, boolean wholeItem)
+	{
+	    boolean found = false;
+		//System.out.println("Searching for:"+tagName+" "+attrName+" "+attrValuePrefix);
+		// Also need to check documentElement (root) itself for a match:
+		NodeList elements = doc.getElementsByTagName(tagName);
+		for(int i = 0; i < elements.getLength(); i++) {
+			Element e = (Element)elements.item(i);
+			String oldVal = e.getAttribute(attrName);
+			if(oldVal.indexOf(attrValueContent) != -1) {
+				String newVal = (wholeItem) ? replacementContent 
+				    : oldVal.replace(attrValueContent, replacementContent);
+				e.setAttribute(attrName, newVal);
+				if(i == 0) { // print message for user information
+					System.out.println(
+						"\tUpdating: " + oldVal + "  To: " + newVal);
+					found = true;
+				}
+				if(onceOnly) {
+					// finished match-and-replace, so 
+					// get out of this loop and method
+					return found;
+				}
+			}
+		}
+		return found;
+	}
+	
+	/** Given a DOM document, finds the first element where nodeName=tagName
+	 * and where the element's inner text contains the string contentValue. 
+	 * Once found, the <b>entire</b> textnode contents of the matching element 
+	 * is then replaced with the replacement string.
+	 * @param doc is the DOM Document object in which to search for
+	 * the element to be replaced
+	 * @param tagName is the name of the element to search for
+	 * @param contentValue is the portion of the textual content of the 
+	 * element that should match for the replacement to happen
+	 * @param replacement is value that will overwrite the matching portion
+	 * of the element's textual content (it will overwrite contentValue).
+	 * @param onceOnly - if true will look for the first match and perform the
+	 * replacement once. If false, it will replace all matches found.
+	 * @param wholeItem - if true, the entire string containing attrValueContent 
+	 * will be replaced by the string replacementContent. If false, only the 
+	 * attrValueContent portion of the original string will be replaced.
+	 * @return true if any replacements have been made.
+	 */
+	protected boolean replaceElementWithValue(Document doc, String tagName,
+			String contentValue, String replacement, boolean onceOnly, boolean wholeItem)
+	{
+	     boolean found = false;
+		// Also need to check documentElement (root) itself for a match:
+		NodeList elements = doc.getElementsByTagName(tagName);
+		for(int i = 0; i < elements.getLength(); i++) {
+			Element e = (Element)elements.item(i);
+			String textNodeContent = getValue(e);
+			if(textNodeContent.indexOf(contentValue) != -1) {
+				// overwrite the matching portion
+				String newVal = (wholeItem) ? replacement
+				    : textNodeContent.replace(contentValue, replacement);
+				// Put it back into the text node of the element
+				e.setTextContent(newVal); 
+				if(i == 0) { // print message for user information
+					System.out.println("\tUpdating: " + textNodeContent 
+							+ "  To: " + newVal);
+					found = true;
+				}
+				// we're now finished if we only wanted to search 
+				// for the first occurrence 
+				if(onceOnly) {
+					return found;
+				}
+			}
+		}
+		return found;
+	} 
+	
+	/** Extract the text from an element, if any.
+	 * @return the text that's nested in an element's body or "" 
+	 * if there's none. 
+	 * @param e is the element whose value is to be extracted.
+	*/
+	public static String getValue(Element e) {
+		Node child = e.getFirstChild();
+		return (child == null) ? "" : child.getNodeValue();
+	}
+	
+	
+	/** Given an Element, this will return its String representation properly 
+	 * indented for display. (The XML declaration will be added at the top 
+	 * since this method will be used here to write proper XML out to a file.)
+	 * @return a string representation of e, formatted for display. 
+	 * @param e is the element to be converted to its string representation.
+	 * @param dtd_SystemId (if not "") is any DOCTYPE with systemId that needs
+	 * to be added back into the file. If "", then no new DOCTYPE entity is
+	 * added into the DOM structure represented by Element e.
+	*/
+	public static String elementToFormattedString(Element e, String dtd_SystemId) 
+		throws Exception 
+	{
+		DOMSource domSource = new DOMSource(e);
+		Transformer transformer = TransformerFactory.newInstance().newTransformer();
+		// Do not set transformer to OMIT_XML_DECLARATION! We're doing
+		// a straightforward copy here
+		transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+		transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+		
+		if(!dtd_SystemId.equals("")) {
+			// put the docType (systemID) back in:
+			transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "log4j.dtd");
+		}
+
+		StringWriter sw = new StringWriter();
+		transformer.transform(domSource, new StreamResult(sw));
+		return sw.toString();
+	}
+	
+	//***************HELPER METHODS USED ONLY ONCE********************/
+	/** 
+     * Moves the (fedoragsearch.war) war file from the given location
+     * into FEDORA_HOME's tomcat folder (i.e. into CATALINA_HOME) and
+     * unpacks it there. Unpacking is achieved by starting the fedora
+     * server after the move. If a fedoragsearch is already unpacked
+     * in CATALINA_HOME, this method will not move the given 
+     * fedoragsearch.war file.
+     * This method does more than merely move fedoragsearch.war: 
+     * regardless of whether the war file exists and is moved or not,
+     * the fedora server is first stopped and at the end it is started.
+     * @param gsearchWarFile the fedoragsearch.war file to be moved and
+     * unpacked.
+     * @throws Exception if an unpacked fedoragsearch does not exist
+     * in the CATALINA_HOME/webapps folder at the end.
+     */
+	protected void moveUnpackWarFile(File gsearchWarFile) throws Exception {
+		final String stopFedora 
+			= gSearchProperties.getProperty("fedora.stop")+scriptExtension;
+		final String startFedora 
+			= gSearchProperties.getProperty("fedora.start")+scriptExtension;
+		
+		// shut down fedora server
+		//this.runProcess(new String[]{"cmd & " + stopFedora + " & exit"});
+		this.runProcess(new String[]{stopFedora}, false);
+				
+		File dest = null;
+		File webapps = new File(CATALINA_HOME,"webapps");
+		// we don't want to overwrite any existing fedoraGSearch folder
+		File unpackedFedoraGSearch = new File(webapps, FEDORAGSEARCH); 
+		
+		if(!gsearchWarFile.exists()) {
+			if(!unpackedFedoraGSearch.exists()) { 
+				// neither war file nor unpacked version exist
+				throw new Exception(
+					"Neither the war file " + gsearchWarFile.getAbsolutePath() 
+					+ "\nnor " + unpackedFedoraGSearch.getAbsolutePath() 
+					+ " exist. Cannot proceed.");
+			} else { // only gsearchWarFile does not exist
+				System.out.println("fedoragsearch.war file: " 
+					+ gsearchWarFile + " does not exist!");
+			} 
+		}
+		
+		if(unpackedFedoraGSearch.exists() && unpackedFedoraGSearch.isDirectory()) 
+		{
+			System.err.println(unpackedFedoraGSearch 
+				+ " exists. Not overwriting with war file " + gsearchWarFile);
+		} else {
+			// perform the move: whatever the original war file was called, 
+			// make sure the destination file has the name fedoragsearch.war
+			dest = new File(webapps, "fedoragsearch.war");
+			System.out.println("Moving " + gsearchWarFile + "\nto " + dest);
+			boolean success = gsearchWarFile.renameTo(dest);
+			if(!success) {
+				System.out.println("gsearchWarFile could not be moved."
+						+ " Trying an OS move command.");
+				// happens on Linux, try moving it in a different way:
+				this.runProcess(new String[] {"mv", 
+						gsearchWarFile.getAbsolutePath(), 
+						dest.getAbsolutePath()}, true);
+			}
+		}
+
+		if(dest != null && !dest.exists()) { 
+			// don't check 'success', as it doesn't return true (fast enough)
+			throw new Exception("Could not move " + gsearchWarFile 
+				+ "\nto " + dest);
+		}
+		// Start the fedora server, and let it unpack fedoragsearch if
+		// it has been freshly copied. Else restart the server all the same
+		this.runProcess(new String[]{startFedora}, false);
+		    //this.runProcess(new String[]{"cmd & " + startFedora + " & exit"});
+		
+		
+		// We'll wait until unpacking of fedoragsearch has finished, 
+		// otherwise installation fails. The way we check is by seeing 
+		// whether the fedoragsearch/WEB-INF/classes folder has been created.
+		// If not, we continue waiting and output "." to the screen 
+		System.out.print("Waiting for fedoragsearch to finish unpacking...");
+		waitForFedoraServer();
+	}
+	
+	/** Makes changes to the fedora.fcfg file located inside FEDORA_HOME. 
+	 * It changes the fedora.server.storage.DOManager to the GSearchDOManager
+	 * and sets the gSearchRESTURL to the specific fedora host and port values
+	 * specified for installation. 
+	 * This method also checks that "greenstone" is in the list of PIDs that
+	 * Fedora recognises. If it's not in the list already, it is added in 
+	 * there.
+	*/
+	protected void customiseFedoraConfigFile() throws Exception 
+	{
+		try{
+			File fedoraConfigFile = new File(
+				gSearchProperties.getProperty("fedora.config.dir"), "fedora.fcfg");
+			
+			Document doc = this.readXML(fedoraConfigFile, "", fedoraConfigFile.getAbsolutePath());
+			
+			// (1) Change class to "fedora.server.storage.GSearchDOManager" in 
+			// <module role="fedora.server.storage.DOManager" class="insert-here">
+			Element module = null;
+			NodeList nodes = doc.getElementsByTagName("module");
+			for(int i = 0; i < nodes.getLength(); i++) {
+				Element e = (Element)nodes.item(i);
+				// getAttr will return "" if no match found
+				if(e.getAttribute("role").equals("fedora.server.storage.DOManager")) {
+					module = e;
+				}
+			}
+			if(module == null) {
+				// can't do anymore, because the rest is dependent on finding module
+				System.err.println(
+					"Unable to find fedora.server.storage.DOManager module element in "
+					+ fedoraConfigFile);
+				return; 
+			} 
+			module.setAttribute("class", "fedora.server.storage.GSearchDOManager");
+			nodes = null;
+			
+			// (2) Set the FedoraGSearch rest url inside the module element found:
+			// <param name="gSearchRESTURL" value="http://?host:?port/fedoragsearch/rest">
+			// And now that we are here and looking for params, might as well 
+			// ensure that "greenstone" is one of the PIDs that Fedora recognises.
+			// Checking for:
+			// <param name="retainPIDs" value="demo test changeme fedora-bdef fedora-bmech tutorial greenstone">
+			nodes = module.getElementsByTagName("param");
+			for(int i = 0; i < nodes.getLength(); i++) {
+				Element e = (Element)nodes.item(i);
+				// getAttr will return "" if no match found
+				String nameAttr = e.getAttribute("name"); 
+				if(nameAttr.equals("gSearchRESTURL")) {
+					e.setAttribute("value",
+						gSearchProperties.getProperty("gsearch.base")
+						+File.separator+"rest"
+						//"http://"+this.host+":"+this.port+"/fedoragsearch/rest");
+					);
+				} else if(nameAttr.equals("retainPIDs")) {
+					String valAttr = e.getAttribute("value");
+					// Check if greenstone not yet in list of pids
+					if(valAttr.indexOf("greenstone") == -1) {
+						// Need to append greenstone to the list of pids
+						// don't forget to precede it by a space!
+						valAttr = valAttr + " greenstone";
+						// put the attribute back
+						e.setAttribute("value", valAttr);
+					} else {
+						System.out.println("\"greenstone\" is already in" 
+							+ " the list of fedora-recognised PIDs");
+					}
+				}
+			}
+			
+			// Now we've updated the necessary Elements, need to write the
+			// it back into the same file
+			OutputStream out = new FileOutputStream(fedoraConfigFile);
+			out.write(elementToFormattedString(
+				doc.getDocumentElement(),"").getBytes());
+			out.close();
+			out = null;
+			fedoraConfigFile = null;
+		} catch(Exception e) {
+			throw new Exception("Tried to parse " 
+				+ gSearchProperties.getProperty("fedora.config.dir")
+				+ File.separator+"fedora.fcfg"
+				+ "\nand then modify it and write it out the same file."
+				+ " But an exception occurred:\n" + e.getMessage());
+		}
+	}
+	
+	/** 
+	 * Creates the Lucene index directory in the right location inside
+	 * FEDORA_HOME into which FedoraGSearch will store the indexes for 
+	 * the Greenstone contents in the Fedora repository.
+	 * If it already exists, the Lucene index directory is not created.
+	 * @throws Exception if the Lucene index directory cannot be created
+	 * in the appropriate location inside FEDORA_HOME
+	 */
+	protected void createLuceneIndexDir() throws Exception {
+		File luceneIndexesDir = new File(gSearchProperties.getProperty(
+			"lucene.indexes.dir"), indexName);
+		if(!luceneIndexesDir.exists()) {
+			System.out.println("Creating directory for the Lucene index: " 
+					+ luceneIndexesDir);
+			if(!luceneIndexesDir.mkdirs()) {
+				throw new Exception("Unable to create Lucene index dir: " 
+						+ luceneIndexesDir);
+			}
+		} else {
+			System.err.println("Lucene index directory " + luceneIndexesDir 
+			+ " exists.\nNot creating it.");
+		}
+	}
+	
+	//******************STATIC METHODS USED BY MAIN**********************/
+	/** Displays a dialog to get user input for 
+	 * - fedora server host, port, username and password, 
+	 * - the names for the fedora generic search index and repository 
+	 * that are to be created, and 
+	 * - for the location of fedoragenericsearch.war (the installer is
+	 * meant to work specifically with Muradora's fedoragenericsearch.war
+	 * since they have edited various property and xml files to make it
+	 * all easier).
+	 * The dialog displays the default values to the user.
+	 * @return a Properties map containing the values entered by the
+	 * user for the various initialisation parameters required by the
+	 * Fedora Generic Search Installer GSearchInstaller. */
+	public static Properties showInputDialog()
+	{	
+		final JTextField fileField = new JTextField(defaults.getProperty(PROP_FILE));
+		JTextField hostField = new JTextField(defaults.getProperty(PROP_HOST));
+		JTextField portField = new JTextField(defaults.getProperty(PROP_PORT));
+		JTextField unameField = new JTextField(defaults.getProperty(PROP_UNAME));
+		JTextField passwField = new JPasswordField(defaults.getProperty(PROP_PASSW));
+		JTextField reposField = new JTextField(defaults.getProperty(PROP_REPOS));
+		JTextField indexField = new JTextField(defaults.getProperty(PROP_INDEX));
+		
+		// Arrange the controls
+		JPanel serverPanel = new JPanel(new GridLayout(4,2));
+		serverPanel.setBorder(
+			BorderFactory.createTitledBorder("Fedora server details"));
+		serverPanel.add(new JLabel(PROP_HOST));
+		serverPanel.add(hostField);
+		serverPanel.add(new JLabel(PROP_PORT));
+		serverPanel.add(portField);
+		serverPanel.add(new JLabel(PROP_UNAME));
+		serverPanel.add(unameField);
+		serverPanel.add(new JLabel(PROP_PASSW));
+		serverPanel.add(passwField);
+		
+		JPanel gSearchPanel = new JPanel(new GridLayout(2,2));
+		gSearchPanel.setBorder(BorderFactory.createTitledBorder(
+			"Customise Fedora Generic Search properties"));
+		gSearchPanel.add(new JLabel(PROP_REPOS + " name"));
+		gSearchPanel.add(reposField);
+		gSearchPanel.add(new JLabel(PROP_INDEX + " name"));
+		gSearchPanel.add(indexField);
+		
+		JButton browseButton = new JButton("Browse");
+		JPanel filePanel = new JPanel(new FlowLayout());
+		filePanel.setBorder(BorderFactory.createTitledBorder(
+			"Locate the fedoragenericsearch.war file"));
+		filePanel.add(new JLabel(PROP_FILE));
+		filePanel.add(fileField);
+		filePanel.add(browseButton);
+		
+		// Now we're going to handle click events on the browseButton:
+		// a fileChooser dialog for opening files displays
+		// First we set up a FileFilter for filtering only war files
+		// FIXME: Do we allow all *.war files to be visible or only
+		// fedoragsearch.war? Advantage: wrong war files can't be chosen;
+		// disadvantage: if fedoragsearch.war was renamed it won't show up.
+		final javax.swing.filechooser.FileFilter warFileFilter 
+			= new javax.swing.filechooser.FileFilter() {
+				public boolean accept(File f) {
+					if(f.isDirectory()) {
+						return true;
+					}
+					// f.getName().equalsIgnoreCase("fedoragsearch.war")) {
+					if(f.getName().endsWith(".war") &&
+						f.getName().toLowerCase().contains("fedoragsearch")) 
+					{
+						return true;
+					} else {
+						return false;
+					}
+				}
+				public String getDescription() {
+					return "fedora generic search *.war file";
+					//return "Tomcat server *.war files";
+				}
+			};
+        // Add the ActionListener to the browseButton to display the FileChooser
+		browseButton.addActionListener(
+			new java.awt.event.ActionListener() {
+				public void actionPerformed(java.awt.event.ActionEvent e) {
+					JFileChooser chooser = new JFileChooser();
+					chooser.setFileFilter(warFileFilter);
+					int returnVal = chooser.showOpenDialog(null);
+
+				    if (returnVal == JFileChooser.APPROVE_OPTION) {
+				    	File file = chooser.getSelectedFile();
+				        fileField.setText(file.getAbsolutePath());
+				    } else { //CANCEL_OPTION or ERROR_OPTION
+				    	fileField.setText(defaults.getProperty(PROP_FILE));
+				    } 
+				}
+			}
+		);
+		
+		// Display the dialog - we'll just use the usual confirmDialog
+		// (the different panels are combined on top of each other)
+		// Then we get automatic behaviour on OK and CANCEL
+		int option = JOptionPane.showConfirmDialog(null, 
+			new Object[]{serverPanel, gSearchPanel, filePanel}, 
+			"Fedora Generic Search Installer - uses Muradora's FGS work",
+			JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
+		
+		// if ok was pressed, we return the values the user entered
+		Properties inputs = new Properties();
+		if (option == JOptionPane.OK_OPTION) {
+		    inputs.setProperty(PROP_UNAME, getSafeValue(unameField, PROP_UNAME));
+		    inputs.setProperty(PROP_PASSW, passwField.getText());
+		    inputs.setProperty(PROP_HOST, getSafeValue(hostField, PROP_HOST));
+		    inputs.setProperty(PROP_PORT, getSafeValue(portField, PROP_PORT));
+		    inputs.setProperty(PROP_REPOS, getSafeValue(reposField, PROP_REPOS));
+		    inputs.setProperty(PROP_INDEX, getSafeValue(indexField, PROP_HOST));
+		    inputs.setProperty(PROP_FILE, getSafeValue(fileField, PROP_FILE));
+		} else { // Cancel option
+		    	System.exit(0);
+		}
+		return inputs;
+	}
+	
+	/** This method returns the value of the textfield for the given 
+	 * GSearchInstaller initialiser property, if this is not the empty string.
+	 * If it is the empty string, the default value for this property is 
+	 * returned.
+	 * @param field is the TextField whose value is being extracted
+	 * @param property is the GSearchInstaller property that the TextField 
+	 * value maps to. 
+	 * @return the contents of the textfield for the property or the defaults
+	 * value for the property if the textfield contained "".
+	*/
+	protected static String getSafeValue(JTextField field, String property) 
+	{
+		String value = field.getText();
+		if(value.equals("")) {
+			return defaults.getProperty(property, "");
+		} 
+		return value;
+	}
+	
+	/** @return a string specifying the requirements of this program. */
+	public static String info() {
+		StringBuffer info = new StringBuffer(GSearchInstaller.class.getSimpleName()
+			+ " is based on the installation instructions at\n");
+		info.append("http://drama.ramp.org.au/cgi-bin/trac.cgi/wiki/InstallingFedoraGSearch");
+		info.append("\n\nBefore running this program, you need to have Fedora installed");
+		info.append("\nand the environment variables FEDORA_HOME and CATALINA_HOME set.\n");
+		info.append("(These would have had to have been set when installing Fedora.)");
+		info.append("\nRun with -help to see the command-line options.\n");
+		return info.toString();
+	}
+	
+	/** If the program is run from the command line and the user executed
+	 * it with -help or help, then this usage String is displayed.
+	 * @return String describing how to use this program when running it
+	 * from the command line. */
+	public static String usage() {
+		StringBuffer usage = new StringBuffer(info());
+		String progName = GSearchInstaller.class.getSimpleName();
+		
+		usage.append("\nRun:\n");
+		usage.append(progName + " -help");
+		usage.append("\n\tTo see this usage message again.");
+		
+		usage.append("\n\n" + progName);
+		usage.append("\n\tTo see a GUI dialog requesting input.");
+		
+		usage.append("\n\n" + progName + " [options]");
+		usage.append("\n\tFor command-line use. (For all options--except the fedora server");
+		usage.append("\n\tpassword--empty strings for values will cause defaults to be used.)");
+		usage.append("\n\n\tOptions:");
+		
+		usage.append("\n\t-file <fedoragsearch.war file>");
+		usage.append("\n\t\tThe full path to the fedoragsearch.war file (default: fedoragsearch.war)");
+		usage.append("\n\t\tThis file is ignored if folder CATALINA_HOME/webapps/fedoragsearch exists.");
+		usage.append("\n\t\thttp://drama.ramp.org.au/software/fedoragsearch.war has the Muradora version.");
+				
+		usage.append("\n\t-host <fedora server host>");
+		usage.append("\n\t\tThe host at which the fedora server is listening (default: localhost)");
+		usage.append("\n\t-port <fedora server port>");
+		usage.append("\n\t\tThe port on which the fedora server is listening (default: 8080)");
+		
+		usage.append("\n\t-username <fedora server username>");
+		usage.append("\n\t\tThe username for accessing the fedora server (default: fedoraAdmin)");
+		usage.append("\n\t-password <fedora server password>");
+		usage.append("\n\t\tThe password for accessing the fedora server (default: '')");
+		
+		usage.append("\n\t-repository <repository name>");
+		usage.append("\n\t\tA name for the repository that is to be indexed (default: Fedora)");
+		usage.append("\n\t-index <index name>");
+		usage.append("\n\t\tA name for the repository index to be created (default: FedoraIndex)\n");
+				
+		return usage.toString();
+	}
+	
+	/** Called to process multiple command line arguments where these 
+	 * arguments are GSearchInstaller constructor options followed by 
+	 * their values.
+	 * @param args are the command-line arguments received by main
+	 * which consist of one or more multiple "-option value" pairs.
+	 * @return a Properties map containing (option, value) pairs
+	 * that will be used by GSearchInstaller to install Muradora's 
+	 * Fedora Generic Search. Empty strings for recognised options
+	 * are replaced by defaults.
+	 * */
+	public static Properties parseInstallationArgs(String[] args) {
+		Properties inputs = new Properties();
+		// add pairs of arguments as Property(option, value) pairs
+		// i+=2: processed two arguments in one go (the option and value)
+		for(int i = 1; i < args.length; i+=2) {
+			if(args[i-1].startsWith("-")) { 
+				// remove any starting hypen from the options
+				args[i-1] = args[i-1].substring(1);
+			} // else the option didn't start with a hyphen.
+			
+			// If the values are empty strings, use default for
+			// that option if it exists (if it does not exist, it
+			// will be ignored by GSearchInstaller anyway).
+			if(args[i].equals("")) {
+				args[i] = defaults.getProperty(args[i-1], "");
+			}
+			// add the (option, value) pair
+			inputs.setProperty(args[i-1], args[i]);
+		}
+		
+		// display any user provided properties
+		Set entries = inputs.entrySet();
+		Iterator i = entries.iterator();
+		while(i.hasNext()) {
+			Map.Entry entry = (Map.Entry)i.next();
+			System.out.print("\tkey: " + entry.getKey() + "\t value: ");
+			if(entry.getKey().equals(PROP_PASSW)) {
+				String value = (String)entry.getValue();
+				for(int j = 0; j < value.length(); j++) {
+					System.out.print("*");
+				}
+				System.out.println();
+			} else {
+				System.out.println(entry.getValue());
+			}
+		}
+		
+		return inputs;
+	}
+	
+	
+	/** 
+	 *	The main method creates a GSearchInstaller to install Fedora Generic
+	 *	Search from a (Muradora) fedoragsearch.war file.
+	 *	The program can be run in one of two ways:
+	 *	- with no arguments: a dialog is displayed requesting inputs for the
+	 *	parameters used by the GSearchInstaller initialisation.
+	 *	- with arguments for command-line invocation. (Run with -help or help
+	 *	to find out what parameter options are there.)
+	*/
+	public static void main(String[] args) {
+		GSearchInstaller installer = null;
+		try {
+			
+			Properties inputs = null;
+			if(args.length < 1) { // 0 args
+				inputs = GSearchInstaller.showInputDialog();
+			} else { // one or more arguments
+				if(args[0].equalsIgnoreCase("-help") 
+						|| args[0].equalsIgnoreCase("help"))
+				{
+					System.out.println(GSearchInstaller.usage());
+					System.exit(1); // help message exits with 1
+				}
+				else // process the multiple arguments 
+				{ 
+					inputs = GSearchInstaller.parseInstallationArgs(args);
+				}
+			}
+			// Now we have the input Properties and can instantiate the 
+			// GSearchInstaller (if "help" option, then it would have exited)
+			installer = new GSearchInstaller(inputs);
+			// try installing
+			installer.install();
+		} catch(Exception e) {
+			System.out.println(e.getMessage());
+			e.printStackTrace();
+			System.out.println("\n" + info());
+			// -1 exit value means an exception occurred during 
+			// Fedora Generic Search Installation
+			System.exit(-1); 
+		}
+		
+		// No exception so far, so now we can index the repository contents.
+		try {
+			installer.indexGreenstoneContents(true);// empty existing index first
+		} catch(Exception e) {
+			System.out.println(e.getMessage());
+			e.printStackTrace();
+			System.out.println("\n" + info());
+			// Exit with -2 if exception during updateIndex
+			System.exit(-2);
+		}
+	
+		// If we're here, then we finished with no exceptions
+		System.out.println("Installation and indexing completed.");
+		System.exit(0);
+	}
+}
Index: other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/repository.properties
===================================================================
--- other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/repository.properties	(revision 15736)
+++ other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/repository.properties	(revision 15736)
@@ -0,0 +1,10 @@
+# $Id: repository.properties,v 1.3 2006/11/27 15:25:31 gertsp Exp $
+
+fgsrepository.repositoryName    = REPOSITORY_NAME
+
+fgsrepository.fedoraSoap                = http://HOST:PORT/fedora/services
+fgsrepository.fedoraUser                = fedoraAdmin
+fgsrepository.fedoraPass                = 
+fgsrepository.fedoraObjectDir           = FEDORA_HOME/data/objects
+
+fgsrepository.defaultGetRepositoryInfoResultXslt = copyXml
Index: other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/repositoryInfo.xml
===================================================================
--- other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/repositoryInfo.xml	(revision 15736)
+++ other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/repositoryInfo.xml	(revision 15736)
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resultPage>
+ <repositoryInfo>
+  <RepositoryShortName>Fedora-GS3</RepositoryShortName>
+  <RepositoryLongName>Fedora repository of Greenstone 3 documents</RepositoryLongName>
+  <RepositoryDeveloper>Auto-generated by GSearchInstaller</RepositoryDeveloper>
+  <RepositoryContact>your-email@wherever.com</RepositoryContact>
+ </repositoryInfo>
+</resultPage>
Index: other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/runSOAPClient.bat
===================================================================
--- other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/runSOAPClient.bat	(revision 15736)
+++ other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/runSOAPClient.bat	(revision 15736)
@@ -0,0 +1,6 @@
+@echo off
+
+set LIB=FEDORA_GSEARCH\WEB-INF\lib
+set JARS=%LIB%\fedora-client.jar;%LIB%\log4j-1.2.14.jar;%LIB%\activation-1.0.2.jar;%LIB%\axis.jar;%LIB%\commons-discovery.jar;%LIB%\commons-logging.jar;%LIB%\jaxrpc.jar;%LIB%\mail.jar;%LIB%\saaj.jar;%LIB%\lucene-core-1.9.1.jar;%LIB%\wsdl4j-1.5.1.jar;%LIB%\PDFBox-0.7.2.jar;%LIB%\xml-apis.jar
+
+java -cp FEDORA_GSEARCH\WEB-INF\classes;%JARS% dk.defxws.fedoragsearch.client.SOAPClient %1 %2 %3 %4 %5 %6 %7 %8
Index: other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/runSOAPClient.sh
===================================================================
--- other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/runSOAPClient.sh	(revision 15736)
+++ other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/runSOAPClient.sh	(revision 15736)
@@ -0,0 +1,17 @@
+#!/bin/sh
+#$Id: runSOAPClient.sh,v 1.2 2006/12/11 14:16:25 cwilper Exp $
+
+# <p><b>License and Copyright: </b>The contents of this file will be subject to the
+# same open source license as the Fedora Repository System at www.fedora.info
+# It is expected to be released with Fedora version 2.2.
+
+# Copyright &copy; 2006 by The Technical University of Denmark.
+# All rights reserved.</p>
+
+# @author  gsp@dtv.dk
+# @version 
+
+LIB=FEDORA_GSEARCH/WEB-INF/lib
+JARS=$LIB/fedora-client.jar:$LIB/log4j-1.2.14.jar:$LIB/activation-1.0.2.jar:$LIB/axis.jar:$LIB/commons-discovery.jar:$LIB/commons-logging.jar:$LIB/jaxrpc.jar:$LIB/mail.jar:$LIB/saaj.jar:$LIB/lucene-core-2.0.0.jar:$LIB/wsdl4j-1.5.1.jar:$LIB/PDFBox-0.7.2.jar:$LIB/xml-apis.jar
+
+java -Xms64m -Xmx96m -cp FEDORA_GSEARCH/WEB-INF/classes:$JARS dk.defxws.fedoragsearch.client.SOAPClient $1 $2 $3 $4 $5 $6 $7 $8
