source: other-projects/trunk/gs3-webservices-democlient/src/GSearchInstaller/org/greenstone/fedora/installer/GSearchInstaller.java@ 18373

Last change on this file since 18373 was 18373, checked in by ak19, 15 years ago

Used fixed value for DTD instead of parameter systemID

  • Property svn:executable set to *
File size: 62.3 KB
Line 
1package org.greenstone.fedora.installer;
2
3import java.util.*;
4import java.util.Map.Entry;
5import java.io.File;
6import java.io.InputStream;
7import java.io.OutputStream;
8import java.io.FileOutputStream;
9import java.io.IOException;
10import java.io.ByteArrayInputStream;
11import java.net.HttpURLConnection;
12import java.net.URL;
13
14import java.io.StringWriter;
15import javax.xml.parsers.DocumentBuilderFactory;
16import javax.xml.parsers.DocumentBuilder;
17import org.w3c.dom.Element;
18import org.w3c.dom.Document;
19import org.w3c.dom.NodeList;
20import org.w3c.dom.Node;
21import javax.xml.transform.OutputKeys;
22import javax.xml.transform.Transformer;
23import javax.xml.transform.TransformerFactory;
24import javax.xml.transform.dom.DOMSource;
25import javax.xml.transform.stream.StreamResult;
26import org.xml.sax.InputSource;
27import org.xml.sax.SAXException;
28import org.xml.sax.EntityResolver;
29
30import javax.swing.*;
31import java.awt.*;
32
33/**
34 * This class essentially follows the instructions at
35 * http://drama.ramp.org.au/cgi-bin/trac.cgi/wiki/InstallingFedoraGSearch
36 * in order to install Fedora Generic Search from their optimised
37 * fedoragsearch.war file. (I've also tested it on the original war file
38 * fedoragsearch.war available from http://defxws2006.cvt.dk/fedoragsearch/
39 * and it works.)
40 * It then does a few minor extra things in order to make Fedora Generic
41 * Search work specifically with a Fedora repository of Greenstone documents.
42 * @author ak19
43*/
44public class GSearchInstaller {
45
46 /** This EntityResolver allows the XML parser to ignore validating
47 * against the DTD specified in the XML since it is pointing to the
48 * wrong location. After parsing, we will put the DTD back in.
49 * (Package class, only used here.)
50 * See http://forum.java.sun.com/thread.jspa?threadID=284209&forumID=34
51 */
52 static class IgnoreDTDEntityResolver implements EntityResolver {
53 public final String systemID;
54
55 /** Constructor that 'resolves' (by ignoring) DTDs for the given
56 * systemID.
57 * @param systemID is the DTD path to be ignored, given
58 * in the XML file as a SYSTEM property. */
59 public IgnoreDTDEntityResolver(String systemID) {
60 this.systemID = systemID;
61 }
62
63 /** If the systemId matches the one this IgnoreDTDEntityResolver
64 * was created for, then the specified DTD is skipped.
65 * @param publicId the public ID in the DOCTYPE (not used here).
66 * @param systemId is the SYSTEM id in the DOCTYPE that specifies
67 * the DTD which is to be checked against this
68 * IgnoreDTDEntityResolver object's systemID. */
69 public InputSource resolveEntity(String publicId, String systemId)
70 throws SAXException, java.io.IOException
71 {
72 // The systemId passed in is the entire local file path, which is
73 // wrong. So we extract just the filename first and then make the
74 // comparison
75 int index = systemId.lastIndexOf(File.separator);
76 if(index == -1) {
77 index = systemId.lastIndexOf("/"); // tends to be / even on Windows
78 }
79 systemId = systemId.substring(index+1, systemId.length());
80
81 if(systemId.equals(this.systemID)) {
82 // this deactivates the DTD
83 return new InputSource(new ByteArrayInputStream(
84 "<?xml version='1.0' encoding='UTF-8'?>".getBytes()));
85 }
86 else return null;
87 }
88 }
89
90 // Some String class constants
91 public static final String FEDORAGSEARCH = "fedoragsearch";
92 public static final String TOMCAT = "tomcat";
93
94 // Custom constants for important and much-used environment variables
95 public final String FEDORA_HOME;
96 public final String CATALINA_HOME;
97 public final String scriptExtension; // .bat for Windows and .sh for Linux
98
99 /** Reads from gsearch.properties file which contains default
100 * directory paths (using variables like FEDORA_HOME and
101 * CATALINA_HOME) and HOST and PORT, and sets these to custom values.
102 * Some of the keys in the properties file include executable
103 * processes (such as for starting and stopping tomcat).
104 */
105 protected Properties gSearchProperties;
106
107 // custom values
108 public final String indexName;
109 public final String repositoryName;
110 public final String host;
111 public final String port;
112 public final String fedoraUsername; // username of fedoraServer
113 public final String fedoraPassword; // password of fedoraServer
114 public final String gsearchWarFileName; // the path to the fedorgsearch warfile
115
116 // some internal class constants
117 protected static String PROP_REPOS = "repository";
118 protected static String PROP_INDEX = "index";
119 protected static String PROP_UNAME = "username";
120 protected static String PROP_PASSW = "password";
121 protected static String PROP_HOST = "host";
122 protected static String PROP_PORT = "port";
123 protected static String PROP_FILE = "file";
124 /** Default initialisation/customisation values. In case the Installer
125 * was not provided all parameters, these are used as fallback values */
126 protected static final Properties defaults = new Properties();
127 static {
128 defaults.setProperty(PROP_REPOS, "Fedora");
129 defaults.setProperty(PROP_INDEX, "FedoraIndex");
130 defaults.setProperty(PROP_UNAME, "fedoraAdmin");
131 defaults.setProperty(PROP_PASSW, "");
132 defaults.setProperty(PROP_HOST, "localhost");
133 defaults.setProperty(PROP_PORT, "8080");
134 defaults.setProperty(PROP_FILE, "fedoragsearch.war");
135 }
136
137 /** GSearchInstaller constructor sets all custom values to defaults */
138 public GSearchInstaller() throws Exception {
139 this(defaults); // use the default options
140 }
141
142 /** GSearchInstaller constructor sets the custom values */
143 public GSearchInstaller(Properties options) throws Exception
144 {
145 System.out.println(
146 "Preparing for installation of Fedora Generic Search...");
147
148 // for those options not set, we use the fallbacks in
149 // the defaults Properties
150 this.repositoryName = options.getProperty(PROP_REPOS,
151 defaults.getProperty(PROP_REPOS));
152 this.indexName = options.getProperty(PROP_INDEX,
153 defaults.getProperty(PROP_INDEX));
154 this.fedoraUsername = options.getProperty(PROP_UNAME,
155 defaults.getProperty(PROP_UNAME));
156 this.fedoraPassword = options.getProperty(PROP_PASSW,
157 defaults.getProperty(PROP_PASSW));
158 this.host = options.getProperty(PROP_HOST,
159 defaults.getProperty(PROP_HOST));
160 this.port = options.getProperty(PROP_PORT,
161 defaults.getProperty(PROP_PORT));
162 this.gsearchWarFileName = options.getProperty(PROP_FILE,
163 defaults.getProperty(PROP_FILE));
164
165 // don't need options anymore
166 options.clear();
167 options = null;
168
169 // Set the script extension to .sh or .bat depending on the OS
170 if(System.getProperty("os.name").toLowerCase().contains("windows")) {
171 // can be "Windows XP" or something
172 scriptExtension = ".bat";
173 } else { // "Linux", which would also be for Mac
174 scriptExtension = ".sh";
175 }
176
177 // Set the environment variables
178 FEDORA_HOME = System.getenv("FEDORA_HOME");
179 if(FEDORA_HOME == null) {
180 throw new Exception("Environment variable FEDORA_HOME not set."
181 + " Unable to proceed");
182 }
183
184 String catalina = System.getenv("CATALINA_HOME");
185 if(catalina != null) {
186 CATALINA_HOME = catalina;
187 } else {
188 // set it to FEDORA_HOME/tomcat
189 CATALINA_HOME = FEDORA_HOME+System.getProperty("file.separator")+TOMCAT;
190 System.err.println("Environment variable CATALINA_HOME "
191 + "not set...\nSetting it to FEDORA_HOME/tomcat: " + CATALINA_HOME);
192 }
193
194 // User information
195 System.out.println("\tFEDORA_HOME:\t" + this.FEDORA_HOME);
196 System.out.println("\tCATALINA_HOME:\t" + this.CATALINA_HOME);
197 System.out.println("\tFedora host:\t" + this.host);
198 System.out.println("\tFedora port:\t" + this.port);
199 System.out.println("\tRepository name:\t" + this.repositoryName);
200 System.out.println("\tIndex name:\t" + this.indexName);
201
202 // Load the all important gsearchProperties first and
203 // customise them using the environment variables just set
204 System.out.println("Customising properties for installation:");
205 gSearchProperties = customiseProperties("gsearch.properties", true);
206 }
207
208 public void install() throws Exception {
209 System.out.println(
210 "Beginning installation of Fedora Generic Search...");
211 // (1) Now we will try moving the fedoragsearch.war file into
212 // fedora tomcat's webapps folder and unpack it there
213 // TODO: check gsearchWarFileName's filename is fedoragsearch.war
214 File gsearchWarFile = new File(gsearchWarFileName);
215 moveUnpackWarFile(gsearchWarFile);
216
217 // (2) Ensure that lucene.indexes directory exists inside FEDORA_HOME
218 // (not inside FedoraGSearch)
219 createLuceneIndexDir();
220
221 System.out.println("Customising files for installation...");
222 // (3) Editing $CATALINA_HOME/webapps/fedoragsearch/WEB-INF/classes/log4j.xml
223 // Changing tag <param name="File" value="LOGPATH/fedoragsearch.log"/>
224 // to <param name="File" value="FEDORA_HOME/server/logs/fedoragsearch.log"/>
225 String xmlFileName="log4j.xml";
226 String dtdFileName="log4j.dtd";
227 InputStream xmlSource = ClassLoader.getSystemResourceAsStream(
228 xmlFileName);
229 // We want to read the XML file but as there is a doctype
230 // referring to a dtd file located elsewhere, we need
231 // to temporarily ignore the dtd while reading the XML
232 Document doc = readXML(xmlSource, dtdFileName, xmlFileName);
233
234 // Now make the necessary changes to the file:
235 // Find the element <param value="xxxLOGPATHxxx"/>
236 // and replace just the LOGPATH bit with FEDORA_HOME
237 String logPath = FEDORA_HOME + "/server/logs/fedoragsearch.log";
238 logPath = logPath.replace(File.separator, "/"); // xml requires the "/"
239 if(!this.replaceElementWithAttrValue(doc, "param", "value",
240 "LOGPATH", logPath, true, false)) {
241 // try again, the element may not contain variable LOGPATH
242 // but it will contain "fedoragsearch.log"
243 this.replaceElementWithAttrValue(doc, "param", "value",
244 "fedoragsearch.log", logPath+"/fedoragsearch.log", true, true);
245 }
246
247 // Finally write out the file to the correct location, and put the
248 // doctype containing the dtd back in.
249 File xmlFile = new File(gSearchProperties.getProperty("classes.dir"),
250 xmlFileName);
251 writeXML(doc, xmlFile, dtdFileName);
252
253 // (4) Copy and customise default property file fedoragsearch.properties
254 // Read from properties file with defaults, replacing placeholders
255 // in the default properties with the custom (environment) variables
256 // specified
257 String propFileName = "fedoragsearch.properties";
258 Properties properties = customiseProperties(propFileName, false);
259 copyPropFile(properties, // the customised properties
260 propFileName, // defaults file to customise
261 new File(gSearchProperties.getProperty("config.dir")) // output path
262 );
263
264 // (5) Rename $CATALINA_HOME/webapps/fedoragsearch/WEB-INF/classes/config/repository/REPOSNAME
265 // as "repositoryName", since we want to reuse some of the files
266 if(!replaceDir(gSearchProperties.getProperty("repository.dir"),
267 "REPOSNAME", repositoryName))
268 {
269 if(!replaceDir(gSearchProperties.getProperty("repository.dir"),
270 "DemoAtDtu", repositoryName)) {
271 replaceDir(gSearchProperties.getProperty("repository.dir"),
272 "Fedora", repositoryName);
273 }
274 }
275
276 // (6) Copy and customise repositoryProperties
277 propFileName = "repository.properties";
278 properties = customiseProperties(propFileName, false);
279 // add fedora server username and password properties
280 properties.setProperty("fgsrepository.fedoraUser", this.fedoraUsername);
281 properties.setProperty("fgsrepository.fedoraPass", this.fedoraPassword);
282 copyPropFile(properties, // the customised properties
283 propFileName, // the output file name (same as input file)
284 new File(gSearchProperties.getProperty("repository.dir"),
285 repositoryName) // output file path
286 );
287
288 // (7) Initially, make an as-is copy of the repositoryInfo file
289 // Later can get user input on customising it
290 File reposInfoXML = new File(gSearchProperties.getProperty("repository.dir")
291 +File.separator+repositoryName, "repositoryInfo.xml");
292 // We need to replace the existing reposInfoXML with the customised one
293 copyTemplateFile("repositoryInfo.xml", reposInfoXML, true);
294 // true="replace"
295
296 // (8) Rename $CATALINA_HOME/webapps/fedoragsearch/WEB-INF/classes/config/index/DemoOnLucene
297 // to "indexName", since it contains other files that are to be used
298 // and will remain unchanged
299 if(!replaceDir(gSearchProperties.getProperty("index.dir"),
300 "DemoOnLucene", indexName)) {
301 replaceDir(gSearchProperties.getProperty("index.dir"),
302 "FedoraIndex", indexName);
303 }
304
305 // (9) Copy and customise indexProperties
306 propFileName = "index.properties";
307 properties = customiseProperties(propFileName, false);
308 // add fedora server username and password properties
309 copyPropFile(properties, // the customised properties
310 propFileName, // the output file name (same as input file)
311 new File(gSearchProperties.getProperty("index.dir"),
312 indexName) // output file path
313 );
314
315 // (10) Editing $CATALINA_HOME/webapps/fedoragsearch/WEB-INF/
316 // classes/config/index/FedoraIndex/indexInfo.xml
317 // Customising tags <resultPage indexName="INDEX_NAME">
318 // and <IndexShortName>INDEX_NAME</IndexShortName>
319 xmlFileName="indexInfo.xml";
320 xmlSource = ClassLoader.getSystemResourceAsStream(
321 xmlFileName);
322 // Read the XML file. There is no doctype
323 // referring to any dtd file in indexInfo.xml
324 doc = readXML(xmlSource, "", xmlFileName);
325 // Now make the necessary changes to the file:
326 // Find the element <resultPage indexName="xxxINDEX_NAMExxx"/>
327 // and replace just the INDEX_NAME bit with indexName
328 replaceElementWithAttrValue(doc, "resultPage", "indexName",
329 "INDEX_NAME", indexName, true, false);
330 // Find the element <IndexShortName>xxxINDEX_NAMExxx</IndexShortName>
331 // and replace just the INDEX_NAME bit with indexName
332 replaceElementWithValue(doc,
333 "IndexShortName", "INDEX_NAME", indexName, true, false);
334
335 // Finally write out the file to the correct location.
336 // Output location should include indexName:
337 xmlFile = new File(
338 gSearchProperties.getProperty("index.dir")+File.separator+indexName,
339 xmlFileName);
340 writeXML(doc, xmlFile, ""); // no dtd to add back in this time.
341
342 // (11) Enter $CATALINA_HOME/webapps/fedoragsearch/WEB-INF/classes/config/rest.
343 // (12) Edit the stylesheets in here to set the path to your fedora/tomcat.
344 String restPath = gSearchProperties.getProperty("rest.dir");
345 String[] xsltFiles = { "demoBrowseIndexToHtml.xslt",
346 "demoGetIndexInfoToHtml.xslt", "demoGetRepositoryInfoToHtml.xslt",
347 "demoGfindObjectsToHtml.xslt", "demoUpdateIndexToHtml.xslt" };
348 for(int i = 0; i < xsltFiles.length; i++) {
349 File outputFile = null;
350 // File demoBrowseIndexToHtml.xslt will be customised from
351 // a template. Otherwise, the input file we read the DOM from
352 // will later be the file we write it out to
353 if(xsltFiles[i].equals("demoBrowseIndexToHtml.xslt")) { //if(i == 0) {
354 // Then don't read from restPath, read local template
355 xmlSource = ClassLoader.getSystemResourceAsStream(
356 xsltFiles[i]); // get template demoBrowseIndexToHtml
357 // Write to restPath
358 outputFile = new File(restPath, xsltFiles[i]);
359 // Read the XML stream (no dtd to worry about)
360 doc = readXML(xmlSource, "", xsltFiles[i]);
361 } else {
362 // Output file written to the same location
363 xmlFile = new File(restPath, xsltFiles[i]);
364 outputFile = xmlFile;
365 // Read the XML file (no dtd to worry about)
366 doc = readXML(xmlFile, "", xmlFile.getAbsolutePath());
367 }
368
369 // Now make the necessary changes to the file:
370 // Find the element <xsl:include href="xxxWEBSERVERPATHxxx"/>
371 // and replace just the WEBSERVERPATH bit with CATALINA_HOME
372 if(!replaceElementWithAttrValue(doc, "xsl:include", "href",
373 "WEBSERVERPATH", CATALINA_HOME, true, false)) {
374 // try again, the element may not contain variable LOGPATH
375 // but it will contain "fedoragsearch.log"
376 replaceElementWithAttrValue(doc, "xsl:include", "href",
377 "demoCommon.xslt",
378 CATALINA_HOME+"/webapps/fedoragsearch/WEB-INF/classes/config/rest/demoCommon.xslt",
379 true, true);
380 }
381
382 // (13) Edit the file demoBrowseIndexToHtml.xslt and replace the
383 // occurrences of indexName in <select name="index">...</select>
384 if(xsltFiles[i].equals("demoBrowseIndexToHtml.xslt")) { //if(i == 0) {
385 replaceElementWithAttrValue(doc,
386 "xsl:when", "test", "INDEX_NAME", indexName, true, false);
387 // two occurrences of the following, so pass false
388 // to ensure it's not a onceOnly replacement, but
389 // replace as often as it occurs.
390 replaceElementWithAttrValue(doc, "option",
391 "value", "INDEX_NAME", indexName, false, false);
392 // again, two occurrences, so replace multiple times
393 replaceElementWithValue(doc, "option",
394 "INDEX_NAME", indexName, false, false);
395 }
396
397 // Finally write out the XML to the specified output file
398 this.writeXML(doc, outputFile, ""); // no dtd to add back in
399 outputFile = null;
400 }
401
402 // (14) Edit the fedora.fcfg file to configure Fedora for automatic
403 // updates to notify notify Fedora GSearch of object changes
404 // for reindexing. Also checks that "greenstone" is in the list of
405 // Fedora-recognised PIDs
406 customiseFedoraConfigFile();
407
408 // (New) Now finish off the installation with the steps described in
409 // gs3-webservices-democlient/docs/HowToFiles/4InstallingFedoraGSearch.html
410 // We're further customising it for greenstone here: only PIDS=greenstone
411 // get indexed
412 // Copy the demoFoxmlToLucene.xslt template into the indexName dir.
413 String demoFoxmlToLucene = "demoFoxmlToLucene.xslt";
414 // output file path is in the indexName directory:
415 File dest = new File(
416 gSearchProperties.getProperty("index.dir")+File.separator+indexName,
417 demoFoxmlToLucene);
418 // copy the template and replace the existing file:
419 this.copyTemplateFile(demoFoxmlToLucene, dest, true);
420 dest = null;
421
422 System.out.println("Customising done.");
423
424 // (15) Restart the tomcat server
425 final String stopFedora
426 = gSearchProperties.getProperty("fedora.stop")+scriptExtension;
427 final String startFedora
428 = gSearchProperties.getProperty("fedora.start")+scriptExtension;
429
430 this.runProcess(new String[]{stopFedora}, false);
431 // wait for fedora to stop before restarting
432 waitForServerToStop();
433 this.runProcess(new String[]{startFedora}, false);
434
435
436 // This time, no way to test whether fedora server (and gsearch) are ready
437 System.out.print("Waiting for the fedora server to become ready...");
438 waitForFedoraServer();
439
440 // Finished installation
441 System.out.println("\nFinished installing Fedora Generic Search.");
442 }
443
444 /** Indexes the contents of the repository(name) specified during Fedora
445 * Generic Search installation. To do so, it runs the FedoraGenericSearch's
446 * runSOAPClient.sh with "host:port updateIndex fromFoxmlFiles".
447 * @param emptyFirst means the index will be created from scratch by first
448 * executing runSOAPClient.sh with the arguments
449 * "host:port updateIndex createEmpty" before updating from FOXML files.*/
450 public void indexGreenstoneContents(boolean emptyFirst) throws Exception {
451 System.out.println("\nBeginning indexing of repository \""
452 + this.repositoryName + "\"...");
453
454 // There is a problem running the runSOAPClient script that is part of
455 // the fedoragsearch installation: it has relative paths in its classpaths.
456 // This means we have to cd into the folder and execute the script from
457 // within the folder containing the script. cd would be a process, then
458 // executing the script would be another, independent process by which time
459 // we're no longer in the correct folder again.
460 // Therefore, this method works with a template file for the script: the
461 // place-holder path will be replaced with the absolute path to the LIB
462 // files needed by the script. And then the script can be executed from
463 // wherever.
464
465 try {
466 // We're going to replace the place-holders in the template script,
467 // and put a modified copy of the script in folder soap.client.dir
468 java.io.BufferedReader in = new java.io.BufferedReader(
469 new java.io.InputStreamReader(
470 ClassLoader.getSystemResourceAsStream("runSOAPClient"
471 +this.scriptExtension)));
472
473 File soapClientScript = new File(
474 gSearchProperties.getProperty("soap.client.dir"),
475 "runSOAPClient" +this.scriptExtension);
476 OutputStream out = new FileOutputStream(soapClientScript);
477
478 // Placeholder's replacement value: denotes the classpath
479 // directory for the LIB folder mentioned in the script.
480 final String FEDORA_GSEARCH
481 = gSearchProperties.getProperty("fedoragsearch.dir");
482 // Read the contents of the template file line by line.
483 // Do the replacements and write the lines out to the file.
484 String line = null;
485 while((line = in.readLine()) != null) {
486 line = line.replace("FEDORA_GSEARCH", FEDORA_GSEARCH);
487 line += "\n"; // append new line at the end of each line
488 out.write(line.getBytes());
489 }
490 in.close();
491 out.close();
492 in = null;
493 out = null;
494
495 // need to make the script executable on Linux
496 if(System.getProperty("os.name").equalsIgnoreCase("Linux")) {
497 this.runProcess(new String[]{"chmod", "u+x",
498 soapClientScript.getAbsolutePath()}, true);
499 }
500
501 // Now run the two steps of the script:
502 // "updateIndex createEmpty"
503 if(emptyFirst) {
504 this.runProcess(new String[]{ soapClientScript.getAbsolutePath(),
505 host+":"+port, "updateIndex", "createEmpty" }, true);
506 }
507
508 // "updateIndex fromFoxmlFiles"
509 this.runProcess(new String[]{ soapClientScript.getAbsolutePath(),
510 host+":"+port, "updateIndex", "fromFoxmlFiles" }, false);
511
512 soapClientScript = null;
513
514 System.out.println("Finished indexing repository \""
515 + this.repositoryName + "\".");
516 System.out.println("Try visiting the Fedora Generic Search REST url"
517 + "\n\t(http://"+host+":"+port+"/fedoragsearch/rest"
518 + "\n\tor https://"+host+":"+port+"/fedoragsearch/rest)");
519 } catch(IOException e){
520 System.out.println("Error when copying runSOAPClient template file.");
521 throw(e);
522 } catch(Exception e){
523 System.out.println("Error when executing updateIndex.");
524 throw(e);
525 }
526 }
527
528
529 //****************************HELPER METHODS****************************/
530 /** Method that will run the process associated with the gSearchProperties key.
531 * Waits until the process is executed.
532 * @param args signify the executable process and its arguments. The first element
533 * must be the key into gSearchProperties whose value denotes the executable process
534 * that is to be run. Subsequent elements are the actual arguments to that process.
535 * @param ignoreWindows if true will not plug the cmd /c start "" at the start of
536 * the arguments. If false, and only of the OS is windows, then these additional
537 * arguments get prepended to those already in the args array. */
538 protected int runProcess(String[] args, boolean ignoreWindows) throws Exception {
539 // for Windows, we need to prepend extra arguments to launch a process
540 // (Question remains: how to get rid of cmd consoles of child processes?)
541 if(!ignoreWindows && System.getProperty("os.name").toLowerCase().contains("windows")) {
542 String[] tmp = args;
543 args = new String[args.length+4];
544 args[0] = "cmd";
545 args[1] = "/c";
546 args[2] = "start";
547 args[3] = "/MIN"; // launches console minimised
548 int j = 4;
549 for(int i = 0; i < tmp.length; i++) {
550 args[j++] = tmp[i];
551 }
552 tmp = null;
553 }
554 System.out.print("Running processCommand: ");
555 for(int i = 0; i < args.length; i++) {
556 System.out.print(args[i] + " ");
557 }
558
559 Process process = Runtime.getRuntime().exec(args);
560 int exitValue = process.waitFor();
561 System.out.println("\n\tExit value is: " + exitValue);
562 // set local reference to null in case args was dynamically allocated
563 args = null;
564
565 process.destroy();
566 return exitValue;
567 }
568
569 /**
570 * Loads properties from the property file denoted by propFileName
571 * and replaces all place-holders (such as FEDORA_HOME, CATALINA_HOME,
572 * HOST, PORT) with the custom values specified for this installation.
573 * These customised properties are returned in the Properties map.
574 * Never overwrite the property file given by propFileName!
575 * They are defaults, meant to be customised elsewhere.
576 *
577 * @param propFileName is the name of the template properties file
578 * to be opened and read from. (Keep it read-only!)
579 * @param display - if true, then prints the contents of the properties
580 * if false, does not.
581 * @return the properties in the template properties file
582 * customised with the values provided during installation.
583 */
584 protected Properties customiseProperties(
585 String propFileName, boolean display) throws Exception
586 {
587 Properties defaultProps = new Properties();
588 InputStream in
589 = GSearchInstaller.class.getClassLoader().getResourceAsStream(
590 propFileName);
591 if(in == null) {
592 throw new Exception(
593 "Unable to locate " + propFileName
594 +".properties file. Cannot proceed.");
595 }
596 defaultProps.load(in);
597 // Create properties that will contain customised values based on
598 // default values
599 Properties properties = new Properties();
600 Set entries = defaultProps.entrySet();
601 Iterator i = entries.iterator();
602 while (i.hasNext()) {
603 Entry e = (Entry)i.next();
604 String key = (String)e.getKey();
605 String value = (String)e.getValue();
606 if(value.startsWith("FEDORA_HOME")) {
607 value = value.replace("FEDORA_HOME", FEDORA_HOME);
608 value = value.replace("/", File.separator);
609 } else if(value.startsWith("CATALINA_HOME")) {
610 value = value.replace("CATALINA_HOME", CATALINA_HOME);
611 value = value.replace("/", File.separator);
612 }
613
614 // now check for any property *containing* placeholder strings
615 // that need to be replaced. Separate if stmts: check each
616 if(value.contains("HOST:PORT")) {
617 value = value.replace("HOST:PORT", host+":"+port);
618 }
619 if(value.contains("REPOSITORY_NAME")) {
620 value = value.replace("REPOSITORY_NAME", repositoryName);
621 }
622 if(value.contains("INDEX_NAME")) {
623 value = value.replace("INDEX_NAME", indexName);
624 }
625 // except for URLs, File path separators have to be system independent
626 /*if(replacePaths && !value.startsWith("http")) {
627 value = value.replace("/", File.separator);
628 }*/
629
630 // Now overwrite the property
631 properties.setProperty(key, value);
632
633
634 // if instructed to print some information:
635 if(display) {
636 System.out.println("\tkey: " + key + "\tvalue: " + value);
637 }
638 }
639 // we're not going to use the default properties anymore
640 defaultProps.clear();
641 defaultProps = null;
642
643 return properties;
644 }
645
646
647 /** Waits for the fedora server to stop */
648 public void waitForServerToStop() throws Exception {
649 URL fedoraServerURL = new URL(gSearchProperties.getProperty("gsearch.base"));
650 boolean running = true;
651 // try the server for 60s tops or until it finally stops running
652 for(int i = 1; running && i <= 60; i++) {
653 try{
654 // now try to connect - if this fails, we know the server has stopped
655 fedoraServerURL.getContent();
656 System.out.print(".");
657 Thread.sleep(1000);
658 }catch(IOException e) {
659 // means we have stopped running, good
660 System.out.println(" Server stopped.");
661 running = false;
662 }
663 }
664 System.out.println();
665 }
666
667 /** Waits for the fedora server to be ready after a server start. */
668 public void waitForFedoraServer() throws Exception {
669 URL fedoraServerURL = new URL(gSearchProperties.getProperty("gsearch.base"));
670 boolean ready = false;
671 for(int i = 1; !ready && i <= 60; i++) { // try the server for 60s tops
672 try{
673 // now try to connect, if it succeeds, then we know the server is ready
674 fedoraServerURL.getContent();
675 ready = true;
676 System.out.println(" Server ready.");
677 }catch(IOException e) {
678 // if an IOException occurs, then it means the server is not yet ready
679 System.out.print(".");
680 Thread.sleep(1000);
681 }
682 }
683 fedoraServerURL = null;
684 System.out.println();
685 }
686
687 /** Method that renames folder src in outputPath to dest.
688 * @param outputPath is the directory in which src resides.
689 * @param src is the name of the folder in outputPath that is
690 * to be renamed.
691 * @param dest is what src is to be renamed to. */
692 protected boolean replaceDir(String outputPath, String src, String dest)
693 {
694 // renaming folder located in outputPath from src to dest
695 File srcDir = new File(outputPath, src);
696 File destDir = new File(outputPath, dest);
697
698 srcDir.renameTo(destDir);
699 if(!destDir.exists()) {
700 System.out.println("Unable to create directory: "
701 + destDir.getAbsolutePath());
702 srcDir = destDir = null;
703 return false;
704 }
705
706 srcDir = destDir = null;
707 return true;
708 }
709
710 /** Stores the given properties in the file outputPath/propFileName.
711 * @param properties is the Properties map to be written out to a file.
712 * @param propFileName is the name of the output properties file.
713 * @param outputPath is the directory into which the properties file
714 * is to be written.
715 * */
716 protected void copyPropFile(Properties properties,
717 String propFileName, File outputPath)
718 throws Exception
719 {
720 // Write the customised properties to an output file of the same
721 // name but in the given outputPath location
722 FileOutputStream out = new FileOutputStream(
723 new File(outputPath, propFileName));
724 properties.store(out, propFileName + " based on "
725 + "Muradora's \"Installing Fedora GSearch for Full Text Search\"");
726 // make sure we close it, since Properties.store() does not close it
727 out.close();
728 outputPath = null; // if no one else has a ref to it, will end up in gc
729 }
730
731 /** Copies internal template src file (in executable jar) to dest file.
732 * If replace is true, then if the dest file exists, it will be overwritten.
733 * From http://exampledepot.com/egs/java.io/CopyFile.html
734 * @param src is the internal file (internal to the jar) to be copied
735 * its inputStream is obtained and copied.
736 * @param dest is the file into which the contents of src are to be copied
737 * @param replace indicates whether dest is to be replaced if it already
738 * exists. If replace is true, then any existing dest is replaced with the
739 * copied output file of the same name. If false, the copy operation does
740 * not take place
741 * @throws IOException if the copying failed.
742 */
743 protected void copyTemplateFile(String src, File dest, boolean replace)
744 throws IOException
745 {
746 if(!replace && dest.exists()) {
747 System.err.println(src + " already exists, not overwritten.");
748 return;
749 }
750 //FileInputStream in = new FileInputStream(src);
751 InputStream in = ClassLoader.getSystemResourceAsStream(src);
752 FileOutputStream out = new FileOutputStream(dest);
753
754 // Transfer bytes from in to out
755 byte[] buf = new byte[1024];
756 int len;
757 try {
758 while ((len = in.read(buf)) > 0) {
759 out.write(buf, 0, len);
760 }
761 } catch(IOException e) {
762 throw e;
763 } finally { // just make sure the file streams are closed
764 in.close();
765 out.close();
766 dest = null;
767 in = null;
768 out = null;
769 }
770 }
771
772 /** Reads from an xmlFile. If dtd_SystemId is not an empty String,
773 * then the XMLFile is not validated against the doctype statement it
774 * contains (the dtd_SystemId entity in the xmlFile is ignored) so
775 * that parsing still succeeds. However, if the DOM Structure returned
776 * by this method is written back out to a file, then make sure that
777 * this doctype is added back into the output file.
778 * @param xmlSource is either an xmlFile or xml InputStream (of a jarred
779 * file, for example) to be read into a DOM structure.
780 * @param dtd_SystemId - if specified, validation against the given DTD
781 * is ignored. The DOM structure returned will not contain the DOCTYPE
782 * entity with the given dtd file reference. If writing the DOM out to
783 * a file later on, then it is advised that this DOC_TYPE is added back
784 * in. If there is no dtd to be validated or whose validation is to be
785 * ignored, pass the empty string for dtd_SystemId.
786 * @param sourceFileName is the name of the (possibly internal) file
787 * that is to be read.
788 * @throws Exception if an error occurred during parsing.
789 * */
790 protected Document readXML(Object xmlSource, String dtd_SystemId,
791 String sourceFileName) throws Exception
792 {
793 Document doc = null;
794 try {
795 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
796 factory.setValidating(false); // doesn't help
797 DocumentBuilder builder = factory.newDocumentBuilder();
798
799 // If dtd is specified, then to ignore the dtd which is/may be
800 // pointing to the wrong location and will therefore invalidate the
801 // parsing effort, we ignore the DTD for the moment. We will add
802 // this back in later when we write the customised xmlFile back out.
803 // (e.g. in <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
804 // the template log4j.xml has log4j.dtd pointing to local filesystem
805 // and that won't work. In this example, log4j.dtd is the dtd_SystemId)
806 // See http://forum.java.sun.com/thread.jspa?threadID=284209&forumID=34
807 if(!dtd_SystemId.equals("")) {
808 builder.setEntityResolver(
809 new IgnoreDTDEntityResolver(dtd_SystemId));
810 }
811 if(xmlSource instanceof java.io.InputStream) {
812 doc = builder.parse((InputStream)xmlSource);
813 } else { // xml source is a file
814 doc = builder.parse((File)xmlSource);
815 }
816
817 // normalize text representation, so that the saved-and-
818 // reloaded version is the same as the DOM constructed
819 doc.getDocumentElement().normalize();
820
821 //System.out.println("xmlFile contents:\n"
822 //+ this.elementToFormattedString(doc.getDocumentElement(), ""));
823 } catch (javax.xml.parsers.ParserConfigurationException e) {
824 throw new Exception ("Error reading XML document "
825 + sourceFileName + " into DOM structure\n" + e);
826 } catch (IOException e) {
827 throw new Exception ("Error parsing XML document "
828 + sourceFileName + " into DOM structure\n" + e);
829 }
830 xmlSource = null;
831 return doc;
832 }
833
834 /**
835 * Writes out a DOM structure to a file.
836 * @param doc is the DOM to be written out to a file
837 * @param xmlFile is the file to write the XML out to
838 * @param dtdToAddBackIn is "" if there's no special DTD to add
839 * back into the DOCTYPE. If not "", it specifies the dtd file to
840 * be added in the DOCTYPE of the XML output file.
841 */
842 protected void writeXML(Document doc, File xmlFile,
843 String dtdToAddBackIn) throws Exception
844 {
845 try {
846 OutputStream out = new FileOutputStream(xmlFile);
847 out.write(elementToFormattedString(
848 doc.getDocumentElement(), dtdToAddBackIn).getBytes());
849 out.close();
850 out = null;
851 xmlFile = null; // local reference not used anymore
852 } catch(Exception e){
853 throw new Exception("Exception when writing out XML to file "
854 + xmlFile.getAbsolutePath() + "\n" + e.getMessage());
855 }
856 }
857
858 /** Given a DOM document, finds the first element where nodeName=tagName
859 * where one of the attributes has the name attrName and whose value
860 * contains attrValueContent. The <b>portion</b> of the attribute value
861 * that matches is then replaced by replacementContent.
862 * If onceOnly is true, the first replacement is made and the method
863 * returns. If false, all matching replacements are made.
864 * @param doc is the DOM Document object in which to search for
865 * the element to be replaced
866 * @param tagName is the name of the element to search for
867 * @param attrName is the name of the attribute of the element to search for
868 * @param attrValueContent is the portion of the attribute value that will
869 * be replaced by replacementContent.
870 * @param replacementContent is the replacement string that will overwrite
871 * the part of the attrName attribute's value that matched attrValueContent
872 * @param onceOnly - if true will look for the first match and perform the
873 * replacement once. If false, it will replace all matches found.
874 * @param wholeItem - if true, the entire string containing attrValueContent
875 * will be replaced by the string replacementContent. If false, only the
876 * attrValueContent portion of the original string will be replaced.
877 * @return true if any replacements have been made.
878 */
879 protected boolean replaceElementWithAttrValue(Document doc, String tagName,
880 String attrName, String attrValueContent, String replacementContent,
881 boolean onceOnly, boolean wholeItem)
882 {
883 boolean found = false;
884 //System.out.println("Searching for:"+tagName+" "+attrName+" "+attrValuePrefix);
885 // Also need to check documentElement (root) itself for a match:
886 NodeList elements = doc.getElementsByTagName(tagName);
887 for(int i = 0; i < elements.getLength(); i++) {
888 Element e = (Element)elements.item(i);
889 String oldVal = e.getAttribute(attrName);
890 if(oldVal.indexOf(attrValueContent) != -1) {
891 String newVal = (wholeItem) ? replacementContent
892 : oldVal.replace(attrValueContent, replacementContent);
893 e.setAttribute(attrName, newVal);
894 if(i == 0) { // print message for user information
895 System.out.println(
896 "\tUpdating: " + oldVal + " To: " + newVal);
897 found = true;
898 }
899 if(onceOnly) {
900 // finished match-and-replace, so
901 // get out of this loop and method
902 return found;
903 }
904 }
905 }
906 return found;
907 }
908
909 /** Given a DOM document, finds the first element where nodeName=tagName
910 * and where the element's inner text contains the string contentValue.
911 * Once found, the <b>entire</b> textnode contents of the matching element
912 * is then replaced with the replacement string.
913 * @param doc is the DOM Document object in which to search for
914 * the element to be replaced
915 * @param tagName is the name of the element to search for
916 * @param contentValue is the portion of the textual content of the
917 * element that should match for the replacement to happen
918 * @param replacement is value that will overwrite the matching portion
919 * of the element's textual content (it will overwrite contentValue).
920 * @param onceOnly - if true will look for the first match and perform the
921 * replacement once. If false, it will replace all matches found.
922 * @param wholeItem - if true, the entire string containing attrValueContent
923 * will be replaced by the string replacementContent. If false, only the
924 * attrValueContent portion of the original string will be replaced.
925 * @return true if any replacements have been made.
926 */
927 protected boolean replaceElementWithValue(Document doc, String tagName,
928 String contentValue, String replacement, boolean onceOnly, boolean wholeItem)
929 {
930 boolean found = false;
931 // Also need to check documentElement (root) itself for a match:
932 NodeList elements = doc.getElementsByTagName(tagName);
933 for(int i = 0; i < elements.getLength(); i++) {
934 Element e = (Element)elements.item(i);
935 String textNodeContent = getValue(e);
936 if(textNodeContent.indexOf(contentValue) != -1) {
937 // overwrite the matching portion
938 String newVal = (wholeItem) ? replacement
939 : textNodeContent.replace(contentValue, replacement);
940 // Put it back into the text node of the element
941 e.setTextContent(newVal);
942 if(i == 0) { // print message for user information
943 System.out.println("\tUpdating: " + textNodeContent
944 + " To: " + newVal);
945 found = true;
946 }
947 // we're now finished if we only wanted to search
948 // for the first occurrence
949 if(onceOnly) {
950 return found;
951 }
952 }
953 }
954 return found;
955 }
956
957 /** Extract the text from an element, if any.
958 * @return the text that's nested in an element's body or ""
959 * if there's none.
960 * @param e is the element whose value is to be extracted.
961 */
962 public static String getValue(Element e) {
963 Node child = e.getFirstChild();
964 return (child == null) ? "" : child.getNodeValue();
965 }
966
967
968 /** Given an Element, this will return its String representation properly
969 * indented for display. (The XML declaration will be added at the top
970 * since this method will be used here to write proper XML out to a file.)
971 * @return a string representation of e, formatted for display.
972 * @param e is the element to be converted to its string representation.
973 * @param dtd_SystemId (if not "") is any DOCTYPE with systemId that needs
974 * to be added back into the file. If "", then no new DOCTYPE entity is
975 * added into the DOM structure represented by Element e.
976 */
977 public static String elementToFormattedString(Element e, String dtd_SystemId)
978 throws Exception
979 {
980 DOMSource domSource = new DOMSource(e);
981 Transformer transformer = TransformerFactory.newInstance().newTransformer();
982 // Do not set transformer to OMIT_XML_DECLARATION! We're doing
983 // a straightforward copy here
984 transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
985 transformer.setOutputProperty(OutputKeys.INDENT, "yes");
986
987 if(!dtd_SystemId.equals("")) {
988 // put the docType (systemID) back in:
989 transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, dtd_SystemId);
990 }
991
992 StringWriter sw = new StringWriter();
993 transformer.transform(domSource, new StreamResult(sw));
994 return sw.toString();
995 }
996
997 //***************HELPER METHODS USED ONLY ONCE********************/
998 /**
999 * Moves the (fedoragsearch.war) war file from the given location
1000 * into FEDORA_HOME's tomcat folder (i.e. into CATALINA_HOME) and
1001 * unpacks it there. Unpacking is achieved by starting the fedora
1002 * server after the move. If a fedoragsearch is already unpacked
1003 * in CATALINA_HOME, this method will not move the given
1004 * fedoragsearch.war file.
1005 * This method does more than merely move fedoragsearch.war:
1006 * regardless of whether the war file exists and is moved or not,
1007 * the fedora server is first stopped and at the end it is started.
1008 * @param gsearchWarFile the fedoragsearch.war file to be moved and
1009 * unpacked.
1010 * @throws Exception if an unpacked fedoragsearch does not exist
1011 * in the CATALINA_HOME/webapps folder at the end.
1012 */
1013 protected void moveUnpackWarFile(File gsearchWarFile) throws Exception {
1014 final String stopFedora
1015 = gSearchProperties.getProperty("fedora.stop")+scriptExtension;
1016 final String startFedora
1017 = gSearchProperties.getProperty("fedora.start")+scriptExtension;
1018
1019 // shut down fedora server
1020 //this.runProcess(new String[]{"cmd & " + stopFedora + " & exit"});
1021 this.runProcess(new String[]{stopFedora}, false);
1022
1023 File dest = null;
1024 File webapps = new File(CATALINA_HOME,"webapps");
1025 // we don't want to overwrite any existing fedoraGSearch folder
1026 File unpackedFedoraGSearch = new File(webapps, FEDORAGSEARCH);
1027
1028 if(!gsearchWarFile.exists()) {
1029 if(!unpackedFedoraGSearch.exists()) {
1030 // neither war file nor unpacked version exist
1031 throw new Exception(
1032 "Neither the war file " + gsearchWarFile.getAbsolutePath()
1033 + "\nnor " + unpackedFedoraGSearch.getAbsolutePath()
1034 + " exist. Cannot proceed.");
1035 } else { // only gsearchWarFile does not exist
1036 System.out.println("fedoragsearch.war file: "
1037 + gsearchWarFile + " does not exist!");
1038 }
1039 }
1040
1041 if(unpackedFedoraGSearch.exists() && unpackedFedoraGSearch.isDirectory())
1042 {
1043 System.err.println(unpackedFedoraGSearch
1044 + " exists. Not overwriting with war file " + gsearchWarFile);
1045 } else {
1046 // perform the move: whatever the original war file was called,
1047 // make sure the destination file has the name fedoragsearch.war
1048 dest = new File(webapps, "fedoragsearch.war");
1049 System.out.println("Moving " + gsearchWarFile + "\nto " + dest);
1050 boolean success = gsearchWarFile.renameTo(dest);
1051 if(!success) {
1052 System.out.println("gsearchWarFile could not be moved."
1053 + " Trying an OS move command.");
1054 // happens on Linux, try moving it in a different way:
1055 this.runProcess(new String[] {"mv",
1056 gsearchWarFile.getAbsolutePath(),
1057 dest.getAbsolutePath()}, true);
1058 }
1059 }
1060
1061 if(dest != null && !dest.exists()) {
1062 // don't check 'success', as it doesn't return true (fast enough)
1063 throw new Exception("Could not move " + gsearchWarFile
1064 + "\nto " + dest);
1065 }
1066 // Start the fedora server, and let it unpack fedoragsearch if
1067 // it has been freshly copied. Else restart the server all the same
1068 this.runProcess(new String[]{startFedora}, false);
1069 //this.runProcess(new String[]{"cmd & " + startFedora + " & exit"});
1070
1071
1072 // We'll wait until unpacking of fedoragsearch has finished,
1073 // otherwise installation fails. The way we check is by seeing
1074 // whether the fedoragsearch/WEB-INF/classes folder has been created.
1075 // If not, we continue waiting and output "." to the screen
1076 System.out.print("Waiting for fedoragsearch to finish unpacking...");
1077 waitForFedoraServer();
1078 }
1079
1080 /** Makes changes to the fedora.fcfg file located inside FEDORA_HOME.
1081 * It changes the fedora.server.storage.DOManager to the GSearchDOManager
1082 * and sets the gSearchRESTURL to the specific fedora host and port values
1083 * specified for installation.
1084 * This method also checks that "greenstone" is in the list of PIDs that
1085 * Fedora recognises. If it's not in the list already, it is added in
1086 * there.
1087 */
1088 protected void customiseFedoraConfigFile() throws Exception
1089 {
1090 try{
1091 File fedoraConfigFile = new File(
1092 gSearchProperties.getProperty("fedora.config.dir"), "fedora.fcfg");
1093
1094 Document doc = this.readXML(fedoraConfigFile, "", fedoraConfigFile.getAbsolutePath());
1095
1096 // (1) Change class to "fedora.server.storage.GSearchDOManager" in
1097 // <module role="fedora.server.storage.DOManager" class="insert-here">
1098 Element module = null;
1099 NodeList nodes = doc.getElementsByTagName("module");
1100 for(int i = 0; i < nodes.getLength(); i++) {
1101 Element e = (Element)nodes.item(i);
1102 // getAttr will return "" if no match found
1103 if(e.getAttribute("role").equals("fedora.server.storage.DOManager")) {
1104 module = e;
1105 }
1106 }
1107 if(module == null) {
1108 // can't do anymore, because the rest is dependent on finding module
1109 System.err.println(
1110 "Unable to find fedora.server.storage.DOManager module element in "
1111 + fedoraConfigFile);
1112 return;
1113 }
1114 module.setAttribute("class", "fedora.server.storage.GSearchDOManager");
1115 nodes = null;
1116
1117 // (2) Set the FedoraGSearch rest url inside the module element found:
1118 // <param name="gSearchRESTURL" value="http://?host:?port/fedoragsearch/rest">
1119 // And now that we are here and looking for params, might as well
1120 // ensure that "greenstone" is one of the PIDs that Fedora recognises.
1121 // Checking for:
1122 // <param name="retainPIDs" value="demo test changeme fedora-bdef fedora-bmech tutorial greenstone">
1123 nodes = module.getElementsByTagName("param");
1124 for(int i = 0; i < nodes.getLength(); i++) {
1125 Element e = (Element)nodes.item(i);
1126 // getAttr will return "" if no match found
1127 String nameAttr = e.getAttribute("name");
1128 if(nameAttr.equals("gSearchRESTURL")) {
1129 e.setAttribute("value",
1130 gSearchProperties.getProperty("gsearch.base")
1131 +File.separator+"rest"
1132 //"http://"+this.host+":"+this.port+"/fedoragsearch/rest");
1133 );
1134 } else if(nameAttr.equals("retainPIDs")) {
1135 String valAttr = e.getAttribute("value");
1136 // Check if greenstone not yet in list of pids
1137 if(valAttr.indexOf("greenstone") == -1) {
1138 // Need to append greenstone to the list of pids
1139 // don't forget to precede it by a space!
1140 valAttr = valAttr + " greenstone";
1141 // put the attribute back
1142 e.setAttribute("value", valAttr);
1143 } else {
1144 System.out.println("\"greenstone\" is already in"
1145 + " the list of fedora-recognised PIDs");
1146 }
1147 }
1148 }
1149
1150 // Now we've updated the necessary Elements, need to write the
1151 // it back into the same file
1152 OutputStream out = new FileOutputStream(fedoraConfigFile);
1153 out.write(elementToFormattedString(
1154 doc.getDocumentElement(),"").getBytes());
1155 out.close();
1156 out = null;
1157 fedoraConfigFile = null;
1158 } catch(Exception e) {
1159 throw new Exception("Tried to parse "
1160 + gSearchProperties.getProperty("fedora.config.dir")
1161 + File.separator+"fedora.fcfg"
1162 + "\nand then modify it and write it out the same file."
1163 + " But an exception occurred:\n" + e.getMessage());
1164 }
1165 }
1166
1167 /**
1168 * Creates the Lucene index directory in the right location inside
1169 * FEDORA_HOME into which FedoraGSearch will store the indexes for
1170 * the Greenstone contents in the Fedora repository.
1171 * If it already exists, the Lucene index directory is not created.
1172 * @throws Exception if the Lucene index directory cannot be created
1173 * in the appropriate location inside FEDORA_HOME
1174 */
1175 protected void createLuceneIndexDir() throws Exception {
1176 File luceneIndexesDir = new File(gSearchProperties.getProperty(
1177 "lucene.indexes.dir"), indexName);
1178 if(!luceneIndexesDir.exists()) {
1179 System.out.println("Creating directory for the Lucene index: "
1180 + luceneIndexesDir);
1181 if(!luceneIndexesDir.mkdirs()) {
1182 throw new Exception("Unable to create Lucene index dir: "
1183 + luceneIndexesDir);
1184 }
1185 } else {
1186 System.err.println("Lucene index directory " + luceneIndexesDir
1187 + " exists.\nNot creating it.");
1188 }
1189 }
1190
1191 //******************STATIC METHODS USED BY MAIN**********************/
1192 /** Displays a dialog to get user input for
1193 * - fedora server host, port, username and password,
1194 * - the names for the fedora generic search index and repository
1195 * that are to be created, and
1196 * - for the location of fedoragenericsearch.war (the installer is
1197 * meant to work specifically with Muradora's fedoragenericsearch.war
1198 * since they have edited various property and xml files to make it
1199 * all easier).
1200 * The dialog displays the default values to the user.
1201 * @return a Properties map containing the values entered by the
1202 * user for the various initialisation parameters required by the
1203 * Fedora Generic Search Installer GSearchInstaller. */
1204 public static Properties showInputDialog()
1205 {
1206 final JTextField fileField = new JTextField(defaults.getProperty(PROP_FILE));
1207 JTextField hostField = new JTextField(defaults.getProperty(PROP_HOST));
1208 JTextField portField = new JTextField(defaults.getProperty(PROP_PORT));
1209 JTextField unameField = new JTextField(defaults.getProperty(PROP_UNAME));
1210 JTextField passwField = new JPasswordField(defaults.getProperty(PROP_PASSW));
1211 JTextField reposField = new JTextField(defaults.getProperty(PROP_REPOS));
1212 JTextField indexField = new JTextField(defaults.getProperty(PROP_INDEX));
1213
1214 // Arrange the controls
1215 JPanel serverPanel = new JPanel(new GridLayout(4,2));
1216 serverPanel.setBorder(
1217 BorderFactory.createTitledBorder("Fedora server details"));
1218 serverPanel.add(new JLabel(PROP_HOST));
1219 serverPanel.add(hostField);
1220 serverPanel.add(new JLabel(PROP_PORT));
1221 serverPanel.add(portField);
1222 serverPanel.add(new JLabel(PROP_UNAME));
1223 serverPanel.add(unameField);
1224 serverPanel.add(new JLabel(PROP_PASSW));
1225 serverPanel.add(passwField);
1226
1227 JPanel gSearchPanel = new JPanel(new GridLayout(2,2));
1228 gSearchPanel.setBorder(BorderFactory.createTitledBorder(
1229 "Customise Fedora Generic Search properties"));
1230 gSearchPanel.add(new JLabel(PROP_REPOS + " name"));
1231 gSearchPanel.add(reposField);
1232 gSearchPanel.add(new JLabel(PROP_INDEX + " name"));
1233 gSearchPanel.add(indexField);
1234
1235 JButton browseButton = new JButton("Browse");
1236 JPanel filePanel = new JPanel(new FlowLayout());
1237 filePanel.setBorder(BorderFactory.createTitledBorder(
1238 "Locate the fedoragenericsearch.war file"));
1239 filePanel.add(new JLabel(PROP_FILE));
1240 filePanel.add(fileField);
1241 filePanel.add(browseButton);
1242
1243 // Now we're going to handle click events on the browseButton:
1244 // a fileChooser dialog for opening files displays
1245 // First we set up a FileFilter for filtering only war files
1246 // FIXME: Do we allow all *.war files to be visible or only
1247 // fedoragsearch.war? Advantage: wrong war files can't be chosen;
1248 // disadvantage: if fedoragsearch.war was renamed it won't show up.
1249 final javax.swing.filechooser.FileFilter warFileFilter
1250 = new javax.swing.filechooser.FileFilter() {
1251 public boolean accept(File f) {
1252 if(f.isDirectory()) {
1253 return true;
1254 }
1255 // f.getName().equalsIgnoreCase("fedoragsearch.war")) {
1256 if(f.getName().endsWith(".war") &&
1257 f.getName().toLowerCase().contains("fedoragsearch"))
1258 {
1259 return true;
1260 } else {
1261 return false;
1262 }
1263 }
1264 public String getDescription() {
1265 return "fedora generic search *.war file";
1266 //return "Tomcat server *.war files";
1267 }
1268 };
1269 // Add the ActionListener to the browseButton to display the FileChooser
1270 browseButton.addActionListener(
1271 new java.awt.event.ActionListener() {
1272 public void actionPerformed(java.awt.event.ActionEvent e) {
1273 JFileChooser chooser = new JFileChooser();
1274 chooser.setFileFilter(warFileFilter);
1275 int returnVal = chooser.showOpenDialog(null);
1276
1277 if (returnVal == JFileChooser.APPROVE_OPTION) {
1278 File file = chooser.getSelectedFile();
1279 fileField.setText(file.getAbsolutePath());
1280 } else { //CANCEL_OPTION or ERROR_OPTION
1281 fileField.setText(defaults.getProperty(PROP_FILE));
1282 }
1283 }
1284 }
1285 );
1286
1287 // Display the dialog - we'll just use the usual confirmDialog
1288 // (the different panels are combined on top of each other)
1289 // Then we get automatic behaviour on OK and CANCEL
1290 int option = JOptionPane.showConfirmDialog(null,
1291 new Object[]{serverPanel, gSearchPanel, filePanel},
1292 "Fedora Generic Search Installer - uses Muradora's FGS work",
1293 JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
1294
1295 // if ok was pressed, we return the values the user entered
1296 Properties inputs = new Properties();
1297 if (option == JOptionPane.OK_OPTION) {
1298 inputs.setProperty(PROP_UNAME, getSafeValue(unameField, PROP_UNAME));
1299 inputs.setProperty(PROP_PASSW, passwField.getText());
1300 inputs.setProperty(PROP_HOST, getSafeValue(hostField, PROP_HOST));
1301 inputs.setProperty(PROP_PORT, getSafeValue(portField, PROP_PORT));
1302 inputs.setProperty(PROP_REPOS, getSafeValue(reposField, PROP_REPOS));
1303 inputs.setProperty(PROP_INDEX, getSafeValue(indexField, PROP_HOST));
1304 inputs.setProperty(PROP_FILE, getSafeValue(fileField, PROP_FILE));
1305 } else { // Cancel option
1306 System.exit(0);
1307 }
1308 return inputs;
1309 }
1310
1311 /** This method returns the value of the textfield for the given
1312 * GSearchInstaller initialiser property, if this is not the empty string.
1313 * If it is the empty string, the default value for this property is
1314 * returned.
1315 * @param field is the TextField whose value is being extracted
1316 * @param property is the GSearchInstaller property that the TextField
1317 * value maps to.
1318 * @return the contents of the textfield for the property or the defaults
1319 * value for the property if the textfield contained "".
1320 */
1321 protected static String getSafeValue(JTextField field, String property)
1322 {
1323 String value = field.getText();
1324 if(value.equals("")) {
1325 return defaults.getProperty(property, "");
1326 }
1327 return value;
1328 }
1329
1330 /** @return a string specifying the requirements of this program. */
1331 public static String info() {
1332 StringBuffer info = new StringBuffer(GSearchInstaller.class.getSimpleName()
1333 + " is based on the installation instructions at\n");
1334 info.append("http://drama.ramp.org.au/cgi-bin/trac.cgi/wiki/InstallingFedoraGSearch");
1335 info.append("\n\nBefore running this program, you need to have Fedora installed");
1336 info.append("\nand the environment variables FEDORA_HOME and CATALINA_HOME set.\n");
1337 info.append("(These would have had to have been set when installing Fedora.)");
1338 info.append("\nRun with -help to see the command-line options.\n");
1339 return info.toString();
1340 }
1341
1342 /** If the program is run from the command line and the user executed
1343 * it with -help or help, then this usage String is displayed.
1344 * @return String describing how to use this program when running it
1345 * from the command line. */
1346 public static String usage() {
1347 StringBuffer usage = new StringBuffer(info());
1348 String progName = GSearchInstaller.class.getSimpleName();
1349
1350 usage.append("\nRun:\n");
1351 usage.append(progName + " -help");
1352 usage.append("\n\tTo see this usage message again.");
1353
1354 usage.append("\n\n" + progName);
1355 usage.append("\n\tTo see a GUI dialog requesting input.");
1356
1357 usage.append("\n\n" + progName + " [options]");
1358 usage.append("\n\tFor command-line use. (For all unspecified options--except the");
1359 usage.append("\n\tfedora server password--default values will be used.)");
1360 usage.append("\n\n\tOptions:");
1361
1362 usage.append("\n\t-file <fedoragsearch.war file>");
1363 usage.append("\n\t\tThe full path to the fedoragsearch.war file (default: fedoragsearch.war)");
1364 usage.append("\n\t\tThis file is ignored if folder CATALINA_HOME/webapps/fedoragsearch exists.");
1365 usage.append("\n\t\thttp://drama.ramp.org.au/software/fedoragsearch.war has the Muradora version.");
1366
1367 usage.append("\n\t-host <fedora server host>");
1368 usage.append("\n\t\tThe host at which the fedora server is listening (default: localhost)");
1369 usage.append("\n\t-port <fedora server port>");
1370 usage.append("\n\t\tThe port on which the fedora server is listening (default: 8080)");
1371
1372 usage.append("\n\t-username <fedora server username>");
1373 usage.append("\n\t\tThe username for accessing the fedora server (default: fedoraAdmin)");
1374 usage.append("\n\t-password <fedora server password>");
1375 usage.append("\n\t\tThe password for accessing the fedora server (default: '')");
1376
1377 usage.append("\n\t-repository <repository name>");
1378 usage.append("\n\t\tA name for the repository that is to be indexed (default: Fedora)");
1379 usage.append("\n\t-index <index name>");
1380 usage.append("\n\t\tA name for the repository index to be created (default: FedoraIndex)\n");
1381
1382 return usage.toString();
1383 }
1384
1385 /** Called to process multiple command line arguments where these
1386 * arguments are GSearchInstaller constructor options followed by
1387 * their values.
1388 * @param args are the command-line arguments received by main
1389 * which consist of one or more multiple "-option value" pairs.
1390 * @return a Properties map containing (option, value) pairs
1391 * that will be used by GSearchInstaller to install Muradora's
1392 * Fedora Generic Search. Empty strings for recognised options
1393 * are replaced by defaults.
1394 * */
1395 public static Properties parseInstallationArgs(String[] args) {
1396 Properties inputs = new Properties();
1397 // add pairs of arguments as Property(option, value) pairs
1398 // i+=2: processed two arguments in one go (the option and value)
1399 for(int i = 1; i < args.length; i+=2) {
1400 if(args[i-1].startsWith("-")) {
1401 // remove any starting hypen from the options
1402 args[i-1] = args[i-1].substring(1);
1403 } // else the option didn't start with a hyphen.
1404
1405 // If the values are empty strings, use default for
1406 // that option if it exists (if it does not exist, it
1407 // will be ignored by GSearchInstaller anyway).
1408 if(args[i].equals("")) {
1409 args[i] = defaults.getProperty(args[i-1], "");
1410 }
1411 // add the (option, value) pair
1412 inputs.setProperty(args[i-1], args[i]);
1413 }
1414
1415 // display any user provided properties
1416 Set entries = inputs.entrySet();
1417 Iterator i = entries.iterator();
1418 while(i.hasNext()) {
1419 Map.Entry entry = (Map.Entry)i.next();
1420 System.out.print("\tkey: " + entry.getKey() + "\t value: ");
1421 if(entry.getKey().equals(PROP_PASSW)) {
1422 String value = (String)entry.getValue();
1423 for(int j = 0; j < value.length(); j++) {
1424 System.out.print("*");
1425 }
1426 System.out.println();
1427 } else {
1428 System.out.println(entry.getValue());
1429 }
1430 }
1431
1432 return inputs;
1433 }
1434
1435
1436 /**
1437 * The main method creates a GSearchInstaller to install Fedora Generic
1438 * Search from a (Muradora) fedoragsearch.war file.
1439 * The program can be run in one of two ways:
1440 * - with no arguments: a dialog is displayed requesting inputs for the
1441 * parameters used by the GSearchInstaller initialisation.
1442 * - with arguments for command-line invocation. (Run with -help or help
1443 * to find out what parameter options are there.)
1444 */
1445 public static void main(String[] args) {
1446 GSearchInstaller installer = null;
1447 try {
1448
1449 Properties inputs = null;
1450 if(args.length < 1) { // 0 args
1451 inputs = GSearchInstaller.showInputDialog();
1452 } else { // one or more arguments
1453 if(args[0].equalsIgnoreCase("-help")
1454 || args[0].equalsIgnoreCase("help"))
1455 {
1456 System.out.println(GSearchInstaller.usage());
1457 System.exit(1); // help message exits with 1
1458 }
1459 else // process the multiple arguments
1460 {
1461 inputs = GSearchInstaller.parseInstallationArgs(args);
1462 }
1463 }
1464 // Now we have the input Properties and can instantiate the
1465 // GSearchInstaller (if "help" option, then it would have exited)
1466 installer = new GSearchInstaller(inputs);
1467 // try installing
1468 installer.install();
1469 } catch(Exception e) {
1470 System.out.println(e.getMessage());
1471 e.printStackTrace();
1472 System.out.println("\n" + info());
1473 // -1 exit value means an exception occurred during
1474 // Fedora Generic Search Installation
1475 System.exit(-1);
1476 }
1477
1478 // No exception so far, so now we can index the repository contents.
1479 try {
1480 installer.indexGreenstoneContents(true);// empty existing index first
1481 } catch(Exception e) {
1482 System.out.println(e.getMessage());
1483 e.printStackTrace();
1484 System.out.println("\n" + info());
1485 // Exit with -2 if exception during updateIndex
1486 System.exit(-2);
1487 }
1488
1489 // If we're here, then we finished with no exceptions
1490 System.out.println("Installation and indexing completed.");
1491 System.exit(0);
1492 }
1493}
Note: See TracBrowser for help on using the repository browser.