View Javadoc
1   /*******************************************************************************
2    * Copyright 2012 Internet2
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   ******************************************************************************/
16  package edu.internet2.middleware.grouperInstaller;
17  
18  import java.io.BufferedInputStream;
19  import java.io.BufferedReader;
20  import java.io.ByteArrayInputStream;
21  import java.io.File;
22  import java.io.FileInputStream;
23  import java.io.FileOutputStream;
24  import java.io.FileReader;
25  import java.io.FilenameFilter;
26  import java.io.IOException;
27  import java.io.InputStream;
28  import java.io.InputStreamReader;
29  import java.io.OutputStream;
30  import java.net.MalformedURLException;
31  import java.net.URL;
32  import java.nio.file.Files;
33  import java.nio.file.Paths;
34  import java.nio.file.StandardOpenOption;
35  import java.security.SecureRandom;
36  import java.text.ParseException;
37  import java.text.SimpleDateFormat;
38  import java.util.ArrayList;
39  import java.util.Arrays;
40  import java.util.Collection;
41  import java.util.Collections;
42  import java.util.Date;
43  import java.util.Enumeration;
44  import java.util.HashMap;
45  import java.util.HashSet;
46  import java.util.Iterator;
47  import java.util.LinkedHashMap;
48  import java.util.LinkedHashSet;
49  import java.util.List;
50  import java.util.Map;
51  import java.util.Properties;
52  import java.util.Set;
53  import java.util.TreeMap;
54  import java.util.TreeSet;
55  import java.util.regex.Matcher;
56  import java.util.regex.Pattern;
57  import java.util.zip.ZipEntry;
58  import java.util.zip.ZipFile;
59  
60  import javax.xml.parsers.DocumentBuilder;
61  import javax.xml.parsers.DocumentBuilderFactory;
62  import javax.xml.xpath.XPath;
63  import javax.xml.xpath.XPathConstants;
64  import javax.xml.xpath.XPathExpression;
65  import javax.xml.xpath.XPathFactory;
66  
67  import org.w3c.dom.Document;
68  import org.w3c.dom.Element;
69  import org.w3c.dom.NamedNodeMap;
70  import org.w3c.dom.Node;
71  import org.w3c.dom.NodeList;
72  
73  import edu.internet2.middleware.grouperInstaller.GrouperInstaller.GrouperDirectories.GrouperInstallType;
74  import edu.internet2.middleware.grouperInstaller.GrouperInstallerIndexFile.PatchFileType;
75  import edu.internet2.middleware.grouperInstaller.morphString.Crypto;
76  import edu.internet2.middleware.grouperInstaller.util.GiDbUtils;
77  import edu.internet2.middleware.grouperInstaller.util.GrouperInstallerUtils;
78  import edu.internet2.middleware.grouperInstaller.util.GrouperInstallerUtils.CommandResult;
79  import edu.internet2.middleware.grouperInstallerExt.org.apache.commons.compress.archivers.tar.TarArchiveEntry;
80  import edu.internet2.middleware.grouperInstallerExt.org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
81  import edu.internet2.middleware.grouperInstallerExt.org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
82  import edu.internet2.middleware.grouperInstallerExt.org.apache.commons.compress.utils.IOUtils;
83  import edu.internet2.middleware.grouperInstallerExt.org.apache.commons.httpclient.HttpClient;
84  import edu.internet2.middleware.grouperInstallerExt.org.apache.commons.httpclient.methods.GetMethod;
85  
86  /**
87   * Install grouper
88   * @author mchyzer
89   *
90   */
91  public class GrouperInstaller {
92  
93    public static final String JAVA_MIN_VERSION = "1.7";
94  
95    /**
96     * location of where grouper is an find all the files within
97     */
98    private GrouperDirectories grouperDirectories = new GrouperDirectories();
99    
100   /**
101    * structure and logic to locate where grouper is installed (or will be?)
102    */
103   public static class GrouperDirectories {
104 
105     /**
106      * which type of install are we
107      */
108     public static enum GrouperInstallType {
109 
110       /**
111        * installed Grouper env
112        */
113       installed,
114       
115       /**
116        * source Grouper env
117        */
118       source;
119       
120     }
121 
122     /**
123      * are we an installed directory or source directory
124      */
125     private GrouperInstallType grouperInstallType;
126 
127     
128     /**
129      * are we an installed directory or source directory
130      * @return the grouperInstallType
131      */
132     public GrouperInstallType getGrouperInstallType() {
133       return this.grouperInstallType;
134     }
135 
136     
137     /**
138      * are we an installed directory or source directory
139      * @param grouperInstallType1 the grouperInstallType to set
140      */
141     public void setGrouperInstallType(GrouperInstallType grouperInstallType1) {
142       this.grouperInstallType = grouperInstallType1;
143     }
144 
145     
146     
147   }
148   
149   /**
150    * default ip address to listen for stuff
151    */
152   private String defaultIpAddress = null;
153   
154 
155   /**
156    * if we should continue or not
157    * @return if should continue
158    * @param autorunPropertiesKey key in properties file to automatically fill in a value
159    */
160   private static boolean shouldContinue(String autorunPropertiesKey) {
161     return shouldContinue(null, null, autorunPropertiesKey);
162   }
163   
164   /**
165    * if we should continue or not
166    * @param hint 
167    * @param exitHint 
168    * @param autorunPropertiesKey key in properties file to automatically fill in a value
169    * @return if should continue
170    */
171   private static boolean shouldContinue(String hint, String exitHint, String autorunPropertiesKey) {
172     if (hint == null) {
173       hint = "Do you want to continue ";
174     }
175     if (!hint.endsWith(" ")) {
176       hint += " ";
177     }
178     System.out.print(hint + "(t|f)? [f] ");
179     boolean shouldContinue = readFromStdInBoolean(false, autorunPropertiesKey);
180     if (!shouldContinue) {
181       if (exitHint == null) {
182         exitHint = "OK, will not continue, exiting...";
183       }
184       if (!GrouperInstallerUtils.isBlank(exitHint)) {
185         System.out.println(exitHint);
186       }
187     }
188     return shouldContinue;
189   }
190   
191   /**
192    * read a string from stdin
193    * @param defaultBoolean null for none, or true of false for if the input is blank
194    * @param autorunPropertiesKey key in properties file to automatically fill in a value
195    * @return the string
196    */
197   private static boolean readFromStdInBoolean(Boolean defaultBoolean, String autorunPropertiesKey) {
198     int i=100;
199     //keep trying until we get it
200     while(true) {
201       String input = readFromStdIn(autorunPropertiesKey);
202       if (GrouperInstallerUtils.isBlank(input) && defaultBoolean != null) {
203         return defaultBoolean;
204       }
205       try {
206         boolean inputBoolean = GrouperInstallerUtils.booleanValue(input);
207         return inputBoolean; 
208       } catch (Exception e) {
209         if (defaultBoolean != null) {
210           System.out.print("Expecting t or f or <blank> but received: '" + input + "', please try again: ");
211         } else {
212           System.out.print("Expecting t or f but received: '" + input + "', please try again: ");
213         }
214       }
215       if (i-- < 0) {
216         throw new RuntimeException("Too many tries for finding a boolean!");
217       }
218     }
219   }
220   
221   /**
222    * read a string from stdin
223    * @param autorunPropertiesKey key in properties file to automatically fill in a value
224    * @return the string
225    */
226   private static String readFromStdIn(String autorunPropertiesKey) {
227     
228     String str = null;
229     
230     if (GrouperInstallerUtils.propertiesContainsKey(autorunPropertiesKey)) {
231 
232       str = GrouperInstallerUtils.propertiesValue(autorunPropertiesKey, false);
233       
234       System.out.println("<using autorun property " + autorunPropertiesKey + ">: '" + str + "'");
235       
236     } else if (GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.autorun.useDefaultsAsMuchAsAvailable", false, false)) {
237       
238       System.out.println("<using default which is blank due to grouperInstaller.autorun.useDefaultsAsMuchAsAvailable and " + autorunPropertiesKey + ">: ");
239       
240     } else {
241 
242       if (GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.print.autorunKeys", false, false)) {
243         System.out.print("<" + autorunPropertiesKey + ">: ");
244       }
245       
246       try {
247         BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
248         str = in.readLine();
249       } catch (Exception e) {
250         throw new RuntimeException("Problem", e);
251       }
252 
253     }
254     
255     return GrouperInstallerUtils.trim(str);
256     
257   }
258 
259   /**
260    * download a file, delete the local file if it exists
261    * @param url
262    * @param localFileName
263    * @param autorunUseLocalFilePropertiesKey key in properties file to automatically fill in a value if download exists, default true
264    */
265   private void downloadFile(String url, String localFileName, String autorunUseLocalFilePropertiesKey) {
266     downloadFile(url, localFileName, false, null, autorunUseLocalFilePropertiesKey);
267   }
268 
269   /**
270    * download a file, delete the local file if it exists
271    * @param url
272    * @param localFileName
273    * @param allow404
274    * @param prefixFor404 print message prefix if 404
275    * @param autorunUseLocalFilePropertiesKey key in properties file to automatically fill in a value if download exists, default true
276    * @return true if downloaded, false if not
277    */
278   private boolean downloadFile(final String url, final String localFileName, final boolean allow404, 
279       final String prefixFor404, final String autorunUseLocalFilePropertiesKey) {
280 
281     boolean useLocalFile = false;
282 
283     final File localFile = new File(localFileName);
284 
285     if (localFile.exists()) {
286       
287       if (this.useAllLocalFiles != null && this.useAllLocalFiles == true) {
288         useLocalFile = true;
289       } else {
290         System.out.print("File exists: " + localFile.getAbsolutePath() + ", should we use the local file (t|f)? [t]: ");
291         useLocalFile = readFromStdInBoolean(true, autorunUseLocalFilePropertiesKey);
292         
293         if (useLocalFile && this.useAllLocalFiles == null) {
294           System.out.print("Would you like to use all local files (t|f)? [t]: ");
295           this.useAllLocalFiles = readFromStdInBoolean(true, "grouperInstaller.autorun.useAllLocalFiles");
296         }
297       }
298     }
299     
300     if (useLocalFile) {
301       return true;
302     }
303 
304     if (allow404 && GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.useLocalFilesOnlyForDevelopment", false, false)) {
305       return false;
306     }
307 
308     final boolean[] result = new boolean[1];
309     
310     Runnable runnable = new Runnable() {
311       
312       public void run() {
313         result[0] = downloadFileHelper(url, localFileName, allow404, prefixFor404, autorunUseLocalFilePropertiesKey);
314       }
315     };
316     
317     GrouperInstallerUtils.threadRunWithStatusDots(runnable, true);
318     
319     return result[0];
320   }
321   
322   /**
323    * download a file, delete the local file if it exists
324    * @param url
325    * @param localFileName
326    * @param allow404
327    * @param prefixFor404 print message prefix if 404
328    * @param autorunUseLocalFilePropertiesKey key in properties file to automatically fill in a value if download exists, default true
329    * @return true if downloaded, false if not
330    */
331   private static boolean downloadFileHelper(String url, String localFileName, boolean allow404, 
332       String prefixFor404, String autorunUseLocalFilePropertiesKey) {
333 
334     final File localFile = new File(localFileName);
335 
336     HttpClient httpClient = new HttpClient();
337 
338     String proxyServer = GrouperInstallerUtils.propertiesValue("download.server.proxyServer", false); 
339     
340     if (!GrouperInstallerUtils.isBlank(proxyServer)) {
341       
342       int proxyPort = GrouperInstallerUtils.propertiesValueInt("download.server.proxyPort", -1, true);
343       httpClient.getHostConfiguration().setProxy(proxyServer, proxyPort);
344     }
345     
346     //see if we are working with local files:
347     {
348       File localFileFromUrl = new File(url);
349       if (localFileFromUrl.exists()) {
350         System.out.println("Copying local file: " + url + " to file: " + localFileName);
351         
352         if (localFile.exists()) {
353           
354           System.out.println("File exists: " + localFile.getAbsolutePath() + ", deleting");
355           
356           if (!localFile.delete()) {
357             throw new RuntimeException("Cant delete file: " + localFile.getAbsolutePath() + "!!!!!");
358           }
359         } else {
360           if (!localFile.getParentFile().exists()) {
361             GrouperInstallerUtils.mkdirs(localFile.getParentFile());
362           }
363 
364         }
365         
366         try {
367           FileOutputStream fileOutputStream = new FileOutputStream(localFile);
368           FileInputStream fileInputStream = new FileInputStream(localFileFromUrl);
369   
370           GrouperInstallerUtils.copy(fileInputStream, fileOutputStream);
371   
372           return true;
373         } catch (Exception exception) {
374           String errorMessage = "Error copying file: " + url;
375           System.out.println(errorMessage);
376           throw new RuntimeException(errorMessage, exception);
377         }
378 
379       }
380       
381       //if it doesnt exist, see if the parent dir exists
382       if (localFileFromUrl.getParentFile().exists()) {
383 
384         //the dir is there but no file...   hmmmm
385         if (allow404) {
386           if (GrouperInstallerUtils.isBlank(prefixFor404)) {
387             prefixFor404 = "File not found: ";
388           }
389           System.out.println(prefixFor404 + url);
390           return false;
391         }
392 
393         //weve got a problem
394         
395       }
396     }
397     
398     GetMethod getMethod = null;
399     try {
400       
401       getMethod = new GetMethod(url);
402       
403       int result = httpClient.executeMethod(getMethod);
404       
405       if (allow404 && result == 404) {
406         if (GrouperInstallerUtils.isBlank(prefixFor404)) {
407           prefixFor404 = "File not found: ";
408         }
409         System.out.println(prefixFor404 + url);
410         return false;
411       }
412       
413       System.out.println("Downloading from URL: " + url + " to file: " + localFileName);
414 
415       if (result != 200) {
416         throw new RuntimeException("Expecting 200 but received: " + result);
417       }
418       
419       InputStream inputStream = getMethod.getResponseBodyAsStream();
420 
421       if (localFile.exists()) {
422         
423         System.out.println("File exists: " + localFile.getAbsolutePath() + ", deleting");
424         
425         if (!localFile.delete()) {
426           throw new RuntimeException("Cant delete file: " + localFile.getAbsolutePath() + "!!!!!");
427         }
428       }
429       
430       if (!localFile.getParentFile().exists()) {
431         GrouperInstallerUtils.mkdirs(localFile.getParentFile());
432       }
433       
434       FileOutputStream fileOutputStream = new FileOutputStream(localFile);
435 
436 
437       GrouperInstallerUtils.copy(inputStream, fileOutputStream);
438 
439       return true;
440 
441     } catch (Exception exception) {
442       String errorMessage = "Error connecting to URL: " + url;
443       System.out.println(errorMessage);
444       throw new RuntimeException(errorMessage, exception);
445     }
446   }
447 
448   /**
449    * 
450    * @param ehcacheBaseFile
451    */
452   public void convertEhcacheBaseToProperties(File ehcacheBaseFile) {
453     //File ehcacheBaseBakFile = this.bakFile(ehcacheBaseFile);
454     //GrouperInstallerUtils.copyFile(existingFile, bakFile, true);
455     //System.out.println("Backing up: " + existingFile.getAbsolutePath() + " to: " + bakFile.getAbsolutePath());
456     
457     NodeList nodeList = GrouperInstallerUtils.xpathEvaluate(ehcacheBaseFile, "/ehcache/cache");
458     
459     Set<String> usedKeys = new HashSet<String>();
460     
461     for (int i=0;i<nodeList.getLength();i++) {
462       
463       Element element = (Element)nodeList.item(i);
464 
465       //  <cache  name="edu.internet2.middleware.grouper.internal.dao.hib3.Hib3MemberDAO.FindBySubject"
466       //      maxElementsInMemory="5000"
467       //      eternal="false"
468       //      timeToIdleSeconds="5"
469       //      timeToLiveSeconds="10"
470       //      overflowToDisk="false"  
471       //      statistics="false"
472       //  />
473 
474       
475       String name = element.getAttribute("name");
476       Integer maxElementsInMemory = GrouperInstallerUtils.intObjectValue(element.getAttribute("maxElementsInMemory"), true);
477       Boolean eternal = GrouperInstallerUtils.booleanObjectValue(element.getAttribute("eternal"));
478       Integer timeToIdleSeconds = GrouperInstallerUtils.intObjectValue(element.getAttribute("timeToIdleSeconds"), true);
479       Integer timeToLiveSeconds = GrouperInstallerUtils.intObjectValue(element.getAttribute("timeToLiveSeconds"), true);
480       Boolean overflowToDisk = GrouperInstallerUtils.booleanObjectValue(element.getAttribute("overflowToDisk"));
481       Boolean statistics = GrouperInstallerUtils.booleanObjectValue(element.getAttribute("statistics"));
482 
483       //any attributes we dont expect?
484       NamedNodeMap configuredNamedNodeMap = element.getAttributes();
485       //see which attributes are new or changed
486       for (int j=0;j<configuredNamedNodeMap.getLength();j++) {
487         Node configuredAttribute = configuredNamedNodeMap.item(j);
488         if (!configuredAttribute.getNodeName().equals("name")
489             && !configuredAttribute.getNodeName().equals("maxElementsInMemory")
490             && !configuredAttribute.getNodeName().equals("eternal")
491             && !configuredAttribute.getNodeName().equals("timeToIdleSeconds")
492             && !configuredAttribute.getNodeName().equals("timeToLiveSeconds")
493             && !configuredAttribute.getNodeName().equals("overflowToDisk")
494             && !configuredAttribute.getNodeName().equals("statistics")) {
495           throw new RuntimeException("Cant process attribute: '" + configuredAttribute.getNodeName() + "'");
496         }
497       }
498       
499       String key = convertEhcacheNameToPropertiesKey(name, usedKeys);
500       
501       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.name = edu.internet2.middleware.grouper.internal.dao.hib3.Hib3GroupDAO
502       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.maxElementsInMemory = 500
503       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.eternal = false
504       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.timeToIdleSeconds = 1
505       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.timeToLiveSeconds = 1
506       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.overflowToDisk = false
507       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.statistics = false
508       
509       System.out.println("cache.name." + key + ".name = " + name);
510       if (maxElementsInMemory != null) {
511         System.out.println("cache.name." + key + ".maxElementsInMemory = " + maxElementsInMemory);
512       }
513       if (eternal != null) {
514         System.out.println("cache.name." + key + ".eternal = " + eternal);
515       }
516       if (timeToIdleSeconds != null) {
517         System.out.println("cache.name." + key + ".timeToIdleSeconds = " + timeToIdleSeconds);
518       }
519       if (timeToLiveSeconds != null) {
520         System.out.println("cache.name." + key + ".timeToLiveSeconds = " + timeToLiveSeconds);
521       }
522       if (overflowToDisk != null) {
523         System.out.println("cache.name." + key + ".overflowToDisk = " + overflowToDisk);
524       }
525       if (statistics != null) {
526         System.out.println("cache.name." + key + ".statistics = " + statistics);
527       }
528       System.out.println("");
529     }
530 
531   }
532 
533   /**
534    * convert a name like: edu.internet2.middleware.grouper.internal.dao.hib3.Hib3GroupDAO
535    * to: edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO
536    * @param ehcacheName 
537    * @param usedKeys
538    * @return the key
539    */
540   private static String convertEhcacheNameToPropertiesKey(String ehcacheName, Set<String> usedKeys) {
541     
542     StringBuilder result = new StringBuilder();
543 
544     //strip off this beginning to get the keys a little smaller
545     if (ehcacheName.startsWith("edu.internet2.middleware.grouper.")) {
546       ehcacheName = ehcacheName.substring("edu.internet2.middleware.grouper.".length());
547     }
548     
549     for (int i=0; i<ehcacheName.length(); i++) {
550       
551       char curChar = ehcacheName.charAt(i);
552       
553       if (Character.isLetter(curChar) || Character.isDigit(curChar)) {
554         result.append(curChar);
555       } else {
556         result.append("_");
557       }
558       
559     }
560 
561     String resultString = result.toString();
562     if (!usedKeys.contains(resultString)) {
563       return resultString;
564     }
565     
566     for (int i=2;i<100;i++) {
567       String newResult = resultString + "_" + i;
568       if (!usedKeys.contains(newResult)) {
569         return newResult;
570       }
571     }
572     
573     throw new RuntimeException("Cant find name for " + ehcacheName);
574   }
575   
576   /**
577    * @param args
578    */
579   public static void main(String[] args) {
580     
581     
582     GrouperInstallerrInstaller.html#GrouperInstaller">GrouperInstaller grouperInstaller = new GrouperInstaller();
583     
584 //    grouperInstaller.reportOnConflictingJars("/Users/mchyzer/git/grouper_v2_6/grouper/temp/jarTest");
585 
586 //    File tommeDir = new File("/Users/vsachdeva/git/i2-grouper-new/grouper/grouper-misc/grouper-installer/container/tomee");
587 //    File serverXmlFile = new File(tommeDir.getAbsolutePath()
588 //        + File.separator + "conf" + File.separator + "server.xml");
589 //    
590 //    //<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
591 //    String newConnectorString = "<Connector port=\"8009\" protocol=\"AJP/1.3\" redirectPort=\"8443\" "
592 //        + "tomcatAuthentication=\"false\" URIEncoding=\"UTF-8\" scheme=\"https\" secure=\"true\" />";
593 //    
594 //    System.out.println("Editing tomee config file: " + serverXmlFile.getAbsolutePath());
595     
596 //    editFile(serverXmlFile, "port=\"8009\"", new String[]{"<Connector"}, 
597 //        null, newConnectorString, "connector modifications in server.xml");
598     
599   //<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
600     //editFile(serverXmlFile, "port=\"([\\d]+)\"", new String[]{"<Connector", "protocol=\"AJP/1.3\""}, null, "", "tomcat JK port");
601     
602     
603     
604 //    String str1 = new Crypto("Urg4lyLGawLdwEvByGmf").encrypt("admin123");
605 //    System.out.println(str1);
606 //    String str = new Crypto("Urg4lyLGawLdwEvByGmf").decrypt("Y8iDZWF8RdowIiCgVKNg9Q==");
607 //    System.out.println(str);
608 
609      grouperInstaller.mainLogic();
610 
611 //    grouperInstaller.upgradeExistingApplicationDirectoryString = "D:\\temp\\temp\\grouperJarCopyDest\\";
612 //    grouperInstaller.grouperBaseBakDir = "D:\\temp\\temp\\grouperJarBak\\";
613     
614 //    grouperInstaller.upgradeJars(new File("D:\\temp\\temp\\grouperJarCopySource"), new File("D:\\temp\\temp\\grouperJarCopyDest"));
615     
616 //    grouperInstaller.version = "2.5.0";
617 //    
618 //    grouperInstaller.grouperTarballDirectoryString = "D:\\temp\\temp\\grouperInstaller\\";
619 //    
620 //    grouperInstaller.grouperInstallDirectoryString = "D:\\temp\\temp\\grouperInstaller\\install\\";
621 //    
622 //    grouperInstaller.installMessagingAwsSqs();
623     
624     //new GrouperInstaller().convertEhcacheBaseToProperties(new File("/Users/mchyzer/git/grouper_v2_3/grouper/conf/ehcache.example.xml"));
625 
626 //    GrouperInstaller.downloadFile("https://github.com/Internet2/grouper/archive/GROUPER_2_2_BRANCH.zip",
627 //        "C:\\app\\grouperInstallerTarballDir\\GROUPER_2_2_BRANCH.zip", false, null, 
628 //        "grouperInstaller.autorun.createPatchDownloadSourceUseLocalIfExist", true
629 //        );
630 
631 //    GrouperInstaller grouperInstaller = new GrouperInstaller();
632 //
633 //    grouperInstaller.upgradeExistingApplicationDirectoryString = "C:\\app\\grouper_2_1_installer\\grouper.ui-2.1.5\\dist\\grouper\\";
634 //  
635 //    grouperInstaller.grouperBaseBakDir = "C:\\app\\grouperInstallerTarballDir\\bak_UI_2014_10_26_15_06_19_957\\";
636 //      
637 //    grouperInstaller.copyFiles("C:\\app\\grouperInstallerTarballDir\\grouper.ui-2.2.1\\dist\\grouper\\",
638 //        "C:\\app\\grouper_2_1_installer\\grouper.ui-2.1.5\\dist\\grouper\\",
639 //        GrouperInstallerUtils.toSet("WEB-INF/lib", "WEB-INF/web.xml", "WEB-INF/classes"));
640 
641     
642    // copyFiles();
643     
644 //    
645 //    
646 //    grouperInstaller.dbUrl = 
647 //    grouperInstaller.dbUser = 
648 //    grouperInstaller.dbPass = 
649 //    grouperInstaller.giDbUtils = new GiDbUtils(grouperInstaller.dbUrl, grouperInstaller.dbUser, grouperInstaller.dbPass);
650 //    grouperInstaller.untarredApiDir = new File("c:/mchyzer/grouper/trunk/grouper-installer/grouper.apiBinary-2.0.2");
651 //    grouperInstaller.grouperInstallDirectoryString = "C:/mchyzer/grouper/trunk/grouper-installer/";
652 //    
653 //    grouperInstaller.version = "2.0.2";
654 //    grouperInstaller.grouperSystemPassword = "myNewPass";
655 //    
656 //    grouperInstaller.addDriverJarToClasspath();
657 ////      
658 //    grouperInstaller.checkDatabaseConnection();
659 ////      grouperInstaller.initDb();
660 ////      
661 ////      grouperInstaller.addQuickstartSubjects();
662 ////      grouperInstaller.addQuickstartData();
663 //    
664 ////    File uiDir = grouperInstaller.downloadUi();
665 ////    File unzippedUiFile = unzip(uiDir.getAbsolutePath());
666 ////    grouperInstaller.untarredUiDir = untar(unzippedUiFile.getAbsolutePath());
667 //
668 ////      grouperInstaller.configureUi();
669 ////
670 //      File antDir = grouperInstaller.downloadAnt();
671 //      File unzippedAntFile = unzip(antDir.getAbsolutePath());
672 //      grouperInstaller.untarredAntDir = untar(unzippedAntFile.getAbsolutePath());
673 ////
674 ////      grouperInstaller.buildUi();
675 //
676 //    File tomcatDir = grouperInstaller.downloadTomcat();
677 //    File unzippedTomcatFile = unzip(tomcatDir.getAbsolutePath());
678 //    grouperInstaller.untarredTomcatDir = untar(unzippedTomcatFile.getAbsolutePath());
679 //
680 //    grouperInstaller.configureTomcat();
681 //    
682 ////    grouperInstaller.configureTomcatUiWebapp();
683 ////    
684 //    grouperInstaller.tomcatConfigureGrouperSystem();
685 //    
686 ////    grouperInstaller.tomcatBounce("restart");
687 //
688 //    File wsDir = grouperInstaller.downloadWs();
689 //
690 //    File unzippedWsFile = unzip(wsDir.getAbsolutePath());
691 //    grouperInstaller.untarredWsDir = untar(unzippedWsFile.getAbsolutePath());
692 //    grouperInstaller.configureWs();
693 //    grouperInstaller.buildWs();
694 //    
695 //    grouperInstaller.configureTomcatWsWebapp();
696 //    grouperInstaller.tomcatBounce("restart");
697 //
698 //    File clientDir = grouperInstaller.downloadClient();
699 //    
700 //    File unzippedClientFile = unzip(clientDir.getAbsolutePath());
701 //    grouperInstaller.untarredClientDir = untar(unzippedClientFile.getAbsolutePath());
702 //    grouperInstaller.configureClient();
703 //
704 //    grouperInstaller.addGrouperSystemWsGroup();
705 //    
706 //    grouperInstaller.runClientCommand();
707 //    
708 //    //CommandResult commandResult = GrouperInstallerUtils.execCommand("cmd /c cd");
709 //    //String result = commandResult.getOutputText();
710 //    //
711 //    //System.out.println(result);
712 //
713 //
714 //    //editPropertiesFile(new File("C:\\mchyzer\\grouper\\trunk\\grouper-installer\\grouper.apiBinary-2.1.0\\conf\\grouper.hibernate.properties"), 
715 //    //    "hibernate.connection.password", "sdf");
716       
717     System.exit(0);
718   }
719 
720   /** e.g. 2.1.0 */
721   private String version;
722   
723   /**
724    * @param isInstallNotUpgrade install will bounce tomcat, configure, etc
725    * 
726    */
727   private void buildUi(boolean isInstallNotUpgrade) {
728     
729     File grouperUiBuildToDir = new File(this.grouperUiBuildToDirName());
730     
731     boolean rebuildUi = true;
732     
733     if (grouperUiBuildToDir.exists()) {
734       boolean defaultRebuild = GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.ui.rebuildIfBuilt", true, false);
735       System.out.print("The Grouper UI has been built in the past, do you want it rebuilt? (t|f) [" 
736           + (defaultRebuild ? "t" : "f") + "]: ");
737       rebuildUi = readFromStdInBoolean(defaultRebuild, "grouperInstaller.autorun.rebuildUiAfterHavingBeenBuilt");
738     }
739     
740     if (!rebuildUi) {
741       return;
742     }
743 
744     if (isInstallNotUpgrade) {
745       //stop tomcat
746       try {
747         tomcatBounce("stop");
748       } catch (Exception e) {
749         System.out.println("Couldnt stop tomcat, ignoring...");
750       }
751     }
752     
753     List<String> commands = new ArrayList<String>();
754     
755     addAntCommands(commands);
756     commands.add("dist");
757     
758     System.out.println("\n##################################");
759     System.out.println("Building UI with command:\n" + this.untarredUiDir.getAbsolutePath() + "> " 
760         + convertCommandsIntoCommand(commands) + "\n");
761     
762     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
763         true, true, null, this.untarredUiDir, null, true);
764     
765     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
766       System.out.println("stderr: " + commandResult.getErrorText());
767     }
768     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
769       System.out.println("stdout: " + commandResult.getOutputText());
770     }
771     
772     if (isInstallNotUpgrade) {
773       System.out.print("Do you want to set the log dir of UI (t|f)? [t]: ");
774       boolean setLogDir = readFromStdInBoolean(true, "grouperInstaller.autorun.setLogDirOfUi");
775       
776       if (setLogDir) {
777         
778         ////set the log dir
779         //C:\apps\grouperInstallerTest\grouper.ws-2.0.2\grouper-ws\build\dist\grouper-ws\WEB-INF\classes\log4j.properties
780         //
781         //${grouper.home}logs
782   
783         String defaultLogDir = this.untarredTomcatDir.getAbsolutePath() + File.separator + "logs" + File.separator + "grouperUi";
784         System.out.print("Enter the UI log dir: [" + defaultLogDir + "]: ");
785         String logDir = readFromStdIn("grouperInstaller.autorun.uiLogDir");
786         logDir = GrouperInstallerUtils.defaultIfBlank(logDir, defaultLogDir);
787         
788         //lets replace \\ with /
789         logDir = GrouperInstallerUtils.replace(logDir, "\\\\", "/");
790         //lets replace \ with /
791         logDir = GrouperInstallerUtils.replace(logDir, "\\", "/");
792   
793         File log4jFile = new File(grouperUiBuildToDirName() + File.separator + "WEB-INF" + File.separator + "classes"
794             + File.separator + "log4j.properties");
795   
796         System.out.println("Editing file: " + log4jFile.getAbsolutePath());
797   
798         //log4j.appender.grouper_event.File = c:/apps/grouperInstallerTest/grouper.apiBinary-2.0.2/logs/grouper_event.log
799         editFile(log4jFile, "log4j\\.\\S+\\.File\\s*=\\s*([^\\s]+logs)/grouper_[^\\s]+\\.log", null, 
800             null, logDir, "UI log directory");
801   
802         File logDirFile = new File(defaultLogDir);
803         if (!logDirFile.exists()) {
804           System.out.println("Creating log directory: " + logDirFile.getAbsolutePath());
805           GrouperInstallerUtils.mkdirs(logDirFile);
806         }
807         //test log dir
808         File testLogDirFile = new File(logDirFile.getAbsolutePath() + File.separator + "testFile" + GrouperInstallerUtils.uniqueId() + ".txt");
809         GrouperInstallerUtils.saveStringIntoFile(testLogDirFile, "test");
810         if (!testLogDirFile.delete()) {
811           throw new RuntimeException("Cant delete file: " +  testLogDirFile.getAbsolutePath());
812         }
813         System.out.println("Created and deleted a test file successfully in dir: " + logDirFile.getAbsolutePath());
814       }
815     }    
816 
817     
818     System.out.println("\nEnd building UI");
819     System.out.println("##################################\n");
820 
821     
822   }
823 
824   /** ps command */
825   private String psCommandUnix;
826   
827   /**
828    * 
829    * @return the ps command in unix
830    */
831   private String psCommand() {
832     if (GrouperInstallerUtils.isWindows()) {
833       throw new RuntimeException("This is windows, why are you looking for sh???");
834     }
835     if (GrouperInstallerUtils.isBlank(this.psCommandUnix)) {
836       if (new File("/bin/ps").exists()) {
837         this.psCommandUnix = "/bin/ps";
838       } else if (new File("/usr/bin/ps").exists()) {
839         this.psCommandUnix = "/usr/bin/ps";
840       } else if (new File("/usr/local/bin/ps").exists()) {
841         this.psCommandUnix = "/usr/local/bin/ps";
842       } else {
843         throw new RuntimeException("Cant find 'ps' command!");
844       }
845     }
846     return this.psCommandUnix;
847   }
848 
849   /** grep command */
850   private String grepCommand;
851   
852   /**
853    * 
854    * @return the grep command in unix
855    */
856   private String grepCommand() {
857     if (GrouperInstallerUtils.isWindows()) {
858       throw new RuntimeException("This is windows, why are you looking for sh???");
859     }
860     if (GrouperInstallerUtils.isBlank(this.grepCommand)) {
861       if (new File("/bin/grep").exists()) {
862         this.grepCommand = "/bin/grep";
863       } else if (new File("/usr/bin/grep").exists()) {
864         this.grepCommand = "/usr/bin/grep";
865       } else if (new File("/usr/local/bin/grep").exists()) {
866         this.grepCommand = "/usr/local/bin/grep";
867       } else {
868         throw new RuntimeException("Cant find 'grep' command!");
869       }
870     }
871     return this.grepCommand;
872   }
873 
874   /** kill command */
875   private String killCommand;
876   
877   /**
878    * 
879    * @return the kill command in unix
880    */
881   private String killCommand() {
882     if (GrouperInstallerUtils.isWindows()) {
883       throw new RuntimeException("This is windows, why are you looking for sh???");
884     }
885     if (GrouperInstallerUtils.isBlank(this.killCommand)) {
886       if (new File("/bin/kill").exists()) {
887         this.killCommand = "/bin/kill";
888       } else if (new File("/usr/bin/kill").exists()) {
889         this.killCommand = "/usr/bin/kill";
890       } else if (new File("/usr/local/bin/kill").exists()) {
891         this.killCommand = "/usr/local/bin/kill";
892       } else {
893         throw new RuntimeException("Cant find 'kill' command!");
894       }
895     }
896     return this.killCommand;
897   }
898 
899   /** sh command */
900   private String shCommand;
901   
902   /**
903    * 
904    * @return the sh command in unix
905    */
906   private String shCommand() {
907     if (GrouperInstallerUtils.isWindows()) {
908       throw new RuntimeException("This is windows, why are you looking for sh???");
909     }
910     
911     if (!GrouperInstallerUtils.isBlank(this.shCommand)) {
912       return this.shCommand;
913     }
914     
915     String[] attempts = new String[]{
916         "bash", "/bin/bash", 
917         "/sbin/bash", "/usr/local/bin/bash", 
918         "/usr/bin/bash", "/usr/sbin/bash", 
919         "/usr/local/sbin/bash", "sh", "/bin/sh", 
920         "/sbin/sh", "/usr/local/bin/sh", 
921         "/usr/bin/sh", "/usr/sbin/sh", 
922         "/usr/local/sbin/sh"}; 
923     
924     for (String attempt : attempts) {
925     
926       try {
927         CommandResult commandResult = GrouperInstallerUtils.execCommand(
928             attempt, 
929             new String[]{"-version"}, true);
930         String shResult = commandResult.getOutputText();
931         if (GrouperInstallerUtils.isBlank(shResult)) {
932           shResult = commandResult.getErrorText();
933         }
934   
935         //if we get a result, thats good
936         if (!GrouperInstallerUtils.isBlank(shResult)) {
937           this.shCommand = attempt;
938           System.out.println("Using shell command: " + attempt);
939           return this.shCommand;
940         }
941         
942       } catch (Exception e) {
943         //this is ok, keep trying
944       }
945     }
946     //ok, we couldnt find it, 
947     System.out.print("Couldn't find the command 'sh'.  Enter the path of 'sh' (e.g. /bin/sh): ");
948     this.shCommand = readFromStdIn("grouperInstaller.autorun.pathOfShCommandIfNotFound");
949 
950     try {
951       CommandResult commandResult = GrouperInstallerUtils.execCommand(
952           this.shCommand, 
953           new String[]{"-version"}, true);
954       String shResult = commandResult.getOutputText();
955       if (GrouperInstallerUtils.isBlank(shResult)) {
956         shResult = commandResult.getErrorText();
957       }
958 
959       //if we get a result, thats good
960       if (!GrouperInstallerUtils.isBlank(shResult)) {
961         return this.shCommand;
962       }
963       
964     } catch (Exception e) {
965       throw new RuntimeException("Error: couldn't run: " + this.shCommand + " -version!", e);
966     }
967 
968     throw new RuntimeException("Error: couldn't run: " + this.shCommand + " -version!");
969     
970   }
971 
972   /**
973    * 
974    * @param commands
975    */
976   private void addGshCommands(List<String> commands) {
977     if (GrouperInstallerUtils.isWindows()) {
978       commands.add("cmd");
979       commands.add("/c");
980       commands.add(gshCommand());
981     } else {
982       //if you add this it messes up when args have spaces
983       //commands.add(shCommand());
984       commands.add(gshCommand());
985     }
986   }
987 
988   /**
989    * 
990    * @param commands
991    */
992   private void addAntCommands(List<String> commands) {
993     if (GrouperInstallerUtils.isWindows()) {
994       commands.add("cmd");
995       commands.add("/c");
996       commands.add(this.untarredAntDir.getAbsolutePath() + File.separator + "bin" + File.separator + "ant.bat");
997     } else {
998       commands.add(shCommand());
999       commands.add(this.untarredAntDir.getAbsolutePath() + File.separator + "bin" + File.separator + "ant");
1000     }
1001   }
1002 
1003   /**
1004    * 
1005    * @param commands
1006    */
1007   private void addMavenCommands(List<String> commands) {
1008     if (GrouperInstallerUtils.isWindows()) {
1009       commands.add("cmd");
1010       commands.add("/c");
1011       commands.add(this.untarredMavenDir.getAbsolutePath() + File.separator + "bin" + File.separator + "mvn.bat");
1012     } else {
1013       commands.add(shCommand());
1014       commands.add(this.untarredMavenDir.getAbsolutePath() + File.separator + "bin" + File.separator + "mvn");
1015     }
1016   }
1017   
1018   /**
1019    * @param arg
1020    * 
1021    */
1022   private void tomeeBounce(String arg) {
1023     
1024     if (!GrouperInstallerUtils.equals("start", arg) && !GrouperInstallerUtils.equals("stop", arg) && !GrouperInstallerUtils.equals("restart", arg)) {
1025       throw new RuntimeException("Expecting arg: start|stop|restart but received: " + arg);
1026     }
1027     
1028     if (GrouperInstallerUtils.equals("restart", arg)) {
1029       
1030       tomeeBounce("stop");
1031       tomeeBounce("start");
1032       return;
1033     }
1034     
1035     if (GrouperInstallerUtils.equals("stop", arg)) {
1036       
1037       if (GrouperInstallerUtils.portAvailable(this.tomeeHttpPort, this.defaultIpAddress)) {
1038         System.out.println("Tomee is supposed to be listening on port: " + this.tomeeHttpPort + ", port not listening, assuming tomee is not running...");
1039         if (!shouldContinue("Should we " + arg + " tomee anyway?", "", "grouperInstaller.autorun." + arg + "TomeeAnyway")) {
1040           return;
1041         }
1042       }
1043 
1044       
1045     } else {
1046       if (!GrouperInstallerUtils.portAvailable(this.tomeeHttpPort, this.defaultIpAddress)) {
1047         System.out.println("Tomee is supposed to be listening on port: " + this.tomeeHttpPort + ", port is already listening!!!!  Why is this????");
1048         if (!shouldContinue("Should we " + arg + " tomee anyway?", "", "grouperInstaller.autorun." + arg + "TomeeAnyway")) {
1049           return;
1050         }
1051       }
1052       
1053     }
1054     
1055     final List<String> commands = new ArrayList<String>();
1056     
1057     commands.add(getJavaCommand());
1058     commands.add("-XX:MaxPermSize=150m");
1059     commands.add("-Xmx640m");
1060     
1061     commands.add("-Dcatalina.home=" + this.untarredTomeeDir.getAbsolutePath());
1062     //commands.add("-Djava.util.logging.config.file=" + this.untarredTomcatDir.getAbsolutePath() + File.separator + "conf" + File.separator + "logging.properties");
1063     
1064     commands.add("-cp");
1065     commands.add(this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin" + File.separator + "bootstrap.jar" + File.pathSeparator
1066         + this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin" + File.separator + "tomcat-juli.jar");
1067     commands.add("org.apache.catalina.startup.Bootstrap");
1068     
1069     if (GrouperInstallerUtils.equals("stop", arg)) {
1070       commands.add("stop");
1071     }
1072     
1073     System.out.println("\n##################################");
1074     
1075     String command = "start".equals(arg) ? "startup" : "shutdown";
1076     
1077     System.out.println("Tomee " + arg + " with command (note you need CATALINA_HOME and JAVA_HOME set):\n  "
1078         + this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin" + File.separator + command
1079         + (GrouperInstallerUtils.isWindows() ? ".bat" : ".sh") + "\n");
1080     
1081     //dont wait
1082     boolean waitFor = GrouperInstallerUtils.equals("stop", arg) ? true : false;
1083     
1084     if (waitFor) {
1085       try {
1086         CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
1087             true, true, null, 
1088             new File(this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin"), null, true);
1089         
1090         if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
1091           System.out.println("stderr: " + commandResult.getErrorText());
1092         }
1093         if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
1094           System.out.println("stdout: " + commandResult.getOutputText());
1095         }
1096       } catch (Throwable e) {
1097         e.printStackTrace();
1098         if (!shouldContinue("grouperInstaller.autorun.continueAfterTomeeError")) {
1099           return;
1100         }
1101       }
1102     } else {
1103       //start in new thread
1104       Thread thread = new Thread(new Runnable() {
1105         
1106         @Override
1107         public void run() {
1108           GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
1109               true, true, null, 
1110               new File(GrouperInstaller.this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin"), 
1111               GrouperInstaller.this.untarredTomeeDir.getAbsolutePath() + File.separator + "logs" + File.separator + "catalina", false);
1112         }
1113       });
1114       thread.setDaemon(true);
1115       thread.start();
1116 
1117     }
1118     
1119     System.out.println("\nEnd tomee " + arg + " (note: logs are in " + this.untarredTomeeDir.getAbsolutePath() + File.separator + "logs)");
1120     System.out.println("##################################\n");
1121 
1122     System.out.print("Should we check ports to see if tomee was able to " + arg + " (t|f)? [t]: ");
1123     
1124     boolean shouldCheckTomee = readFromStdInBoolean(true, "grouperInstaller.autorun." + arg + "TomeeCheckPorts");
1125     
1126     if (shouldCheckTomee) {
1127       System.out.print("Waiting for tomee to " + arg +  "...");
1128       boolean success = false;
1129       for (int i=0;i<60;i++) {
1130         GrouperInstallerUtils.sleep(1000);
1131         //check port
1132         boolean portAvailable = GrouperInstallerUtils.portAvailable(this.tomeeHttpPort, this.defaultIpAddress);
1133         if (GrouperInstallerUtils.equals("start", arg)) {
1134           if (!portAvailable) {
1135             success = true;
1136             System.out.println("\nTomee listening on port: " + this.tomeeHttpPort);
1137             break;
1138           }
1139         } else {
1140           if (portAvailable) {
1141             success = true;
1142             System.out.println("\nTomee not listening on port: " + this.tomeeHttpPort);
1143             break;
1144           }
1145         }
1146         System.out.print(".");
1147       }
1148       if (!success) {
1149         System.out.println("Trying to " + arg + " tomee but couldnt properly detect " + arg + " on port " + this.tomeeHttpPort);
1150         System.out.print("Press <enter> to continue... ");
1151         readFromStdIn("grouperInstaller.autorun.tomeePortProblem");
1152       }
1153     } else {
1154       System.out.println("Waiting 10 seconds for tomee to " + arg + "...");
1155       GrouperInstallerUtils.sleep(10000);
1156     }
1157   }
1158 
1159   
1160   /**
1161    * @param arg
1162    * 
1163    */
1164   private void tomcatBounce(String arg) {
1165     
1166     if (!GrouperInstallerUtils.equals("start", arg) && !GrouperInstallerUtils.equals("stop", arg) && !GrouperInstallerUtils.equals("restart", arg)) {
1167       throw new RuntimeException("Expecting arg: start|stop|restart but received: " + arg);
1168     }
1169     
1170     if (GrouperInstallerUtils.equals("restart", arg)) {
1171       
1172       tomcatBounce("stop");
1173       tomcatBounce("start");
1174       return;
1175     }
1176     
1177     if (GrouperInstallerUtils.equals("stop", arg)) {
1178       
1179       if (GrouperInstallerUtils.portAvailable(this.tomcatHttpPort, this.defaultIpAddress)) {
1180         System.out.println("Tomcat is supposed to be listening on port: " + this.tomcatHttpPort + ", port not listening, assuming tomcat is not running...");
1181         if (!shouldContinue("Should we " + arg + " tomcat anyway?", "", "grouperInstaller.autorun." + arg + "TomcatAnyway")) {
1182           return;
1183         }
1184       }
1185 
1186       
1187     } else {
1188       if (!GrouperInstallerUtils.portAvailable(this.tomcatHttpPort, this.defaultIpAddress)) {
1189         System.out.println("Tomcat is supposed to be listening on port: " + this.tomcatHttpPort + ", port is already listening!!!!  Why is this????");
1190         if (!shouldContinue("Should we " + arg + " tomcat anyway?", "", "grouperInstaller.autorun." + arg + "TomcatAnyway")) {
1191           return;
1192         }
1193       }
1194       
1195     }
1196     
1197     final List<String> commands = new ArrayList<String>();
1198     
1199 //    <java jar="${tomcat.home}/bin/bootstrap.jar" fork="true">
1200 //    03          <jvmarg value="-Dcatalina.home=${tomcat.home}"/>
1201 //    04      </java>
1202 //    05  </target>
1203 //    06   
1204 //    07  <target name="tomcat-stop">
1205 //    08      <java jar="${tomcat.home}/bin/bootstrap.jar" fork="true">
1206 //    09          <jvmarg value="-Dcatalina.home=${tomcat.home}"/>
1207 //    10          <arg line="stop"/>
1208 //    11      </java>
1209     
1210     commands.add(getJavaCommand());
1211     commands.add("-XX:MaxPermSize=150m");
1212     commands.add("-Xmx640m");
1213     
1214     commands.add("-Dcatalina.home=" + this.untarredTomcatDir.getAbsolutePath());
1215     //commands.add("-Djava.util.logging.config.file=" + this.untarredTomcatDir.getAbsolutePath() + File.separator + "conf" + File.separator + "logging.properties");
1216     
1217     //later versions of tomcat need the juli jar...
1218     if (new File(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin" + File.separator + "tomcat-juli.jar").exists()) {
1219       
1220       commands.add("-cp");
1221       commands.add(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin" + File.separator + "bootstrap.jar" + File.pathSeparator
1222           + this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin" + File.separator + "tomcat-juli.jar");
1223       commands.add("org.apache.catalina.startup.Bootstrap");
1224     } else {
1225 
1226       commands.add("-jar");
1227       commands.add(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin" + File.separator + "bootstrap.jar");
1228     }
1229     
1230     if (GrouperInstallerUtils.equals("stop", arg)) {
1231       commands.add("stop");
1232     }
1233     
1234     System.out.println("\n##################################");
1235     
1236     String command = "start".equals(arg) ? "startup" : "shutdown";
1237     
1238     System.out.println("Tomcat " + arg + " with command (note you need CATALINA_HOME and JAVA_HOME set):\n  "
1239         + this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin" + File.separator + command
1240         + (GrouperInstallerUtils.isWindows() ? ".bat" : ".sh") + "\n");
1241     
1242     //dont wait
1243     boolean waitFor = GrouperInstallerUtils.equals("stop", arg) ? true : false;
1244     
1245     if (waitFor) {
1246       try {
1247         CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
1248             true, true, null, 
1249             new File(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin"), null, true);
1250         
1251         if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
1252           System.out.println("stderr: " + commandResult.getErrorText());
1253         }
1254         if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
1255           System.out.println("stdout: " + commandResult.getOutputText());
1256         }
1257       } catch (Throwable e) {
1258         e.printStackTrace();
1259         if (!shouldContinue("grouperInstaller.autorun.continueAfterTomcatError")) {
1260           return;
1261         }
1262       }
1263     } else {
1264       //start in new thread
1265       Thread thread = new Thread(new Runnable() {
1266         
1267         @Override
1268         public void run() {
1269           GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
1270               true, true, null, 
1271               new File(GrouperInstaller.this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin"), 
1272               GrouperInstaller.this.untarredTomcatDir.getAbsolutePath() + File.separator + "logs" + File.separator + "catalina", false);
1273         }
1274       });
1275       thread.setDaemon(true);
1276       thread.start();
1277 
1278     }
1279     
1280     System.out.println("\nEnd tomcat " + arg + " (note: logs are in " + this.untarredTomcatDir.getAbsolutePath() + File.separator + "logs)");
1281     System.out.println("##################################\n");
1282 
1283     System.out.print("Should we check ports to see if tomcat was able to " + arg + " (t|f)? [t]: ");
1284     
1285     boolean shouldCheckTomcat = readFromStdInBoolean(true, "grouperInstaller.autorun." + arg + "TomcatCheckPorts");
1286     
1287     if (shouldCheckTomcat) {
1288       System.out.print("Waiting for tomcat to " + arg +  "...");
1289       boolean success = false;
1290       for (int i=0;i<60;i++) {
1291         GrouperInstallerUtils.sleep(1000);
1292         //check port
1293         boolean portAvailable = GrouperInstallerUtils.portAvailable(this.tomcatHttpPort, this.defaultIpAddress);
1294         if (GrouperInstallerUtils.equals("start", arg)) {
1295           if (!portAvailable) {
1296             success = true;
1297             System.out.println("\nTomcat listening on port: " + this.tomcatHttpPort);
1298             break;
1299           }
1300         } else {
1301           if (portAvailable) {
1302             success = true;
1303             System.out.println("\nTomcat not listening on port: " + this.tomcatHttpPort);
1304             break;
1305           }
1306         }
1307         System.out.print(".");
1308       }
1309       if (!success) {
1310         System.out.println("Trying to " + arg + " tomcat but couldnt properly detect " + arg + " on port " + this.tomcatHttpPort);
1311         System.out.print("Press <enter> to continue... ");
1312         readFromStdIn("grouperInstaller.autorun.tomcatPortProblem");
1313       }
1314     } else {
1315       System.out.println("Waiting 10 seconds for tomcat to " + arg + "...");
1316       GrouperInstallerUtils.sleep(10000);
1317     }
1318   }
1319   
1320   /** db url */
1321   private String dbUrl;
1322 
1323   /** db user */
1324   private String dbUser;
1325 
1326   /** db pass */
1327   private String dbPass;
1328 
1329   /** untarred dir */
1330   private File untarredApiDir;
1331 
1332   /** untarred dir */
1333   private File untarredUiDir;
1334 
1335   /** untarred dir */
1336   private File untarredWsDir;
1337 
1338   /** untarred dir */
1339   private File untarredAntDir;
1340 
1341   /** untarred dir */
1342   private File untarredMavenDir;
1343 
1344   /** untarred dir */
1345   private File untarredTomcatDir;
1346   
1347   /** untarred tomee dir */
1348   private File untarredTomeeDir;
1349   
1350   /** main install dir, must end in file separator */
1351   private String grouperTarballDirectoryString;
1352   
1353   /** main install dir, must end in file separator */
1354   private String grouperInstallDirectoryString;
1355   
1356   /** base bak dir for backing up files that are upgraded, ends in File separator */
1357   private String grouperBaseBakDir;
1358   
1359   /** grouper system password */
1360   private String grouperSystemPassword;
1361   
1362   /**
1363    * 
1364    */
1365   private void tomeeConfigureGrouperSystem() {
1366     
1367 //    while (true) {
1368 //      System.out.print("Enter the GrouperSystem password: ");
1369 //      this.grouperSystemPassword = readFromStdIn("grouperInstaller.autorun.grouperSystemPassword");
1370 //      this.grouperSystemPassword = GrouperInstallerUtils.defaultString(this.grouperSystemPassword);
1371 //      
1372 //      if (!GrouperInstallerUtils.isBlank(this.grouperSystemPassword)) {
1373 //        break;
1374 //      }
1375 //      System.out.println("The GrouperSystem password cannot be blank!");
1376 //    }
1377 
1378     System.out.print("Do you want to set the GrouperSystem password in " + this.untarredTomeeDir + File.separator + "conf" + File.separator + "tomcat-users.xml? [t]: ");
1379     boolean setGrouperSystemPassword = readFromStdInBoolean(true, "grouperInstaller.autorun.setGrouperSystemPasswordInTomeeUsers");
1380     if (setGrouperSystemPassword) {
1381 
1382       //write to the tomee_users file
1383       //get the password
1384       File tomeeUsersXmlFile = new File(this.untarredTomeeDir.getAbsolutePath() + File.separator + "conf" + File.separator + "tomcat-users.xml");
1385       String existingPassword = GrouperInstallerUtils.xpathEvaluateAttribute(tomeeUsersXmlFile, "tomcat-users/user[@username='GrouperSystem']", "password");
1386       
1387       System.out.println("Editing file: " + tomeeUsersXmlFile.getAbsolutePath());
1388 
1389       NodeList existingRole = GrouperInstallerUtils.xpathEvaluate(tomeeUsersXmlFile, "tomcat-users/role");
1390       
1391       //<role rolename="grouper_user"/>
1392       //<user username="GrouperSystem" password="chang3m3" roles="grouper_user"/>
1393 
1394       
1395       if (existingPassword == null) {
1396 
1397         addToXmlFile(tomeeUsersXmlFile, ">",  new String[]{"<tomcat-users"}, "<user username=\"GrouperSystem\" password=\"" 
1398             + this.grouperSystemPassword + "\" roles=\"grouper_user\"/>", "Tomcat user GrouperSystem");
1399          
1400       } else {
1401         
1402         if (GrouperInstallerUtils.equals(existingPassword, this.grouperSystemPassword)) {
1403           System.out.println("  - password is already set to that value, leaving file unchanged...");
1404 
1405         } else {
1406           
1407           editFile(tomeeUsersXmlFile, "password=\"([^\"]*)\"", new String[]{"<user", "username=\"GrouperSystem\""}, 
1408               null, this.grouperSystemPassword, "Tomcat password for user GrouperSystem");
1409           
1410         }
1411         
1412       }
1413 
1414       if (existingRole == null || existingRole.getLength() == 0) {
1415         
1416         //add the role
1417         addToXmlFile(tomeeUsersXmlFile, ">",  new String[]{"<tomcat-users"}, "<role rolename=\"grouper_user\"/>", "Tomcat role grouper_user");
1418         
1419       }
1420     }
1421     
1422   }
1423   
1424   /**
1425    * 
1426    */
1427   private void tomcatConfigureGrouperSystem() {
1428     
1429     while (true) {
1430       System.out.print("Enter the GrouperSystem password: ");
1431       this.grouperSystemPassword = readFromStdIn("grouperInstaller.autorun.grouperSystemPassword");
1432       this.grouperSystemPassword = GrouperInstallerUtils.defaultString(this.grouperSystemPassword);
1433       
1434       if (!GrouperInstallerUtils.isBlank(this.grouperSystemPassword)) {
1435         break;
1436       }
1437       System.out.println("The GrouperSystem password cannot be blank!");
1438     }
1439 
1440     System.out.print("Do you want to set the GrouperSystem password in " + this.untarredTomcatDir + File.separator + "conf" + File.separator + "tomcat-users.xml? [t]: ");
1441     boolean setGrouperSystemPassword = readFromStdInBoolean(true, "grouperInstaller.autorun.setGrouperSystemPasswordInTomcatUsers");
1442     if (setGrouperSystemPassword) {
1443 
1444       //write to the tomcat_users file
1445       //get the password
1446       File tomcatUsersXmlFile = new File(this.untarredTomcatDir.getAbsolutePath() + File.separator + "conf" + File.separator + "tomcat-users.xml");
1447       String existingPassword = GrouperInstallerUtils.xpathEvaluateAttribute(tomcatUsersXmlFile, "tomcat-users/user[@username='GrouperSystem']", "password");
1448       
1449       System.out.println("Editing file: " + tomcatUsersXmlFile.getAbsolutePath());
1450 
1451       NodeList existingRole = GrouperInstallerUtils.xpathEvaluate(tomcatUsersXmlFile, "tomcat-users/role");
1452       
1453       //<role rolename="grouper_user"/>
1454       //<user username="GrouperSystem" password="chang3m3" roles="grouper_user"/>
1455 
1456       
1457       if (existingPassword == null) {
1458 
1459         addToXmlFile(tomcatUsersXmlFile, ">",  new String[]{"<tomcat-users"}, "<user username=\"GrouperSystem\" password=\"" 
1460             + this.grouperSystemPassword + "\" roles=\"grouper_user\"/>", "Tomcat user GrouperSystem");
1461          
1462       } else {
1463         
1464         if (GrouperInstallerUtils.equals(existingPassword, this.grouperSystemPassword)) {
1465           System.out.println("  - password is already set to that value, leaving file unchanged...");
1466 
1467         } else {
1468           
1469           editFile(tomcatUsersXmlFile, "password=\"([^\"]*)\"", new String[]{"<user", "username=\"GrouperSystem\""}, 
1470               null, this.grouperSystemPassword, "Tomcat password for user GrouperSystem");
1471           
1472         }
1473         
1474       }
1475 
1476       if (existingRole == null || existingRole.getLength() == 0) {
1477         
1478         //add the role
1479         addToXmlFile(tomcatUsersXmlFile, ">",  new String[]{"<tomcat-users"}, "<role rolename=\"grouper_user\"/>", "Tomcat role grouper_user");
1480         
1481       }
1482     }
1483     
1484   }
1485   
1486   /**
1487    * 
1488    */
1489   private void configureUi() {
1490     //build properties file
1491     File buildPropertiesFile = new File(this.untarredUiDir.getAbsolutePath() + File.separator + "build.properties");
1492     if (!buildPropertiesFile.exists()) {
1493       File buildPropertiesTemplateFile = new File(this.untarredUiDir.getAbsolutePath() + File.separator + "build.properties.template");
1494       System.out.println("Copying file: " + buildPropertiesTemplateFile.getAbsolutePath() + " to file: " + buildPropertiesFile);
1495       GrouperInstallerUtils.copyFile(buildPropertiesTemplateFile, buildPropertiesFile, true);
1496     }
1497     
1498     //set the grouper property
1499     System.out.println("Editing " + buildPropertiesFile.getAbsolutePath() + ": ");
1500     String apiDir = GrouperInstallerUtils.replace(this.untarredApiDir.getAbsolutePath(),"\\\\", "/");
1501     apiDir = GrouperInstallerUtils.replace(apiDir, "\\", "/");
1502     editPropertiesFile(buildPropertiesFile, "grouper.folder", apiDir, false);
1503     editPropertiesFile(buildPropertiesFile, "should.copy.context.xml.to.metainf", "false", false);
1504     
1505   }
1506   
1507   /**
1508    * 
1509    */
1510   private void configureWs() {
1511     //build properties file
1512     File buildPropertiesFile = new File(this.untarredWsDir.getAbsolutePath() + File.separator 
1513         + "grouper-ws" + File.separator + "build.properties");
1514     if (!buildPropertiesFile.exists()) {
1515       File buildPropertiesTemplateFile = new File(this.untarredWsDir.getAbsolutePath() 
1516           + File.separator + "grouper-ws" + File.separator + "build.example.properties");
1517       System.out.println("Copying file: " + buildPropertiesTemplateFile.getAbsolutePath() + " to file: " + buildPropertiesFile);
1518       GrouperInstallerUtils.copyFile(buildPropertiesTemplateFile, buildPropertiesFile);
1519     }
1520     
1521     //set the grouper property
1522     System.out.println("Editing " + buildPropertiesFile.getAbsolutePath() + ": ");
1523     String apiDir = GrouperInstallerUtils.replace(this.untarredApiDir.getAbsolutePath(),"\\\\", "/");
1524     apiDir = GrouperInstallerUtils.replace(apiDir, "\\", "/");
1525     editPropertiesFile(buildPropertiesFile, "grouper.dir", apiDir, false);
1526     
1527   }
1528 
1529   /**
1530    * main function of grouper installer
1531    */
1532   public static enum GrouperInstallerMainFunction {
1533     
1534     /** install grouper */
1535     admin {
1536 
1537       @Override
1538       public void logic(GrouperInstaller grouperInstaller) {
1539         
1540         grouperInstaller.mainAdminLogic();
1541 
1542       }
1543     },
1544 
1545     /** install grouper */
1546     @Deprecated //TODO delete it.
1547     install {
1548 
1549       @Override
1550       public void logic(GrouperInstaller grouperInstaller) {
1551         
1552         grouperInstaller.mainInstallLogic();
1553 
1554       }
1555     },
1556     
1557     /** upgrade grouper */
1558     upgrade {
1559 
1560       @Override
1561       public void logic(GrouperInstaller grouperInstaller) {
1562         
1563         grouperInstaller.mainUpgradeLogic();
1564 
1565       }
1566     },
1567     
1568     /** create patch */
1569     createPatch {
1570 
1571       @Override
1572       public void logic(GrouperInstaller grouperInstaller) {
1573         
1574         grouperInstaller.mainCreatePatchLogic();
1575 
1576       }
1577     },
1578     
1579     /** see if there are patches available for grouper */
1580     patch {
1581 
1582       @Override
1583       public void logic(GrouperInstaller grouperInstaller) {
1584         
1585         grouperInstaller.mainPatchLogic();
1586 
1587       }
1588     },
1589     
1590     /** build container */  
1591     buildContainer {
1592 
1593       @Override
1594       public void logic(GrouperInstaller grouperInstaller) {
1595         grouperInstaller.mainBuildContainerLogic();
1596         
1597       }
1598       
1599     },
1600     
1601     /** install container */  
1602     installContainer {
1603 
1604       @Override
1605       public void logic(GrouperInstaller grouperInstaller) {
1606         grouperInstaller.mainInstallContainerLogic();
1607         
1608       }
1609       
1610     }
1611     ;
1612 
1613     /**
1614      * run the logic for the installer function
1615      * @param grouperInstaller
1616      */
1617     public abstract void logic(GrouperInstaller grouperInstaller);
1618     
1619     /**
1620      * 
1621      * @param string
1622      * @param exceptionIfInvalid
1623      * @param exceptionIfBlank
1624      * @return the action
1625      */
1626     public static GrouperInstallerMainFunction valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
1627       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerMainFunction.class, string, exceptionIfBlank, exceptionIfInvalid);
1628     }
1629 
1630 
1631     /**
1632      * convert a string to the enum
1633      * @param theString
1634      * @param exceptionOnInvalid
1635      * @return the enum
1636      */
1637     public static GrouperInstallerMainFunction valueOfIgnoreCase(String theString, boolean exceptionOnInvalid) {
1638       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerMainFunction.class, theString, false, exceptionOnInvalid);
1639     }
1640   }
1641 
1642   private static String javaCommand = null;
1643 
1644   /**
1645    * When the installer starts, it tests and validates the java command. If found it will set the path to it for later use.
1646    *
1647    * @return path to java executable. Set by {@link #validJava()}
1648    */
1649   public static String getJavaCommand() {
1650     if (javaCommand != null) {
1651       return javaCommand;
1652     } else {
1653       throw new RuntimeException("Unable to determine \"java\" command to execute");
1654     }
1655   }
1656 
1657   private static String getJavaCommand(boolean throwIfNull) {
1658     if (throwIfNull) {
1659       return getJavaCommand();
1660     } else {
1661       return javaCommand;
1662     }
1663   }
1664 
1665   private static void setJavaCommand(String theJavaCommand) {
1666     //System.out.println("INFO: external java command set to '" + theJavaCommand + "'");
1667     javaCommand = theJavaCommand;
1668   }
1669 
1670   /**
1671    * Validate java and javac commands through various methods - using JAVA_HOME/bin/(cmd) and shell path location
1672    */
1673   private static void validJava() {
1674     boolean hadError = false;
1675     boolean cmdHadError = false;
1676     String command = null;
1677 
1678     // the most important check is the JAVA_HOME is set properly; this will be needed for any ant or maven commands, etc.
1679     String javaHome = System.getenv("JAVA_HOME");
1680 
1681     if (GrouperInstallerUtils.isBlank(javaHome)) {
1682       System.out.println("Non-fatal ERROR: you should have the environment variable JAVA_HOME set to a " + JAVA_MIN_VERSION + "+ JDK (currently not set)");
1683       hadError = true;
1684     } else {
1685       // try JAVA_HOME/bin/java
1686       command = javaHome + File.separator + "bin" + File.separator + "java";
1687       cmdHadError = validJavaOutput(command, "$JAVA_HOME/bin/java", false, false);
1688       if (getJavaCommand(false) == null && !cmdHadError) {
1689         setJavaCommand(command);
1690       }
1691       hadError = hadError || cmdHadError;
1692 
1693       // try JAVA_HOME/bin/javac
1694       command = javaHome + File.separator + "bin" + File.separator + "javac";
1695       cmdHadError = validJavaOutput(command, "$JAVA_HOME/bin/javac", true, false);
1696       hadError = hadError || cmdHadError;
1697     }
1698 
1699     // try bare "java" with shell path resolver
1700     command = "java";
1701     cmdHadError = validJavaOutput(command, "java command in the PATH", false, false);
1702     if (getJavaCommand(false) == null && !cmdHadError) {
1703       setJavaCommand(command);
1704     }
1705     hadError = hadError || cmdHadError;
1706 
1707     // try bare "javac" with shell path resolver
1708     command = "javac";
1709     cmdHadError = validJavaOutput(command, "javac command in the PATH", true, false);
1710     hadError = hadError || cmdHadError;
1711 
1712     // if nothing else works, the effective home for the java command running the installer is a minimal catch-all, suitable
1713     // for running simple tasks like starting Tomcat
1714     if (getJavaCommand(false) == null) {
1715       command = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
1716       cmdHadError = validJavaOutput(command, "the current java command running the installer", false, false);
1717       if (!cmdHadError) {
1718         setJavaCommand(command);
1719       }
1720       hadError = hadError || cmdHadError;
1721     }
1722 
1723     if (hadError) {
1724       // assume all the above checks output something on error, otherwise the line below won't make sense
1725       System.out.println("WARNING: JAVA_HOME or Java path errors may cause issues when running external commands - these should be fixed before continuing.");
1726       System.out.print("Press <enter> to continue... ");
1727       readFromStdIn("grouperInstaller.autorun.javaInvalid");
1728     }
1729   }
1730  
1731   /**
1732    * take a java command (e.g. java, or javac, or %JAVA_HOME%/bin/java and make sure version is valid
1733    * @param command
1734    * @param what
1735    * @param jdkTest
1736    * @param fatal
1737    * @return if error
1738    */
1739   private static boolean validJavaOutput(String command, String what, boolean jdkTest, boolean fatal) {
1740     
1741     boolean printStackOnError = GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.printStackOnJavaVersionErrors", false, false);
1742 
1743     try {
1744     
1745       List<String> commands = new ArrayList<String>();
1746       
1747       commands.add(command);
1748       commands.add("-version");
1749         
1750       CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
1751           true, true, null, null, null, false, true, printStackOnError);
1752       
1753       //note this is printed view stderr not stdout
1754       String output = commandResult.getErrorText();
1755 
1756       // find the first numeric match of a.b or a.b.c*
1757       Pattern javaVersionPattern = Pattern.compile(".*?[^\\d]*(\\d+\\.\\d+(\\.\\d+)?).*", Pattern.DOTALL);
1758       Matcher javaVersionMatcher = javaVersionPattern.matcher(output);
1759       if (!javaVersionMatcher.matches()) {
1760         output = commandResult.getOutputText();
1761         javaVersionMatcher = javaVersionPattern.matcher(output);
1762         
1763         if (!javaVersionMatcher.matches()) {
1764           if (jdkTest) {
1765             System.out.println((fatal ? "" : "Non-fatal ") + "ERROR: can't find 'javac' command in " + what + ", Java needs to be a JDK not a JRE!");
1766           }
1767           System.out.println((fatal ? "" : "Non-fatal ") + "ERROR trying to check java output, make sure you have " + what 
1768               + " set to Java " + (jdkTest ? "JDK (not JRE) " : "") + JAVA_MIN_VERSION + "+\n"
1769               + commandResult.getErrorText() + "\n" + commandResult.getOutputText());
1770           if (!fatal) {
1771             return true;
1772           }
1773           System.out.print("Press <enter> to continue... ");
1774           readFromStdIn("grouperInstaller.autorun.javaInvalid");
1775           System.exit(1);
1776         }
1777       }
1778       
1779       String versionString = javaVersionMatcher.group(1);
1780 
1781       if (GrouperInstallerUtils.compareVersions(versionString, JAVA_MIN_VERSION) < 0) {
1782         System.out.println((fatal ? "" : "Non-fatal ") + "ERROR: " + what
1783           + (jdkTest ? " requires to be" : " should be") + " invoked with Java " + JAVA_MIN_VERSION + "+"
1784           + (jdkTest ? " JDK" : "") + ", but was: " + versionString);
1785         if (!fatal) {
1786           return true;
1787         }
1788         System.out.print("Press <enter> to continue... ");
1789         readFromStdIn("grouperInstaller.autorun.javaInvalid");
1790         System.exit(1);
1791       }
1792       return false;
1793     } catch (RuntimeException re) {
1794 
1795       if (printStackOnError) {
1796         re.printStackTrace();
1797       }
1798 
1799       System.out.println((fatal ? "" : "Non-fatal ") + "ERROR trying to check java output, make sure you have " + what 
1800           + " set to Java " + (jdkTest ? "JDK (not JRE) " : "") + JAVA_MIN_VERSION + "+\n" + re.getMessage());
1801       return true;
1802     }
1803   }
1804 
1805   /**
1806    * 
1807    */
1808   private void mainLogic() {
1809     
1810     validJava();
1811     
1812     this.grouperInstallerMainFunction = this.grouperInstallerMainFunction();
1813     
1814     this.grouperInstallerMainFunction.logic(this);
1815     
1816   }
1817   
1818   /**
1819    * what are we doing
1820    */
1821   private GrouperInstallerMainFunction grouperInstallerMainFunction;
1822   
1823   /**
1824    * @param appDir e.g. this.upgradeExistingApplicationDirectoryString
1825    */
1826   public void reportOnConflictingJars(String appDir) {
1827     
1828     System.out.println("\n##################################");
1829     System.out.println("Looking for conflicting jars\n");
1830 
1831     //look for conflicting jars
1832     List<File> allLibraryJars = findAllLibraryFiles(appDir);
1833     
1834     System.out.println("Found " + GrouperInstallerUtils.length(allLibraryJars) + " jars");
1835     
1836     Set<String> alreadyProcessed = new HashSet<String>();
1837     
1838     for (File jarFile : new ArrayList<File>(allLibraryJars)) {
1839       try {
1840         if (!jarFile.exists()) {
1841           allLibraryJars.remove(jarFile);
1842           continue;
1843         }
1844         
1845          Set<String> baseNames = GrouperInstallerUtils.jarFileBaseNames(jarFile.getName());
1846         
1847         //dont print multiple times
1848         if (alreadyProcessed.containsAll(baseNames)) {
1849           continue;
1850         }
1851         
1852         alreadyProcessed.addAll(baseNames);
1853         
1854         List<File> relatedFiles = GrouperInstallerUtils.nonNull(GrouperInstallerUtils.jarFindJar(allLibraryJars, jarFile.getName()));
1855         Iterator<File> relatedFilesIterator = relatedFiles.iterator();
1856         
1857         while (relatedFilesIterator.hasNext()) {
1858           if (jarFile.equals(relatedFilesIterator.next())) {
1859             relatedFilesIterator.remove();
1860           }
1861         }
1862         
1863         if (GrouperInstallerUtils.length(relatedFiles) >= 1) {
1864           
1865           if (relatedFiles.size() == 1) {
1866             File relatedFile = relatedFiles.iterator().next();
1867             File newerVersion = GrouperInstallerUtils.jarNewerVersion(relatedFile, jarFile);
1868             if (newerVersion != null) {
1869               
1870               if (newerVersion.equals(jarFile)) {
1871                 System.out.println("There is a conflicting jar: " + jarFile.getAbsolutePath());
1872                 System.out.println("Deleting older jar: " + relatedFile.getAbsolutePath());
1873                 GrouperInstallerUtils.fileDelete(relatedFile);
1874                 allLibraryJars.remove(relatedFile);
1875               } else {
1876                 System.out.println("There is a conflicting jar: " + relatedFile.getAbsolutePath());
1877                 System.out.println("Deleting older jar: " + jarFile.getAbsolutePath());
1878                 GrouperInstallerUtils.fileDelete(jarFile);
1879                 allLibraryJars.remove(jarFile);
1880               }
1881               System.out.print("Press <enter> to continue... ");
1882               readFromStdIn("grouperInstaller.autorun.conflictingJarContinue");
1883               continue;
1884             }
1885           }
1886           
1887           System.out.println("There is a conflicting jar: " + GrouperInstallerUtils.toStringForLog(relatedFiles));
1888           System.out.println("You should probably delete one of these files (oldest one?) or consult the Grouper team.");
1889           System.out.print("Press <enter> to continue... ");
1890           readFromStdIn("grouperInstaller.autorun.conflictingJarContinue");
1891         }
1892         
1893   //      if (GrouperInstallerUtils.length(relatedFiles) == 0) {
1894   //        System.out.println("Why is jar file not found??? " + jarFile.getAbsolutePath());
1895   //      }
1896       } catch (RuntimeException re) {
1897         GrouperInstallerUtils.injectInException(re, "Problem with jar: " + jarFile.getAbsolutePath());
1898         throw re;
1899       }
1900     }
1901   }
1902   
1903   /**
1904    * which app is being upgraded
1905    */
1906   private AppToUpgrade appToUpgrade;
1907 
1908   /**
1909    * patch grouper
1910    */
1911   private void mainCreatePatchLogic() {
1912 
1913     //####################################
1914     //Find out what directory to upgrade to.  This ends in a file separator
1915     this.grouperTarballDirectoryString = grouperUpgradeTempDirectory();
1916 
1917     //see what we are upgrading: api, ui, ws, client
1918     this.appToUpgrade = grouperAppToUpgradeOrPatch("create a patch for");
1919     
1920     if (this.appToUpgrade == AppToUpgrade.CLIENT) {
1921       throw new RuntimeException("Cant create patches for client, just put the client patch files in an API patch");
1922     }
1923     
1924     String branchToCreatePatchFor = null;
1925     {
1926       String defaultBranchToCreatePatchFor = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.branchToCreatePatchFor", false);
1927       
1928       if (GrouperInstallerUtils.isBlank(defaultBranchToCreatePatchFor)) {
1929         //grouper.version = 2.2.1
1930         // convert to GROUPER_2_2_BRANCH
1931         String grouperVersion = GrouperInstallerUtils.propertiesValue("grouper.version", true);
1932 
1933         grouperVersion = GrouperInstallerUtils.replace(grouperVersion, ".", "_");
1934 
1935         Pattern pattern = Pattern.compile("(\\d+_\\d+_)\\d+");
1936         Matcher matcher = pattern.matcher(grouperVersion);
1937         if (matcher.matches()) {
1938           String majorMinor = matcher.group(1);
1939           defaultBranchToCreatePatchFor = "GROUPER_" + majorMinor + "BRANCH";
1940         }
1941 
1942         
1943       }
1944       
1945       System.out.print("What branch do you want to create a patch for (e.g. GROUPER_2_2_BRANCH)? [" + defaultBranchToCreatePatchFor + "]: ");
1946       branchToCreatePatchFor = readFromStdIn("grouperInstaller.autorun.branchToCreatePatchFor");
1947       if (GrouperInstallerUtils.isBlank(branchToCreatePatchFor)) {
1948         branchToCreatePatchFor = defaultBranchToCreatePatchFor;
1949       }
1950     }
1951 
1952     String branchForPspToCreatePatchFor = null;
1953 
1954     if (this.appToUpgrade == AppToUpgrade.PSP) {
1955       String defaultBranchForPspToCreatePatchFor = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.branchForPspToCreatePatchFor", false);
1956       
1957       if (GrouperInstallerUtils.isBlank(defaultBranchForPspToCreatePatchFor)) {
1958         //grouper.version = 2.2.1
1959         // convert to PSP_2_2_BRANCH
1960         String grouperVersion = GrouperInstallerUtils.propertiesValue("grouper.version", true);
1961 
1962         grouperVersion = GrouperInstallerUtils.replace(grouperVersion, ".", "_");
1963 
1964         Pattern pattern = Pattern.compile("(\\d+_\\d+_)\\d+");
1965         Matcher matcher = pattern.matcher(grouperVersion);
1966         if (matcher.matches()) {
1967           String majorMinor = matcher.group(1);
1968           defaultBranchForPspToCreatePatchFor = "PSP_" + majorMinor + "BRANCH";
1969         }
1970       }
1971       
1972       System.out.print("What PSP branch do you want to create a patch for (e.g. GROUPER_2_2_BRANCH)? [" + defaultBranchForPspToCreatePatchFor + "]: ");
1973       branchForPspToCreatePatchFor = readFromStdIn("grouperInstaller.autorun.branchForPspToCreatePatchFor");
1974       if (GrouperInstallerUtils.isBlank(branchForPspToCreatePatchFor)) {
1975         branchForPspToCreatePatchFor = defaultBranchForPspToCreatePatchFor;
1976       }
1977       
1978     }
1979     
1980     int nextPatchIndex = -1;
1981     
1982     {
1983       String grouperVersion = GrouperInstallerUtils.propertiesValue("grouper.version", true);
1984   
1985       grouperVersion = GrouperInstallerUtils.replace(grouperVersion, ".", "_");
1986   
1987       nextPatchIndex = this.downloadPatches(this.appToUpgrade, grouperVersion);
1988     }
1989     
1990     //see if dir is there: e.g. grouper_v2_2_1_ui_patch_0
1991     String patchName = null;
1992     
1993     {
1994       String grouperVersion = GrouperInstallerUtils.propertiesValue("grouper.version", true);
1995 
1996       grouperVersion = GrouperInstallerUtils.replace(grouperVersion, ".", "_");
1997 
1998       patchName = "grouper_v" + grouperVersion + "_" + this.appToUpgrade.name().toLowerCase() + "_patch_" + nextPatchIndex;
1999     }
2000  
2001     {
2002       System.out.println("Next patch index for " + this.appToUpgrade + " is " + nextPatchIndex + ". ok (" + patchName + ")? (t|f)? [t]:");
2003       boolean continueOn = readFromStdInBoolean(true, "grouperInstaller.autorun.patchIndexIsOk");
2004       if (!continueOn) {
2005         System.out.println("Patch index is not ok");
2006         throw new RuntimeException("Patch index is not ok");
2007       }
2008     }
2009     
2010     downloadAndUnzipGrouperSource(branchToCreatePatchFor);
2011 
2012     File sourceTagDir = null;
2013 
2014     {
2015       String grouperVersion = GrouperInstallerUtils.propertiesValue("grouper.version", true);
2016       String grouperTag = GrouperInstallerUtils.replace(grouperVersion, ".", "_");
2017       System.out.println("Using Grouper tag: " + grouperTag);
2018       downloadAndUnzipGrouperSource("GROUPER_" + grouperTag);
2019       
2020       sourceTagDir = new File(this.grouperTarballDirectoryString + "GROUPER_" + grouperTag
2021           + File.separator + "grouper-GROUPER_" + grouperTag);
2022       
2023     }
2024     
2025     //grouper is in downloadDir/GROUPER_2_2_BRANCH/grouper-GROUPER_2_2_BRANCH
2026     File sourceDir = new File(this.grouperTarballDirectoryString + branchToCreatePatchFor
2027         + File.separator + "grouper-" + branchToCreatePatchFor);
2028 
2029     if (!sourceDir.exists()) {
2030       throw new RuntimeException("Why does source dir not exist??? " + sourceDir);
2031     }
2032 
2033     //grouper is in downloadDir/GROUPER_2_2_BRANCH/grouper-GROUPER_2_2_BRANCH
2034     File pspSourceDir = null;
2035     File pspSourceTagDir = null;
2036 
2037     if (this.appToUpgrade == AppToUpgrade.PSP) {
2038       downloadAndUnzipPspSource(branchForPspToCreatePatchFor);
2039 
2040       pspSourceDir = new File(this.grouperTarballDirectoryString + branchForPspToCreatePatchFor
2041           + File.separator + "grouper-psp-" + branchForPspToCreatePatchFor);
2042 
2043       if (!pspSourceDir.exists()) {
2044         throw new RuntimeException("Why does PSP source dir not exist??? " + pspSourceDir);
2045       }
2046 
2047       String grouperVersion = GrouperInstallerUtils.propertiesValue("grouper.version", true);
2048       System.out.println("Using PSP tag: " + grouperVersion);
2049       downloadAndUnzipPspSource(grouperVersion);
2050 
2051       pspSourceTagDir = new File(this.grouperTarballDirectoryString + grouperVersion
2052           + File.separator + "grouper-psp-" + grouperVersion);
2053 
2054       if (!pspSourceTagDir.exists()) {
2055         throw new RuntimeException("Why does PSP source tag dir not exist??? " + pspSourceTagDir);
2056       }
2057     }
2058     
2059     //get ant and maven
2060     this.downloadAndUnzipAnt();
2061     this.downloadAndUnzipMaven();
2062 
2063     if (this.appToUpgrade == AppToUpgrade.API) {
2064       //have to build client first
2065       this.buildClient(new File(sourceDir + File.separator + "grouper-misc" + File.separator + "grouperClient"));
2066       this.buildClient(new File(sourceTagDir + File.separator + "grouper-misc" + File.separator + "grouperClient"));
2067       
2068     }
2069 
2070     // Grouper API has always been built for everything, but its build is actually broken
2071     // right now, and, in fact, there is no need to build Grouper API for PSPNG
2072     // Therefore: Disabling Grouper API build when building PSPNG so it can at least be patched
2073     if (this.appToUpgrade != AppToUpgrade.PSPNG) {
2074       this.untarredApiDir = new File(sourceDir + File.separator + "grouper");
2075       this.buildGrouperApi(new File(sourceDir + File.separator + "grouper"));
2076       this.untarredApiDir = new File(sourceTagDir + File.separator + "grouper");
2077       this.buildGrouperApi(new File(sourceTagDir + File.separator + "grouper"));
2078     }
2079     
2080     if (this.appToUpgrade == AppToUpgrade.API) {
2081       //other packages, e.g. messaging
2082       this.buildMessagingRabbitmq(new File(sourceDir + File.separator + "grouper-misc" + File.separator + "grouper-messaging-rabbitmq"));
2083       this.buildMessagingRabbitmq(new File(sourceTagDir + File.separator + "grouper-misc" + File.separator + "grouper-messaging-rabbitmq"));
2084 
2085       this.buildMessagingActivemq(new File(sourceDir + File.separator + "grouper-misc" + File.separator + "grouper-messaging-activemq"));
2086       this.buildMessagingActivemq(new File(sourceTagDir + File.separator + "grouper-misc" + File.separator + "grouper-messaging-activemq"));
2087 
2088       this.buildMessagingAws(new File(sourceDir + File.separator + "grouper-misc" + File.separator + "grouper-messaging-aws"));
2089       this.buildMessagingAws(new File(sourceTagDir + File.separator + "grouper-misc" + File.separator + "grouper-messaging-aws"));
2090 
2091       this.buildDuo(new File(sourceDir + File.separator + "grouper-misc" + File.separator + "grouper-duo"));
2092       this.buildDuo(new File(sourceTagDir + File.separator + "grouper-misc" + File.separator + "grouper-duo"));
2093       
2094       this.buildBox(new File(sourceDir + File.separator + "grouper-misc" + File.separator + "grouper-box"));
2095       this.buildBox(new File(sourceTagDir + File.separator + "grouper-misc" + File.separator + "grouper-box"));
2096       
2097     }
2098     
2099 
2100     if (this.appToUpgrade == AppToUpgrade.UI) {
2101       //lets build the UI
2102       this.untarredApiDir = new File(sourceDir + File.separator + "grouper");
2103       this.untarredUiDir = new File(sourceDir + File.separator + "grouper-ui");
2104       this.configureUi();
2105       this.buildUi(false);
2106 
2107       this.untarredApiDir = new File(sourceTagDir + File.separator + "grouper");
2108       this.untarredUiDir = new File(sourceTagDir + File.separator + "grouper-ui");
2109       this.configureUi();
2110       this.buildUi(false);
2111 
2112     }
2113     
2114     if (this.appToUpgrade == AppToUpgrade.WS) {
2115       //lets build the WS
2116       this.untarredApiDir = new File(sourceDir + File.separator + "grouper");
2117       this.untarredWsDir = new File(sourceDir + File.separator + "grouper-ws");
2118       this.configureWs();
2119       this.buildWs(false);
2120 
2121       this.untarredApiDir = new File(sourceTagDir + File.separator + "grouper");
2122       this.untarredWsDir = new File(sourceTagDir + File.separator + "grouper-ws");
2123       this.configureWs();
2124       this.buildWs(false);
2125     }    
2126     
2127     if (this.appToUpgrade == AppToUpgrade.PSPNG) {
2128       this.untarredPspngDir = new File(sourceDir + File.separator + "grouper-misc" + File.separator + "grouper-pspng");
2129       this.buildPspng(this.untarredPspngDir);
2130 
2131       this.untarredPspngDir = new File(sourceTagDir + File.separator + "grouper-misc" + File.separator + "grouper-pspng");
2132       this.buildPspng(this.untarredPspngDir);
2133     }    
2134 
2135     if (this.appToUpgrade == AppToUpgrade.PSP) {
2136       this.untarredApiDir = new File(sourceDir + File.separator + "grouper");
2137       this.buildPsp(pspSourceDir);
2138 
2139       this.untarredApiDir = new File(sourceTagDir + File.separator + "grouper");
2140       this.buildPsp(pspSourceTagDir);
2141     }    
2142 
2143     //lets index files
2144     Map<String, GrouperInstallerIndexFile> indexOfFiles = new TreeMap<String, GrouperInstallerIndexFile>();
2145     Map<String, GrouperInstallerIndexFile> indexOfTagFiles = new TreeMap<String, GrouperInstallerIndexFile>();
2146 
2147     patchCreateIndexFiles(indexOfFiles, sourceDir, pspSourceDir);
2148     patchCreateIndexFiles(indexOfTagFiles, sourceTagDir, pspSourceTagDir);
2149 
2150     Set<GrouperInstallerIndexFile> grouperInstallerIndexFilesToAddToPatch = new HashSet<GrouperInstallerIndexFile>();
2151     
2152     //lets get the files from the user
2153     OUTER: for (int i=0;i<10;i++) {
2154       
2155       if (i==9) {
2156         throw new RuntimeException("You need to enter valid files!");
2157       }
2158 
2159       //if subsequent pass, then start fresh
2160       grouperInstallerIndexFilesToAddToPatch.clear();
2161       
2162       System.out.println("\nThe following could be filename if no dupes: Something.java.\n"
2163           + "Could be package path: edu/whatever/Something.java\n"
2164           + "could be path in module: dist/build/edu/internet2/middleware/grouper/changeLog/esb/consumer/EsbEvent.java\n"
2165           + "could be: webapp/WEB-INF/grouperUi2/index/index.jsp");
2166       System.out.println("Enter the comma separated list of files (dont use .class, use .java) to make a patch from: [required]\n");
2167       String filesToMakePatchFromCommaSeparated = readFromStdIn("grouperInstaller.autorun.patchFilesCommaSeparated");
2168       if (GrouperInstallerUtils.isBlank(filesToMakePatchFromCommaSeparated)) {
2169         System.out.println("This is a required field!");
2170         continue;
2171       }
2172       
2173       Set<String> fileKeys = new LinkedHashSet<String>(GrouperInstallerUtils.nonNull(
2174           GrouperInstallerUtils.splitTrimToList(filesToMakePatchFromCommaSeparated, ",")));
2175 
2176       for (String fileKey : fileKeys) {
2177         
2178         if (fileKey.endsWith(".class")) {
2179           System.out.println("Do not specify .class files, only .java files (will be compiled): '" + fileKey + "'!!!  please re-enter the list");
2180           continue OUTER;
2181         }
2182 
2183         GrouperInstallerIndexFile grouperInstallerIndexFile = indexOfFiles.get(fileKey);
2184         if (grouperInstallerIndexFile == null) {
2185           grouperInstallerIndexFile = indexOfTagFiles.get(fileKey);
2186           //see if we are deleting
2187           if (grouperInstallerIndexFile == null) {
2188             System.out.println("Cant find file: '" + fileKey + "'!!!  please re-enter the list");
2189             continue OUTER;
2190           }
2191         }
2192         
2193         if (grouperInstallerIndexFile.isHasMultipleFilesBySimpleName()
2194             && GrouperInstallerUtils.equals(fileKey, grouperInstallerIndexFile.getSimpleName())) {
2195           System.out.println("This name is in the index multiple times, please be more specific: '" 
2196               + fileKey + "', " + grouperInstallerIndexFile.getErrors());
2197           continue OUTER;
2198         }
2199         
2200         if (grouperInstallerIndexFile.isHasMultipleFilesByRelativePath()
2201             && GrouperInstallerUtils.equals(fileKey, grouperInstallerIndexFile.getRelativePath())) {
2202           System.out.println("This relative path is in the index multiple times, please be more specific: '" 
2203               + fileKey + "', " + grouperInstallerIndexFile.getErrors());
2204           continue OUTER;
2205         }
2206 
2207         if (grouperInstallerIndexFile.isHasMultipleFilesByPath()
2208             && GrouperInstallerUtils.equals(fileKey, grouperInstallerIndexFile.getPath())) {
2209           System.out.println("This path is in the index multiple times, please be more specific: '" 
2210               + fileKey + "', " + grouperInstallerIndexFile.getErrors());
2211           continue OUTER;
2212         }
2213         
2214         grouperInstallerIndexFilesToAddToPatch.add(grouperInstallerIndexFile);
2215       }
2216       break OUTER;
2217     }
2218     
2219     //ok, we have our list of files
2220     //lets go from java to class
2221     for (GrouperInstallerIndexFile grouperInstallerIndexFile : new HashSet<GrouperInstallerIndexFile>(grouperInstallerIndexFilesToAddToPatch)) {
2222 
2223       if (grouperInstallerIndexFile.getSimpleName().endsWith(".java")) {
2224         
2225         String relativePathJava = grouperInstallerIndexFile.getRelativePath();
2226         String relativePathPrefix = GrouperInstallerUtils.substringBeforeLast(relativePathJava, ".");
2227         String relativePathClass = relativePathPrefix + ".class";
2228 
2229         GrouperInstallerIndexFile grouperInstallerIndexFileClassFile = indexOfFiles.get(relativePathClass);
2230         
2231         //this will happen in a delete
2232         if (grouperInstallerIndexFileClassFile == null) {
2233           continue;
2234         }
2235         
2236         //this shouldnt happen
2237         if (grouperInstallerIndexFileClassFile.isHasMultipleFilesByRelativePath()) {
2238           throw new RuntimeException("Class file has multiple files by relative path???? " + relativePathClass);
2239         }
2240 
2241         //found class file
2242         grouperInstallerIndexFilesToAddToPatch.add(grouperInstallerIndexFileClassFile);
2243         
2244         //lets get all the inner classes
2245         File parentFile = grouperInstallerIndexFileClassFile.getFile().getParentFile();
2246         
2247         //with slash if needed, not sure why a class wouldnt have a package, but handle the case anyways
2248         String parentRelativePathWithSlash = GrouperInstallerUtils.substringBeforeLast(grouperInstallerIndexFileClassFile.getRelativePath(), "/") + "/";
2249         if (!grouperInstallerIndexFileClassFile.getRelativePath().contains("/")) {
2250           parentRelativePathWithSlash = "";
2251         }
2252         String fileNameInnerClassPrefix = GrouperInstallerUtils.substringBeforeLast(
2253             grouperInstallerIndexFileClassFile.getFile().getName(), ".") + "$";
2254         for (File siblingFile : parentFile.listFiles()) {
2255           if (siblingFile.getName().endsWith(".class") && GrouperInstallerUtils.filePathStartsWith(siblingFile.getName(),fileNameInnerClassPrefix)) {
2256             //this is an inner class
2257             String innerClassRelativePath = parentRelativePathWithSlash + siblingFile.getName();
2258             GrouperInstallerIndexFile innerClassIndexFile = indexOfFiles.get(innerClassRelativePath);
2259             if (innerClassIndexFile == null) {
2260               throw new RuntimeException("Cant find inner class index file??? " + innerClassRelativePath);
2261             }
2262             if (innerClassIndexFile.isHasMultipleFilesByRelativePath()) {
2263               throw new RuntimeException("Inner class file has multiple files by relative path??? " + innerClassRelativePath);
2264             }
2265             //found class file
2266             grouperInstallerIndexFilesToAddToPatch.add(innerClassIndexFile);
2267           }
2268         }
2269       }
2270     }
2271 
2272     File patchDir = new File(this.grouperTarballDirectoryString + "patches" + File.separator + patchName);
2273     
2274     if (patchDir.exists()) {
2275       if (patchDir.isFile()) {
2276         throw new RuntimeException("Why is patch directory a file???? " + patchDir.getAbsolutePath());
2277       }
2278       
2279       System.out.println("Local patch dir exists, is it ok to be automatically deleted? (t|f)? [t]:");
2280       boolean continueOn = readFromStdInBoolean(true, "grouperInstaller.autorun.deleteLocalPatchFile");
2281       if (!continueOn) {
2282         System.out.println("Cant continue if not deleting patch dir: " + patchDir.getAbsolutePath());
2283         throw new RuntimeException("Cant continue if not deleting patch dir: " + patchDir.getAbsolutePath());
2284       }
2285       
2286       //delete this dir
2287       GrouperInstallerUtils.deleteRecursiveDirectory(patchDir.getAbsolutePath());
2288       
2289     }
2290 
2291     
2292     //lets look for dependencies
2293     Set<String> dependencyPatchNames = new TreeSet<String>();
2294     
2295     //keep track of files to put in the "old" dir
2296     Map<GrouperInstallerIndexFile, File> indexFileToOldFile = new HashMap<GrouperInstallerIndexFile, File>();
2297     
2298     //go from most recent to oldest
2299     for (int i=nextPatchIndex-1;i>=0;i--) {
2300       
2301       //lets find the patch dir
2302       String currentPatchName = GrouperInstallerUtils.substringBeforeLast(patchName, "_") + "_" + i;
2303       
2304       File currentPatchDir = new File(this.grouperTarballDirectoryString + "patches" + File.separator + currentPatchName);
2305 
2306       Iterator<GrouperInstallerIndexFile> iterator = grouperInstallerIndexFilesToAddToPatch.iterator();
2307       
2308       while (iterator.hasNext()) {
2309         
2310         GrouperInstallerIndexFile indexFileToAdd = iterator.next();
2311         
2312         //dont check twice
2313         if (indexFileToOldFile.containsKey(indexFileToAdd)) {
2314           continue;
2315         }
2316         
2317         GrouperInstallerIndexFile indexFileToAddFromBranch = indexOfFiles.get(indexFileToAdd.getRelativePath());
2318         
2319         //note the old file will be in the patch's new directory
2320         File oldFile = new File(currentPatchDir.getAbsolutePath() + File.separator 
2321             + "new" + File.separator + indexFileToAdd.getPatchFileType().getDirName()
2322             + File.separator + GrouperInstallerUtils.replace(indexFileToAdd.getRelativePath(), "/", File.separator));
2323         if (oldFile.exists() && oldFile.isFile()) {
2324 
2325           if (indexFileToAddFromBranch != null && GrouperInstallerUtils.contentEquals(indexFileToAdd.getFile(), oldFile)) {
2326             System.out.println("New file is same as old file: " + indexFileToAdd.getFile().getAbsolutePath() + ", " 
2327                 + oldFile.getAbsolutePath());
2328             System.out.println("This file will not be included in patch");
2329             //remove from patch
2330             iterator.remove();
2331           } else {
2332 
2333             //this is now a dependency
2334             dependencyPatchNames.add(currentPatchName);
2335             
2336             //link this with the installer index file
2337             indexFileToOldFile.put(indexFileToAdd, oldFile);
2338           }          
2339         }
2340         
2341       }
2342       
2343     }
2344     
2345     {
2346       String patchNameDependenciesString = null;
2347       
2348       OUTER: for (int i=0;i<10;i++) {
2349         if (i==9) {
2350           throw new RuntimeException("Invalid patch names!");
2351         }
2352         if (dependencyPatchNames.size() == 0) {
2353           
2354           System.out.println("No dependency patches are detected, enter any patch names that are "
2355               + "dependencies that you know of (comma separated), or blank for none:\n");
2356           patchNameDependenciesString = readFromStdIn("grouperInstaller.autorun.patchNameDependenciesCommaSeparated");
2357           
2358         } else {
2359     
2360           System.out.println("These " + dependencyPatchNames.size() + " patches are detected: " 
2361               + GrouperInstallerUtils.join(dependencyPatchNames.iterator(), ", "));
2362           System.out.println("Enter any patch names that are dependencies that you know of (comma separated), or blank for none:\n");
2363           patchNameDependenciesString = readFromStdIn("grouperInstaller.autorun.patchNameDependenciesCommaSeparated");
2364     
2365         }
2366         if (!GrouperInstallerUtils.isBlank(patchNameDependenciesString)) {
2367           List<String> patchNameDependeciesFromUser = GrouperInstallerUtils.splitTrimToList(patchNameDependenciesString, ",");
2368           for (String currentPatchName : patchNameDependeciesFromUser) {
2369             if (!patchNameValid(currentPatchName)) {
2370               System.out.println("Invalid patch name! '" + currentPatchName + "', enter them again!");
2371               continue OUTER;
2372             }
2373           }
2374           dependencyPatchNames.addAll(patchNameDependeciesFromUser);
2375         }
2376         break;
2377       }
2378       
2379     }    
2380 
2381     //find old files from the tag
2382     Iterator<GrouperInstallerIndexFile> iterator = grouperInstallerIndexFilesToAddToPatch.iterator();
2383     
2384     while (iterator.hasNext()) {
2385       GrouperInstallerIndexFile currentIndexFile = iterator.next();
2386       //see if its covered in another patch
2387       if (indexFileToOldFile.containsKey(currentIndexFile)) {
2388         continue;
2389       }
2390       
2391       //dont have old files from java or classes, thats only for other patches to do
2392       if (currentIndexFile.getSimpleName().endsWith(".class") || currentIndexFile.getSimpleName().endsWith(".java")) {
2393         continue;
2394       }
2395 
2396       GrouperInstallerIndexFile currentIndexFileFromBranch = indexOfFiles.get(currentIndexFile.getRelativePath());
2397       
2398       //look for the old file
2399       GrouperInstallerIndexFile currentIndexFileFromTag = indexOfTagFiles.get(currentIndexFile.getPath());
2400       if (currentIndexFileFromTag == null) {
2401         currentIndexFileFromTag = indexOfTagFiles.get(currentIndexFile.getRelativePath());
2402       }
2403       if (currentIndexFileFromTag != null) {
2404         if (currentIndexFileFromTag.isHasMultipleFilesByPath()) {
2405           throw new RuntimeException("Why multiple paths???? " + currentIndexFile + ", " + currentIndexFile.getPath());
2406         }
2407         if (currentIndexFileFromBranch != null && GrouperInstallerUtils.contentEquals(currentIndexFileFromTag.getFile(), currentIndexFile.getFile())) {
2408           System.out.println("New file is same as old file: " + currentIndexFile.getFile().getAbsolutePath() + ", " 
2409               + currentIndexFileFromTag.getFile().getAbsolutePath());
2410           System.out.println("This file will not be included in patch");
2411           //remove from patch
2412           iterator.remove();
2413         } else {
2414           //add this as an old file
2415           indexFileToOldFile.put(currentIndexFile, currentIndexFileFromTag.getFile());
2416         }
2417       }
2418     }
2419     
2420     if (grouperInstallerIndexFilesToAddToPatch.size() == 0) {
2421       throw new RuntimeException("There are no files to put in patch!");
2422     }
2423     
2424 
2425     //# will show up on screen so user knows what it is
2426     //description = This patch fixes GRP-1080: browse folders refresh button only works in chrome, not other browsers
2427     System.out.print("\nEnter a description for this patch, e.g. GRP-123: fixes a problem with such and such: [required]\n");
2428     String patchDescription = readFromStdIn("grouperInstaller.autorun.patchDescription");
2429     
2430     if (GrouperInstallerUtils.isBlank(patchDescription)) {
2431       throw new RuntimeException("Cant have a blank description!");
2432     }
2433 
2434     //# (note, will try to get this from patch description, if its there, this can be blank)
2435     Matcher patchJiraKeyMatcher = Pattern.compile(".*(GRP-\\d+).*").matcher(patchDescription);
2436     String defaultPatchJiraKey = "";
2437     if (patchJiraKeyMatcher.matches()) {
2438       defaultPatchJiraKey = patchJiraKeyMatcher.group(1);
2439     }
2440     System.out.print("\nEnter a Jira key (e.g. GRP-123) for this patch: [required] " 
2441         + (GrouperInstallerUtils.isBlank(defaultPatchJiraKey) ? "" : ("[" + defaultPatchJiraKey + "]")) + "\n");
2442     String patchJiraKey = readFromStdIn("grouperInstaller.autorun.patchJiraKey");
2443     
2444     if (GrouperInstallerUtils.isBlank(patchJiraKey)) {
2445       if (!GrouperInstallerUtils.isBlank(defaultPatchJiraKey)) {
2446         patchJiraKey = defaultPatchJiraKey;
2447       } else {
2448         throw new RuntimeException("Cant have a blank jira key!");
2449       }
2450     }
2451     
2452     if (!Pattern.compile("^GRP-\\d+$").matcher(patchJiraKey).matches()) {
2453       throw new RuntimeException("Patch jira key must be valid: '" + patchJiraKey + "'");
2454     }
2455 
2456     String patchRiskLevel = null;
2457     
2458     {
2459       //# low, medium, or high risk to applying the patch
2460       //risk = low
2461       System.out.println("Enter the risk level for the patch: (low|medium|high): [required] ");
2462       String patchRiskLevelInput = readFromStdIn("grouperInstaller.autorun.patchRiskLevel");
2463       
2464       if (GrouperInstallerUtils.equalsIgnoreCase("low", patchRiskLevelInput)) {
2465         patchRiskLevel = "low";
2466       } else if (GrouperInstallerUtils.equalsIgnoreCase("medium", patchRiskLevelInput)) {
2467         patchRiskLevel = "medium";
2468       } else if (GrouperInstallerUtils.equalsIgnoreCase("high", patchRiskLevelInput)) {
2469         patchRiskLevel = "high";
2470       } else {
2471         throw new RuntimeException("Invalid risk level: '" + patchRiskLevelInput + "', expecting low|medium|high");
2472       }
2473       
2474     }
2475     
2476     //# is this is a security patch (true or false)
2477     //security = false
2478     System.out.println("Is this a security patch? (t|f): [t] ");
2479     boolean securityPatch = readFromStdInBoolean(false, "grouperInstaller.autorun.patchSecurity");
2480 
2481     boolean requiresRestart = false;
2482     for (GrouperInstallerIndexFile currentIndexFile : grouperInstallerIndexFilesToAddToPatch) {
2483       if (currentIndexFile.getSimpleName().endsWith(".jar")
2484           || currentIndexFile.getSimpleName().endsWith(".java")) {
2485         requiresRestart = true;
2486       }
2487     }
2488     //# if this patch requires restart of processes (true or false)
2489     //requiresRestart = false
2490     if (requiresRestart) {
2491       System.out.println("It is detected that your patch requires restart");
2492     } else {
2493       System.out.println("It is NOT detected that your patch requires restart, please confirm this, does it require restart (t|f)? [f] ");
2494       requiresRestart = readFromStdInBoolean(false, "grouperInstaller.autorun.overrideDoesntRequireRestart");
2495       
2496       if (requiresRestart) {
2497         System.out.println("Perhaps the maintainer of the Grouper Installer can use this feedback to make a better guess on restart, let them know");
2498         GrouperInstallerUtils.sleep(2000);
2499       }
2500     }
2501     //at this point we can build the patch dir and file and put files in there
2502     
2503     //# patches that this patch is dependant on (comma separated)
2504     //dependencies = 
2505 
2506     //create the dir
2507     GrouperInstallerUtils.mkdirs(patchDir);
2508     
2509     {
2510       String patchPropertiesContents = "# will show up on screen so user knows what it is\n"
2511           + "description = " + patchDescription + "\n"
2512           + "\n"
2513           + "# patches that this patch is dependant on (comma separated)\n"
2514           + "dependencies = " + GrouperInstallerUtils.join(dependencyPatchNames.iterator(), ", ") + "\n"
2515           + "\n"
2516           + "# low, medium, or high risk to applying the patch\n"
2517           + "risk = " + patchRiskLevel + "\n"
2518           + "\n"
2519           + "# is this is a security patch (true or false)\n"
2520           + "security = " + securityPatch + "\n"
2521           + "\n"
2522           + "# if this patch requires restart of processes (true or false)\n"
2523           + "requiresRestart = " + requiresRestart + "\n";
2524       String patchPropertiesFileName = patchDir + File.separator + patchDir.getName() + ".properties";
2525       GrouperInstallerUtils.saveStringIntoFile(new File(patchPropertiesFileName), patchPropertiesContents);
2526     }
2527     
2528     //lets do old files
2529     //start with old files
2530     if (indexFileToOldFile.size() > 0) {
2531       GrouperInstallerUtils.mkdirs(new File(patchDir.getAbsolutePath() + File.separator + "old"));
2532       for (GrouperInstallerIndexFile currentIndexFile : indexFileToOldFile.keySet()) {
2533 
2534         File oldFile = new File(patchDir.getAbsolutePath() + File.separator + "old" + File.separator
2535             + currentIndexFile.getPatchFileType().getDirName() + File.separator
2536             + GrouperInstallerUtils.replace(currentIndexFile.getRelativePath(), "/", File.separator));
2537         
2538         GrouperInstallerUtils.mkdirs(oldFile.getParentFile());
2539         
2540         System.out.println("Copying old file from " + indexFileToOldFile.get(currentIndexFile).getAbsolutePath()
2541             + "\n   to: " + oldFile.getAbsolutePath());
2542         
2543         GrouperInstallerUtils.copyFile(indexFileToOldFile.get(currentIndexFile), oldFile);
2544         
2545       }
2546     }
2547 
2548     //now put new files in place
2549     {
2550       GrouperInstallerUtils.mkdirs(new File(patchDir.getAbsolutePath() + File.separator + "new"));
2551       for (GrouperInstallerIndexFile currentIndexFile : grouperInstallerIndexFilesToAddToPatch) {
2552 
2553         //is it a delete? then there is no new file
2554         if (!indexOfFiles.containsKey(currentIndexFile.getRelativePath())) {
2555           continue;
2556         }
2557         
2558         File newFile = new File(patchDir.getAbsolutePath() + File.separator + "new" + File.separator
2559             + currentIndexFile.getPatchFileType().getDirName() + File.separator
2560             + GrouperInstallerUtils.replace(currentIndexFile.getRelativePath(), "/", File.separator));
2561         
2562         GrouperInstallerUtils.mkdirs(newFile.getParentFile());
2563         
2564         System.out.println("Copying new file from " + currentIndexFile.getFile().getAbsolutePath()
2565             + "\n   to: " + newFile.getAbsolutePath());
2566         
2567         GrouperInstallerUtils.copyFile(currentIndexFile.getFile().getAbsoluteFile(), newFile);
2568         
2569       }
2570     }
2571 
2572     {
2573       //generate the wiki markup
2574       //    <tr>
2575       //      <td>
2576       //        <p>
2577       //          <a href="https://software.internet2.edu/grouper/release/2.2.1/patches/grouper_v2_2_1_api_patch_1.tar.gz">grouper_v2_2_1_api_patch_1</a>
2578       //        </p>
2579       //      </td>
2580       //      <td>
2581       //        <p>
2582       //          <a href="https://bugs.internet2.edu/jira/browse/GRP-1096">GRP-1096: Use threads for 2.2 upgrade to decrease time of upgrade</a>
2583       //        </p>
2584       //      </td>
2585       //      <td>
2586       //        <p>classes/edu/internet2/middleware/grouper/internal/dao/hib3/Hib3StemSetDAO.java <br class="atl-forced-newline"/> classes/edu/internet2/middleware/grouper/misc/MigrateLegacyAttributes.java <br class="atl-forced-newline"/> classes/edu/internet2/middleware/grouper/misc/AddMissingGroupSets.java <br class="atl-forced-newline"/> classes/edu/internet2/middleware/grouper/misc/SyncPITTables.java <br class="atl-forced-newline"/> classes/edu/internet2/middleware/grouper/misc/SyncStemSets.java <br class="atl-forced-newline"/> classes/grouper.base.properties</p>
2587       //      </td>
2588       //    </tr>
2589       String grouperVersion = GrouperInstallerUtils.propertiesValue("grouper.version", true);
2590 
2591       String wikiMarkup = "    <tr>\n"
2592         + "      <td>\n"
2593         + "        <p>" + new SimpleDateFormat("yyyy/MM/dd").format(new Date()) + "</p>\n"
2594         + "      </td>\n"
2595         + "      <td>\n"
2596         + "        <p>\n"
2597         + "          <a href=\"https://software.internet2.edu/grouper/release/" + grouperVersion + "/patches/" + patchName + ".tar.gz\">" + patchName + "</a>\n"
2598         + "        </p>\n"
2599         + "      </td>\n"
2600         + "      <td>\n"
2601         + "        <p>\n"
2602         + "          <a href=\"https://bugs.internet2.edu/jira/browse/" + patchJiraKey + "\">" + patchDescription + "</a>\n"
2603         + "        </p>\n"
2604         + "      </td>\n"
2605         + "      <td>\n"
2606         + "        <p>";
2607       
2608       boolean isFirst = true;
2609       for (GrouperInstallerIndexFile currentIndexFile : grouperInstallerIndexFilesToAddToPatch) {
2610         
2611         //just do java files
2612         if (currentIndexFile.getSimpleName().endsWith(".class")) {
2613           continue;
2614         }
2615         
2616         //classes/edu/internet2/middleware/grouper/internal/dao/hib3/Hib3StemSetDAO.java 
2617         //<br class=\"atl-forced-newline\"/>
2618 
2619         if (!isFirst) {
2620           wikiMarkup += "<br class=\"atl-forced-newline\"/>";
2621         }
2622         wikiMarkup += currentIndexFile.getPatchFileType().getDirName() + "/" 
2623             + currentIndexFile.getRelativePath();
2624 
2625         isFirst = false;
2626         
2627       }
2628       wikiMarkup += "</p>\n      </td>\n"
2629         + "    </tr>\n";
2630     
2631       System.out.println("Here is the wiki markup for the release notes page, copy and paste that into confluence using the <> button:");
2632       System.out.println("\n" + wikiMarkup + "\n");
2633       System.out.print("Press <enter> to continue... ");
2634       readFromStdIn("grouperInstaller.autorun.patchContinueAfterWikiMarkup");
2635     }
2636     
2637     // tar this up
2638     File tarfile = new File(patchDir.getParentFile() + File.separator + patchName + ".tar");
2639     GrouperInstallerUtils.tar(patchDir, tarfile);
2640     
2641     System.out.println("\nDo you want to name this file as a test version so you can test it without affecting other users? (t|f) [t]: ");
2642     boolean patchUseTestFileName = readFromStdInBoolean(true, "grouperInstaller.autorun.patchNameFileAsTestVersion");
2643 
2644     File gzipfile = new File(patchDir.getParentFile() + File.separator + patchName + (patchUseTestFileName ? "_test" : "") + ".tar.gz");
2645     GrouperInstallerUtils.gzip(tarfile, gzipfile);
2646 
2647     System.out.println("\nSUCCESS: your patch is here: " + gzipfile.getAbsolutePath());
2648 
2649   }
2650 
2651   /**
2652    * patch pattern
2653    */
2654   private static final Pattern patchNamePattern = Pattern.compile("^grouper_v(\\d+)_(\\d+)_(\\d+)_(api|ws|ui|psp|pspng)_patch_(\\d+)$");
2655 
2656   
2657   /**
2658    * see if valid patch name e.g. grouper_v2_2_1_api_patch_0
2659    * @param patchName
2660    * @return true for valid
2661    */
2662   private static boolean patchNameValid(String patchName) {
2663     //validate patch names
2664     return patchNamePattern.matcher(patchName).matches();
2665 
2666   }
2667   
2668   /**
2669    * index files from a source directory
2670    * @param theIndexOfFiles index of label to the index file object
2671    * @param theSourceDir to look for files in
2672    * @param thePspSourceDir is psp source dir to look for files in
2673    */
2674   private void patchCreateIndexFiles(Map<String, GrouperInstallerIndexFile> theIndexOfFiles, File theSourceDir, File thePspSourceDir) {
2675     System.out.println("\nCreating file index to make patches from " + theSourceDir.getAbsolutePath() + "...\n");
2676     
2677     switch(this.appToUpgrade) {
2678       case CLIENT:
2679         throw new RuntimeException("No patching client, patch API instead");
2680       case API:
2681 
2682         //index the grouper client
2683 // dont think we need lib from client, only api
2684 //        this.patchCreateProcessFiles(indexOfFiles, 
2685 //            new File(sourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouperClient"),
2686 //            new File(sourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouperClient" + File.separator + "lib"),
2687 //            PatchFileType.lib);
2688 
2689         this.patchCreateProcessFiles(theIndexOfFiles, 
2690             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouperClient"),
2691             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouperClient" 
2692                 + File.separator + "dist" + File.separator + "bin"),
2693             PatchFileType.clazz);
2694 
2695         this.patchCreateProcessFiles(theIndexOfFiles, 
2696             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouperClient"),
2697             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouperClient" 
2698                 + File.separator + "src" + File.separator + "java"),
2699             PatchFileType.clazz);
2700 
2701         this.patchCreateProcessFiles(theIndexOfFiles, 
2702             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouperClient"),
2703             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouperClient" 
2704                 + File.separator + "src" + File.separator + "ext"),
2705             PatchFileType.clazz);
2706 
2707         this.patchCreateProcessFiles(theIndexOfFiles, 
2708             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouperClient"),
2709             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouperClient" 
2710                 + File.separator + "conf"),
2711             PatchFileType.clazz);
2712 
2713         // rabbitmq
2714         this.patchCreateProcessFiles(theIndexOfFiles, 
2715             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-rabbitmq"),
2716             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-rabbitmq" 
2717                 + File.separator + "src" + File.separator + "main" + File.separator + "java"),
2718             PatchFileType.clazz);
2719 
2720         this.patchCreateProcessFiles(theIndexOfFiles, 
2721             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-rabbitmq"),
2722             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-rabbitmq" 
2723                 + File.separator + "dist" + File.separator + "bin"),
2724             PatchFileType.clazz);
2725 
2726         this.patchCreateProcessFiles(theIndexOfFiles, 
2727             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-rabbitmq"),
2728             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-rabbitmq"
2729                 + File.separator + "lib"),
2730             PatchFileType.lib);
2731 
2732         //active mq
2733         this.patchCreateProcessFiles(theIndexOfFiles, 
2734             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-activemq"),
2735             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-activemq" 
2736                 + File.separator + "src" + File.separator + "main" + File.separator + "java"),
2737             PatchFileType.clazz);
2738 
2739         this.patchCreateProcessFiles(theIndexOfFiles, 
2740             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-activemq"),
2741             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-activemq" 
2742                 + File.separator + "dist" + File.separator + "bin"),
2743             PatchFileType.clazz);
2744 
2745         this.patchCreateProcessFiles(theIndexOfFiles, 
2746             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-activemq"),
2747             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-activemq"
2748                 + File.separator + "lib"),
2749             PatchFileType.lib);
2750 
2751         //aws
2752         this.patchCreateProcessFiles(theIndexOfFiles, 
2753             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-aws"),
2754             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-aws" 
2755                 + File.separator + "src" + File.separator + "main" + File.separator + "java"),
2756             PatchFileType.clazz);
2757 
2758         this.patchCreateProcessFiles(theIndexOfFiles, 
2759             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-aws"),
2760             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-aws" 
2761                 + File.separator + "dist" + File.separator + "bin"),
2762             PatchFileType.clazz);
2763 
2764         this.patchCreateProcessFiles(theIndexOfFiles, 
2765             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-aws"),
2766             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-aws"
2767                 + File.separator + "lib"),
2768             PatchFileType.lib);
2769 
2770         // duo
2771         this.patchCreateProcessFiles(theIndexOfFiles, 
2772             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-duo"),
2773             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-duo" 
2774                 + File.separator + "src"),
2775             PatchFileType.clazz);
2776 
2777         this.patchCreateProcessFiles(theIndexOfFiles, 
2778             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-duo"),
2779             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-duo" 
2780                 + File.separator + "dist" + File.separator + "bin"),
2781             PatchFileType.clazz);
2782 
2783         // box
2784         this.patchCreateProcessFiles(theIndexOfFiles, 
2785             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-box"),
2786             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-box" 
2787                 + File.separator + "src"),
2788             PatchFileType.clazz);
2789 
2790         this.patchCreateProcessFiles(theIndexOfFiles, 
2791             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-box"),
2792             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-box" 
2793                 + File.separator + "changeLogConsumerSource"),
2794             PatchFileType.clazz);
2795 
2796         this.patchCreateProcessFiles(theIndexOfFiles, 
2797             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-box"),
2798             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-box" 
2799                 + File.separator + "dist" + File.separator + "bin"),
2800             PatchFileType.clazz);
2801 
2802         //add grouper api files
2803         this.patchCreateProcessFiles(theIndexOfFiles, 
2804             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper"),
2805             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper" + File.separator + "lib"),
2806             PatchFileType.lib);
2807 
2808         this.patchCreateProcessFiles(theIndexOfFiles, 
2809             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper"),
2810             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper" + File.separator + "dist" 
2811                 + File.separator + "build" + File.separator + "grouper"),
2812             PatchFileType.clazz);
2813 
2814         this.patchCreateProcessFiles(theIndexOfFiles,
2815             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper"),
2816             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper" + File.separator + "conf"),
2817             PatchFileType.clazz);
2818 
2819         this.patchCreateProcessFiles(theIndexOfFiles,
2820             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper"),
2821             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper" + File.separator + "src" 
2822                 + File.separator + "grouper"),
2823             PatchFileType.clazz);
2824 
2825         this.patchCreateProcessFiles(theIndexOfFiles,
2826             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper"),
2827             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper" + File.separator + "src" 
2828                 + File.separator + "esb"),
2829             PatchFileType.clazz);
2830 
2831 // do this at some point
2832 //        this.patchCreateProcessFiles(theIndexOfFiles,
2833 //            new File(theSourceDir.getAbsolutePath() + File.separator + "grouper"),
2834 //            new File(theSourceDir.getAbsolutePath() + File.separator + "grouper" + File.separator + "src" 
2835 //                + File.separator + "test"),
2836 //            PatchFileType.clazz);
2837 
2838         this.patchCreateProcessFiles(theIndexOfFiles,
2839             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper"),
2840             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper" + File.separator + "bin"),
2841             PatchFileType.bin);
2842 
2843 
2844         break;
2845       case UI:
2846         
2847         this.patchCreateProcessFiles(theIndexOfFiles, 
2848             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ui"),
2849             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ui" 
2850                 + File.separator + "java" + File.separator + "lib"),
2851             PatchFileType.lib);
2852         
2853         this.patchCreateProcessFiles(theIndexOfFiles, 
2854             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ui"),
2855             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ui" 
2856                 + File.separator + "java" + File.separator + "src"),
2857             PatchFileType.clazz);
2858         
2859         this.patchCreateProcessFiles(theIndexOfFiles, 
2860             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ui"),
2861             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ui" 
2862                 + File.separator + "conf"),
2863             PatchFileType.clazz);
2864         
2865         this.patchCreateProcessFiles(theIndexOfFiles, 
2866             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ui"),
2867             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ui" 
2868                 + File.separator + "temp" + File.separator + "jarBin"),
2869             PatchFileType.clazz);
2870         
2871         this.patchCreateProcessFiles(theIndexOfFiles, 
2872             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ui"),
2873             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ui" 
2874                 + File.separator + "webapp"),
2875             PatchFileType.file);
2876 
2877         break;
2878       case WS:
2879         
2880         this.patchCreateProcessFiles(theIndexOfFiles, 
2881             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws"),
2882             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws" 
2883                 + File.separator + "lib" + File.separator + "grouper-ws"),
2884             PatchFileType.lib);
2885 
2886         this.patchCreateProcessFiles(theIndexOfFiles, 
2887             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws"),
2888             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws" 
2889                 + File.separator + "lib" + File.separator + "rampart"),
2890             PatchFileType.lib);
2891 
2892         this.patchCreateProcessFiles(theIndexOfFiles, 
2893             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws"),
2894             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws" 
2895                 + File.separator + "build" + File.separator + "grouper-ws"),
2896             PatchFileType.clazz);
2897         
2898         this.patchCreateProcessFiles(theIndexOfFiles, 
2899             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws"),
2900             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws" 
2901                 + File.separator + "conf"),
2902             PatchFileType.clazz);
2903         
2904         // we need to get all the source folders except test, note, each release adds another
2905         File parentSourceDir = new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws"
2906             + File.separator + "src");
2907 
2908         for (File wsSourceDir : parentSourceDir.listFiles()) {
2909           if (wsSourceDir.isFile() || !wsSourceDir.getName().startsWith("grouper")) {
2910             continue;
2911           }
2912           this.patchCreateProcessFiles(theIndexOfFiles, 
2913               new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws"),
2914               new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws" 
2915                   + File.separator + "src" + File.separator + wsSourceDir.getName()),
2916               PatchFileType.clazz);
2917         }
2918 
2919         //files
2920         this.patchCreateProcessFiles(theIndexOfFiles, 
2921             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws"),
2922             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws" 
2923                 + File.separator + "webapp"),
2924             PatchFileType.file);
2925         
2926         break;
2927         
2928       case PSP:
2929         this.patchCreateProcessFiles(theIndexOfFiles, 
2930             new File(thePspSourceDir.getAbsolutePath() + File.separator + "psp"),
2931             new File(thePspSourceDir.getAbsolutePath() + File.separator + "psp" + File.separator + "target" 
2932                 + File.separator + "dependency"),
2933             PatchFileType.lib);
2934         this.patchCreateProcessFiles(theIndexOfFiles, 
2935             new File(thePspSourceDir.getAbsolutePath() + File.separator + "psp"),
2936             new File(thePspSourceDir.getAbsolutePath() + File.separator + "psp" + File.separator + "src" 
2937                 + File.separator + "main" + File.separator + "java"),
2938             PatchFileType.clazz);
2939         this.patchCreateProcessFiles(theIndexOfFiles, 
2940             new File(thePspSourceDir.getAbsolutePath() + File.separator + "psp"),
2941             new File(thePspSourceDir.getAbsolutePath() + File.separator + "psp" + File.separator + "src" 
2942                 + File.separator + "main" + File.separator + "resources"),
2943             PatchFileType.clazz);
2944         this.patchCreateProcessFiles(theIndexOfFiles, 
2945             new File(thePspSourceDir.getAbsolutePath() + File.separator + "psp"),
2946             new File(thePspSourceDir.getAbsolutePath() + File.separator + "psp" + File.separator + "target" 
2947                 + File.separator + "classes"),
2948             PatchFileType.clazz);
2949 
2950         break;
2951       case PSPNG:
2952 
2953         this.patchCreateProcessFiles(theIndexOfFiles, 
2954             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-pspng"),
2955             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-pspng" 
2956                 + File.separator + "target" + File.separator + "dependency"),
2957             PatchFileType.lib);
2958 
2959         this.patchCreateProcessFiles(theIndexOfFiles, 
2960             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-pspng"),
2961             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-pspng" 
2962                 + File.separator + "src" + File.separator + "main" + File.separator + "java"),
2963             PatchFileType.clazz);
2964 
2965         this.patchCreateProcessFiles(theIndexOfFiles, 
2966             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-pspng"),
2967             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-pspng" 
2968                 + File.separator + "target" + File.separator + "classes"),
2969             PatchFileType.clazz);
2970 
2971         break;
2972     }
2973     
2974     //print out files for debugging
2975     //for (String key : theIndexOfFiles.keySet()) {
2976     //  if (key.toLowerCase().contains("mygroupsmemberships")) {
2977     //    System.out.println(key + " -> " + theIndexOfFiles.get(key));
2978     //  }
2979     //}
2980 
2981     System.out.println("\nDone creating file index to make patches from " + theSourceDir.getAbsolutePath() + "... found " + theIndexOfFiles.size() + " files\n");
2982 
2983   }
2984   
2985   /**
2986    * @param directory to look in
2987    * @param projectDirectory is the directory where the project is for the files
2988    * @param indexOfFiles the index
2989    * @param patchFileType
2990    */
2991   private void patchCreateProcessFiles(Map<String, GrouperInstallerIndexFile> indexOfFiles, File projectDirectory, File directory, 
2992       PatchFileType patchFileType) {
2993     
2994     this.patchCreateProcessFilesHelper(indexOfFiles, projectDirectory, directory, patchFileType, "");
2995 
2996   }
2997 
2998   /**
2999    * @param directory to look in
3000    * @param projectDirectory is the directory where the project is for the files
3001    * @param indexOfFiles
3002    * @param relativePath in the main path to look in, helps with restrictions
3003    * @param patchFileType
3004    */
3005   private void patchCreateProcessFilesHelper(Map<String, GrouperInstallerIndexFile> indexOfFiles, 
3006       File projectDirectory, File directory, 
3007       PatchFileType patchFileType, String relativePath) {
3008 
3009     try {
3010       //lets spider through directory and add files to index
3011       //get the files into a vector
3012       File[] allFiles = directory.listFiles();
3013   
3014       //loop through the array
3015       for (int i = 0; i < allFiles.length; i++) {
3016   
3017         File currentFileOrDirectory = allFiles[i];
3018         
3019         if (-1 < currentFileOrDirectory.getName().indexOf("..")) {
3020           continue; //dont go to the parent directory
3021         }
3022   
3023         //go to sub directory
3024         String newRelativePath = GrouperInstallerUtils.isBlank(relativePath) ? currentFileOrDirectory.getName() 
3025             : (relativePath + "/" + currentFileOrDirectory.getName());
3026   
3027         if (currentFileOrDirectory.isFile()) {
3028   
3029           boolean addFile = false;
3030           
3031           String fileRelativePath = GrouperInstallerUtils.fileRelativePath(projectDirectory, currentFileOrDirectory);
3032   
3033           switch(patchFileType) {
3034   
3035             case lib:
3036               
3037               if (currentFileOrDirectory.getName().endsWith(".jar")) {
3038                 addFile = true;
3039               }
3040   
3041               break;
3042             case file:
3043               addFile = true;
3044               
3045               if (currentFileOrDirectory.getName().endsWith(".jar")) {
3046                 addFile = false;
3047               }
3048   
3049               if (currentFileOrDirectory.getName().endsWith(".class")) {
3050                 addFile = false;
3051               }
3052   
3053               if (currentFileOrDirectory.getName().endsWith(".java")) {
3054                 addFile = false;
3055               }
3056   
3057               //these are classes not files
3058               if (GrouperInstallerUtils.filePathStartsWith(fileRelativePath,"WEB-INF/classes")) {
3059                 addFile = false;
3060               }
3061   
3062               //these are libs not files
3063               if (GrouperInstallerUtils.filePathStartsWith(fileRelativePath,"WEB-INF/lib")) {
3064                 addFile = false;
3065               }
3066   
3067               break;
3068             default: 
3069               addFile = true;
3070           }
3071   
3072           if (addFile) {
3073             GrouperInstallerIndexFilee.html#GrouperInstallerIndexFile">GrouperInstallerIndexFile grouperInstallerIndexFile = new GrouperInstallerIndexFile();
3074             grouperInstallerIndexFile.setSimpleName(currentFileOrDirectory.getName());
3075             grouperInstallerIndexFile.setRelativePath(newRelativePath);
3076             grouperInstallerIndexFile.setFile(currentFileOrDirectory);
3077             grouperInstallerIndexFile.setPatchFileType(patchFileType);
3078             grouperInstallerIndexFile.setPath(fileRelativePath);
3079             
3080             //add by name
3081             if (patchCreateAddFileToIndex(indexOfFiles, grouperInstallerIndexFile, currentFileOrDirectory.getName())) {
3082               //different file
3083               indexOfFiles.get(currentFileOrDirectory.getName()).setHasMultipleFilesBySimpleName(true);
3084               System.out.println("Note: duplicate file by name: " + currentFileOrDirectory.getAbsolutePath().replace('\\', '/') 
3085                   + ", " + currentFileOrDirectory.getName() + ", " + newRelativePath.replace('\\', '/') + ", " 
3086                   + indexOfFiles.get(currentFileOrDirectory.getName()).getRelativePath().replace('\\', '/')  + ", "
3087                   + grouperInstallerIndexFile.getPath().replace('\\', '/') + ", "
3088                   + indexOfFiles.get(currentFileOrDirectory.getName()).getPath().replace('\\', '/'));
3089             }
3090             
3091             //add by relative path
3092             if (patchCreateAddFileToIndex(indexOfFiles, grouperInstallerIndexFile, newRelativePath)) {
3093               //different file
3094               indexOfFiles.get(currentFileOrDirectory.getName()).setHasMultipleFilesByRelativePath(true);
3095               System.out.println("Note: duplicate file by relative path: " + currentFileOrDirectory.getAbsolutePath().replace('\\', '/') 
3096                   + ", " + currentFileOrDirectory.getName() + ", " + newRelativePath.replace('\\', '/') + ", " 
3097                   + indexOfFiles.get(currentFileOrDirectory.getName()).getRelativePath().replace('\\', '/')  + ", "
3098                   + grouperInstallerIndexFile.getPath().replace('\\', '/') + ", "
3099                   + indexOfFiles.get(currentFileOrDirectory.getName()).getPath().replace('\\', '/'));
3100             }
3101   
3102             //add by path
3103             if (patchCreateAddFileToIndex(indexOfFiles, grouperInstallerIndexFile, grouperInstallerIndexFile.getPath())) {
3104               //different file
3105               indexOfFiles.get(currentFileOrDirectory.getName()).setHasMultipleFilesByPath(true);
3106               System.out.println("Note: duplicate file by path: " + currentFileOrDirectory.getAbsolutePath() .replace('\\', '/') 
3107                   + ", " + currentFileOrDirectory.getName() + ", " + newRelativePath.replace('\\', '/') + ", " 
3108                   + indexOfFiles.get(currentFileOrDirectory.getName()).getRelativePath().replace('\\', '/')  + ", "
3109                   + grouperInstallerIndexFile.getPath().replace('\\', '/') + ", "
3110                   + indexOfFiles.get(currentFileOrDirectory.getName()).getPath().replace('\\', '/'));
3111             }
3112           }
3113           
3114         } else {
3115                   
3116           patchCreateProcessFilesHelper(indexOfFiles, projectDirectory, currentFileOrDirectory, patchFileType, newRelativePath);
3117           
3118         }
3119       }
3120     } catch (Exception e) {
3121       throw new RuntimeException("Problem with directory: " + directory.getAbsolutePath(), e);
3122     }
3123     
3124   }
3125 
3126   /**
3127    * @param indexOfFiles database of files by various lookup names
3128    * @param grouperInstallerIndexFile file to add
3129    * @param key add by this key
3130    * @return true if file already there and different
3131    */
3132   private boolean patchCreateAddFileToIndex(Map<String, GrouperInstallerIndexFile> indexOfFiles, 
3133       GrouperInstallerIndexFile grouperInstallerIndexFile, String key) {
3134     
3135     //convert slashes on key
3136     key = key.replace('\\', '/');
3137     
3138     grouperInstallerIndexFile.getErrors().append("Key: ").append(key).append(", ");
3139     
3140     GrouperInstallerIndexFile currentFileInIndex = indexOfFiles.get(key);
3141     if (currentFileInIndex == null) {
3142       indexOfFiles.put(key, grouperInstallerIndexFile);
3143     } else {
3144       currentFileInIndex.getErrors().append("Key: ").append(key).append(",");
3145       //skip these, who cares, too many dupes
3146       if (!GrouperInstallerUtils.equals(grouperInstallerIndexFile.getSimpleName(), "package-info.java")
3147           && !GrouperInstallerUtils.equals(grouperInstallerIndexFile.getSimpleName(), "package.html")) {
3148         if (!GrouperInstallerUtils.equals(grouperInstallerIndexFile.computeSha1(), currentFileInIndex.computeSha1())) {
3149           return true;
3150         }
3151       }
3152     }
3153     return false;
3154   }
3155   
3156   /**
3157    * build PSP
3158    * @param pspDir
3159    */
3160   private void buildPsp(File pspDir) {
3161     if (!pspDir.exists() || pspDir.isFile()) {
3162       throw new RuntimeException("Cant find psp: " + pspDir.getAbsolutePath());
3163     }
3164     
3165     File pspBuildToDir = new File(pspDir.getAbsolutePath() + File.separator + "psp" 
3166         + File.separator + "target" + File.separator + "classes");
3167     
3168     boolean rebuildPsp = true;
3169     
3170     if (pspBuildToDir.exists()) {
3171       System.out.print("The PSP has been built in the past, do you want it rebuilt? (t|f) [t]: ");
3172       rebuildPsp = readFromStdInBoolean(true, "grouperInstaller.autorun.rebuildPspAfterHavingBeenBuilt");
3173     }
3174     
3175     if (!rebuildPsp) {
3176       return;
3177     }
3178     
3179     List<String> commands = new ArrayList<String>();
3180     
3181 //    \bin\mvn compile -DskipTests
3182     addMavenCommands(commands);
3183 
3184     //put 'compile -DskipTests' in there so it wont run tests which we dont want to do
3185     // dependency:copy-dependencies package -DskipTests
3186     //not compile
3187     commands.add("dependency:copy-dependencies");
3188     commands.add("package");
3189     commands.add("-DskipTests");
3190     commands.add("-Drat.ignoreErrors=true");
3191     commands.add("-Dlicense.skip=true");
3192     
3193     System.out.println("\n##################################");
3194     System.out.println("Building PSP with command:\n" + pspDir.getAbsolutePath() + "> " 
3195         + convertCommandsIntoCommand(commands) + "\n");
3196     
3197     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
3198         true, true, null, new File(pspDir.getAbsolutePath() + File.separator + "psp-parent"), null, true);
3199     
3200     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
3201       System.out.println("stderr: " + commandResult.getErrorText());
3202     }
3203     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
3204       System.out.println("stdout: " + commandResult.getOutputText());
3205     }
3206 
3207     System.out.println("\nEnd building PSP");
3208     System.out.println("##################################\n");
3209     
3210   }
3211   
3212   /**
3213    * build ws scim
3214    */
3215   private void buildWsScim() {
3216     
3217     File grouperWsScimSourcesDir =  new File(this.untarredWsDir.getAbsolutePath() + File.separator + "grouper-ws-scim");
3218     
3219     if (!grouperWsScimSourcesDir.exists() || grouperWsScimSourcesDir.isFile()) {
3220       throw new RuntimeException("Cant find grouper-ws-scim: " + grouperWsScimSourcesDir.getAbsolutePath());
3221     }
3222     
3223     File grouperWsScimBuildToDir = new File(grouperWsScimSourcesDir.getAbsolutePath() + File.separator + "target" + File.separator + "classes");
3224     
3225     boolean rebuildWsScim = true;
3226     
3227     if (grouperWsScimBuildToDir.exists()) {
3228       System.out.print("The Grouper WS Scim has been built in the past, do you want it rebuilt? (t|f) [t]: ");
3229       rebuildWsScim = readFromStdInBoolean(true, "grouperInstaller.autorun.rebuildWsScimAfterHavingBeenBuilt");
3230     }
3231     
3232     if (!rebuildWsScim) {
3233       return;
3234     }
3235     
3236     List<String> commands = new ArrayList<String>();
3237     
3238 //    \bin\mvn compile -DskipTests
3239     addMavenCommands(commands);
3240 
3241     //put 'compile -DskipTests' in there so it wont run tests which we dont want to do
3242     // dependency:copy-dependencies package -DskipTests
3243     //not compile
3244     commands.add("dependency:copy-dependencies");
3245     commands.add("package");
3246     commands.add("-DskipTests");
3247     commands.add("-Drat.ignoreErrors=true");
3248     commands.add("-Dlicense.skip=true");
3249     
3250     System.out.println("\n##################################");
3251     System.out.println("Building Grouper WS Scim with command:\n" + grouperWsScimSourcesDir.getAbsolutePath() + "> " 
3252         + convertCommandsIntoCommand(commands) + "\n");
3253     
3254     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
3255         true, true, null, new File(grouperWsScimSourcesDir.getAbsolutePath()), null, true);
3256     
3257     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
3258       System.out.println("stderr: " + commandResult.getErrorText());
3259     }
3260     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
3261       System.out.println("stdout: " + commandResult.getOutputText());
3262     }
3263 
3264     System.out.println("\nEnd building grouper-ws-scim");
3265     System.out.println("##################################\n");
3266     
3267   }
3268 
3269   /**
3270    * build grouper API
3271    * @param grouperApiDir
3272    */
3273   private void buildGrouperApi(File grouperApiDir) {
3274 
3275     if (!grouperApiDir.exists() || grouperApiDir.isFile()) {
3276       throw new RuntimeException("Cant find grouper api: " + grouperApiDir.getAbsolutePath());
3277     }
3278     
3279     File grouperBuildToDir = new File(grouperApiDir.getAbsolutePath() + File.separator + "dist" + File.separator + "build" 
3280         + File.separator + "grouper");
3281     
3282     boolean rebuildGrouperApi = true;
3283     
3284     if (grouperBuildToDir.exists()) {
3285       System.out.print("The Grouper API has been built in the past, do you want it rebuilt? (t|f) [t]: ");
3286       rebuildGrouperApi = readFromStdInBoolean(true, "grouperInstaller.autorun.rebuildGrouperApiAfterHavingBeenBuilt");
3287     }
3288     
3289     if (!rebuildGrouperApi) {
3290       return;
3291     }
3292     
3293     List<String> commands = new ArrayList<String>();
3294     
3295     addAntCommands(commands);
3296 
3297     //this will run tests which we dont want to do
3298     commands.add("dist");
3299     
3300     System.out.println("\n##################################");
3301     System.out.println("Building grouper API with command:\n" + grouperApiDir.getAbsolutePath() + "> " 
3302         + convertCommandsIntoCommand(commands) + "\n");
3303     
3304     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
3305         true, true, null, grouperApiDir, null, true);
3306     
3307     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
3308       System.out.println("stderr: " + commandResult.getErrorText());
3309     }
3310     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
3311       System.out.println("stdout: " + commandResult.getOutputText());
3312     }
3313 
3314     System.out.println("\nEnd building grouper API");
3315     System.out.println("##################################\n");
3316     
3317   }
3318   
3319 
3320 
3321   /**
3322    * build client API
3323    * @param clientDir
3324    */
3325   private void buildClient(File clientDir) {
3326     if (!clientDir.exists() || clientDir.isFile()) {
3327       throw new RuntimeException("Cant find client: " + clientDir.getAbsolutePath());
3328     }
3329     
3330     File clientBuildToDir = new File(clientDir.getAbsoluteFile() + File.separator + "dist" + File.separator + "bin");
3331     
3332     boolean rebuildClient = true;
3333     
3334     if (clientBuildToDir.exists()) {
3335       System.out.print("The Grouper client has been built in the past, do you want it rebuilt? (t|f) [t]: ");
3336       rebuildClient = readFromStdInBoolean(true, "grouperInstaller.autorun.rebuildClientAfterHavingBeenBuilt");
3337     }
3338     
3339     if (!rebuildClient) {
3340       return;
3341     }
3342 
3343     List<String> commands = new ArrayList<String>();
3344     
3345     addAntCommands(commands);
3346     
3347     System.out.println("\n##################################");
3348     System.out.println("Building client with command:\n" + clientDir.getAbsolutePath() + "> " 
3349         + convertCommandsIntoCommand(commands) + "\n");
3350     
3351     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
3352         true, true, null, clientDir, null, true);
3353     
3354     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
3355       System.out.println("stderr: " + commandResult.getErrorText());
3356     }
3357     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
3358       System.out.println("stdout: " + commandResult.getOutputText());
3359     }
3360 
3361     System.out.println("\nEnd building client");
3362     System.out.println("##################################\n");
3363     
3364   }
3365 
3366   /**
3367    * build client API
3368    * @param duoDir
3369    */
3370   private void buildDuo(File duoDir) {
3371     if (!duoDir.exists() || duoDir.isFile()) {
3372       throw new RuntimeException("Cant find duo: " + duoDir.getAbsolutePath());
3373     }
3374     
3375     File duoBuildToDir = new File(duoDir.getAbsoluteFile() + File.separator + "dist" + File.separator + "bin");
3376     
3377     boolean rebuildDuo = true;
3378     
3379     if (duoBuildToDir.exists()) {
3380       System.out.print("Grouper duo has been built in the past, do you want it rebuilt? (t|f) [t]: ");
3381       rebuildDuo = readFromStdInBoolean(true, "grouperInstaller.autorun.rebuildDuoAfterHavingBeenBuilt");
3382     }
3383     
3384     if (!rebuildDuo) {
3385       return;
3386     }
3387 
3388     List<String> commands = new ArrayList<String>();
3389     
3390     addAntCommands(commands);
3391     
3392     System.out.println("\n##################################");
3393     System.out.println("Building duo with command:\n" + duoDir.getAbsolutePath() + "> " 
3394         + convertCommandsIntoCommand(commands) + "\n");
3395     
3396     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
3397         true, true, null, duoDir, null, true);
3398     
3399     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
3400       System.out.println("stderr: " + commandResult.getErrorText());
3401     }
3402     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
3403       System.out.println("stdout: " + commandResult.getOutputText());
3404     }
3405 
3406     System.out.println("\nEnd building duo");
3407     System.out.println("##################################\n");
3408     
3409   }
3410 
3411   /**
3412    * build box API
3413    * @param boxDir
3414    */
3415   private void buildBox(File boxDir) {
3416     if (!boxDir.exists() || boxDir.isFile()) {
3417       throw new RuntimeException("Cant find box: " + boxDir.getAbsolutePath());
3418     }
3419     
3420     File duoBuildToDir = new File(boxDir.getAbsoluteFile() + File.separator + "dist" + File.separator + "bin");
3421     
3422     boolean rebuildBox = true;
3423     
3424     if (duoBuildToDir.exists()) {
3425       System.out.print("Grouper box has been built in the past, do you want it rebuilt? (t|f) [t]: ");
3426       rebuildBox = readFromStdInBoolean(true, "grouperInstaller.autorun.rebuildBoxAfterHavingBeenBuilt");
3427     }
3428     
3429     if (!rebuildBox) {
3430       return;
3431     }
3432 
3433     List<String> commands = new ArrayList<String>();
3434     
3435     addAntCommands(commands);
3436     
3437     System.out.println("\n##################################");
3438     System.out.println("Building box with command:\n" + boxDir.getAbsolutePath() + "> " 
3439         + convertCommandsIntoCommand(commands) + "\n");
3440     
3441     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
3442         true, true, null, boxDir, null, true);
3443     
3444     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
3445       System.out.println("stderr: " + commandResult.getErrorText());
3446     }
3447     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
3448       System.out.println("stdout: " + commandResult.getOutputText());
3449     }
3450 
3451     System.out.println("\nEnd building box");
3452     System.out.println("##################################\n");
3453     
3454   }
3455 
3456   /**
3457    * build client API
3458    * @param messagingRabbitMqDir
3459    */
3460   private void buildMessagingRabbitmq(File messagingRabbitMqDir) {
3461     if (!messagingRabbitMqDir.exists() || messagingRabbitMqDir.isFile()) {
3462       throw new RuntimeException("Cant find messaging rabbitmq: " + messagingRabbitMqDir.getAbsolutePath());
3463     }
3464     
3465     File messaginRabbitmqBuildToDir = new File(messagingRabbitMqDir.getAbsoluteFile() + File.separator + "dist" + File.separator + "bin");
3466     
3467     boolean rebuildMessagingRabbitmq = true;
3468     
3469     if (messaginRabbitmqBuildToDir.exists()) {
3470       System.out.print("Grouper messaging rabbitmq has been built in the past, do you want it rebuilt? (t|f) [t]: ");
3471       rebuildMessagingRabbitmq = readFromStdInBoolean(true, "grouperInstaller.autorun.rebuildMessagingRabbitmqAfterHavingBeenBuilt");
3472     }
3473     
3474     if (!rebuildMessagingRabbitmq) {
3475       return;
3476     }
3477 
3478     List<String> commands = new ArrayList<String>();
3479     
3480     addAntCommands(commands);
3481     
3482     System.out.println("\n##################################");
3483     System.out.println("Building messaging rabbitmq with command:\n" + messagingRabbitMqDir.getAbsolutePath() + "> " 
3484         + convertCommandsIntoCommand(commands) + "\n");
3485     
3486     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
3487         true, true, null, messagingRabbitMqDir, null, true);
3488     
3489     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
3490       System.out.println("stderr: " + commandResult.getErrorText());
3491     }
3492     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
3493       System.out.println("stdout: " + commandResult.getOutputText());
3494     }
3495 
3496     System.out.println("\nEnd building messaging rabbitmq");
3497     System.out.println("##################################\n");
3498     
3499   }
3500 
3501   /**
3502    * admin
3503    */
3504   private void mainAdminLogic() {
3505     
3506     this.version = GrouperInstallerUtils.propertiesValue("grouper.version", true);
3507 
3508     GrouperInstallerAdminAction grouperInstallerAdminAction = 
3509         (GrouperInstallerAdminAction)promptForEnum(
3510             "What admin action do you want to do (manage, upgradeTask, develop)? : ",
3511             "grouperInstaller.autorun.adminAction", GrouperInstallerAdminAction.class);
3512     
3513     switch(grouperInstallerAdminAction) {
3514       case manage:
3515         mainManageLogic();
3516         break;
3517 
3518       case develop:
3519         mainDevelopLogic();
3520         break;
3521 
3522       case upgradeTask:
3523         mainUpgradeTaskLogic();
3524         break;
3525     }
3526         
3527   }
3528 
3529   /**
3530    * admin manage
3531    */
3532   private void mainManageLogic() {
3533     
3534     //####################################
3535     //Find out what directory to install to.  This ends in a file separator
3536     this.grouperInstallDirectoryString = grouperInstallDirectory();
3537 
3538     GrouperInstallerManageAction grouperInstallerManageAction = null;
3539 
3540     while (true) {
3541       grouperInstallerManageAction = 
3542           (GrouperInstallerManageAction)promptForEnum(
3543               "What do you want to manage (logs, services, back, exit)? : ",
3544               "grouperInstaller.autorun.manageAction", GrouperInstallerManageAction.class);
3545 
3546       switch(grouperInstallerManageAction) {
3547         case logs:
3548       
3549           adminManageLogs();
3550   
3551           break;
3552         case services:
3553 
3554           adminManageServices();
3555           
3556           break;
3557         case exit:
3558           
3559           System.exit(0);
3560           
3561           break;
3562         case back:
3563           
3564           this.mainAdminLogic();
3565   
3566           break;
3567       }
3568       
3569       System.out.print("Press <enter> to continue or type 'exit' to end: ");
3570       String result = readFromStdIn("grouperInstaller.autorun.manageContinue");
3571       if (GrouperInstallerUtils.equalsIgnoreCase(result, "exit")) {
3572         System.exit(0);
3573       }
3574       //add some space
3575       System.out.println("");
3576     }
3577   }
3578 
3579   /**
3580    * admin manage
3581    */
3582   private void mainDevelopLogic() {
3583     
3584     GrouperInstallerDevelopAction grouperInstallerDevelopAction = null;
3585 
3586     while (true) {
3587       grouperInstallerDevelopAction = 
3588           (GrouperInstallerDevelopAction)promptForEnum(
3589               "What do you want to develop (translate, back, exit)? : ",
3590               "grouperInstaller.autorun.developAction", GrouperInstallerDevelopAction.class);
3591 
3592       switch(grouperInstallerDevelopAction) {
3593         case translate:
3594 
3595           adminTranslate();
3596 
3597           break;
3598         case exit:
3599 
3600           System.exit(0);
3601 
3602           break;
3603         case back:
3604           
3605           this.mainAdminLogic();
3606   
3607           break;
3608       }
3609       
3610       System.out.print("Press <enter> to continue or type 'exit' to end: ");
3611       String result = readFromStdIn("grouperInstaller.autorun.developContinue");
3612       if (GrouperInstallerUtils.equalsIgnoreCase(result, "exit")) {
3613         System.exit(0);
3614       }
3615       //add some space
3616       System.out.println("");
3617     }
3618   }
3619 
3620   /**
3621    * try 10 times to get enum
3622    * @param prompt
3623    * @param configKey
3624    * @param theClass
3625    * @return the object
3626    */
3627   public static Object promptForEnum(String prompt, String configKey, Class<?> theClass) {
3628     return promptForEnum(prompt, configKey, theClass, null, null);
3629   }
3630 
3631   /**
3632    * try 10 times to get enum
3633    * @param prompt
3634    * @param configKey
3635    * @param enumClass
3636    * @param theDefault
3637    * @param configKeyForDefault
3638    * @return the object
3639    */
3640   public static Object promptForEnum(String prompt, String configKey, Class<?> enumClass, Object theDefault, String configKeyForDefault) {
3641 
3642     //if we are using a config key
3643     if (!GrouperInstallerUtils.isBlank(configKeyForDefault)) {
3644       String defaultAction = GrouperInstallerUtils.propertiesValue(configKeyForDefault, false);
3645       if (!GrouperInstallerUtils.isBlank(defaultAction)) {
3646         theDefault = GrouperInstallerUtils.callMethod(enumClass, null, "valueOfIgnoreCase",
3647             new Class<?>[]{String.class, boolean.class, boolean.class}, new Object[]{defaultAction, true, true});
3648       }
3649       defaultAction = GrouperInstallerUtils.defaultIfBlank(defaultAction, "install");
3650     }
3651     if (theDefault != null) {
3652       prompt += "[" + ((Enum<?>)theDefault).name() + "]: ";
3653     }
3654     
3655     for (int i=0;i<10;i++) {
3656       System.out.print(prompt);
3657       String input = readFromStdIn(configKey);
3658       if (GrouperInstallerUtils.isBlank(input)) {
3659         if (theDefault != null) {
3660           return theDefault;
3661         }
3662         System.out.println("Input is required");
3663         continue;
3664       }
3665 
3666       //call a static method via reflection
3667       Object result = GrouperInstallerUtils.callMethod(enumClass, null, "valueOfIgnoreCase",
3668           new Class<?>[]{String.class, boolean.class, boolean.class}, new Object[]{input, false, false});
3669       if (result != null) {
3670         return result;
3671       } 
3672     }
3673     throw new RuntimeException("Cant find valid answer!!!!");
3674   }
3675   
3676   /**
3677    * 
3678    */
3679   private void adminManageServices() {
3680     
3681     //see what we are upgrading: api, ui, ws, client
3682     GrouperInstallerAdminManageService grouperInstallerAdminManageService = 
3683         (GrouperInstallerAdminManageService)promptForEnum(
3684             "What service do you want to manage?  database, tomcat, grouperDaemon? : ",
3685             "grouperInstaller.autorun.serviceToManage", GrouperInstallerAdminManageService.class);
3686 
3687     GrouperInstallerAdminManageServiceAction grouperInstallerAdminManageServiceAction = 
3688         (GrouperInstallerAdminManageServiceAction)promptForEnum(
3689             "What " + grouperInstallerAdminManageService + " action do you want to perform?  stop, start, restart, status? : ",
3690             "grouperInstaller.autorun.serviceToManageAction", GrouperInstallerAdminManageServiceAction.class);
3691 
3692     switch (grouperInstallerAdminManageService) {
3693       case grouperDaemon:
3694         adminManageGrouperDaemon(grouperInstallerAdminManageServiceAction);
3695         
3696         break;
3697       case database:
3698 
3699         adminManageDatabase(grouperInstallerAdminManageServiceAction);
3700         break;
3701        case tomcat:
3702 
3703         adminManageTomcat(grouperInstallerAdminManageServiceAction);
3704         
3705 
3706         break;
3707 
3708     }
3709     
3710   }
3711 
3712   /**
3713    * translate a ui text file
3714    */
3715   private void adminTranslate() {
3716 
3717     System.out.println("What is the location of the grouper.text.en.us.base.properties file: ");
3718     String grouperTextEnUsBasePropertiesName = readFromStdIn("grouperInstaller.autorun.translate.from");
3719 
3720     if (GrouperInstallerUtils.isBlank(grouperTextEnUsBasePropertiesName)) {
3721       System.out.println("The location of the grouper.text.en.us.base.properties file is required!");
3722       System.exit(1);
3723     }
3724 
3725     File grouperTextEnUsBasePropertiesFile = new File(grouperTextEnUsBasePropertiesName);
3726 
3727     if (grouperTextEnUsBasePropertiesFile.isDirectory()) {
3728       grouperTextEnUsBasePropertiesName = GrouperInstallerUtils.stripLastSlashIfExists(grouperTextEnUsBasePropertiesName);
3729       grouperTextEnUsBasePropertiesName = grouperTextEnUsBasePropertiesName + File.separator + "grouper.text.en.us.base.properties";
3730       grouperTextEnUsBasePropertiesFile = new File(grouperTextEnUsBasePropertiesName);
3731     }
3732 
3733     if (!grouperTextEnUsBasePropertiesFile.isFile() || !grouperTextEnUsBasePropertiesFile.exists()) {
3734       System.out.println("The grouper.text.en.us.base.properties file is not found! " + grouperTextEnUsBasePropertiesFile.getAbsolutePath());
3735       System.exit(1);
3736     }
3737     
3738     System.out.println("What is the location of the translated file: ");
3739     String grouperTranslatedBasePropertiesName = readFromStdIn("grouperInstaller.autorun.translate.to");
3740 
3741     if (GrouperInstallerUtils.isBlank(grouperTextEnUsBasePropertiesName)) {
3742       System.out.println("The location of the translated file is required!");
3743       System.exit(0);
3744     }
3745 
3746     File grouperTranslatedBasePropertiesFile = new File(grouperTranslatedBasePropertiesName);
3747 
3748     if (!grouperTranslatedBasePropertiesFile.isFile() || !grouperTranslatedBasePropertiesFile.exists()) {
3749       System.out.println("The translated file is not found! " + grouperTextEnUsBasePropertiesFile.getAbsolutePath());
3750       System.exit(0);
3751     }
3752     
3753     //backup the existing file
3754     File grouperTranslatedBasePropertiesFileBak = new File(GrouperInstallerUtils.prefixOrSuffix(
3755         grouperTranslatedBasePropertiesFile.getAbsolutePath(), ".properties", true) + "." 
3756         + new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS").format(new Date()) + ".properties");
3757     
3758     // GrouperInstallerUtils.newlineFromFile(GrouperInstallerUtils.readFileIntoString(grouperTranslatedBasePropertiesFile));
3759     String newline = "\n";
3760     
3761     GrouperInstallerUtils.copyFile(grouperTranslatedBasePropertiesFile, grouperTranslatedBasePropertiesFileBak);
3762     System.out.println("The translated file was backed up to: " + grouperTranslatedBasePropertiesFileBak.getAbsolutePath());
3763     
3764     System.out.print("Do you want to edit this file inline (if not will just run a report) (t|f) [t]: ");
3765     boolean editInline = readFromStdInBoolean(true, "grouperInstaller.translate.editInline");
3766    
3767     StringBuilder output = new StringBuilder();
3768     
3769     String grouperTextEnUsBasePropertiesContents = GrouperInstallerUtils.readFileIntoString(grouperTextEnUsBasePropertiesFile);
3770     String grouperTranslatedBasePropertiesContents = GrouperInstallerUtils.readFileIntoString(grouperTranslatedBasePropertiesFile);
3771 
3772     //go through the original properties line by line
3773     String[] grouperTextEnUsBasePropertiesLines = GrouperInstallerUtils.splitLines(grouperTextEnUsBasePropertiesContents);
3774     String[] grouperTranslatedBasePropertiesLines = GrouperInstallerUtils.splitLines(grouperTranslatedBasePropertiesContents);
3775     Properties existingTranslatedLinesByKey = new Properties();
3776 
3777     //make raw properties
3778     for (String grouperTranslatedBasePropertiesLine : grouperTranslatedBasePropertiesLines) {
3779       int equalsIndex = grouperTranslatedBasePropertiesLine.indexOf('=');
3780       if (equalsIndex != -1) {
3781         String propertyName = GrouperInstallerUtils.prefixOrSuffix(grouperTranslatedBasePropertiesLine, "=", true).trim();
3782         String propertyValue = GrouperInstallerUtils.prefixOrSuffix(grouperTranslatedBasePropertiesLine, "=", false).trim();
3783         if (!GrouperInstallerUtils.isBlank(propertyValue)) {
3784           existingTranslatedLinesByKey.put(propertyName, grouperTranslatedBasePropertiesLine);
3785         }
3786       }
3787     }
3788 
3789     StringBuilder propertyAndComments = new StringBuilder();
3790     int diffCount = 0;
3791 
3792     int lineCount = 1;
3793     
3794     for (String grouperTextEnUsBasePropertiesLine: grouperTextEnUsBasePropertiesLines) {
3795       
3796       Map<String, Object> debugMap = new LinkedHashMap<String, Object>();
3797       
3798       grouperTextEnUsBasePropertiesLine = grouperTextEnUsBasePropertiesLine.trim();
3799       
3800       boolean isBlank = GrouperInstallerUtils.isBlank(grouperTextEnUsBasePropertiesLine);
3801       boolean isComment = grouperTextEnUsBasePropertiesLine.trim().startsWith("#");
3802       boolean isProperty = !isBlank && !isComment && grouperTextEnUsBasePropertiesLine.contains("=");
3803       
3804       if (!isBlank && !isComment && !isProperty) {
3805         System.out.print("Line " + lineCount + " is not a blank, comment, or property, hit <enter> to continue");
3806         readFromStdIn("grouperInstaller.autorun.translateIssueContinue");
3807       }
3808       
3809       debugMap.put("isBlank", isBlank);
3810       debugMap.put("isComment", isComment);
3811       debugMap.put("isProperty", isProperty);
3812       
3813       propertyAndComments.append(newline).append(grouperTextEnUsBasePropertiesLine);
3814 
3815       if (!isProperty) {
3816         output.append(grouperTextEnUsBasePropertiesLine).append(newline);
3817         debugMap.put("clearPropertyAndComments", false);
3818       } else {
3819         int equalsIndex = grouperTextEnUsBasePropertiesLine.indexOf('=');
3820         if (equalsIndex == -1) {
3821           //shouldnt happen
3822           throw new RuntimeException("Coding error: " + grouperTextEnUsBasePropertiesLine);
3823         }
3824         
3825         String propertyName = grouperTextEnUsBasePropertiesLine.substring(0, equalsIndex).trim();
3826 
3827         debugMap.put("propertyName", propertyName);
3828 
3829         String translatedPropertyLine = existingTranslatedLinesByKey.getProperty(propertyName);
3830         
3831         debugMap.put("hasTranslation", !GrouperInstallerUtils.isBlank(translatedPropertyLine));
3832 
3833         // see if there is already a translation
3834         if (!GrouperInstallerUtils.isBlank(translatedPropertyLine)) {
3835  
3836           //just append everything to the new file
3837           output.append(translatedPropertyLine).append(newline);
3838           
3839         } else {
3840           diffCount++;
3841 
3842           //there is no translation
3843           if (!editInline) {
3844             System.out.println(diffCount + ": Translate line " + lineCount + ":");
3845           }
3846 
3847           System.out.println("");
3848           System.out.println(propertyAndComments.toString().trim() + newline);
3849           
3850           //there is no translation
3851           if (editInline) {
3852             System.out.print("\n" + diffCount + ": Enter a translation for line " + lineCount + ":");
3853             String translatedValue = readFromStdIn("autorun.translate.value");
3854             
3855             output.append(propertyName).append("=").append(translatedValue).append(newline);
3856 
3857           } else {
3858             
3859             output.append(propertyName).append("=").append(newline);
3860             
3861           }
3862           
3863         }
3864         debugMap.put("clearPropertyAndComments", true);
3865         propertyAndComments = new StringBuilder();
3866         
3867       }
3868       
3869       if (GrouperInstallerUtils.propertiesValueBoolean("printDebugInfo", false, false)) {
3870         System.out.println(GrouperInstallerUtils.mapToString(debugMap));
3871       }
3872       
3873       lineCount++;
3874     }
3875     GrouperInstallerUtils.saveStringIntoFile(grouperTranslatedBasePropertiesFile, output.toString(), true, true);
3876     
3877     if (diffCount == 0) {
3878       System.out.println("The translated file is complete");
3879     } else {
3880       if (!editInline) {
3881         System.out.println("You have " + diffCount + " missing properties, they need translation.");
3882       } else {
3883         System.out.println("You translated " + diffCount + " missing properties.");
3884       }
3885     }
3886     System.exit(0);
3887   }
3888 
3889   /**
3890    * 
3891    * @param grouperInstallerAdminManageServiceAction
3892    */
3893   private void adminManageTomcat(
3894       GrouperInstallerAdminManageServiceAction grouperInstallerAdminManageServiceAction) {
3895     //tomcat dir
3896     File catalinaServerXmlFile = new File(this.grouperInstallDirectoryString + "conf" + File.separator + "server.xml");
3897     if (!catalinaServerXmlFile.exists()) {
3898       //if used the webapps dir
3899       catalinaServerXmlFile = new File(this.grouperInstallDirectoryString + ".." + File.separator + ".." + File.separator + "conf" + File.separator + "server.xml");
3900     }
3901     //normal installer dir
3902     if (!catalinaServerXmlFile.exists()) {
3903       catalinaServerXmlFile = new File(this.grouperInstallDirectoryString + File.separator 
3904           + "apache-tomcat-" + this.tomcatVersion() + "" + File.separator + "conf" + File.separator + "server.xml");
3905     }
3906 
3907     this.untarredTomcatDir = catalinaServerXmlFile.getParentFile().getParentFile();       
3908 
3909     //  /Server/Service/Connector <Connector port="8080" protocol="HTTP/1.1" 
3910     this.tomcatHttpPort = GrouperInstallerUtils.xpathEvaluateAttributeInt(catalinaServerXmlFile, "/Server/Service/Connector[@protocol='HTTP/1.1']", "port", -1);
3911 
3912     System.out.print("Enter the default IP address for checking ports (just hit enter to accept the default unless on a machine with no network, might want to change to 127.0.0.1): [0.0.0.0]: ");
3913     this.defaultIpAddress = readFromStdIn("grouperInstaller.autorun.defaultIpAddressForPorts");
3914     
3915     if (GrouperInstallerUtils.isBlank(this.defaultIpAddress)) {
3916       this.defaultIpAddress = "0.0.0.0";
3917     }
3918 
3919     switch (grouperInstallerAdminManageServiceAction) {
3920       case stop:
3921       case start:
3922       case restart:
3923         
3924         tomcatBounce(grouperInstallerAdminManageServiceAction.name().toString());
3925         break;
3926       case status:
3927         
3928         if (!GrouperInstallerUtils.portAvailable(this.tomcatHttpPort, this.defaultIpAddress)) {
3929           System.out.println("Tomcat is running.  It is detected to be listening on port: " + this.tomcatHttpPort);
3930         } else {
3931           System.out.println("Tomcat is stopped.  It is not detected to be listening on port: " + this.tomcatHttpPort);
3932         }
3933         break;
3934     }
3935   }
3936 
3937   /**
3938    * 
3939    * @param grouperInstallerAdminManageServiceAction
3940    */
3941   private void adminManageDatabase(
3942       GrouperInstallerAdminManageServiceAction grouperInstallerAdminManageServiceAction) {
3943     List<File> grouperHibernatePropertiesFiles = GrouperInstallerUtils.fileListRecursive(new File(this.grouperInstallDirectoryString), "grouper.hibernate.properties");
3944     
3945     if (GrouperInstallerUtils.length(grouperHibernatePropertiesFiles) == 0) {
3946       System.out.println("Cant find a grouper.hibernate.properties in the install directory: " + this.grouperInstallDirectoryString);
3947     }
3948 
3949     //lets see which one
3950     File grouperHibernatePropertiesFileLocal = null;
3951     String url = null;
3952     
3953     for (File file : grouperHibernatePropertiesFiles) {
3954       Properties grouperHibernateProperties = GrouperInstallerUtils.propertiesFromFile(file);
3955       String urlFromFile = grouperHibernateProperties.getProperty("hibernate.connection.url");
3956 
3957       if (url == null) {
3958         grouperHibernatePropertiesFileLocal = file;
3959         url = urlFromFile;
3960       }
3961       if (!GrouperInstallerUtils.equals(url, urlFromFile)) {
3962         System.out.println("You have " + grouperHibernatePropertiesFiles.size() 
3963           + " grouper.hibernate.properties files in the install directory "
3964           + this.grouperInstallDirectoryString + " with different urls: " + url + ", " + urlFromFile
3965           + ", sync up your config files or specify an install directory that has one grouper.hibernate.properties"); 
3966         for (File current : grouperHibernatePropertiesFiles) {
3967           System.out.println("\n  " + current.getAbsolutePath());
3968         }
3969       }
3970     }
3971     
3972     Properties grouperHibernateProperties = GrouperInstallerUtils.propertiesFromFile(grouperHibernatePropertiesFileLocal);
3973 
3974     this.dbUrl = url;
3975     this.dbUser = GrouperInstallerUtils.defaultString(grouperHibernateProperties.getProperty("hibernate.connection.username"));
3976     this.dbPass = GrouperInstallerUtils.defaultString(grouperHibernateProperties.getProperty("hibernate.connection.password"));
3977     this.giDbUtils = new GiDbUtils(this.dbUrl, this.dbUser, this.dbPass);
3978     this.giDbUtils.registerDriverOnce(this.grouperInstallDirectoryString);
3979     
3980     System.out.println("grouper.hibernate.properties read from: " + grouperHibernatePropertiesFileLocal.getAbsolutePath());
3981     System.out.println("Database URL (hibernate.connection.url from grouper.hibernate.properties) is: " + this.dbUrl);
3982     
3983     if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.status) {
3984       System.out.println("Trying query: " + this.giDbUtils.checkConnectionQuery());
3985       //check connection to database
3986       Exception exception = this.giDbUtils.checkConnection();
3987       if (exception == null) {
3988         System.out.println("Database is up and connection from Java successful.");
3989       } else {
3990         System.out.print("Database could not be connected to from Java.  Perhaps it is down or there is a network problem?\n"
3991             + "  Do you want to see the stacktrace from the connection error? (t|f) [f]: ");
3992         boolean showStack = readFromStdInBoolean(false, "grouperInstaller.autorun.printStackFromDbConnectionError");
3993         if (showStack) {
3994           exception.printStackTrace();
3995         }
3996       }
3997     } else {          
3998       
3999       System.out.println("Error: you are using an external database, (URL above), you need to " + grouperInstallerAdminManageServiceAction + " that database yourself");
4000         
4001     }
4002   }
4003 
4004   /**
4005    * 
4006    * @param grouperInstallerAdminManageServiceAction
4007    */
4008   private void adminManageGrouperDaemon(
4009       GrouperInstallerAdminManageServiceAction grouperInstallerAdminManageServiceAction) {
4010     boolean done = false;
4011     if (!GrouperInstallerUtils.isWindows()) {
4012       
4013       System.out.println("In unix you should have a /etc/init.d or launchctl script which manages the grouper daemon (see details on wiki).");
4014       System.out.print("If you have a service configured please enter name or <enter> to continue without a service: ");
4015       String daemonName = readFromStdIn("grouperInstaller.autorun.grouperDaemonNameOrContinue");
4016       if (!GrouperInstallerUtils.isBlank(daemonName)) {
4017         done = true;
4018         boolean isService = true;
4019         String command = "/sbin/service";
4020         if (!new File(command).exists()) {
4021           command = "/usr/sbin/service";
4022         }
4023         if (!new File(command).exists()) {
4024           command = "/bin/launchctl";
4025           isService = false;
4026         }
4027         if (!new File(command).exists()) {
4028           System.out.println("Cannot find servie command, looked for /sbin/service, /usr/sbin/service, and /bin/launchctl.  "
4029               + "Your version of unix services is not supported.  Contact the Grouper support team.");
4030           System.exit(1);
4031         }
4032         if (isService) {
4033           List<String> commands = new ArrayList<String>();
4034           commands.add(command);
4035           commands.add(daemonName);
4036           commands.add(grouperInstallerAdminManageServiceAction.name());
4037           
4038           System.out.println(grouperInstallerAdminManageServiceAction + " " + daemonName
4039               + " with command: " + convertCommandsIntoCommand(commands) + "\n");
4040 
4041           GrouperInstallerUtils.execCommand(
4042               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
4043               new File(this.grouperInstallDirectoryString), null, false, false, true);
4044         } else {
4045           // <pid> <status> mytask
4046           if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.status) {
4047             // launchctl list | grep mytask
4048             List<String> commandsToRun = new ArrayList<String>();
4049             commandsToRun.add(shCommand());
4050             commandsToRun.add("-c");
4051             commandsToRun.add(command + " list | " + grepCommand() + " " + daemonName);
4052             
4053             System.out.println(grouperInstallerAdminManageServiceAction + " " + daemonName
4054                 + " with command: " + convertCommandsIntoCommand(commandsToRun) + "\n");
4055 
4056             GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commandsToRun, String.class), true, true, null, 
4057                 new File(this.grouperInstallDirectoryString), null, false, false, true);
4058             
4059           } else {
4060             // launchctl start|stop mytask
4061             if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.stop 
4062                 || grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
4063               //stop daemon
4064               List<String> commands = new ArrayList<String>();
4065               commands.add(command);
4066               commands.add("stop");
4067               commands.add(daemonName);
4068               
4069               System.out.println("stopping " + daemonName
4070                   + " with command: " + convertCommandsIntoCommand(commands) + "\n");
4071 
4072               GrouperInstallerUtils.execCommand(
4073                   GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
4074                   new File(this.grouperInstallDirectoryString), null, false, false, true);
4075               
4076             }
4077  
4078             if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
4079               GrouperInstallerUtils.sleep(3000);
4080             }
4081  
4082             if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.start 
4083                 || grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
4084               //start daemon
4085               List<String> commands = new ArrayList<String>();
4086               commands.add(command);
4087               commands.add("start");
4088               commands.add(daemonName);
4089               
4090               System.out.println("starting " + daemonName
4091                   + " with command: " + convertCommandsIntoCommand(commands) + "\n");
4092 
4093               GrouperInstallerUtils.execCommand(
4094                   GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
4095                   new File(this.grouperInstallDirectoryString), null, false, false, true);
4096               
4097               GrouperInstallerUtils.sleep(5000);
4098             }
4099           }              
4100         }
4101       }
4102     }
4103 
4104     if (!done) {
4105       
4106       if (new File(this.grouperInstallDirectoryString + "grouper.apiBinary-" + this.version).exists()) {
4107         this.untarredApiDir = new File(this.grouperInstallDirectoryString + "grouper.apiBinary-" + this.version);
4108       }
4109       
4110       //this is for loader dir
4111       if (new File(this.grouperInstallDirectoryString + "WEB-INF").exists()) {
4112         this.upgradeExistingApplicationDirectoryString = this.grouperInstallDirectoryString;
4113       } else if (new File(this.grouperInstallDirectoryString + "bin").exists()) {
4114         this.upgradeExistingApplicationDirectoryString = this.grouperInstallDirectoryString;
4115       }
4116       
4117       String gshCommandLocal = gshCommand();
4118       if (gshCommandLocal.endsWith(".sh")) {
4119         gshCommandLocal = gshCommandLocal.substring(0, gshCommandLocal.length()-".sh".length());
4120       }
4121       if (gshCommandLocal.endsWith(".bat")) {
4122         gshCommandLocal = gshCommandLocal.substring(0, gshCommandLocal.length()-".bat".length());
4123       }
4124       
4125       if (!GrouperInstallerUtils.isWindows()) {
4126         if (gshCommandLocal.contains(" ")) {
4127           System.out.println("On unix the gsh command cannot contain whitespace!");
4128           System.exit(1);
4129         }
4130       }
4131       
4132       // ps -ef | grep -- -loader | grep -v grep
4133       List<String> psCommands = new ArrayList<String>();
4134       psCommands.add(shCommand());
4135       psCommands.add("-c");
4136       psCommands.add( psCommand() + " -ef | " + grepCommand() + " " + gshCommandLocal + " | " 
4137           + grepCommand() + " -- -loader | " + grepCommand() + " -v grep");
4138 
4139       //unix
4140       //appadmin 14477     1  0 01:24 pts/0    00:00:00 /bin/sh /opt/tomcats/tomcat_d_gsh/webapps/grouper_v2_2/WEB-INF/bin/gsh -loader
4141       //appadmin 14478 14477 92 01:24 pts/0    00:00:03 /opt/java6/bin/java -Xms64m -Xmx750m -Dgrouper.home=/opt/tomcats/tomcat_d_gsh/webapps/grouper_v2_2/WEB-INF/bin/../ -classpath /opt/tomcats/tomcat_d_gsh/webapps/grouper_v2_2/WEB-INF/bin/../classes:/opt/tomcats/tomcat_d_gsh/webapps/grouper_v2_2/WEB-INF/bin/../lib/*: edu.internet2.middleware.grouper.app.gsh.GrouperShellWrapper -loader
4142       
4143       //mac
4144       //0     1     0   0 Sun06PM ??         1:15.38 /sbin/launchd
4145       //0    45     1   0 Sun06PM ??         0:06.80 /usr/sbin/syslogd
4146       
4147       Pattern pidPattern = Pattern.compile("^[^\\s]+\\s+([^\\s]+)\\s+.*$");
4148       
4149       if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.stop 
4150           || grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
4151 
4152         if (GrouperInstallerUtils.isWindows()) {
4153           System.out.print("In windows you need to find the java process in task manager and kill it, press <enter> to continue... ");
4154           readFromStdIn("grouperInstaller.autorun.enterToContinueWindowsCantKillProcess");
4155         } else {
4156 
4157           System.out.println("Stopping the grouper daemon is not an exact science, be careful!");
4158           System.out.println("This script will find the process id of the daemon and kill it.  Make it is correct!");
4159           System.out.println("Finding the grouper daemon process with: " + convertCommandsIntoCommand(psCommands));
4160 
4161           //stop daemon
4162           CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(psCommands, String.class), false, true, null, 
4163              new File(this.grouperInstallDirectoryString), null, false, false, true);
4164           if (commandResult.getExitCode() != 0 && !GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
4165             throw new RuntimeException("Could not execute: " + convertCommandsIntoCommand(psCommands) 
4166                + "\n" + commandResult.getErrorText()
4167                + "\n" + commandResult.getOutputText());
4168           }
4169           String outputText = GrouperInstallerUtils.defaultString(commandResult.getOutputText());
4170           if (GrouperInstallerUtils.isBlank(outputText)) {
4171             System.out.println("Cannot find the grouper daemon process, it is not running");
4172           } else {
4173             outputText = outputText.replace('\r', '\n');
4174             outputText = GrouperInstallerUtils.replace(outputText, "\r", "\n");
4175             List<String> lines = GrouperInstallerUtils.splitTrimToList(outputText, "\n");
4176             int MAX_LINES = 2;
4177             if (lines.size() > MAX_LINES) {
4178               System.out.println("Found more output than expected, please examine the services on your system and kill them manually");
4179               for (String line : lines) {
4180                 System.out.println(line);
4181               }
4182             } else {
4183               Set<String> pidsDone = new HashSet<String>();
4184               for (int i=0; i<MAX_LINES; i++) {
4185                 // ^[^\s]+\s+([^\s]+)\s+.*$
4186                 // start, then first thing, then spaces, then second thing is the pic, then spaces, then whatever and end string
4187                 Matcher matcher = pidPattern.matcher(lines.get(0));
4188                 if (matcher.matches()) {
4189                   String pid = matcher.group(1);
4190                   if (pidsDone.contains(pid)) {
4191                     System.out.println("Could not kill pid " + pid);
4192                     System.exit(1);
4193                   }
4194                   List<String> killCommandList = GrouperInstallerUtils.splitTrimToList(killCommand() + " -KILL " + pid, " ");
4195                   System.out.println("The command to kill the daemon is: " + convertCommandsIntoCommand(killCommandList));
4196                   System.out.print("Found pid " + pid + ", do you want this script to kill it? (t|f) [t]: ");
4197                   boolean killDaemon = readFromStdInBoolean(true, "grouperInstaller.autorun.killPidOfDaemon");
4198                   
4199                   if (killDaemon) {
4200 
4201                     //keep track that we tried this one
4202                     pidsDone.add(pid);
4203                     
4204                     commandResult = GrouperInstallerUtils.execCommand(
4205                         GrouperInstallerUtils.toArray(killCommandList, String.class), true, true, null, 
4206                        null, null, true, false, true);
4207                     
4208                     GrouperInstallerUtils.sleep(5000);
4209 
4210                     //get next line, hopefully first one isnt there anymore, maybe not second either...
4211                     commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(psCommands, String.class), false, true, null, 
4212                        null, null, false, false, true);
4213 
4214                     if (commandResult.getExitCode() != 0 && !GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
4215                       throw new RuntimeException("Could not execute: " + convertCommandsIntoCommand(psCommands) 
4216                          + "\n" + commandResult.getErrorText()
4217                          + "\n" + commandResult.getOutputText());
4218                     }
4219                     
4220                     outputText = GrouperInstallerUtils.defaultString(commandResult.getOutputText());
4221                     if (GrouperInstallerUtils.isBlank(outputText)) {
4222                       break;
4223                     }
4224                     
4225                     outputText = outputText.replace('\r', '\n');
4226                     outputText = GrouperInstallerUtils.replace(outputText, "\r", "\n");
4227                     lines = GrouperInstallerUtils.splitTrimToList(outputText, "\n");
4228                     
4229                   } else {
4230                     break;
4231                   }
4232                 }
4233               }
4234             }
4235           }
4236         }
4237       }
4238 
4239       if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
4240         GrouperInstallerUtils.sleep(3000);
4241       }
4242 
4243       if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.start 
4244           || grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
4245         //start
4246         startLoader(false);
4247         GrouperInstallerUtils.sleep(5000);
4248       }
4249 
4250       if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.status && GrouperInstallerUtils.isWindows()) {
4251         System.out.println("Cant get status of loader when running on Windows.  Look in your task manager for a java process (difficult to tell which one).");
4252       } else {
4253                    
4254         //no matter what, status, or other, do a status
4255         if (!GrouperInstallerUtils.isWindows()) {
4256           //stop daemon
4257           System.out.println("Finding the grouper daemon process with: " + convertCommandsIntoCommand(psCommands));
4258           CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(psCommands, String.class), false, true, null, 
4259              null, null, false, false, true);
4260           if (commandResult.getExitCode() != 0 && !GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
4261             throw new RuntimeException("Could not execute: " + convertCommandsIntoCommand(psCommands) 
4262                + "\n" + commandResult.getErrorText()
4263                + "\n" + commandResult.getOutputText());
4264           }
4265           String outputText = GrouperInstallerUtils.defaultString(commandResult.getOutputText());
4266           if (GrouperInstallerUtils.isBlank(outputText)) {
4267             System.out.println("Cannot find the grouper daemon process, it is not running");
4268           } else {
4269             outputText = outputText.replace('\r', '\n');
4270             outputText = GrouperInstallerUtils.replace(outputText, "\r", "\n");
4271             List<String> lines = GrouperInstallerUtils.splitTrimToList(outputText, "\n");
4272             System.out.println("Grouper loader is running, here is the process output:");
4273             for (String line : lines) {
4274               System.out.println(line);
4275             }
4276           }
4277         }
4278       }
4279     }
4280   }
4281 
4282   /**
4283    * admin manage logs
4284    */
4285   private void adminManageLogs() {
4286     //see what we are upgrading: api, ui, ws, client
4287     this.appToUpgrade = grouperAppToUpgradeOrPatch("look at logs for");
4288 
4289     System.out.println("Find where the application is running, then find the log4j.properties in the classpath.");
4290     
4291     switch (this.appToUpgrade) {
4292       case PSP:
4293       case PSPNG:
4294         System.out.println("This runs in the API, so logging for the API will be examined.");
4295         //pass through to API
4296       case API:
4297         System.out.println("The API (generally invoked via GSH) logs to where the log4.properties specifies.");
4298         File log4jPropertiesFile = new File(this.grouperInstallDirectoryString + "grouper.apiBinary-" + this.version + File.separator 
4299             + "conf" + File.separator + "log4j.properties");
4300         
4301         if (!log4jPropertiesFile.exists()) {
4302           
4303           List<File> allFiles = GrouperInstallerUtils.fileListRecursive(new File(this.grouperInstallDirectoryString));
4304           log4jPropertiesFile = null;
4305           boolean multipleFound = false;
4306           for (File file : allFiles) {
4307             if ("log4j.properties".equals(file.getName())) {
4308               if (log4jPropertiesFile != null) {
4309                 multipleFound = true;
4310                 log4jPropertiesFile = null;
4311                 break;
4312               }
4313               log4jPropertiesFile = file;
4314             }
4315           }
4316           if (multipleFound || log4jPropertiesFile == null) {
4317             System.out.print("What is the absolute path of the log4j.properties? : ");
4318             String log4jPropertiesLocation = readFromStdIn("grouperInstaller.autorun.log4jPropertiesLocation");
4319             log4jPropertiesFile = new File(log4jPropertiesLocation);
4320             if (!log4jPropertiesFile.exists()) {
4321               System.out.println("Bad location: " + log4jPropertiesFile.getAbsolutePath());
4322               System.exit(1);
4323             }
4324           }
4325         }
4326         
4327         File logFile = new File(this.grouperInstallDirectoryString  
4328             + "logs" + File.separator + "grouper_error.log");
4329         String grouperHomeWithSlash = this.grouperInstallDirectoryString;
4330 
4331         if (!logFile.exists()) {
4332           logFile = new File(this.grouperInstallDirectoryString + "grouper.apiBinary-" + this.version + File.separator 
4333               + "logs" + File.separator + "grouper_error.log");
4334           grouperHomeWithSlash = this.grouperInstallDirectoryString + "grouper.apiBinary-" + this.version + File.separator;
4335         }              
4336         System.out.println("By default the installer configures the log file to be: " + logFile.getAbsolutePath());
4337         
4338         
4339         analyzeLogFile(log4jPropertiesFile, grouperHomeWithSlash, null, null);
4340         break;
4341       case CLIENT:
4342         System.out.println("The client generally logs to STDOUT.  Check the grouper.client.properties or if there is a log4j.properties in the clients classpath.");
4343         break;
4344       case WS:
4345       case UI:
4346         File catalinaLogFile = new File(this.grouperInstallDirectoryString + "logs");
4347         if (!catalinaLogFile.exists()) {
4348           //if used the webapps dir
4349           catalinaLogFile = new File(this.grouperInstallDirectoryString + ".." + File.separator + ".." + File.separator + "logs");
4350         }
4351         if (!catalinaLogFile.exists()) {
4352           catalinaLogFile = new File(this.grouperInstallDirectoryString + File.separator 
4353               + "apache-tomcat-" + this.tomcatVersion() + "" + File.separator + "logs");
4354         }
4355         
4356         System.out.println("Tomcat logs STDOUT and STDERR to the catalinaErr.log "
4357             + "and catalinaOut.log logfiles, which should be here: " + catalinaLogFile.getAbsolutePath());
4358         if (!catalinaLogFile.exists()) {
4359           System.out.println("Warning: that directory does not exist, so you will need to locate the logs directory for tomcat.");
4360         }
4361         System.out.println("Locate the " + this.appToUpgrade + " application files.");
4362         System.out.println("By default the installer has the " + this.appToUpgrade + " running based on the tomcat server.xml, "
4363             + "but could also run in the webapps dir.");
4364         
4365         File serverXmlFile = new File(catalinaLogFile.getParentFile().getAbsolutePath() + File.separator + "conf" + File.separator + "server.xml");
4366         
4367         if (!serverXmlFile.exists()) {
4368           System.out.println("server.xml not found: " + serverXmlFile.getAbsolutePath());
4369         } else {
4370           //<Context docBase="/Users/mchyzer/tmp/grouperInstaller/grouper.ws-2.3.0/grouper-ws/build/dist/grouper-ws" path="/grouper-ws" reloadable="false"/>
4371           //<Context docBase="/Users/mchyzer/tmp/grouperInstaller/grouper.ui-2.3.0/dist/grouper" path="/grouper" reloadable="false"/>
4372 
4373           System.out.println("The server.xml is located: " + serverXmlFile.getAbsolutePath());
4374 
4375           String tomcatPath = this.appToUpgrade == AppToUpgrade.UI ? "grouper" : "grouper-ws";
4376 
4377           System.out.print("What is the URL starting path? [" + tomcatPath + "]: ");
4378           String newTomcatPath = readFromStdIn(this.appToUpgrade == AppToUpgrade.UI ? "grouperInstaller.autorun.urlPathForUi" : "grouperInstaller.autorun.urlPathForWs");
4379           
4380           if (!GrouperInstallerUtils.isBlank(newTomcatPath)) {
4381             tomcatPath = newTomcatPath;
4382           }
4383 
4384           if (tomcatPath.endsWith("/") || tomcatPath.endsWith("\\")) {
4385             tomcatPath = tomcatPath.substring(0, tomcatPath.length()-1);
4386           }
4387           if (tomcatPath.startsWith("/") || tomcatPath.startsWith("\\")) {
4388             tomcatPath = tomcatPath.substring(1, tomcatPath.length());
4389           }                  
4390           String currentDocBase = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
4391               "Server/Service/Engine/Host/Context[@path='/" + tomcatPath + "']", "docBase");
4392 
4393           if (this.appToUpgrade == AppToUpgrade.UI) {
4394             //<Context docBase="/Users/mchyzer/tmp/grouperInstaller/grouper.ui-2.3.0/dist/grouper" path="/grouper" reloadable="false"/>
4395             System.out.println("Looking for an entry in the server.xml that looks like this:");
4396             System.out.println("  <Context docBase=\""
4397                 + GrouperInstallerUtils.defaultIfBlank(currentDocBase, this.grouperInstallDirectoryString 
4398                     + "grouper.ui-" + this.version + File.separator + "dist" + File.separator 
4399                     + "grouper")
4400                 + "\" path=\"/" + tomcatPath + "\" reloadable=\"false\"/>");
4401 
4402           } else if (this.appToUpgrade == AppToUpgrade.WS) {
4403             //<Context docBase="/Users/mchyzer/tmp/grouperInstaller/grouper.ws-2.3.0/grouper-ws/build/dist/grouper-ws" path="/grouper-ws" reloadable="false"/>
4404             System.out.println("Looking for an entry in the server.xml that looks like this:");
4405             System.out.println("  <Context docBase=\""
4406                 + GrouperInstallerUtils.defaultIfBlank(currentDocBase, this.grouperInstallDirectoryString 
4407                     + "grouper.ws-" + this.version + File.separator + "grouper-ws" 
4408                     + File.separator + "build" + File.separator + "dist" + File.separator 
4409                     + "grouper-ws")
4410                 + "\" path=\"/" + tomcatPath + "\" reloadable=\"false\"/>");
4411           }
4412           
4413           if (!GrouperInstallerUtils.isBlank(currentDocBase)) {
4414             System.out.println("The docBase for the " + tomcatPath + " entry in the server.xml is: " + currentDocBase);
4415           } else {
4416             //check webapps
4417             System.out.println("The docBase could not be found in the server.xml, check in the tomcat" 
4418                 + File.separator + "webapps directory");
4419             currentDocBase = catalinaLogFile.getParentFile().getAbsolutePath() + File.separator + "webapps" + File.separator + tomcatPath;
4420             if (!new File(currentDocBase).exists()) {
4421               System.out.println("Cant find where grouper is linked from tomcat, looked in server.xml and the webapps directory");
4422               currentDocBase = null;
4423             }
4424           }
4425           if (currentDocBase != null) {
4426             log4jPropertiesFile = new File(currentDocBase + File.separator + "WEB-INF" + File.separator + "classes" + File.separator + "log4j.properties");
4427             
4428             analyzeLogFile(log4jPropertiesFile, "${grouper.home}" + File.separator, new File(catalinaLogFile + File.separator + "catalinaOut.log"),
4429                 new File(catalinaLogFile + File.separator + "catalinaErr.log"));
4430           }
4431         }
4432         
4433         break;
4434         
4435       default: 
4436         throw new RuntimeException("Not expecting appToUpgrade: " + this.appToUpgrade + "!");
4437     }
4438     
4439   }
4440   
4441   /**
4442    * 
4443    * @param log4jPropertiesFile
4444    * @param grouperHomeWithSlash
4445    * @param stdoutLocation
4446    * @param stderrLocation
4447    */
4448   private void analyzeLogFile(File log4jPropertiesFile, String grouperHomeWithSlash, File stdoutLocation, File stderrLocation) {
4449     System.out.println("The log4j.properties is located in: " 
4450         + log4jPropertiesFile.getAbsolutePath());
4451 
4452     if (!log4jPropertiesFile.exists()) {
4453     
4454       System.out.println("Error, the log4j.properties file could not be found.");
4455 
4456     } else {
4457       
4458       System.out.println("Examine the log4j.properties to see where it is logging");
4459       
4460       Properties log4jProperties = GrouperInstallerUtils.propertiesFromFile(log4jPropertiesFile);
4461       
4462       //= ERROR, grouper_error")
4463       String rootLoggerValue = log4jProperties.getProperty("log4j.rootLogger");              
4464       
4465       System.out.println("Generally the log4j.rootLogger property shows where logs go, it is set to: " + rootLoggerValue);
4466       
4467       Pattern pattern = Pattern.compile("\\s*[A-Z]+\\s*,\\s*(\\w+)\\s*");
4468       Matcher matcher = pattern.matcher(rootLoggerValue);
4469       if (!matcher.matches()) {
4470         System.out.println("Examine the log4j.properties for more information");
4471       } else {
4472         String logger = matcher.group(1);
4473         System.out.println("The log4j.rootLogger property in log4j.properties is set to: " + logger);
4474         //log4j.appender.grouper_error = org.apache.log4j.DailyRollingFileAppender
4475         //log4j.appender.grouper_error.File = ${grouper.home}logs/grouper_error.log
4476         String logFileName = log4jProperties.getProperty("log4j.appender." + logger + ".File");
4477         if (!GrouperInstallerUtils.isBlank(logFileName)) {
4478           System.out.println("There is a property in log4j.properties: log4j.appender." + logger + ".File = " + logFileName);
4479           if (logFileName.contains("${grouper.home}")) {
4480             logFileName = GrouperInstallerUtils.replace(logFileName, "${grouper.home}", grouperHomeWithSlash);
4481           }
4482           System.out.println("Grouper log should be: " + logFileName);
4483         } else {
4484           //log4j.appender.grouper_stdout = org.apache.log4j.ConsoleAppender
4485           String appender = log4jProperties.getProperty("log4j.appender." + logger);
4486           //log4j.appender.grouper_stderr.Target                    = System.err
4487           String target = log4jProperties.getProperty("log4j.appender." + logger + ".Target");
4488           String targetFriendly = null;
4489           if (GrouperInstallerUtils.equals(target, "System.err")) {
4490             targetFriendly = "STDERR";
4491           } else if (GrouperInstallerUtils.equals(target, "System.out")) {
4492             targetFriendly = "STDOUT";
4493           }
4494           if (GrouperInstallerUtils.equals(appender, "org.apache.log4j.ConsoleAppender") && targetFriendly != null) {
4495             System.out.println("Since log4j.properties log4j.appender." + logger + " = org.apache.log4j.ConsoleAppender you are logging to " + targetFriendly);
4496             if (GrouperInstallerUtils.equals(target, "System.err") && stderrLocation != null) {
4497               System.out.println("Grouper logs should be in " + stderrLocation.getAbsolutePath());
4498             } else if (GrouperInstallerUtils.equals(target, "System.out") && stdoutLocation != null) {
4499               System.out.println("Grouper logs should be in " + stdoutLocation.getAbsolutePath());
4500             }
4501           } else {
4502             System.out.println("Examine the log4j.properties for more information");
4503           }
4504         }
4505       }
4506     }
4507   }
4508   
4509   /**
4510    * admin
4511    */
4512   private void mainUpgradeTaskLogic() {
4513     
4514     GrouperInstallerUpgradeTaskAction grouperInstallerConvertAction = 
4515         (GrouperInstallerUpgradeTaskAction)promptForEnum(
4516             "What upgrade task do you want to do (convertEhcacheXmlToProperties, convertSourcesXmlToProperties, analyzeAndFixJars)? : ",
4517             "grouperInstaller.autorun.upgradeTaskAction", GrouperInstallerUpgradeTaskAction.class);
4518 
4519     switch(grouperInstallerConvertAction) {
4520       case convertEhcacheXmlToProperties:
4521 
4522         System.out.println("Note, you need to convert the ehcache.xml file for each Grouper runtime, e.g. loader, WS, UI.");
4523         System.out.println("Note, you need to be running Grouper 2.3.0 with API patch 35 installed.");
4524         System.out.print("Enter the location of the ehcache.xml file: ");
4525         String convertEhcacheXmlLocation = readFromStdIn("grouperInstaller.autorun.convertEhcacheXmlLocation");
4526 
4527         File ehcacheXmlFile = new File(convertEhcacheXmlLocation);
4528         if (!ehcacheXmlFile.exists()) {
4529           System.out.println("Cant find ehcache.xml: " + ehcacheXmlFile.getAbsolutePath());
4530           System.exit(1);
4531         }
4532 
4533         File grouperCacheBaseProperties = new File(ehcacheXmlFile.getParentFile().getAbsolutePath() + File.separator + "grouper.cache.base.properties");
4534 
4535         {
4536           System.out.print("Enter the location of the grouper.cache.base.properties file [" + grouperCacheBaseProperties.getAbsolutePath() + "]: ");
4537           String grouperCacheBasePropertiesLocation = readFromStdIn("grouperInstaller.autorun.convertEhcacheBasePropertiesLocation");
4538   
4539           if (!GrouperInstallerUtils.isBlank(grouperCacheBasePropertiesLocation)) {
4540             grouperCacheBaseProperties = new File(grouperCacheBasePropertiesLocation);
4541           }
4542         }
4543         
4544         File grouperCacheProperties = new File(ehcacheXmlFile.getParentFile().getAbsolutePath() + File.separator + "grouper.cache.properties");
4545 
4546         {
4547           System.out.print("Enter the location of the grouper.cache.properties file (to be created)  [" + grouperCacheProperties.getAbsolutePath() + "]: ");
4548           String grouperCachePropertiesLocation = readFromStdIn("grouperInstaller.autorun.convertEhcachePropertiesLocation");
4549   
4550           if (!GrouperInstallerUtils.isBlank(grouperCachePropertiesLocation)) {
4551             grouperCacheProperties = new File(grouperCachePropertiesLocation);
4552           }
4553         }
4554         
4555         try {
4556           convertEhcacheXmlToProperties(grouperCacheBaseProperties, grouperCacheProperties,
4557               ehcacheXmlFile.toURI().toURL());
4558         } catch (MalformedURLException mue) {
4559           throw new RuntimeException("Malformed url on " + convertEhcacheXmlLocation);
4560         }
4561 
4562         System.out.println("File was written: " + grouperCacheProperties.getAbsolutePath());
4563 
4564         break;
4565         
4566       case analyzeAndFixJars:
4567         
4568         //Find out what directory to install to.  This ends in a file separator
4569         this.grouperInstallDirectoryString = grouperInstallDirectory();
4570 
4571         reportOnConflictingJars(this.grouperInstallDirectoryString);
4572         
4573         break;
4574         
4575       case convertSourcesXmlToProperties:
4576 
4577         System.out.println("Note, you need to convert the sources.xml file for each Grouper runtime, e.g. loader, WS, UI.");
4578         System.out.println("Note, to use subject sources from subject.properties, you need to be running Grouper 2.3.0+ with API patch 40 installed.");
4579         System.out.print("Enter the location of the sources.xml file: ");
4580         String convertSourcesXmlLocation = readFromStdIn("grouperInstaller.autorun.convertSourceXmlLocation");
4581 
4582         File sourcesXmlFile = new File(convertSourcesXmlLocation);
4583         if (!sourcesXmlFile.exists()) {
4584           System.out.println("Cant find sources.xml: " + sourcesXmlFile.getAbsolutePath());
4585           System.exit(1);
4586         }
4587 
4588         File subjectProperties = new File(sourcesXmlFile.getParentFile().getAbsolutePath() + File.separator + "subject.properties");
4589 
4590         {
4591           System.out.print("Enter the location of the subject.properties file [" + subjectProperties.getAbsolutePath() + "]: ");
4592           String grouperCacheBasePropertiesLocation = readFromStdIn("grouperInstaller.autorun.convertSubjectPropertiesLocation");
4593   
4594           if (!GrouperInstallerUtils.isBlank(grouperCacheBasePropertiesLocation)) {
4595             subjectProperties = new File(grouperCacheBasePropertiesLocation);
4596           }
4597         }
4598         
4599         try {
4600           convertSourcesXmlToProperties(subjectProperties, sourcesXmlFile.toURI().toURL());
4601         } catch (MalformedURLException mue) {
4602           throw new RuntimeException("Malformed url on " + convertSourcesXmlLocation);
4603         }
4604 
4605         System.out.println("File was written: " + subjectProperties.getAbsolutePath());
4606         System.out.println("You should archive your sources.xml and remove it from your project since it is now unused:\n  " 
4607             + sourcesXmlFile.getAbsolutePath());
4608 
4609         break;
4610     }
4611 
4612   }
4613   
4614   /**
4615    * patch grouper
4616    */
4617   private void mainPatchLogic() {
4618     
4619     //####################################
4620     //Find out what directory to upgrade to.  This ends in a file separator
4621     this.grouperTarballDirectoryString = grouperUpgradeTempDirectory();
4622     
4623     //see what we are upgrading: api, ui, ws, client
4624     this.appToUpgrade = grouperAppToUpgradeOrPatch("patch");
4625 
4626     //get the directory where the existing installation is
4627     this.upgradeExistingApplicationDirectoryString = upgradeExistingDirectory();
4628 
4629     GrouperInstallerPatchAction grouperInstallerPatchAction = 
4630         (GrouperInstallerPatchAction)promptForEnum(
4631             "What do you want to do with patches (install, revert, status, fixIndexFile)? ",
4632             "grouperInstaller.autorun.patchAction", GrouperInstallerPatchAction.class, GrouperInstallerPatchAction.install, null);
4633     
4634     switch(grouperInstallerPatchAction) {
4635       case install:
4636         
4637         fixIndexFileIfOk();
4638 
4639         //loop through applications, check patches
4640         this.appToUpgrade.patch(this);
4641 
4642         break;
4643         
4644       case revert:
4645         
4646         fixIndexFileIfOk();
4647 
4648         //look through applications, check for reverts
4649         this.appToUpgrade.revertPatch(this);
4650         break;
4651         
4652       case status:
4653         
4654         fixIndexFileIfOk();
4655 
4656         //print out status for applications
4657         this.appToUpgrade.patchStatus(this);
4658         break;
4659         
4660       case fixIndexFile:
4661         
4662         //print out status for applications
4663         this.appToUpgrade.fixIndexFile(this);
4664         break;
4665         
4666       default:
4667         throw new RuntimeException("Invalid patch action: " + grouperInstallerPatchAction);  
4668     }
4669     
4670   }
4671 
4672   /**
4673    * 
4674    */
4675   public static enum GrouperInstallerPatchAction {
4676 
4677     /** fix index file */
4678     fixIndexFile,
4679     
4680     /** install patches */
4681     install,
4682 
4683     /**
4684      * revert patches
4685      */
4686     revert,
4687     
4688     /**
4689      * get status on patches
4690      */
4691     status;
4692     
4693     /**
4694      * 
4695      * @param string
4696      * @param exceptionIfInvalid
4697      * @param exceptionIfBlank
4698      * @return the action
4699      */
4700     public static GrouperInstallerPatchAction valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
4701       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerPatchAction.class, string, exceptionIfBlank, exceptionIfInvalid);
4702     }
4703     
4704   }
4705   
4706   /**
4707    * 
4708    */
4709   public static enum GrouperInstallerAdminAction {
4710 
4711     /** manage */
4712     manage,
4713     
4714     /** develop */
4715     develop,
4716     
4717     /** convert */
4718     upgradeTask;
4719     
4720     /**
4721      * 
4722      * @param string
4723      * @param exceptionIfInvalid
4724      * @param exceptionIfBlank
4725      * @return the action
4726      */
4727     public static GrouperInstallerAdminAction valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
4728       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerAdminAction.class, string, exceptionIfBlank, exceptionIfInvalid);
4729     }
4730     
4731   }
4732   
4733   /**
4734    * 
4735    */
4736   public static enum GrouperInstallerUpgradeTaskAction {
4737 
4738     /** analyze and fix jars */
4739     analyzeAndFixJars,
4740     
4741     /** convert */
4742     convertEhcacheXmlToProperties,
4743     
4744     /** convert sources xml */
4745     convertSourcesXmlToProperties;
4746     
4747     /**
4748      * 
4749      * @param string
4750      * @param exceptionIfInvalid
4751      * @param exceptionIfBlank
4752      * @return the action
4753      */
4754     public static GrouperInstallerUpgradeTaskAction valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
4755       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerUpgradeTaskAction.class, string, exceptionIfBlank, exceptionIfInvalid);
4756     }
4757     
4758   }
4759   
4760   /**
4761    * 
4762    */
4763   public static enum GrouperInstallerAdminManageService {
4764 
4765     /** tomcat */
4766     tomcat,
4767     
4768     /** database */
4769     database,
4770     
4771     /** daemon (loader) */
4772     grouperDaemon;
4773     
4774     /**
4775      * 
4776      * @param string
4777      * @param exceptionIfInvalid
4778      * @param exceptionIfBlank
4779      * @return the action
4780      */
4781     public static GrouperInstallerAdminManageService valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
4782       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerAdminManageService.class, string, exceptionIfBlank, exceptionIfInvalid);
4783     }
4784     
4785   }
4786   
4787   /**
4788    * 
4789    */
4790   public static enum GrouperInstallerManageAction {
4791 
4792     /** logs */
4793     logs,
4794 
4795     /** back to admin */
4796     back,
4797 
4798     /** exit */
4799     exit,
4800 
4801     /** services */
4802     services;
4803     
4804     /**
4805      * 
4806      * @param string
4807      * @param exceptionIfInvalid
4808      * @param exceptionIfBlank
4809      * @return the action
4810      */
4811     public static GrouperInstallerManageAction valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
4812       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerManageAction.class, string, exceptionIfBlank, exceptionIfInvalid);
4813     }
4814     
4815   }
4816   
4817   /**
4818    * 
4819    */
4820   public static enum GrouperInstallerDevelopAction {
4821 
4822     /** logs */
4823     translate,
4824 
4825     /** back to admin */
4826     back,
4827 
4828     /** exit */
4829     exit;
4830 
4831     /**
4832      * 
4833      * @param string
4834      * @param exceptionIfInvalid
4835      * @param exceptionIfBlank
4836      * @return the action
4837      */
4838     public static GrouperInstallerDevelopAction valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
4839       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerDevelopAction.class, string, exceptionIfBlank, exceptionIfInvalid);
4840     }
4841     
4842   }
4843   
4844   /**
4845    * get the existing properties file of patches
4846    * @return the file for patches
4847    */
4848   private Properties patchExistingProperties() {
4849     File patchExistingPropertiesFile = this.patchExistingPropertiesFile();
4850     if (patchExistingPropertiesFile == null || !patchExistingPropertiesFile.exists()) {
4851       return new Properties();
4852     }
4853     return GrouperInstallerUtils.propertiesFromFile(patchExistingPropertiesFile);
4854    }
4855 
4856   /**
4857    * get the existing properties file of patches
4858    * @return the file for patches
4859    */
4860   private File patchExistingPropertiesFile() {
4861     
4862     //dont cache this in a variable since the upgrade existing application variable
4863     File patchExistingPropertiesFile = null;
4864     //if theres a web-inf, put it there, if not, put it in regular...
4865     if (new File(this.upgradeExistingApplicationDirectoryString + "WEB-INF").exists()) {
4866       patchExistingPropertiesFile = new File(this.upgradeExistingApplicationDirectoryString 
4867           + "WEB-INF" + File.separator + "grouperPatchStatus.properties");
4868     } else {
4869       patchExistingPropertiesFile = new File(this.upgradeExistingApplicationDirectoryString 
4870           + "grouperPatchStatus.properties");
4871     }
4872     return patchExistingPropertiesFile;
4873   }
4874   
4875   /**
4876    * 
4877    */
4878   private void mainUpgradeLogic() {
4879 
4880     System.out.print("You should backup your files and database before you start.  Press <enter> to continue. ");
4881     readFromStdIn("grouperInstaller.autorun.backupFiles");
4882     
4883     System.out.println("\n##################################");
4884     System.out.println("Gather upgrade information\n");
4885 
4886     //####################################
4887     //Find out what directory to upgrade to.  This ends in a file separator
4888     this.grouperTarballDirectoryString = grouperUpgradeTempDirectory();
4889     
4890     //GRP-1429: grouperInstaller tarballs dir fails on upgrade
4891     //set this here since on upgrade some things download to this dir...
4892     this.grouperInstallDirectoryString = this.grouperTarballDirectoryString;
4893     
4894     //see what we are upgrading: api, ui, ws, client
4895     this.appToUpgrade = grouperAppToUpgradeOrPatch("upgrade");
4896 
4897     for (int i=0;i<10;i++) {
4898       System.out.println("Are there any running processes using this installation?  tomcats?  loader?  psp?  etc?  (t|f)? [f]:");
4899       boolean runningProcesses = readFromStdInBoolean(true, "grouperInstaller.autorun.runningProcesses");
4900       if (runningProcesses) {
4901         break;
4902       }
4903       System.out.println("Please stop any processes using this installation...");
4904       //lets sleep for a bit to let it start
4905       GrouperInstallerUtils.sleep(2000);
4906     }
4907     
4908     //get the directory where the existing installation is
4909     this.upgradeExistingApplicationDirectoryString = upgradeExistingDirectory();
4910 
4911     this.version = GrouperInstallerUtils.propertiesValue("grouper.version", true);
4912     System.out.println("Upgrading to grouper " + this.appToUpgrade.name() + " version: " + this.version);
4913  
4914     fixIndexFileIfOk();
4915     
4916     System.out.println("\n##################################");
4917     System.out.println("Download and build grouper packages\n");
4918 
4919     //download new files
4920     this.appToUpgrade.downloadAndBuildGrouperProjects(this);
4921 
4922     System.out.println("End download and build grouper packages\n");
4923     System.out.println("\n##################################");
4924 
4925     this.grouperBaseBakDir = this.grouperTarballDirectoryString + "bak_" + this.appToUpgrade + "_" 
4926         + new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS").format(new Date()) + File.separator; 
4927 
4928     GrouperInstallerUtils.tempFilePathForJars = this.grouperBaseBakDir 
4929         + "jarToDelete" + File.separator;
4930 
4931     //when we revert patches, default should be true
4932     this.revertAllPatchesDefault = true;
4933     try {
4934       this.appToUpgrade.upgradeApp(this);
4935     } finally {
4936       this.revertAllPatchesDefault = false;
4937     }
4938 
4939     this.reportOnConflictingJars(this.upgradeExistingApplicationDirectoryString);
4940     
4941     System.out.println("\nGrouper is upgraded from " + (this.originalGrouperJarVersion == null ? null : this.originalGrouperJarVersion) 
4942         + " to " + GrouperInstallerUtils.propertiesValue("grouper.version", true) +  "\n");
4943 
4944     //this file keeps track of partial upgrades
4945     GrouperInstallerUtils.fileDelete(this.grouperUpgradeOriginalVersionFile);
4946 
4947     //reset this so that patches go against new version
4948     this.grouperVersionOfJar = null;
4949     
4950   }
4951 
4952   /**
4953    * 
4954    */
4955   private void fixIndexFileIfOk() {
4956     Properties patchesExistingProperties = patchExistingProperties();
4957     
4958     //see what is already there
4959     String existingDate = patchesExistingProperties.getProperty("grouperInstallerLastFixedIndexFile.date");
4960 
4961     boolean defaultToFixIndex = true;
4962     
4963     if (!GrouperInstallerUtils.isBlank(existingDate)) {
4964       try {
4965         Date theDate = GrouperInstallerUtils.dateMinutesSecondsFormat.parse(existingDate);
4966         //this is when the installer was fixed to do the index file correctly
4967         if (theDate.getTime() > GrouperInstallerUtils.dateValue("20150929").getTime()) {
4968           defaultToFixIndex = false;
4969         }
4970       } catch (ParseException pe) {
4971         System.out.println("Cant parse date: " + existingDate);
4972       }
4973     }
4974 
4975     //if we are affecting 2.2.2+ then dont recommend this
4976     if (defaultToFixIndex) {
4977       
4978       //see the version
4979       String grouperVersion = this.grouperVersionOfJar().toString();
4980 
4981       GiGrouperVersion giGrouperVersion = GiGrouperVersion.valueOfIgnoreCase(grouperVersion);
4982       
4983       if (giGrouperVersion.greaterOrEqualToArg(GiGrouperVersion.valueOfIgnoreCase("2.2.2"))) {
4984         defaultToFixIndex = false;
4985       }
4986       
4987     }
4988     
4989     
4990     System.out.println("Do you want to fix the patch index file (download all patches and see if they are installed?) (" + (defaultToFixIndex ? "recommended" : "not recommended") + ") (t|f)? [" + (defaultToFixIndex ? "t" : "f") + "]: ");
4991     boolean fixIndexFile = readFromStdInBoolean(defaultToFixIndex, "grouperInstaller.autorun.fixIndexFile");
4992     if (fixIndexFile) {
4993       this.appToUpgrade.fixIndexFile(this);
4994     }
4995   }
4996 
4997   /**
4998    * upgrade the client
4999    */
5000   private void upgradeClient() {
5001 
5002     System.out.println("\n##################################");
5003     System.out.println("Upgrading grouper client\n");
5004 
5005     this.compareAndReplaceJar(this.grouperClientJar, new File(this.untarredClientDir + File.separator + "grouperClient.jar"), true, null);
5006 
5007     this.compareUpgradePropertiesFile(this.grouperClientBasePropertiesFile, 
5008       new File(this.untarredClientDir + File.separator + "grouper.client.base.properties"),
5009       this.grouperClientPropertiesFile,
5010       this.grouperClientExamplePropertiesFile,
5011       GrouperInstallerUtils.toSet("grouperClient.webService.client.version"),
5012       "grouperInstaller.autorun.removeRedundantPropetiesFromGrouperClient"
5013     );
5014 
5015     
5016   }
5017 
5018 
5019   /**
5020    * upgrade the ui
5021    */
5022   private void upgradeUi() {
5023 
5024     this.upgradeApiPreRevertPatch();
5025 
5026     System.out.println("You need to revert all patches to upgrade");
5027     this.patchRevertUi();
5028         
5029     System.out.println("\n##################################");
5030     System.out.println("Upgrading UI\n");
5031     
5032     //copy the jars there
5033     System.out.println("\n##################################");
5034     System.out.println("Upgrading UI jars\n");
5035 
5036     this.upgradeJars(new File(this.untarredUiDir + File.separator + "dist" + File.separator + "grouper" + File.separator 
5037         + "WEB-INF" + File.separator + "lib" + File.separator));
5038 
5039     System.out.println("\n##################################");
5040     System.out.println("Upgrading UI files\n");
5041 
5042     //copy files there
5043     this.copyFiles(this.untarredUiDir + File.separator + "dist" + File.separator + "grouper" + File.separator,
5044         this.upgradeExistingApplicationDirectoryString,
5045         GrouperInstallerUtils.toSet("WEB-INF/lib", "WEB-INF/web.xml", "WEB-INF/classes",
5046             "WEB-INF/bin/gsh", "WEB-INF/bin/gsh.bat", "WEB-INF/bin/gsh.sh"));
5047 
5048     {
5049       boolean hadChange = false;
5050       for (String gshName : new String[]{"gsh", "gsh.bat", "gsh.sh"}) {
5051         File newGshFile = new File(this.untarredUiDir + File.separator + "dist" 
5052             + File.separator + "grouper" + File.separator + "WEB-INF" + File.separator + "bin" 
5053             + File.separator + gshName);
5054 
5055         File existingGshFile = new File(this.upgradeExistingApplicationDirectoryString 
5056             + "WEB-INF" + File.separator + "bin" + File.separator + gshName);
5057 
5058         if (!GrouperInstallerUtils.contentEquals(newGshFile, existingGshFile)) {
5059           this.backupAndCopyFile(newGshFile, existingGshFile, true);
5060           if (!GrouperInstallerUtils.equals("gsh.bat", gshName)) {
5061             hadChange = true;
5062           }
5063         }
5064         
5065       }
5066       if (hadChange) {
5067         //set executable and dos2unix
5068         gshExcutableAndDos2Unix(this.untarredUiDir + File.separator + "dist" 
5069             + File.separator + "grouper" + File.separator + "WEB-INF" + File.separator + "bin" 
5070             + File.separator);
5071       }
5072     }
5073     
5074     upgradeWebXml(new File(this.untarredUiDir + File.separator + "dist" 
5075             + File.separator + "grouper" + File.separator + "WEB-INF" + File.separator + "web.xml"),
5076             new File(this.upgradeExistingApplicationDirectoryString 
5077                 + File.separator + "WEB-INF" + File.separator + "web.xml"));
5078     
5079     System.out.println("\n##################################");
5080     System.out.println("Upgrading UI config files\n");
5081 
5082     this.changeConfig("WEB-INF/classes/resources/grouper/nav.properties", 
5083         "WEB-INF/classes/grouperText/grouper.text.en.us.base.properties",
5084         "WEB-INF/classes/grouperText/grouper.text.en.us.properties", null, GiGrouperVersion.valueOfIgnoreCase("2.2.0"), true, 
5085         "grouperInstaller.autorun.continueAfterNavProperties",
5086         "grouperInstaller.autorun.removeOldKeysFromNavProperties");
5087 
5088     this.changeConfig("WEB-INF/classes/resources/grouper/media.properties", 
5089         "WEB-INF/classes/grouper-ui.base.properties",
5090         "WEB-INF/classes/grouper-ui.properties", null, GiGrouperVersion.valueOfIgnoreCase("2.2.0"), false,
5091         "grouperInstaller.autorun.continueAfterMediaProperties",
5092         "grouperInstaller.autorun.removeOldKeysFromMediaProperties");
5093 
5094     {
5095       File newBaseOwaspFile = new File(this.untarredUiDir + File.separator + "dist" 
5096           + File.separator + "grouper" + File.separator + "WEB-INF" + File.separator + "classes" 
5097           + File.separator + "Owasp.CsrfGuard.properties");
5098 
5099       File newOwaspFile = new File(this.untarredUiDir + File.separator + "dist" 
5100           + File.separator + "grouper" + File.separator + "WEB-INF" + File.separator + "classes" 
5101           + File.separator + "Owasp.CsrfGuard.overlay.properties");
5102 
5103       if (this.owaspCsrfGuardBaseFile == null) {
5104         this.owaspCsrfGuardBaseFile = new File(this.upgradeExistingClassesDirectoryString + newBaseOwaspFile.getName());
5105       }
5106       
5107       if (this.owaspCsrfGuardFile == null) {
5108         this.owaspCsrfGuardFile = new File(this.upgradeExistingClassesDirectoryString + newOwaspFile.getName());
5109       }
5110       
5111       this.backupAndCopyFile(newBaseOwaspFile, this.owaspCsrfGuardBaseFile, true);
5112 
5113       boolean editedOwaspOverlay = this.owaspCsrfGuardFile != null && this.owaspCsrfGuardFile.exists();
5114 
5115       File bakFile = this.backupAndCopyFile(newOwaspFile, this.owaspCsrfGuardFile, true);
5116 
5117       if (bakFile != null && editedOwaspOverlay) {
5118         if (!GrouperInstallerUtils.contentEquals(this.owaspCsrfGuardFile, newOwaspFile)) {
5119           System.out.println("If you have edited the Owasp.CsrfGuard.overlay.properties please merge the changes to the new file");
5120           System.out.println("Press <enter> when done");
5121           readFromStdIn("grouperInstaller.autorun.continueAfterEditedOwaspCsrfGuard");
5122         }
5123       }
5124     }
5125     
5126     this.upgradeApiPostRevertPatch();
5127 
5128     //patch it
5129     this.patchUi();
5130     
5131   }
5132 
5133   /**
5134    * upgrade the psp
5135    */
5136   private void upgradePsp() {
5137 
5138     this.upgradeApiPreRevertPatch();
5139 
5140     System.out.println("You need to revert all patches to upgrade");
5141     this.patchRevertPsp();
5142     
5143     System.out.println("\n##################################");
5144     System.out.println("Upgrading PSP\n");
5145     
5146     //copy the jars there
5147     System.out.println("\n##################################");
5148     System.out.println("Upgrading PSP jars\n");
5149 
5150     this.upgradeJars(new File(this.untarredPspDir + File.separator + "lib" + File.separator + "custom" + File.separator),
5151         new File(new File(this.upgradeExistingLibDirectoryString).getParentFile().getAbsolutePath() + File.separator + "custom"));
5152 
5153     System.out.println("\n##################################");
5154     System.out.println("Upgrading PSP files\n");
5155 
5156     //copy files there (this is the conf examples)
5157     this.copyFiles(this.untarredPspDir + File.separator + "conf" + File.separator,
5158         this.upgradeExistingApplicationDirectoryString + "conf" + File.separator, null);
5159 
5160     this.upgradeApiPostRevertPatch();
5161     
5162     //patch it
5163     this.patchPsp();
5164   }
5165 
5166   /**
5167    * upgrade the pspng
5168    */
5169   private void upgradePspng() {
5170 
5171     this.upgradeApiPreRevertPatch();
5172 
5173     System.out.println("You need to revert all patches to upgrade");
5174     this.patchRevertPspng();
5175     
5176     System.out.println("\n##################################");
5177     System.out.println("Upgrading PSPNG\n");
5178     
5179     //copy the jars there
5180     System.out.println("\n##################################");
5181     System.out.println("Upgrading PSPNG jars\n");
5182 
5183     this.upgradeJars(new File(this.untarredPspngDir + File.separator + "lib" + File.separator + "custom" + File.separator),
5184         new File(new File(this.upgradeExistingLibDirectoryString).getParentFile().getAbsolutePath() + File.separator + "custom"));
5185 
5186     System.out.println("\n##################################");
5187     System.out.println("Upgrading PSPNG files\n");
5188 
5189     //copy files there (this is the conf examples)
5190     this.copyFiles(this.untarredPspngDir + File.separator + "conf" + File.separator,
5191         this.upgradeExistingApplicationDirectoryString + "conf" + File.separator, null);
5192 
5193     this.upgradeApiPostRevertPatch();
5194     
5195     //patch it
5196     this.patchPspng();
5197   }
5198 
5199   
5200   /**
5201    * upgrade a web.xml file
5202    * @param newWebXml
5203    * @param existingWebXml
5204    */
5205   public void upgradeWebXml(File newWebXml, File existingWebXml) {
5206     
5207     File bakFile = backupAndCopyFile(newWebXml, existingWebXml, true);
5208     
5209     if (bakFile != null) {
5210       //it existed
5211       NodeList nodeList = GrouperInstallerUtils.xpathEvaluate(bakFile, "/web-app/security-constraint");
5212       boolean tookOutAuthn = false;
5213       if (nodeList == null || nodeList.getLength() == 0) {
5214         //take out authn from web.xml
5215         String webXmlContents = GrouperInstallerUtils.readFileIntoString(existingWebXml);
5216         int startAuthnIndex = webXmlContents.indexOf("<security-constraint>");
5217         int endAuthnIndex = webXmlContents.indexOf("</security-role>");
5218         if (startAuthnIndex != -1 && endAuthnIndex != -1 && endAuthnIndex > startAuthnIndex) {
5219           endAuthnIndex = endAuthnIndex + "</security-role>".length();
5220           //authn is there
5221           webXmlContents = webXmlContents.substring(0, startAuthnIndex) + webXmlContents.substring(endAuthnIndex, webXmlContents.length());
5222           GrouperInstallerUtils.saveStringIntoFile(existingWebXml, webXmlContents);
5223           tookOutAuthn = true;
5224           System.out.println("Taking out basic authentication from " + existingWebXml + " since it wasnt there before");
5225         }
5226       }
5227       System.out.println("If you customized the web.xml please merge your changes back in "
5228           + (tookOutAuthn ? "\n  Note: basic authentication was removed from the new web.xml to be consistent with the old web.xml" : "")
5229           + "\n  New file: " + existingWebXml.getAbsolutePath() + ", bak file:" + bakFile.getAbsolutePath() );
5230       System.out.println("Press the <enter> key to continue");
5231       readFromStdIn("grouperInstaller.autorun.continueAfterMergeWebXml");
5232       
5233       if (tookOutAuthn) {
5234         GrouperInstallerUtils.xpathEvaluate(existingWebXml, "/web-app");        
5235       }
5236     }
5237   }
5238 
5239   
5240   /**
5241    * @param legacyPropertiesFileRelativePath legacy file we are converting from
5242    * @param propertiesFileRelativePath 
5243    * @param propertiesToIgnore
5244    * @param basePropertiesFileRelativePath
5245    * @param versionMigrationHappened
5246    * @param removeOldCopy
5247    * @param autorunPropertiesKey key in properties file to automatically fill in a value
5248    * @param autorunPropertiesKeyRemoveOldKeys key in properties file to automatically fill in a value to remove old keys
5249    */
5250   @SuppressWarnings("unchecked")
5251   private void changeConfig(String legacyPropertiesFileRelativePath,
5252       String basePropertiesFileRelativePath,
5253       String propertiesFileRelativePath,
5254       Set<String> propertiesToIgnore,
5255       GiGrouperVersion versionMigrationHappened, boolean removeOldCopy, 
5256       String autorunPropertiesKey, String autorunPropertiesKeyRemoveOldKeys) {
5257 
5258     File legacyPropertiesFile = new File(this.upgradeExistingApplicationDirectoryString + legacyPropertiesFileRelativePath);
5259     File newBasePropertiesFile = new File(this.untarredUiDir + File.separator + "dist" 
5260         + File.separator + "grouper" + File.separator + basePropertiesFileRelativePath);
5261     File existingBasePropertiesFile = new File(this.upgradeExistingApplicationDirectoryString + basePropertiesFileRelativePath);
5262     File existingPropertiesFile = new File(this.upgradeExistingApplicationDirectoryString + propertiesFileRelativePath);
5263 
5264     this.compareUpgradePropertiesFile(existingBasePropertiesFile, newBasePropertiesFile, 
5265         existingPropertiesFile, null, propertiesToIgnore, autorunPropertiesKeyRemoveOldKeys);
5266 
5267     //look for existing properties in legacy file, and if there are properties in the base, then remove them
5268     if (legacyPropertiesFile.exists()) {
5269       
5270       Properties existingBaseProperties = GrouperInstallerUtils.propertiesFromFile(existingBasePropertiesFile);
5271       Properties existingProperties = GrouperInstallerUtils.propertiesFromFile(existingPropertiesFile);
5272       Properties legacyProperties = GrouperInstallerUtils.propertiesFromFile(legacyPropertiesFile);
5273       Set<String> propertyNamesToRemove = new LinkedHashSet<String>();
5274       Set<String> propertyNamesWrongValue = new LinkedHashSet<String>();
5275 
5276       for (String propertyName : (Set<String>)(Object)existingBaseProperties.keySet()) {
5277         if (legacyProperties.containsKey(propertyName)) {
5278           
5279           Object existingValue = existingProperties.containsKey(propertyName) ?
5280              existingProperties.get(propertyName) : existingBaseProperties.get(propertyName);
5281           
5282           //it might be in the override, what about other overrides?  who knows
5283           if (!GrouperInstallerUtils.equals(existingValue, 
5284               legacyProperties.get(propertyName))) {
5285 
5286             propertyNamesWrongValue.add(propertyName);
5287           }
5288           propertyNamesToRemove.add(propertyName);
5289         }
5290       }
5291       
5292       //if we found some, see if we can remove them
5293       if (propertyNamesToRemove.size() > 0) {
5294         
5295         if (propertyNamesWrongValue.size() > 0) {
5296 
5297           //these are properties that we different in the previous legacy file
5298           System.out.println(legacyPropertiesFileRelativePath + " has properties that have a different value than\n  the new place they are managed: "
5299               + basePropertiesFileRelativePath + ",\n  and the everride(s) which could be: " + propertiesFileRelativePath);
5300           System.out.println("Review these properties and merge the values, this could have happened due to changes in Grouper:");
5301           for (String propertyName: propertyNamesWrongValue) {
5302             System.out.println(" - " + propertyName);
5303           }
5304           System.out.println("When you are done merging press <enter>");
5305           readFromStdIn(autorunPropertiesKey);
5306 
5307         }
5308 
5309         if (removeOldCopy) {
5310           
5311           System.out.println(legacyPropertiesFileRelativePath + " is not used anymore by grouper, can it be backed up and removed (t|f)? [t]: ");
5312           boolean removeLegacy = readFromStdInBoolean(true, autorunPropertiesKeyRemoveOldKeys);
5313           if (removeLegacy) {
5314             File backupLegacy = bakFile(legacyPropertiesFile);
5315             GrouperInstallerUtils.copyFile(legacyPropertiesFile, backupLegacy, true);
5316             GrouperInstallerUtils.fileDelete(legacyPropertiesFile);
5317             System.out.println("File as removed.  Backup path: " + backupLegacy.getAbsolutePath());
5318           }
5319           
5320         } else {
5321           System.out.println(legacyPropertiesFileRelativePath + " has properties that can be removed since they are now managed in "
5322               + basePropertiesFileRelativePath);
5323           System.out.print("Would you like to have the properties automatically removed from " 
5324               + legacyPropertiesFile.getName() + " (t|f)? [t]: ");
5325           boolean removeRedundantProperties = readFromStdInBoolean(true, autorunPropertiesKeyRemoveOldKeys);
5326           
5327           if (removeRedundantProperties) {
5328             removeRedundantProperties(legacyPropertiesFile, propertyNamesToRemove);
5329           }
5330         }
5331       }
5332     }
5333   }
5334 
5335   
5336   /**
5337    * copy files if they are different from one place to another, print out statuses
5338    * @param fromDirString where to copy files from
5339    * @param toDirString where to copy files to
5340    * @param relativePathsToIgnore
5341    */
5342   public void copyFiles(String fromDirString, String toDirString, 
5343       Set<String> relativePathsToIgnore) {
5344     
5345     fromDirString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(fromDirString);
5346     toDirString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(toDirString);
5347 
5348     {
5349       //lets massage all the paths so they dont start or end with slash and
5350       //so they have File.separator instead of the wrong slash
5351       Set<String> tempRelativePathsToIgnore = new HashSet<String>();
5352       for (String path : GrouperInstallerUtils.nonNull(relativePathsToIgnore)) {
5353         path = GrouperInstallerUtils.fileMassagePathsNoLeadingOrTrailing(path);
5354         tempRelativePathsToIgnore.add(path);
5355       }
5356       relativePathsToIgnore = tempRelativePathsToIgnore;
5357     }
5358 
5359     int insertCount = 0;
5360     int updateCount = 0;
5361     Map<String, Boolean> relativeFilePathsChangedAndIfInsert = new LinkedHashMap<String, Boolean>();
5362 
5363     List<File> allFiles = GrouperInstallerUtils.fileListRecursive(new File(fromDirString));
5364     for (File fileToCopyFrom : allFiles) {
5365       String relativePath = null;
5366       {
5367         //get the relative path with no leading or trailing slash
5368         String path = fileToCopyFrom.getAbsolutePath();
5369         if (!GrouperInstallerUtils.filePathStartsWith(path,fromDirString)) {
5370           throw new RuntimeException("Why does path not start with fromDirString: " + path + ", " + fromDirString);
5371         }
5372         relativePath = path.substring(fromDirString.length());
5373         relativePath = GrouperInstallerUtils.fileMassagePathsNoLeadingOrTrailing(relativePath);
5374       }
5375       boolean ignore = false;
5376       
5377       //ignore paths passed in
5378       for (String pathToIgnore : relativePathsToIgnore) {
5379         if (GrouperInstallerUtils.filePathStartsWith(relativePath,pathToIgnore)) {
5380           ignore = true;
5381           break;
5382         }
5383       }
5384       
5385       if (!ignore) {
5386         
5387         //File to copy to
5388         File fileToCopyTo = new File(toDirString + relativePath);
5389         if (fileToCopyTo.exists()) {
5390           //compare contents
5391           if (GrouperInstallerUtils.contentEquals(fileToCopyFrom, fileToCopyTo)) {
5392             continue;
5393           }
5394           //not equals, make backup
5395           updateCount++;
5396 
5397           relativeFilePathsChangedAndIfInsert.put(relativePath, false);
5398 
5399           this.backupAndCopyFile(fileToCopyFrom, fileToCopyTo, false);
5400 
5401           continue;
5402         }
5403         
5404         //insert
5405         insertCount++;
5406         relativeFilePathsChangedAndIfInsert.put(relativePath, true);
5407         GrouperInstallerUtils.copyFile(fileToCopyFrom, fileToCopyTo, false);
5408         
5409       }
5410     }
5411 
5412     System.out.println("Upgrading files from: " + fromDirString + "\n  to: " + toDirString 
5413         + (GrouperInstallerUtils.length(relativePathsToIgnore) == 0 ? "" : 
5414         ("\n  ignoring paths: " + GrouperInstallerUtils.join(relativePathsToIgnore.iterator(), ", "))));
5415     System.out.println("Compared " + allFiles.size() + " files and found " 
5416         + insertCount + " adds and " + updateCount + " updates");
5417 
5418     if (insertCount > 0 || updateCount > 0) {
5419       
5420       System.out.println((insertCount + updateCount) + " files were backed up to: " + this.grouperBaseBakDir);
5421 
5422       boolean listFiles = insertCount + updateCount <= 10;
5423       if (!listFiles) {
5424         System.out.println("Do you want to see the list of files changed (t|f)? [f]: ");
5425         listFiles = readFromStdInBoolean(false, "grouperInstaller.autorun.viewListOfFilesChangedInCopy");
5426       }
5427 
5428       if (listFiles) {
5429 
5430         for (String relativeFilePathChanged : relativeFilePathsChangedAndIfInsert.keySet()) {
5431           boolean isInsert = relativeFilePathsChangedAndIfInsert.get(relativeFilePathChanged);
5432           System.out.println(relativeFilePathChanged + " was " + (isInsert ? "added" : "updated"));
5433         }
5434       }
5435     }
5436     
5437   }
5438 
5439   /**
5440    *
5441    * @param sourceDir directory to copy files from
5442    * @param targetDir directory to copy files to
5443    * @param filesToCopyFromSource list of files to copy, if exist in source and differ in target
5444    */
5445   private void syncFilesInDirWithBackup(String sourceDir, String targetDir, String[] filesToCopyFromSource) {
5446     for (String filename : filesToCopyFromSource) {
5447       File srcFile = new File(sourceDir + filename);
5448       File targetFile = new File(targetDir + filename);
5449 
5450       if (srcFile.isFile() && !GrouperInstallerUtils.contentEquals(srcFile, targetFile)) {
5451         try {
5452           @SuppressWarnings("unused")
5453           File bakFile = backupAndCopyFile(srcFile, targetFile, true);
5454         } catch (Exception e) {
5455           System.out.println(" - failed to copy newer bin file " + filename + ": " + e.getMessage());
5456         }
5457       }
5458     }
5459   }
5460 
5461   /**
5462    * upgrade the api
5463    */
5464   private void upgradeApi() {
5465     this.upgradeApiPreRevertPatch();
5466 
5467     System.out.println("You need to revert all patches to upgrade");
5468     this.patchRevertApi();
5469     
5470     this.upgradeApiPostRevertPatch();
5471   }
5472 
5473 
5474   /**
5475    * upgrade the api
5476    */
5477   private void upgradeApiPreRevertPatch() {
5478 
5479     //make sure existing gsh is executable and dos2unix
5480     gshExcutableAndDos2Unix(new File(gshCommand()).getParentFile().getAbsolutePath(), "existing");
5481     
5482     this.runChangeLogTempToChangeLog();
5483   }
5484   
5485   
5486   /**
5487    * upgrade the api
5488    */
5489   private void upgradeApiPostRevertPatch() {
5490 
5491     //revert patches
5492     this.upgradeClient();
5493 
5494     System.out.println("\n##################################");
5495     System.out.println("Upgrading API\n");
5496 
5497     //lets get the version of the existing jar
5498     this.originalGrouperJarVersionOrUpgradeFileVersion();
5499 
5500     this.compareAndReplaceJar(this.grouperJar, 
5501         new File(this.untarredApiDir + File.separator + "dist" + File.separator 
5502             + "lib" + File.separator + "grouper.jar"), true, null);
5503     
5504     if (this.appToUpgrade == AppToUpgrade.API) {
5505       boolean hadChange = false;
5506       for (String gshName : new String[]{"gsh", "gsh.bat", "gsh.sh"}) {
5507         File newGshFile = new File(this.untarredApiDir + File.separator + "bin" 
5508             + File.separator + gshName);
5509 
5510         File existingGshFile = new File(this.upgradeExistingApplicationDirectoryString 
5511             + "bin" + File.separator + gshName);
5512 
5513         if (!GrouperInstallerUtils.contentEquals(newGshFile, existingGshFile)) {
5514           this.backupAndCopyFile(newGshFile, existingGshFile, true);
5515           if (!GrouperInstallerUtils.equals("gsh.bat", gshName)) {
5516             hadChange = true;
5517           }
5518         }
5519         
5520       }
5521       if (hadChange) {
5522         //set executable and dos2unix
5523         gshExcutableAndDos2Unix(this.untarredApiDir + File.separator + "bin" 
5524             + File.separator);
5525       }
5526     }
5527 
5528     System.out.println("\n##################################");
5529     System.out.println("Upgrading API config files\n");
5530 
5531     this.compareUpgradePropertiesFile(this.grouperBasePropertiesFile, 
5532       new File(this.untarredApiDir + File.separator + "conf" + File.separator + "grouper.base.properties"),
5533       this.grouperPropertiesFile,
5534       this.grouperExamplePropertiesFile, null, 
5535       "grouperInstaller.autorun.removeRedundantPropetiesFromGrouperProperties"
5536     );
5537       
5538     this.compareUpgradePropertiesFile(this.grouperHibernateBasePropertiesFile, 
5539         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "grouper.hibernate.base.properties"),
5540         this.grouperHibernatePropertiesFile,
5541         this.grouperHibernateExamplePropertiesFile, null, 
5542         "grouperInstaller.autorun.removeRedundantPropetiesFromGrouperHibernateProperties"
5543       );
5544         
5545     this.compareUpgradePropertiesFile(this.grouperLoaderBasePropertiesFile, 
5546         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "grouper-loader.base.properties"),
5547         this.grouperLoaderPropertiesFile,
5548         this.grouperLoaderExamplePropertiesFile, null,
5549         "grouperInstaller.autorun.removeRedundantPropetiesFromGrouperLoaderProperties"
5550       );
5551     
5552     this.compareUpgradePropertiesFile(this.subjectBasePropertiesFile, 
5553         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "subject.base.properties"),
5554         this.subjectPropertiesFile,
5555         null, null,
5556         "grouperInstaller.autorun.removeRedundantPropetiesFromSubjectProperties"
5557       );
5558     
5559     this.compareUpgradePropertiesFile(this.grouperCacheBasePropertiesFile, 
5560         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "grouper.cache.base.properties"),
5561         this.grouperCachePropertiesFile,
5562         null, null,
5563         "grouperInstaller.autorun.removeRedundantPropetiesFromGrouperCacheProperties"
5564       );
5565 
5566     this.upgradeEhcacheXml();
5567     this.upgradeEhcacheXmlToProperties();
5568     this.upgradeSourcesXmlToProperties();
5569     
5570     this.compareAndCopyFile(this.grouperUtf8File, 
5571         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "grouperUtf8.txt"),
5572         true,
5573         new File(this.upgradeExistingClassesDirectoryString)
5574         );
5575     
5576     this.compareAndCopyFile(this.gshFileLoadPropertiesFile, 
5577         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "GSHFileLoad.properties"),
5578         true,
5579         new File(this.upgradeExistingClassesDirectoryString)
5580         );
5581     
5582     this.compareAndCopyFile(this.groovyshProfileFile, 
5583         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "groovysh.profile"),
5584         true,
5585         new File(this.upgradeExistingClassesDirectoryString)
5586         );
5587     
5588     this.compareAndCopyFile(this.grouperClientUsageExampleFile, 
5589         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "grouper.client.usage.example.txt"),
5590         true,
5591         new File(this.upgradeExistingClassesDirectoryString)
5592         );
5593     
5594     //do this only if less than 2.3.1
5595     if (new GiGrouperVersionersion(this.version).lessThanArg(new GiGrouperVersion("2.3.1"))) {
5596       System.out.println("\nYou should compare " + this.grouperPropertiesFile.getParentFile().getAbsolutePath() + File.separator + "sources.xml"
5597           + "\n  with " + this.untarredApiDir + File.separator + "conf" + File.separator + "sources.xml");
5598       System.out.print("Press <enter> to continue after you have merged the sources.xml. ");
5599       readFromStdIn("grouperInstaller.autorun.continueAfterMergingSourcesXml");
5600     }
5601     
5602     System.out.println("\n##################################");
5603     System.out.println("Upgrading API jars\n");
5604 
5605     this.upgradeJars(new File(this.untarredApiDir + File.separator + "lib" 
5606       + File.separator + "grouper" + File.separator));
5607     
5608     if (this.appToUpgrade.isApiOrganized()) {
5609 
5610       //if we need to put the jars in the jdbcSamples dir...
5611       this.upgradeJars(new File(this.untarredApiDir + File.separator + "lib" 
5612           + File.separator + "jdbcSamples" + File.separator), 
5613           new File(new File(this.upgradeExistingLibDirectoryString).getParentFile().getAbsolutePath() + File.separator + "jdbcSamples"));
5614     
5615     } else {
5616     
5617       this.upgradeJars(new File(this.untarredApiDir + File.separator + "lib" 
5618           + File.separator + "jdbcSamples" + File.separator));
5619     
5620     }
5621 
5622     {
5623       File subjectJar = new File(this.upgradeExistingLibDirectoryString + "subject.jar");
5624       if (subjectJar.exists()) {
5625         this.backupAndDeleteFile(subjectJar, true);
5626       }
5627     }
5628     
5629     {
5630       File oroJar = new File(this.upgradeExistingLibDirectoryString + "jakarta-oro.jar");
5631       if (oroJar.exists()) {
5632         this.backupAndDeleteFile(oroJar, true);
5633       }
5634     }
5635     
5636     System.out.println("\n##################################");
5637     System.out.println("Patch API\n");
5638 
5639     //patch it
5640     this.patchApi();
5641 
5642     //make sure log4j is debugging sql statements
5643     log4jDebugSql(this.upgradeExistingClassesDirectoryString + "log4j.properties");
5644     
5645     //verify that grouper.hibernate.properties doesn't have legacy properties
5646     removeLegacyHibernateProperties(this.upgradeExistingClassesDirectoryString + "grouper.hibernate.properties");
5647     
5648     System.out.println("\n##################################");
5649     System.out.println("Upgrading DB (registry)\n");
5650 
5651     this.apiUpgradeDbVersion(true);
5652 
5653     this.apiUpgradeAdditionalGshScripts();
5654 
5655   }
5656 
5657   /**
5658    * run additional GSH scripts based on what we are upgrading from...
5659    */
5660   private void apiUpgradeAdditionalGshScripts() {
5661     GiGrouperVersion giGrouperVersion = this.originalGrouperJarVersionOrUpgradeFileVersion();
5662     if (giGrouperVersion == null) {
5663       System.out.println("Grouper jar file: " + (this.grouperJar == null ? null : this.grouperJar.getAbsolutePath()));
5664       System.out.println("ERROR, cannot find grouper version in grouper jar file, do you want to continue? (t|f)? [f]: ");
5665       boolean continueScript = readFromStdInBoolean(false, "grouperInstaller.autorun.shouldContinueAfterNoGrouperVersionFound");
5666       if (!continueScript) {
5667         System.exit(1);
5668       }
5669     }
5670 
5671     boolean lessThan2_0 = this.originalGrouperJarVersion.lessThanArg(new GiGrouperVersion("2.0.0"));
5672     {
5673       String runUsduAutorun = null;
5674       if (lessThan2_0) {
5675         System.out.println("You are upgrading from pre API version 2.0.0, do you want to run Unresolvable Subject Deletion Utility (USDU) (recommended) (t|f)? [t]: ");
5676         runUsduAutorun = "grouperInstaller.autorun.runUsduPre2.0.0";
5677       } else {
5678         System.out.println("You are upgrading from after API version 2.0.0, so you dont have to do this,\n  "
5679             + "but do you want to run Unresolvable Subject Deletion Utility (USDU) (not recommended) (t|f)? [f]: ");
5680         runUsduAutorun = "grouperInstaller.autorun.runUsduPost2.0.0";
5681       }
5682       boolean runScript = readFromStdInBoolean(lessThan2_0, runUsduAutorun);
5683       
5684       if (runScript) {
5685         
5686         //running with command on command line doenst work on linux since the args with whitespace translate to 
5687         //save the commands to a file, and runt he file
5688         StringBuilder gshCommands = new StringBuilder();
5689   
5690         //gsh 0% GrouperSession.startRootSession()
5691         //edu.internet2.middleware.grouper.GrouperSession: 6f94c99d5b0948a3be96f94f00ab4d87,'GrouperSystem','application'
5692         //gsh 1% // run USDU to resolve all the subjects with type=person
5693         //gsh 3% usdu()
5694   
5695         gshCommands.append("grouperSession = GrouperSession.startRootSession();\n");
5696         gshCommands.append("usdu();\n");
5697   
5698         File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "gshUsdu.gsh");
5699         GrouperInstallerUtils.saveStringIntoFile(gshFile, gshCommands.toString());
5700         
5701         List<String> commands = new ArrayList<String>();
5702   
5703         addGshCommands(commands);
5704         commands.add(gshFile.getAbsolutePath());
5705   
5706         System.out.println("\n##################################");
5707         System.out.println("Running USDU with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
5708   
5709         GrouperInstallerUtils.execCommand(
5710             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
5711            new File(this.gshCommand()).getParentFile(), null, true);
5712   
5713       }
5714     }
5715     
5716     {
5717 
5718       String autorunResolveGroupSubjects = null;
5719       if (lessThan2_0) {
5720         System.out.println("You are upgrading from pre API version 2.0.0, do you want to resolve all group subjects (recommended) (t|f)? [t]: ");
5721         autorunResolveGroupSubjects = "grouperInstaller.autorun.resolveGroupSubjectsPre2.0.0";
5722       } else {
5723         System.out.println("You are upgrading from after API version 2.0.0, so you dont have to do this,\n  "
5724             + "but do you want to resolve all group subjects (not recommended) (t|f)? [f]: ");
5725         autorunResolveGroupSubjects = "grouperInstaller.autorun.resolveGroupSubjectsPost2.0.0";
5726       }
5727       boolean runScript = readFromStdInBoolean(lessThan2_0, autorunResolveGroupSubjects);
5728       
5729       if (runScript) {
5730         
5731         //running with command on command line doenst work on linux since the args with whitespace translate to 
5732         //save the commands to a file, and runt he file
5733         StringBuilder gshCommands = new StringBuilder();
5734   
5735         //gsh 5% GrouperSession.startRootSession();
5736         //edu.internet2.middleware.grouper.GrouperSession: 4163fb08b3b24922b55a14010d48e121,'GrouperSystem','application'
5737         //gsh 6% for (String g : HibernateSession.byHqlStatic().createQuery("select uuid from Group").listSet(String.class)) { subj = SubjectFinder.findByIdAndSource(g, "g:gsa", true); GrouperDAOFactory.getFactory().getMember().findBySubject(subj).updateMemberAttributes(subj, true); }
5738   
5739         gshCommands.append("grouperSession = GrouperSession.startRootSession();\n");
5740         gshCommands.append("for (String g : HibernateSession.byHqlStatic().createQuery(\"select uuid from Group\").listSet(String.class)) { subj = SubjectFinder.findByIdAndSource(g, \"g:gsa\", true); GrouperDAOFactory.getFactory().getMember().findBySubject(subj).updateMemberAttributes(subj, true); }\n");
5741   
5742         File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "gshGroupUsdu.gsh");
5743         GrouperInstallerUtils.saveStringIntoFile(gshFile, gshCommands.toString());
5744         
5745         List<String> commands = new ArrayList<String>();
5746   
5747         addGshCommands(commands);
5748         commands.add(gshFile.getAbsolutePath());
5749   
5750         System.out.println("\n##################################");
5751         System.out.println("Resolving group subjects with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
5752 
5753         GrouperInstallerUtils.execCommand(
5754             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
5755            new File(this.gshCommand()).getParentFile(), null, true);
5756       }
5757     }
5758 
5759     {
5760       boolean lessThan2_1 = giGrouperVersion.lessThanArg(new GiGrouperVersion("2.1.0"));
5761       String autorunSeeRuleCheckType = null;
5762       if (lessThan2_1) {
5763         System.out.println("You are upgrading from pre API version 2.1.0, do you want to "
5764             + "see if you have rules with ruleCheckType: flattenedPermission* (recommended) (t|f)? [t]: ");
5765         autorunSeeRuleCheckType = "grouperInstaller.autorun.seeRulesFlattenedPermissionsPre2.1.0";
5766       } else {
5767         System.out.println("You are upgrading from after API version 2.1.0, so you dont have to do this,\n  "
5768             + "but do you want to see if you have rules with ruleCheckType: flattenedPermission* (not recommended) (t|f)? [f]: ");
5769         autorunSeeRuleCheckType = "grouperInstaller.autorun.seeRulesFlattenedPermissionsPost2.1.0";
5770       }
5771       boolean runScript = readFromStdInBoolean(lessThan2_1, autorunSeeRuleCheckType);
5772       
5773       if (runScript) {
5774         
5775         //running with command on command line doenst work on linux since the args with whitespace translate to 
5776         //save the commands to a file, and runt he file
5777         StringBuilder gshCommands = new StringBuilder();
5778     
5779         gshCommands.append("\"Count: \" + HibernateSession.bySqlStatic().select(int.class, \"SELECT count(*) FROM grouper_rules_v WHERE rule_check_type LIKE 'flattenedPermission%'\");\n");
5780   
5781         File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "gshRuleFlattenedPermissionCount.gsh");
5782         GrouperInstallerUtils.saveStringIntoFile(gshFile, gshCommands.toString());
5783         
5784         List<String> commands = new ArrayList<String>();
5785   
5786         addGshCommands(commands);
5787         commands.add(gshFile.getAbsolutePath());
5788   
5789         System.out.println("\n##################################");
5790         System.out.println("Counting flattenedPermission rules with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
5791 
5792         CommandResult commandResult = GrouperInstallerUtils.execCommand(
5793           GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
5794           new File(this.gshCommand()).getParentFile(), null, true);
5795 
5796         if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
5797           System.out.println("stderr: " + commandResult.getErrorText());
5798         }
5799         if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
5800           System.out.println("stdout: " + commandResult.getOutputText());
5801         }
5802 
5803         String result = commandResult.getOutputText().trim();
5804         String[] lines = GrouperInstallerUtils.splitLines(result);
5805         {
5806           Pattern pattern = Pattern.compile("^Count: ([0-9]+)$");
5807           int count = -1;
5808           for (String line : lines) {
5809             Matcher matcher = pattern.matcher(line);
5810             if (matcher.matches()) {
5811               count = GrouperInstallerUtils.intValue(matcher.group(1));
5812               break;
5813             }
5814           }
5815           if (count == -1) {
5816             System.out.println("Error getting count of rules, would you like to continue (t|f)? [t]:");
5817             if (!readFromStdInBoolean(true, "grouperInstaller.autorun.shouldContinueAfterErrorCountFlattenedRules")) {
5818               System.exit(1);
5819             }
5820           } else {
5821             if (count > 0) {
5822               System.out.println("You have " + count + " flattenedPermission rules that need to be removed.  You need to look in the view grouper_rules_v and notify the owners and remove these rules.  Do you want to continue (t|f)? [t]: ");
5823               
5824               if (!readFromStdInBoolean(true, "grouperInstaller.autorun.shouldContinueAfterFoundFlattenedRules")) {
5825                 System.exit(1);
5826               }
5827             }
5828           }
5829         }
5830       }
5831     }
5832 
5833     {
5834       boolean lessThan2_2_0 = giGrouperVersion.lessThanArg(new GiGrouperVersion("2.2.0"));
5835       String autorunRun2_2gshScript = null;
5836       if (lessThan2_2_0) {
5837         System.out.println("You are upgrading from pre API version 2.2.0, "
5838             + "do you want to run the 2.2 upgrade GSH script (recommended) (t|f)? [t]: ");
5839         autorunRun2_2gshScript = "grouperInstaller.autorun.run2.2gshUpgradeScriptPre2.2.0";
5840       } else {
5841         System.out.println("You are upgrading from after API version 2.2.0, so you dont have to do this,\n  "
5842             + "but do you want to run the 2.2 upgrade GSH script (not recommended) (t|f)? [f]: ");
5843         autorunRun2_2gshScript = "grouperInstaller.autorun.run2.2gshUpgradeScriptPost2.2.0";
5844       }
5845       boolean runScript = readFromStdInBoolean(lessThan2_2_0, autorunRun2_2gshScript);
5846       
5847       if (runScript) {
5848         
5849         File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "misc" + File.separator + "postGrouper2_2Upgrade.gsh");
5850         
5851         List<String> commands = new ArrayList<String>();
5852   
5853         addGshCommands(commands);
5854         commands.add(gshFile.getAbsolutePath());
5855   
5856         System.out.println("\n##################################");
5857         System.out.println("Running 2.2 upgrade GSH with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
5858   
5859         GrouperInstallerUtils.execCommand(
5860             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
5861            new File(this.gshCommand()).getParentFile(), null, true);
5862   
5863       }
5864       
5865     }
5866 
5867     {
5868       boolean lessThan2_2_1 = giGrouperVersion.lessThanArg(new GiGrouperVersion("2.2.1"));
5869       String autorunRun2_2_1gshUpgradeScript = null;
5870       if (lessThan2_2_1) {
5871         System.out.println("You are upgrading from pre API version 2.2.1, do you want to "
5872             + "run the 2.2.1 upgrade GSH script (recommended) (t|f)? [t]: ");
5873         autorunRun2_2_1gshUpgradeScript = "grouperInstaller.autorun.run2.2.1gshUpgradeScriptPre2.2.1";
5874       } else {
5875         System.out.println("You are upgrading from after API version 2.2.1, so you dont have to do this,\n  "
5876             + "but do you want to run the 2.2.1 upgrade GSH script (not recommended) (t|f)? [f]: ");
5877         autorunRun2_2_1gshUpgradeScript = "grouperInstaller.autorun.run2.2.1gshUpgradeScriptPost2.2.1";
5878       }
5879       boolean runScript = readFromStdInBoolean(lessThan2_2_1, autorunRun2_2_1gshUpgradeScript);
5880       
5881       if (runScript) {
5882         
5883         File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "misc" + File.separator + "postGrouper2_2_1Upgrade.gsh");
5884         
5885         List<String> commands = new ArrayList<String>();
5886 
5887         addGshCommands(commands);
5888         commands.add(gshFile.getAbsolutePath());
5889 
5890         System.out.println("\n##################################");
5891         System.out.println("Running 2.2.1 upgrade GSH with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
5892 
5893         GrouperInstallerUtils.execCommand(
5894             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
5895            new File(this.gshCommand()).getParentFile(), null, true);
5896 
5897       }
5898     }
5899 
5900     {
5901       boolean lessThan2_3_0 = giGrouperVersion.lessThanArg(new GiGrouperVersion("2.3.0"));
5902       String autorunRun2_3_0gshUpgradeScript = null;
5903       if (lessThan2_3_0) {
5904         System.out.println("You are upgrading from pre API version 2.3.0, do you want to "
5905             + "run the 2.3.0 upgrade GSH script (recommended) (t|f)? [t]: ");
5906         autorunRun2_3_0gshUpgradeScript = "grouperInstaller.autorun.run2.3.0gshUpgradeScriptPre2.3.0";
5907       } else {
5908         System.out.println("You are upgrading from after API version 2.3.0, so you dont have to do this,\n  "
5909             + "but do you want to run the 2.3.0 upgrade GSH script (not recommended) (t|f)? [f]: ");
5910         autorunRun2_3_0gshUpgradeScript = "grouperInstaller.autorun.run2.3.0gshUpgradeScriptPost2.3.0";
5911       }
5912       boolean runScript = readFromStdInBoolean(lessThan2_3_0, autorunRun2_3_0gshUpgradeScript);
5913       
5914       if (runScript) {
5915         
5916         File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "misc" + File.separator + "postGrouper2_3_0Upgrade.gsh");
5917         
5918         List<String> commands = new ArrayList<String>();
5919 
5920         addGshCommands(commands);
5921         commands.add(gshFile.getAbsolutePath());
5922 
5923         System.out.println("\n##################################");
5924         System.out.println("Running 2.3.0 upgrade GSH with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
5925 
5926         GrouperInstallerUtils.execCommand(
5927             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
5928            new File(this.gshCommand()).getParentFile(), null, true);
5929 
5930       }
5931     }
5932 
5933   }
5934 
5935   /**
5936    * grouper version of jar
5937    */
5938   private GiGrouperVersion grouperVersionOfJar = null;
5939   
5940   /**
5941    * 
5942    * @return the version
5943    */
5944   private GiGrouperVersion grouperVersionOfJar() {
5945     if (this.grouperVersionOfJar == null) {
5946       String grouperJarVersionString = null;
5947       if (this.grouperJar != null && this.grouperJar.exists()) {
5948         grouperJarVersionString = GrouperInstallerUtils.jarVersion(this.grouperJar);
5949         
5950       } else if (this.grouperClientJar != null && this.grouperClientJar.exists()) {
5951         grouperJarVersionString = GrouperInstallerUtils.jarVersion(this.grouperClientJar);
5952         
5953       }
5954       if (!GrouperInstallerUtils.isBlank(grouperJarVersionString)) {
5955         this.grouperVersionOfJar = new GiGrouperVersion(grouperJarVersionString);
5956       }
5957   
5958       if (this.grouperVersionOfJar == null) {
5959         throw new RuntimeException("Cant find version of grouper! " + this.grouperJar + ", " + this.grouperClientJar);
5960       }
5961     }
5962     
5963     return this.grouperVersionOfJar;
5964   }
5965   
5966   /**
5967    * grouper jar version e.g. 2.1.5
5968    */
5969   private GiGrouperVersion originalGrouperJarVersion = null;
5970   
5971   /**
5972    * keep trac if we have found it or not
5973    */
5974   private boolean originalGrouperJarVersionRetrieved = false;
5975   
5976   /**
5977    * get the version of the grouper jar
5978    * @return the version or null if couldnt be found
5979    */
5980   private GiGrouperVersion originalGrouperJarVersionOrUpgradeFileVersion() {
5981 
5982     if (!this.originalGrouperJarVersionRetrieved) {
5983 
5984       this.originalGrouperJarVersionRetrieved = true;
5985       
5986       //lets see if an upgrade went halfway through
5987       this.grouperUpgradeOriginalVersionFile = new File(this.upgradeExistingApplicationDirectoryString + "grouperUpgradeOriginalVersion.txt");
5988 
5989       this.originalGrouperJarVersion = this.grouperVersionOfJar();
5990       
5991       if (this.grouperUpgradeOriginalVersionFile.exists()) {
5992         String grouperJarVersionString = GrouperInstallerUtils.readFileIntoString(this.grouperUpgradeOriginalVersionFile);
5993         GiGrouperVersionrsion.html#GiGrouperVersion">GiGrouperVersion fileGrouperJarVersion = new GiGrouperVersion(grouperJarVersionString);
5994         
5995         if (fileGrouperJarVersion != this.originalGrouperJarVersion) {
5996           
5997           System.out.println("It is detected that an upgrade did not complete from version " + fileGrouperJarVersion);
5998           this.originalGrouperJarVersion = fileGrouperJarVersion;
5999         }
6000       } else {
6001         GrouperInstallerUtils.writeStringToFile(this.grouperUpgradeOriginalVersionFile, this.originalGrouperJarVersion.toString());
6002       }
6003     }
6004     
6005     return this.originalGrouperJarVersion;
6006   }
6007   
6008   /**
6009    * file where version is kept for partial upgrades
6010    */
6011   private File grouperUpgradeOriginalVersionFile;
6012   
6013   /**
6014    * @param firstTime if first time
6015    */
6016   private void apiUpgradeDbVersion(boolean firstTime) {
6017     
6018     if (!GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.api.checkDdlVersion", true, false)) {
6019       System.out.println("Not checking DDL version since grouper.installer.properties: grouperInstaller.default.api.checkDdlVersion = false");
6020       return;
6021     }
6022 
6023     List<String> commands = new ArrayList<String>();
6024 
6025     addGshCommands(commands);
6026     commands.add("-registry");
6027     commands.add("-check");
6028     commands.add("-noprompt");
6029 
6030     System.out.println("\n##################################");
6031     System.out.println("Checking API database version with command: " + convertCommandsIntoCommand(commands) + "\n");
6032 
6033     CommandResult commandResult = GrouperInstallerUtils.execCommand(
6034         GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
6035         new File(this.gshCommand()).getParentFile(), null, true);
6036     
6037     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
6038       System.out.println("stdout: " + commandResult.getOutputText());
6039     }
6040     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
6041       System.out.println("stderr: " + commandResult.getErrorText());
6042     }
6043 
6044     String result = commandResult.getErrorText().trim();
6045     
6046     // Grouper ddl object type 'Grouper' has dbVersion: 27 and java version: 28
6047     // NOTE: Grouper database schema DDL may require updates, but the temp change log must 
6048     // be empty to perform an upgrade.  To process the temp change log, start up your current 
6049     // version of GSH and run: loaderRunOneJob("CHANGE_LOG_changeLogTempToChangeLog")
6050     if (result != null && result.contains("CHANGE_LOG_changeLogTempToChangeLog")) {
6051       System.out.println("You must run the change log temp to change log before upgrading.  You can start the upgrader again and run it.");
6052       System.exit(1);
6053     }
6054     
6055     String[] lines = GrouperInstallerUtils.splitLines(result);
6056     {
6057       boolean okWithVersion = false;
6058       boolean notOkWithVersion = false;
6059       for (String line : lines) {
6060         line = line.toLowerCase();
6061         //expecting stderr: NOTE: database table/object structure (ddl) is up to date
6062         if (line.contains("ddl") && line.contains("up to date") && line.contains("note:")) {
6063           okWithVersion = true;
6064         }
6065         //cant have this line
6066         if (line.contains("requires updates")) {
6067           notOkWithVersion = true;
6068         }
6069       }
6070       if (okWithVersion && !notOkWithVersion) {
6071         return;
6072       }
6073     }
6074 
6075     if (!firstTime) {
6076       System.out.println("Error: we tried to upgrade the database but it didnt work, would you like to continue skipping DDL (t|f)? ");
6077       boolean continueOn = readFromStdInBoolean(null, "grouperInstaller.autorun.shouldContinueIfErrorUpgradingDatabase");
6078       if (continueOn) {
6079         return;
6080       }
6081     }
6082     
6083     //we need to upgrade the DDL
6084     //Grouper ddl object type 'Grouper' has dbVersion: 26 and java version: 28
6085     //Grouper database schema DDL requires updates
6086     //(should run script manually and carefully, in sections, verify data before drop statements, backup/export important data before starting, follow change log on confluence, dont run exact same script in multiple envs - generate a new one for each env),
6087     //script file is:
6088     //C:\app\grouper_2_2_0_installer\grouper.apiBinary-2.2.0\ddlScripts\grouperDdl_20141014_10_17_12_577.sql
6089     //Note: this script was not executed due to option passed in
6090     //To run script via gsh, carefully review it, then run this:
6091     //gsh -registry -runsqlfile C:\\app\\grouper_2_2_0_installer\\grouper.apiBinary-2.2.0\\ddlScripts\\grouperDdl_20141014_10_17_12_577.sql
6092 
6093     System.out.println("Review the script(s) above if there are any, do you want the upgrader to run it to upgrade the DDL for you (t|f)? [t]: ");
6094     boolean runIt = readFromStdInBoolean(true, "grouperInstaller.autorun.shouldRunDdlScript");
6095     
6096     if (runIt) {
6097 
6098       boolean foundScript = false;
6099 
6100       for (String line : lines) {
6101         if (line.contains("-registry -runsqlfile")) {
6102           
6103           String regexPattern = "^[^\\s]+\\s+-registry -runsqlfile (.*)$";
6104           Pattern pattern = Pattern.compile(regexPattern);
6105           
6106           Matcher matcher = pattern.matcher(line);
6107           
6108           if (!matcher.matches()) {
6109             throw new RuntimeException("Expected " + regexPattern + " but received: " + line);
6110           }
6111 
6112           String fileName = matcher.group(1);
6113           
6114           commands = new ArrayList<String>();
6115           
6116           addGshCommands(commands);
6117           commands.add("-registry");
6118           commands.add("-noprompt");
6119           commands.add("-runsqlfile");
6120           commands.add(fileName);
6121           
6122           foundScript = true;
6123           
6124           System.out.println("\n##################################");
6125           System.out.println("Upgrading database with command: " + convertCommandsIntoCommand(commands) + "\n");
6126 
6127           commandResult = GrouperInstallerUtils.execCommand(
6128               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
6129              new File(this.gshCommand()).getParentFile(), null, true);
6130 
6131           //no out/err since printing as we go
6132           System.out.println("\nDone upgrading database");
6133           System.out.println("\n##################################\n");
6134         }
6135       }
6136       //cant find script, thats ok, just check and go
6137       if (!foundScript) {
6138         throw new RuntimeException("didnt find script to to run: " + result);
6139       }
6140 
6141       //check again to make sure ok
6142       apiUpgradeDbVersion(false);
6143     }
6144   }
6145 
6146   /**
6147    * upgrade jars from a directory
6148    * @param fromDir jars from this directory
6149    */
6150   private void upgradeJars(File fromDir) {
6151     this.upgradeJars(fromDir, new File(this.upgradeExistingLibDirectoryString));
6152   }
6153   
6154   /**
6155    * upgrade jars from a directory
6156    * @param fromDir jars from this directory
6157    * @param toDir is where jars go if not there
6158    */
6159   private void upgradeJars(File fromDir, File toDir) {
6160 
6161     //for each jar in the directory
6162     if (!fromDir.exists() || !fromDir.isDirectory()) {
6163       throw new RuntimeException("Why does jar directory not exist? " + fromDir);
6164     }
6165     
6166     int changes = 0;
6167     
6168     // sort the files to get them a little more reproducible
6169     File[] fromFiles = GrouperInstallerUtils.nonNull(fromDir.listFiles(), File.class);
6170     List<File> fromFilesList = GrouperInstallerUtils.toList(fromFiles);
6171     Collections.sort(fromFilesList);
6172     for (File jarFile : fromFilesList) {
6173       
6174       //only do jar files
6175       if (!jarFile.getName().endsWith(".jar")) {
6176         continue;
6177       }
6178       
6179       // File existingJar = this.findLibraryFile(jarFile.getName(), false);
6180 
6181       List<File> relatedJars = null;
6182       
6183       relatedJars = GrouperInstallerUtils.jarFindJar(toDir, jarFile.getName());
6184       
6185       boolean foundFile = false;
6186       if (GrouperInstallerUtils.length(relatedJars) > 0) {
6187         
6188         for (File relatedJar : relatedJars) {
6189           if (!relatedJar.exists()) {
6190             continue;
6191           }
6192           if (GrouperInstallerUtils.fileSha1(relatedJar).equals(GrouperInstallerUtils.fileSha1(jarFile))) {
6193             if (relatedJar.getName().equals(jarFile.getName())) {
6194               foundFile = true;
6195               continue;
6196             }
6197           }
6198           
6199           File bakFile = bakFile(relatedJar);
6200           
6201           System.out.println("Deleting " + relatedJar.getAbsolutePath() + ", backed up to: " + bakFile.getAbsolutePath());
6202           changes++;
6203           boolean moved = GrouperInstallerUtils.fileMove(relatedJar, bakFile, false);
6204           if (!moved) {
6205             System.out.println("Non-fatal error: could not delete file: " + relatedJar.getAbsolutePath() 
6206                 + ",\ndelete this file when all processed are terminated.  Press <enter> to acknowledge this.");
6207             readFromStdIn("grouperInstaller.autorun.continueAfterCantDeleteJar");
6208           }
6209         }
6210       }
6211       if (!foundFile) {
6212         changes += this.compareAndReplaceJar(null, jarFile, false, toDir) ? 1 : 0;
6213       }
6214           
6215       
6216 //      if (existingJar == null) {
6217 //        //see if one exists by another version
6218 //        if (GrouperInstallerUtils.length(relatedJars) == 1) {
6219 //          existingJar = relatedJars.get(0);
6220 //        }
6221 //      }
6222 
6223     }
6224 
6225     System.out.println("Upgraded " + changes + " jar files from: " + fromDir.getAbsolutePath()
6226         + "\n  to: " + toDir.getAbsolutePath());
6227     
6228   }
6229   
6230   /**
6231    * 
6232    */
6233   private void upgradeEhcacheXml() {
6234 
6235     //ehcache, prompt to see if do it (if difference than example, and if old example different than new example?
6236     File newEhcacheExample = new File(this.untarredApiDir + File.separator + "conf" + File.separator + "ehcache.xml");
6237 
6238     //this file is done
6239     if (!newEhcacheExample.exists() || this.ehcacheFile == null || !this.ehcacheFile.exists()) {
6240       return;
6241     }
6242     
6243     //lets see if different
6244     String existingEhcacheContents = GrouperInstallerUtils.readFileIntoString(this.ehcacheFile);
6245     String existingExampleEhcacheContents = GrouperInstallerUtils.readFileIntoString(this.ehcacheExampleFile);
6246     String newEhcacheContents = GrouperInstallerUtils.readFileIntoString(newEhcacheExample);
6247     
6248     //if existing is the same as new...
6249     if (GrouperInstallerUtils.equals(existingEhcacheContents, newEhcacheContents)) {
6250       //make sure example is up to date
6251       if (this.ehcacheExampleFile != null && !GrouperInstallerUtils.equals(existingExampleEhcacheContents, newEhcacheContents)) {
6252         this.backupAndCopyFile(newEhcacheExample, this.ehcacheExampleFile, true);
6253       }
6254 
6255       //we are all good
6256       return;
6257     }
6258 
6259     //lets backup the example and regular file
6260     File ehcacheBakFile = bakFile(this.ehcacheFile);
6261     GrouperInstallerUtils.copyFile(this.ehcacheFile, ehcacheBakFile, true);
6262 
6263     boolean mergeFiles = true;
6264     
6265     if (this.ehcacheExampleFile != null) {
6266       File ehcacheExampleBakFile = bakFile(this.ehcacheExampleFile);
6267   
6268       GrouperInstallerUtils.copyFile(this.ehcacheExampleFile, ehcacheExampleBakFile, true);
6269     } else {
6270       GrouperInstallerUtils.copyFile(newEhcacheExample, this.ehcacheFile, true);
6271       mergeFiles = false;
6272     }
6273 
6274     if (mergeFiles) {
6275       //if the ehcache is the same as the example, lets just copy
6276       if (GrouperInstallerUtils.equals(existingEhcacheContents, existingExampleEhcacheContents)) {
6277         this.backupAndCopyFile(newEhcacheExample, this.ehcacheFile, false);
6278         if (this.ehcacheExampleFile != null) {
6279           this.backupAndCopyFile(newEhcacheExample, this.ehcacheExampleFile, false);
6280         }
6281         return;
6282       }
6283 
6284       //the ehcache file is different from the example and different from the new one, so merge it in
6285       mergeEhcacheXmlFiles(newEhcacheExample, this.ehcacheExampleFile, this.ehcacheFile);
6286     }
6287 
6288     System.out.println("Compare you old ehcache.xml with the new ehcache.xml file: " 
6289         + "\n  Old file: "
6290         + ehcacheBakFile.getAbsolutePath()
6291         + "\n  New file: " + this.ehcacheFile.getAbsolutePath()
6292         + "\n  Press <enter> when done");
6293     readFromStdIn("grouperInstaller.autorun.continueAfterCompareEhcache");
6294 
6295   }
6296 
6297   /**
6298    * 
6299    */
6300   private void upgradeEhcacheXmlToProperties() {
6301 
6302     //dont do this is less than 2.3.1
6303     if (new GiGrouperVersionersion(this.version).lessThanArg(new GiGrouperVersion("2.3.1"))) {
6304       return;
6305     }
6306     
6307     // the file may have been added during the install
6308     if (this.grouperCacheBasePropertiesFile == null) {
6309       this.grouperCacheBasePropertiesFile = findClasspathFile("grouper.cache.base.properties", false);
6310     }
6311     
6312     //this file is done
6313     if ((this.ehcacheFile == null || !this.ehcacheFile.exists())
6314         && this.grouperCacheBasePropertiesFile.exists() && this.grouperCachePropertiesFile.exists()) {
6315       return;
6316     }
6317     
6318     System.out.print("Do you want to convert from ehcache.xml to grouper.cache.properties, note you need to do this to upgrade (t|f)? [t]: ");
6319     boolean convert = readFromStdInBoolean(true, "grouperInstaller.autorun.convertEhcacheXmlToProperties");
6320 
6321     if (!convert) {
6322       System.out.println("Note: grouper will not run, but whatever you want to do!!!!");
6323     }
6324     
6325     // the file may have been added during the install
6326     if (this.grouperCachePropertiesFile == null) {
6327       this.grouperCachePropertiesFile = findClasspathFile("grouper.cache.properties", false);
6328     }
6329     
6330     if (this.grouperCachePropertiesFile.exists()) {
6331       //see if there is anything in it
6332       Properties grouperCacheProperties = GrouperInstallerUtils.propertiesFromFile(this.grouperCachePropertiesFile);
6333       if (grouperCacheProperties.size() > 0) {
6334         this.backupAndDeleteFile(this.grouperCachePropertiesFile, true);
6335       } else {
6336         GrouperInstallerUtils.fileDelete(this.grouperCachePropertiesFile);
6337       }
6338     }
6339 
6340     URL ehcacheXmlUrl = null;
6341     
6342     try {
6343       ehcacheXmlUrl = this.ehcacheFile.toURI().toURL();
6344     } catch (Exception e) {
6345       throw new RuntimeException("Problem with ehcache.xml: " + (this.ehcacheFile == null ? null : this.ehcacheFile.getAbsoluteFile()), e);
6346     }
6347     
6348     //convert
6349     convertEhcacheXmlToProperties(this.grouperCacheBasePropertiesFile, this.grouperCachePropertiesFile, ehcacheXmlUrl);
6350     
6351     File bakFile = bakFile(this.grouperCachePropertiesFile);
6352     GrouperInstallerUtils.copyFile(this.grouperCachePropertiesFile, bakFile, true);
6353     this.backupAndDeleteFile(this.ehcacheFile, true);
6354     this.backupAndDeleteFile(this.ehcacheExampleFile, true);
6355     
6356   }
6357 
6358   /**
6359    * 
6360    * @param newFile
6361    * @param existingFile
6362    * @param printDetails
6363    * @return the bakFile
6364    */
6365   public File backupAndCopyFile(File newFile, File existingFile, boolean printDetails) {
6366     
6367     if (!GrouperInstallerUtils.contentEquals(newFile, existingFile)) {
6368       
6369       File bakFile = null;
6370           
6371       boolean fileExists = existingFile.exists();
6372       if (fileExists) {
6373         bakFile = bakFile(existingFile);
6374         GrouperInstallerUtils.copyFile(existingFile, bakFile, true);
6375         if (printDetails) {
6376           System.out.println("Backing up: " + existingFile.getAbsolutePath() + " to: " + bakFile.getAbsolutePath());
6377         }
6378       }
6379       if (printDetails) {
6380         System.out.println("Copying " + (fileExists ? "new file" : "upgraded file") + ": " + newFile.getAbsolutePath() + " to: " + existingFile.getAbsolutePath());
6381       }
6382       GrouperInstallerUtils.copyFile(newFile, existingFile, false);
6383       return bakFile;
6384       
6385     }
6386 
6387     if (printDetails) {
6388       System.out.println(existingFile.getAbsolutePath() + " has not been updated so it was not changed");
6389     }
6390     
6391     return null;
6392   }
6393 
6394   /**
6395    * @param file
6396    * @param printDetails
6397    * @return the bakFile
6398    */
6399   public File backupAndDeleteFile(File file, boolean printDetails) {
6400 
6401     if (file != null && file.exists()) {
6402 
6403       File bakFile = null;
6404 
6405       bakFile = bakFile(file);
6406       GrouperInstallerUtils.copyFile(file, bakFile, true);
6407       if (printDetails) {
6408         System.out.println("Backing up: " + file.getAbsolutePath() + " to: " + bakFile.getAbsolutePath());
6409       }
6410       if (printDetails) {
6411         System.out.println("Deleting file: " + file.getAbsolutePath());
6412       }
6413       GrouperInstallerUtils.fileDelete(file);
6414       return bakFile;
6415 
6416     }
6417 
6418     if (printDetails) {
6419       System.out.println(file + " did not exist so it was not deleted");
6420     }
6421 
6422     return null;
6423   }
6424 
6425   /**
6426    * 
6427    * @param existingFile
6428    * @return the bak file
6429    */
6430   public File bakFile(File existingFile) {
6431     String existingFilePath = existingFile.getAbsolutePath();
6432     if (!GrouperInstallerUtils.filePathStartsWith(existingFilePath, this.upgradeExistingApplicationDirectoryString)) {
6433       throw new RuntimeException("Why does existing path not start with upgrade path??? " 
6434           + existingFilePath + ", " + this.upgradeExistingApplicationDirectoryString);
6435     }
6436     
6437     String bakString = this.grouperBaseBakDir 
6438         + existingFilePath.substring(this.upgradeExistingApplicationDirectoryString.length());
6439 
6440     File bakFile = new File(bakString);
6441     return bakFile;
6442   }
6443   
6444   /**
6445    * @param existingBasePropertiesFile 
6446    * @param newBasePropertiesFile 
6447    * @param existingPropertiesFile 
6448    * @param existingExamplePropertiesFile 
6449    * @param propertiesToIgnore
6450    * @param autorunPropertiesKeyRemoveRedundantProperties key in properties file to automatically fill in a value
6451    */
6452   private void compareUpgradePropertiesFile(File existingBasePropertiesFile, 
6453       File newBasePropertiesFile,
6454       File existingPropertiesFile,
6455       File existingExamplePropertiesFile,
6456       Set<String> propertiesToIgnore, String autorunPropertiesKeyRemoveRedundantProperties) {
6457 
6458     boolean hadChange = false;
6459     
6460     if (!newBasePropertiesFile.exists() || !newBasePropertiesFile.isFile()) {
6461       throw new RuntimeException("Why does this file not exist? " + newBasePropertiesFile.getAbsolutePath());
6462     }
6463     
6464     //if there is an existing base properties file, compare and replace and done
6465     if (existingBasePropertiesFile != null && existingBasePropertiesFile.exists() && existingBasePropertiesFile.isFile()) {
6466       
6467       String existingBaseContents = GrouperInstallerUtils.readFileIntoString(existingBasePropertiesFile);
6468       String newBaseContents = GrouperInstallerUtils.readFileIntoString(newBasePropertiesFile);
6469       
6470       if (!GrouperInstallerUtils.equals(existingBaseContents, newBaseContents)) {
6471         
6472         String existingBasePropertiesFilePath = existingBasePropertiesFile.getAbsolutePath();
6473         if (!GrouperInstallerUtils.filePathStartsWith(existingBasePropertiesFilePath, this.upgradeExistingApplicationDirectoryString)) {
6474           throw new RuntimeException("Why does existing path not start with upgrade path??? " 
6475               + existingBasePropertiesFilePath + ", " + this.upgradeExistingApplicationDirectoryString);
6476         }
6477         
6478         String bakBasePropertiesString = this.grouperBaseBakDir + existingBasePropertiesFilePath.substring(this.upgradeExistingApplicationDirectoryString.length());
6479 
6480         File bakBasePropertiesFile = new File(bakBasePropertiesString);
6481         
6482         //make sure parents exist
6483         GrouperInstallerUtils.createParentDirectories(bakBasePropertiesFile);
6484 
6485         System.out.println(existingBasePropertiesFile.getName() + " has changes and was upgraded.\n  It is backed up to " 
6486             + bakBasePropertiesFile.getAbsolutePath());
6487         
6488         hadChange = true;
6489         
6490         GrouperInstallerUtils.fileMove(existingBasePropertiesFile, bakBasePropertiesFile);
6491         
6492         GrouperInstallerUtils.copyFile(newBasePropertiesFile, existingBasePropertiesFile, true);
6493 
6494       }
6495       
6496     } else {
6497       
6498       hadChange = true;
6499       
6500       System.out.println(newBasePropertiesFile.getName() + " didn't exist and was installed.");
6501       
6502       //its null, but we dont have the path...
6503       if (existingBasePropertiesFile == null) {
6504         existingBasePropertiesFile = new File(this.upgradeExistingClassesDirectoryString + newBasePropertiesFile.getName());
6505       }
6506       GrouperInstallerUtils.copyFile(newBasePropertiesFile, existingBasePropertiesFile, true);
6507     }
6508     
6509     // if there is an example there, it can be removed
6510     if (existingExamplePropertiesFile != null && existingExamplePropertiesFile.exists() && existingExamplePropertiesFile.isFile()) {
6511 
6512       String existingExamplePropertiesFilePath = existingExamplePropertiesFile.getAbsolutePath();
6513       if (!GrouperInstallerUtils.filePathStartsWith(existingExamplePropertiesFilePath, this.upgradeExistingApplicationDirectoryString)) {
6514         throw new RuntimeException("Why does existing path not start with upgrade path??? " 
6515             + existingExamplePropertiesFilePath + ", " + this.upgradeExistingApplicationDirectoryString);
6516       }
6517       
6518       String bakExamplePropertiesString = this.grouperBaseBakDir 
6519           + existingExamplePropertiesFilePath.substring(this.upgradeExistingApplicationDirectoryString.length());
6520 
6521       File bakExamplePropertiesFile = new File(bakExamplePropertiesString);
6522       
6523       //make sure parents exist
6524       GrouperInstallerUtils.createParentDirectories(bakExamplePropertiesFile);
6525 
6526       System.out.println(existingExamplePropertiesFile.getName() + " is not needed and was deleted.\n  It is backed up to " 
6527           + bakExamplePropertiesFile.getAbsolutePath());
6528 
6529       GrouperInstallerUtils.fileMove(existingExamplePropertiesFile, bakExamplePropertiesFile);
6530     
6531     }
6532 
6533     if (existingPropertiesFile != null && existingPropertiesFile.exists() && existingPropertiesFile.isFile()) {
6534       
6535       // now then, if there is a properties file, we can look for duplicate configs, and remove them...
6536       Set<String> duplicateConfigPropertyNames = configPropertyDuplicates(newBasePropertiesFile, existingPropertiesFile);
6537 
6538       if (GrouperInstallerUtils.length(propertiesToIgnore) > 0 && GrouperInstallerUtils.length(duplicateConfigPropertyNames) > 0) {
6539         duplicateConfigPropertyNames.addAll(propertiesToIgnore);
6540       }
6541       
6542       if (GrouperInstallerUtils.length(duplicateConfigPropertyNames) > 0) {
6543 
6544         hadChange = true;
6545         
6546         System.out.println(existingPropertiesFile.getName() + " has " + duplicateConfigPropertyNames.size() 
6547             + " properties that can be removed since the values are the same in "
6548             + newBasePropertiesFile.getName());
6549 
6550         System.out.println("Would you like to have the " + duplicateConfigPropertyNames.size() 
6551             + " redundant properties automatically removed from " 
6552             + existingPropertiesFile.getName() + " (t|f)? [t]: ");
6553         boolean removeRedundantProperties = readFromStdInBoolean(true, autorunPropertiesKeyRemoveRedundantProperties);
6554         
6555         if (removeRedundantProperties) {
6556 
6557           String existingPropertiesFilePath = existingPropertiesFile.getAbsolutePath();
6558           if (!GrouperInstallerUtils.filePathStartsWith(existingPropertiesFilePath, this.upgradeExistingApplicationDirectoryString)) {
6559             throw new RuntimeException("Why does existing path not start with upgrade path??? " 
6560                 + existingPropertiesFilePath + ", " + this.upgradeExistingApplicationDirectoryString);
6561           }
6562           
6563           String bakPropertiesString = this.grouperBaseBakDir 
6564               + existingPropertiesFilePath.substring(this.upgradeExistingApplicationDirectoryString.length());
6565 
6566           File bakPropertiesFile = new File(bakPropertiesString);
6567           
6568           //make sure parents exist
6569           GrouperInstallerUtils.createParentDirectories(bakPropertiesFile);
6570 
6571           System.out.println(existingPropertiesFile.getName() + " had redundant properties removed after being backed up to " 
6572               + bakPropertiesFile.getAbsolutePath());
6573 
6574           GrouperInstallerUtils.copyFile(existingPropertiesFile, bakPropertiesFile, true);
6575           
6576           removeRedundantProperties(existingPropertiesFile, duplicateConfigPropertyNames);
6577           
6578         }
6579       }
6580     } else {
6581       
6582       hadChange = true;
6583       
6584       //if we didnt have a properties file, create one
6585       //file is null...
6586       String contents = "\n# The " + newBasePropertiesFile.getName().replace(".base", "") 
6587           + " file uses Grouper Configuration Overlays (documented on wiki)\n"
6588           + "# By default the configuration is read from " + newBasePropertiesFile.getName() + "\n"
6589           + "# (which should not be edited), and the " +newBasePropertiesFile.getName().replace(".base", "") + " overlays\n"
6590           + "# the base settings.  See the " + newBasePropertiesFile.getName() + " for the possible\n"
6591           + "# settings that can be applied to the " + newBasePropertiesFile.getName().replace(".base", "") + "\n\n";
6592 
6593       File file = null;
6594       
6595       if (existingPropertiesFile != null) {
6596         file = existingPropertiesFile;
6597       } else if (existingBasePropertiesFile != null) {
6598         file = new File(existingBasePropertiesFile.getAbsolutePath().replace(".base", ""));
6599 //      } else {
6600 //        String fileName =  existingPropertiesFile != null ? existingPropertiesFile.getAbsolutePath() : this.upgradeExistingClassesDirectoryString + newBasePropertiesFile.getName().replace(".base", "");
6601 //        file = new File(fileName);
6602       }
6603       
6604       System.out.println("Created overlay config file: " + file.getAbsolutePath());
6605       
6606       GrouperInstallerUtils.saveStringIntoFile(file, contents);
6607     }
6608     
6609     if (!hadChange) {
6610       System.out.println("Found no changes in " + existingBasePropertiesFile.getAbsolutePath());
6611     }
6612     
6613   }
6614 
6615   /**
6616    * remove duplicate properties
6617    * @param propertiesFile
6618    * @param duplicatePropertyNames
6619    */
6620   private static void removeRedundantProperties(File propertiesFile, Set<String> duplicatePropertyNames) {
6621     
6622     String fileContents = GrouperInstallerUtils.readFileIntoString(propertiesFile);
6623     
6624     String newline = GrouperInstallerUtils.newlineFromFile(fileContents);
6625 
6626     StringBuilder newContents = new StringBuilder();
6627 
6628     String[] lines = GrouperInstallerUtils.splitLines(fileContents);
6629     
6630     boolean inStartComments = true;
6631     boolean inHeaderComments = false;
6632 
6633     StringBuilder captureHeader = new StringBuilder();
6634     StringBuilder propertyAndComments = new StringBuilder();
6635     
6636     for (String line: lines) {
6637       
6638       line = line.trim();
6639       
6640       boolean isBlank = GrouperInstallerUtils.isBlank(line);
6641       boolean isComment = line.startsWith("#");
6642       boolean isSingleComment = line.startsWith("#") && !line.startsWith("##");
6643       boolean isHeaderComment = line.contains("#####");
6644       boolean isProperty = !isBlank && !isComment;
6645       
6646       //if in header then we are done with the start comments
6647       if (isHeaderComment) {
6648         inStartComments = false;
6649       }
6650 
6651       //we want to keep the start comments
6652       if (inStartComments) {
6653         
6654         if (isBlank || isComment) {
6655           newContents.append(line).append(newline);
6656           continue;
6657         }
6658         inStartComments = false;
6659       }
6660 
6661       //we are done with headers
6662       if (isProperty || isBlank || isSingleComment) {
6663         inHeaderComments = false;
6664       }
6665 
6666       if (isHeaderComment) {
6667         //if header and in headers, then we arent in headers
6668         if (inHeaderComments) {
6669           inHeaderComments = false;
6670         } else {
6671           //if this is a header, and we arent in headers, then we are in headers
6672           inHeaderComments = true;          
6673           captureHeader.setLength(0);
6674         }
6675       }
6676 
6677       if (isHeaderComment || inHeaderComments) {
6678         propertyAndComments.setLength(0);
6679         captureHeader.append(line).append(newline);
6680         continue;
6681       }
6682       
6683       if (isProperty) {
6684         
6685         //get the property
6686         int equalsIndex = line.indexOf('=');
6687         if (equalsIndex == -1) {
6688           //uh... ignore this... 
6689           System.out.println("Invalid line removed from properties file: " + propertiesFile.getAbsolutePath() + ":\n  " + line);
6690           continue;
6691         }
6692         
6693         String propertyName = line.substring(0, equalsIndex).trim();
6694         //unescape colons...
6695         if (duplicatePropertyNames.contains(propertyName) || duplicatePropertyNames.contains(propertyName.replace("\\:", ":"))) {
6696           propertyAndComments.setLength(0);
6697           //remove it!
6698           continue;
6699         }
6700 
6701         //keep it
6702         propertyAndComments.append(line).append(newline);
6703 
6704         //we need a header if there is one
6705         if (captureHeader.length() > 0) {
6706           newContents.append(newline);
6707           newContents.append(captureHeader);
6708           captureHeader.setLength(0);
6709         }
6710 
6711         //append the property and contents
6712         newContents.append(propertyAndComments);
6713 
6714         propertyAndComments.setLength(0);
6715         continue;
6716       }
6717       
6718       //must be whitespace or comment...
6719       propertyAndComments.append(line).append(newline);
6720     }
6721     
6722     GrouperInstallerUtils.saveStringIntoFile(propertiesFile, newContents.toString());
6723     
6724   }
6725   
6726   /**
6727    * 
6728    * @param file1
6729    * @param file2
6730    * @return the property names which are the same
6731    */
6732   @SuppressWarnings("unchecked")
6733   public static Set<String> configPropertyDuplicates(File file1, File file2) {
6734     Properties file1properties = GrouperInstallerUtils.propertiesFromFile(file1);
6735     Properties file2properties = GrouperInstallerUtils.propertiesFromFile(file2);
6736     
6737     Set<String> duplicatePropertyNames = new LinkedHashSet<String>();
6738     
6739     for (String propertyName : (Set<String>)(Object)file2properties.keySet()) {
6740       
6741       String file1Value = GrouperInstallerUtils.trimToEmpty(file1properties.getProperty(propertyName));
6742       String file2Value = GrouperInstallerUtils.trimToEmpty(file2properties.getProperty(propertyName));
6743       
6744       if (GrouperInstallerUtils.equals(file1Value, file2Value)) {
6745         duplicatePropertyNames.add(propertyName);
6746       }
6747       
6748     }
6749     return duplicatePropertyNames;
6750   }
6751   
6752   
6753   /**
6754    * the location of the existing installation, must end in file separator
6755    */
6756   private String upgradeExistingApplicationDirectoryString;
6757   
6758   /**
6759    * 
6760    */
6761   private static enum AppToUpgrade {
6762     
6763     /**
6764      * upgrading the UI
6765      */
6766     UI {
6767 
6768       @Override
6769       public void patchStatus(GrouperInstaller grouperInstaller) {
6770         grouperInstaller.patchStatusUi();
6771       }
6772 
6773       @Override
6774       public void patch(GrouperInstaller grouperInstaller) {
6775         grouperInstaller.patchUi();
6776       }
6777 
6778       @Override
6779       public void revertPatch(GrouperInstaller grouperInstaller) {
6780         grouperInstaller.patchRevertUi();
6781       }
6782 
6783       @Override
6784       public boolean validateExistingDirectory(GrouperInstaller grouperInstaller) {
6785         //API and client are in the UI
6786         if (!API.validateExistingDirectory(grouperInstaller)) {
6787           return false;
6788         }
6789         
6790         //no need to check if it exists... its new in 2.2
6791 
6792         //grouperInstaller.mediaPropertiesFile = grouperInstaller.findClasspathFile("media.properties", false);
6793 
6794         //media should be there, but not forever
6795         
6796         return true;
6797       }
6798 
6799       @Override
6800       public void downloadAndBuildGrouperProjects(GrouperInstaller grouperInstaller) {
6801         API.downloadAndBuildGrouperProjects(grouperInstaller);
6802         
6803         //####################################
6804         //download and configure ui
6805         grouperInstaller.downloadAndConfigureUi();
6806 
6807         //####################################
6808         //get ant
6809         grouperInstaller.downloadAndUnzipAnt();
6810 
6811         //####################################
6812         //build UI
6813         grouperInstaller.buildUi(false);
6814 
6815         File serverXml = null;
6816         for (int i=0;i<10;i++) {
6817           String defaultServerXml = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.ui.server.xml", false);
6818           System.out.println("What is the location of your tomcat server.xml for the UI?  "
6819               + "Note, if you dont use tomcat just leave it blank or type 'blank': " 
6820               + (GrouperInstallerUtils.isBlank(defaultServerXml) ? "" : ("[" + defaultServerXml + "]: ")));
6821           String serverXmlLocation = readFromStdIn("grouperInstaller.autorun.locationOfTomcatServerXml");
6822           
6823           if (GrouperInstallerUtils.equals(defaultServerXml, "blank")) {
6824             defaultServerXml = null;
6825             break;
6826           }
6827           
6828           if (GrouperInstallerUtils.isBlank(serverXmlLocation)) {
6829             if (GrouperInstallerUtils.isNotBlank(defaultServerXml)) {
6830               serverXmlLocation = defaultServerXml;
6831             } else {
6832               break;
6833             }
6834           }
6835           serverXml = new File(serverXmlLocation);
6836           if (serverXml.exists() && serverXml.isFile()) {
6837             break;
6838           }
6839           if (i != 9) {
6840             System.out.println("Error: server.xml cant be found, try again.");
6841           }
6842         }
6843         if (serverXml != null && serverXml.exists() && serverXml.isFile()) {
6844           grouperInstaller.configureTomcatUriEncoding(serverXml);
6845         }
6846                 
6847       }
6848 
6849       @Override
6850       public void upgradeApp(GrouperInstaller grouperInstaller) {
6851         grouperInstaller.upgradeUi();
6852       }
6853 
6854       @Override
6855       public void fixIndexFile(GrouperInstaller grouperInstaller) {
6856         grouperInstaller.fixIndexFileUi();
6857       }
6858 
6859       @Override
6860       public boolean isApiOrganized() {
6861         return false;
6862       }
6863     },
6864     
6865     /**
6866      * upgrading the API
6867      */
6868     API {
6869 
6870       @Override
6871       public boolean validateExistingDirectory(GrouperInstaller grouperInstaller) {
6872 
6873         //client is in the API
6874         if (!CLIENT.validateExistingDirectory(grouperInstaller)) {
6875           return false;
6876         }
6877         
6878         grouperInstaller.subjectPropertiesFile = grouperInstaller.findClasspathFile("subject.properties", false);
6879         grouperInstaller.subjectBasePropertiesFile = grouperInstaller.findClasspathFile("subject.base.properties", false);
6880 
6881         grouperInstaller.grouperUtf8File = grouperInstaller.findClasspathFile("grouperUtf8.txt", false);
6882         grouperInstaller.gshFileLoadPropertiesFile = grouperInstaller.findClasspathFile("GSHFileLoad.properties", false);
6883         grouperInstaller.grouperClientUsageExampleFile = grouperInstaller.findClasspathFile("grouper.client.usage.example.txt", false);
6884         grouperInstaller.groovyshProfileFile = grouperInstaller.findClasspathFile("groovysh.profile", false);
6885 
6886         //no need to check if it exists... its new in 2.2
6887         
6888         grouperInstaller.grouperPropertiesFile = grouperInstaller.findClasspathFile("grouper.properties", false);
6889         grouperInstaller.grouperBasePropertiesFile = grouperInstaller.findClasspathFile("grouper.base.properties", false);
6890         grouperInstaller.grouperExamplePropertiesFile = grouperInstaller.findClasspathFile("grouper.example.properties", false);
6891 
6892         if (grouperInstaller.grouperBasePropertiesFile == null 
6893             && grouperInstaller.grouperPropertiesFile == null 
6894             && grouperInstaller.grouperExamplePropertiesFile == null) {
6895           return false;
6896         }
6897         
6898         grouperInstaller.grouperHibernatePropertiesFile = grouperInstaller.findClasspathFile("grouper.hibernate.properties", false);
6899         grouperInstaller.grouperHibernateBasePropertiesFile = grouperInstaller.findClasspathFile("grouper.hibernate.base.properties", false);
6900         grouperInstaller.grouperHibernateExamplePropertiesFile = grouperInstaller.findClasspathFile("grouper.hibernate.example.properties", false);
6901 
6902         if (grouperInstaller.grouperHibernateBasePropertiesFile == null 
6903             && grouperInstaller.grouperHibernatePropertiesFile == null 
6904             && grouperInstaller.grouperHibernateExamplePropertiesFile == null) {
6905           return false;
6906         }
6907         
6908         grouperInstaller.grouperLoaderPropertiesFile = grouperInstaller.findClasspathFile("grouper-loader.properties", false);
6909         grouperInstaller.grouperLoaderBasePropertiesFile = grouperInstaller.findClasspathFile("grouper-loader.base.properties", false);
6910         grouperInstaller.grouperLoaderExamplePropertiesFile = grouperInstaller.findClasspathFile("grouper-loader.example.properties", false);
6911 
6912         if (grouperInstaller.grouperLoaderBasePropertiesFile == null 
6913             && grouperInstaller.grouperLoaderPropertiesFile == null 
6914             && grouperInstaller.grouperLoaderExamplePropertiesFile == null) {
6915           return false;
6916         }
6917         
6918         grouperInstaller.grouperCachePropertiesFile = grouperInstaller.findClasspathFile("grouper.cache.properties", false);
6919         grouperInstaller.grouperCacheBasePropertiesFile = grouperInstaller.findClasspathFile("grouper.cache.base.properties", false);
6920 
6921 //        //these must exist after 2.3.1+
6922 //        if (grouperInstaller.grouperCacheBasePropertiesFile == null 
6923 //            && grouperInstaller.grouperCachePropertiesFile == null 
6924 //            && new GiGrouperVersion(grouperInstaller.version).greaterOrEqualToArg(new GiGrouperVersion("2.3.1"))
6925 //            ) {
6926 //          return false;
6927 //        }
6928 
6929         //this must exist
6930         grouperInstaller.grouperJar = grouperInstaller.findLibraryFile("grouper.jar", false);
6931         if (grouperInstaller.grouperJar == null) {
6932           return false;
6933         }
6934 
6935         grouperInstaller.ehcacheFile = grouperInstaller.findClasspathFile("ehcache.xml", false);
6936         grouperInstaller.ehcacheExampleFile = grouperInstaller.findClasspathFile("ehcache.example.xml", false);        
6937         
6938         //all good
6939         return true;
6940       }
6941 
6942       @Override
6943       public void downloadAndBuildGrouperProjects(GrouperInstaller grouperInstaller) {
6944         CLIENT.downloadAndBuildGrouperProjects(grouperInstaller);
6945         
6946         //download api and set executable and dos2unix etc
6947         grouperInstaller.downloadAndConfigureApi();
6948 
6949       }
6950 
6951       @Override
6952       public void upgradeApp(GrouperInstaller grouperInstaller) {
6953         grouperInstaller.upgradeApi();
6954       }
6955 
6956       @Override
6957       public void patch(GrouperInstaller grouperInstaller) {
6958         grouperInstaller.patchApi();
6959       }
6960 
6961       @Override
6962       public void revertPatch(GrouperInstaller grouperInstaller) {
6963         grouperInstaller.patchRevertApi();
6964       }
6965 
6966       @Override
6967       public void patchStatus(GrouperInstaller grouperInstaller) {
6968         grouperInstaller.patchStatusApi();
6969       }
6970 
6971       @Override
6972       public void fixIndexFile(GrouperInstaller grouperInstaller) {
6973         grouperInstaller.fixIndexFileApi();
6974       }
6975 
6976       @Override
6977       public boolean isApiOrganized() {
6978         return true;
6979       }
6980     },
6981 
6982     /**
6983      * upgrading the client
6984      */
6985     CLIENT {
6986 
6987       @Override
6988       public void patchStatus(GrouperInstaller grouperInstaller) {
6989         throw new RuntimeException("Cant patch status client.  Client patches will be in the API if applicable");
6990       }
6991 
6992       @Override
6993       public void patch(GrouperInstaller grouperInstaller) {
6994         throw new RuntimeException("Cant patch client.  Client patches will be in the API if applicable");
6995       }
6996 
6997       @Override
6998       public void revertPatch(GrouperInstaller grouperInstaller) {
6999         throw new RuntimeException("Cant revert client.  Client patches will be in the API if applicable");
7000       }
7001 
7002       @Override
7003       public boolean validateExistingDirectory(GrouperInstaller grouperInstaller) {
7004 
7005         grouperInstaller.grouperClientPropertiesFile = grouperInstaller.findClasspathFile("grouper.client.properties", false);
7006         grouperInstaller.grouperClientBasePropertiesFile = grouperInstaller.findClasspathFile("grouper.client.base.properties", false);
7007         grouperInstaller.grouperClientExamplePropertiesFile = grouperInstaller.findClasspathFile("grouper.client.example.properties", false);
7008 
7009         if (grouperInstaller.grouperClientBasePropertiesFile == null 
7010             && grouperInstaller.grouperClientPropertiesFile == null 
7011             && grouperInstaller.grouperClientExamplePropertiesFile == null) {
7012           if (grouperInstaller.appToUpgrade == CLIENT) {
7013             return false;
7014           }
7015         }
7016         
7017         //this must exist
7018         grouperInstaller.grouperClientJar = grouperInstaller.findLibraryFile("grouperClient.jar", false);
7019         if (grouperInstaller.grouperClientJar == null) {
7020           if (grouperInstaller.appToUpgrade == CLIENT) {
7021             return false;
7022           }
7023         }
7024         
7025         //all good
7026         return true;
7027       }
7028 
7029       @Override
7030       public void downloadAndBuildGrouperProjects(GrouperInstaller grouperInstaller) {
7031         grouperInstaller.downloadAndBuildClient();
7032       }
7033 
7034       @Override
7035       public void upgradeApp(GrouperInstaller grouperInstaller) {
7036         grouperInstaller.upgradeClient();
7037       }
7038       
7039       @Override
7040       public void fixIndexFile(GrouperInstaller grouperInstaller) {
7041         throw new RuntimeException("Not implemented");
7042       }
7043 
7044       @Override
7045       public boolean isApiOrganized() {
7046         return false;
7047       }
7048     },
7049 
7050     /**
7051      * upgrading the WS
7052      */
7053     WS {
7054 
7055       @Override
7056       public void patchStatus(GrouperInstaller grouperInstaller) {
7057         grouperInstaller.patchStatusWs();
7058       }
7059 
7060       @Override
7061       public void patch(GrouperInstaller grouperInstaller) {
7062         grouperInstaller.patchWs();
7063       }
7064 
7065       @Override
7066       public void revertPatch(GrouperInstaller grouperInstaller) {
7067         grouperInstaller.patchRevertWs();
7068       }
7069 
7070       @Override
7071       public boolean validateExistingDirectory(GrouperInstaller grouperInstaller) {
7072         //API and client are in the UI
7073         if (!API.validateExistingDirectory(grouperInstaller)) {
7074           return false;
7075         }
7076 
7077         grouperInstaller.grouperWsPropertiesFile = grouperInstaller.findClasspathFile("grouper-ws.properties", false);
7078         grouperInstaller.grouperWsBasePropertiesFile = grouperInstaller.findClasspathFile("grouper-ws.base.properties", false);
7079         grouperInstaller.grouperWsExamplePropertiesFile = grouperInstaller.findClasspathFile("grouper-ws.example.properties", false);
7080 
7081         if (grouperInstaller.grouperWsBasePropertiesFile == null 
7082             && grouperInstaller.grouperWsPropertiesFile == null 
7083             && grouperInstaller.grouperWsExamplePropertiesFile == null) {
7084           return false;
7085         }
7086 
7087         return true;
7088       }
7089 
7090       @Override
7091       public void downloadAndBuildGrouperProjects(GrouperInstaller grouperInstaller) {
7092         API.downloadAndBuildGrouperProjects(grouperInstaller);
7093         
7094         //####################################
7095         //download and configure ws
7096         grouperInstaller.downloadAndUntarWs();
7097         
7098         //####################################
7099         //configure where api is
7100         grouperInstaller.configureWs();
7101 
7102         //####################################
7103         //get ant
7104         grouperInstaller.downloadAndUnzipAnt();
7105 
7106         //####################################
7107         //build Ws
7108         grouperInstaller.buildWs(false);
7109 
7110       }
7111 
7112       @Override
7113       public void upgradeApp(GrouperInstaller grouperInstaller) {
7114         grouperInstaller.upgradeWs();
7115       }
7116 
7117       @Override
7118       public void fixIndexFile(GrouperInstaller grouperInstaller) {
7119         grouperInstaller.fixIndexFileWs();
7120       }
7121 
7122       @Override
7123       public boolean isApiOrganized() {
7124         return false;
7125       }
7126     }, 
7127     
7128     /**
7129      * upgrading the UI
7130      */
7131     PSP {
7132 
7133       @Override
7134       public void patchStatus(GrouperInstaller grouperInstaller) {
7135         grouperInstaller.patchStatusPsp();
7136       }
7137 
7138       @Override
7139       public void patch(GrouperInstaller grouperInstaller) {
7140         grouperInstaller.patchPsp();
7141       }
7142 
7143       @Override
7144       public void revertPatch(GrouperInstaller grouperInstaller) {
7145         grouperInstaller.patchRevertPsp();
7146       }
7147 
7148       @Override
7149       public boolean validateExistingDirectory(GrouperInstaller grouperInstaller) {
7150         //API and client are in the UI
7151         if (!API.validateExistingDirectory(grouperInstaller)) {
7152           return false;
7153         }
7154         
7155         File customLibDir = new File(grouperInstaller.upgradeExistingApplicationDirectoryString + "lib" + File.separator + "custom");
7156         if (!customLibDir.exists()) {
7157           return false;
7158         }
7159 
7160         //see if psp jar is there
7161         List<File> files = GrouperInstallerUtils.jarFindJar(customLibDir, "psp.jar");
7162                 
7163         if (GrouperInstallerUtils.length(files) == 0) {
7164           return false;
7165         }
7166 
7167         return true;
7168       }
7169     
7170       @Override
7171       public void downloadAndBuildGrouperProjects(GrouperInstaller grouperInstaller) {
7172         API.downloadAndBuildGrouperProjects(grouperInstaller);
7173         
7174         //####################################
7175         //download and configure psp
7176         grouperInstaller.downloadAndBuildPsp();
7177     
7178       }
7179     
7180       @Override
7181       public void upgradeApp(GrouperInstaller grouperInstaller) {
7182         grouperInstaller.upgradePsp();
7183       }
7184 
7185       @Override
7186       public void fixIndexFile(GrouperInstaller grouperInstaller) {
7187         grouperInstaller.fixIndexFilePsp();
7188       }
7189       @Override
7190       public boolean isApiOrganized() {
7191         return true;
7192       }
7193     }, 
7194     
7195     /**
7196      * upgrading the UI
7197      */
7198     PSPNG {
7199     
7200       @Override
7201       public void patchStatus(GrouperInstaller grouperInstaller) {
7202         grouperInstaller.patchStatusPspng();
7203       }
7204     
7205       @Override
7206       public void patch(GrouperInstaller grouperInstaller) {
7207         grouperInstaller.patchPspng();
7208       }
7209     
7210       @Override
7211       public void revertPatch(GrouperInstaller grouperInstaller) {
7212         grouperInstaller.patchRevertPspng();
7213       }
7214     
7215       @Override
7216       public boolean validateExistingDirectory(GrouperInstaller grouperInstaller) {
7217         //API and client are in the UI
7218         if (!API.validateExistingDirectory(grouperInstaller)) {
7219           return false;
7220         }
7221         
7222         File customLibDir = new File(grouperInstaller.upgradeExistingApplicationDirectoryString + "lib" + File.separator + "custom");
7223         if (!customLibDir.exists()) {
7224           return false;
7225         }
7226     
7227         //see if psp jar is there
7228         String grouperVersion = grouperInstaller.grouperVersionOfJar().toString();
7229 
7230         List<File> files = GrouperInstallerUtils.jarFindJar(customLibDir, "grouper-pspng-" + grouperVersion + ".jar");
7231 
7232         if (GrouperInstallerUtils.length(files) == 0) {
7233           return false;
7234         }
7235     
7236         return true;
7237       }
7238     
7239       @Override
7240       public void downloadAndBuildGrouperProjects(GrouperInstaller grouperInstaller) {
7241         API.downloadAndBuildGrouperProjects(grouperInstaller);
7242         
7243         //####################################
7244         //download and configure psp
7245         grouperInstaller.downloadAndBuildPspng();
7246     
7247       }
7248     
7249       @Override
7250       public void upgradeApp(GrouperInstaller grouperInstaller) {
7251         grouperInstaller.upgradePspng();
7252       }
7253     
7254       @Override
7255       public void fixIndexFile(GrouperInstaller grouperInstaller) {
7256         grouperInstaller.fixIndexFilePspng();
7257       }
7258       @Override
7259       public boolean isApiOrganized() {
7260         return true;
7261       }
7262     };
7263 
7264     /**
7265      * if the organization is API organzied (e.g. has lib/jdbcSamples dir)
7266      * @return true/false
7267      */
7268     public abstract boolean isApiOrganized();
7269     
7270     /**
7271      * validate that the existing directory is valid, and find all the file paths
7272      * @param grouperInstaller 
7273      * @return true if valid, false if not
7274      */
7275     public abstract boolean validateExistingDirectory(GrouperInstaller grouperInstaller);
7276     
7277     /**
7278      * based on what is being upgraded, download and build the grouper projects
7279      * @param grouperInstaller
7280      */
7281     public abstract void downloadAndBuildGrouperProjects(GrouperInstaller grouperInstaller);
7282     
7283     /**
7284      * upgrade this app
7285      * @param grouperInstaller
7286      */
7287     public abstract void upgradeApp(GrouperInstaller grouperInstaller);
7288     
7289     /**
7290      * patch this app
7291      * @param grouperInstaller
7292      */
7293     public abstract void patch(GrouperInstaller grouperInstaller);
7294     
7295     /**
7296      * revert patch this app
7297      * @param grouperInstaller
7298      */
7299     public abstract void revertPatch(GrouperInstaller grouperInstaller);
7300     
7301     /**
7302      * patch status for this app
7303      * @param grouperInstaller
7304      */
7305     public abstract void patchStatus(GrouperInstaller grouperInstaller);
7306     
7307     /**
7308      * fix index file for this app
7309      * @param grouperInstaller
7310      */
7311     public abstract void fixIndexFile(GrouperInstaller grouperInstaller);
7312     
7313     /**
7314      * 
7315      * @param string
7316      * @param exceptionIfInvalid
7317      * @param exceptionIfBlank
7318      * @return the action
7319      */
7320     @SuppressWarnings("unused")
7321     public static AppToUpgrade valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
7322       return GrouperInstallerUtils.enumValueOfIgnoreCase(AppToUpgrade.class, string, exceptionIfBlank, exceptionIfInvalid);
7323     }
7324 
7325   }
7326 
7327   /**
7328    * patch names installed (used for dependency checking), without the file ending e.g. grouper_v2_2_1_api_patch_0
7329    */
7330   private Set<String> patchesInstalled = new HashSet<String>();
7331   
7332   /**
7333    * if grouper is stopped
7334    */
7335   private boolean grouperStopped = false;
7336 
7337   /**
7338    * if should revert all
7339    */
7340   private Boolean revertAllPatches = null;
7341   
7342   /**
7343    * if we should use all local files
7344    */
7345   private Boolean useAllLocalFiles = null;
7346   
7347   /**
7348    * if we should use all unzipped files
7349    */
7350   private Boolean useAllUnzippedFiles = null;
7351   
7352   /**
7353    * if we should use all untarred directories
7354    */
7355   private Boolean useAllUntarredDirectories = null;
7356   
7357   /**
7358    * default for revert all patches
7359    */
7360   private boolean revertAllPatchesDefault = false;
7361   
7362   /**
7363    * if should revert all
7364    */
7365   private Boolean installAllPatches = null;
7366   
7367   /**
7368    * if should install some patches
7369    */
7370   private Boolean installPatchesUpToACertainPatchLevel = null;
7371   
7372   /**
7373    * if should install up to patch levels, comma separated
7374    * e.g. grouper_v2_3_0_api_patch_9, grouper_v2_3_0_ui_patch_10, grouper_v2_3_0_ws_patch_5
7375    */
7376   private String installPatchesUpToThesePatchLevels = null;
7377   
7378   /**
7379    * if should install certain specified
7380    */
7381   private Boolean installCertainSpecifiedPatches = null;
7382   
7383   /**
7384    * if should install up to patch levels, comma separated
7385    * e.g. grouper_v2_3_0_api_patch_0, grouper_v2_3_0_api_patch_1, grouper_v2_3_0_ui_patch_0
7386    */
7387   private String installCertainSpecifiedPatchesList = null;
7388   
7389   /**
7390    * if should revert certain specified
7391    */
7392   private Boolean revertCertainSpecifiedPatches = null;
7393   
7394   /**
7395    * if should revert up to patch levels, comma separated
7396    * e.g. grouper_v2_3_0_api_patch_0, grouper_v2_3_0_api_patch_1, grouper_v2_3_0_ui_patch_0
7397    */
7398   private String revertCertainSpecifiedPatchesList = null;
7399   
7400   /**
7401    * revert patches for an app
7402    * @param thisAppToRevert
7403    * @param originalAppToUpgrade 
7404    * @return if reverted
7405    */
7406   private boolean revertPatches(AppToUpgrade thisAppToRevert, AppToUpgrade originalAppToUpgrade) {
7407 
7408     if (thisAppToRevert == AppToUpgrade.CLIENT) {
7409       throw new RuntimeException("Cant revert " + thisAppToRevert);
7410     }
7411     
7412     Properties patchesExistingProperties = patchExistingProperties();
7413     
7414     String grouperVersion = this.grouperVersionOfJar().toString();
7415 
7416     grouperVersion = GrouperInstallerUtils.replace(grouperVersion, ".", "_");
7417 
7418     Map<Integer, String> patchNumberToNameBase = new LinkedHashMap<Integer, String>();
7419     
7420     boolean foundPatch = false;
7421 
7422     Map<String, Set<String>> installedPatchDependencies = new HashMap<String, Set<String>>();
7423     
7424     File patchExistingPropertiesFile = patchExistingPropertiesFile();
7425     
7426     for (int i=1000;i>=0;i--) {
7427       
7428       //grouper_v2_2_1_api_patch_0.state
7429       String keyBase = "grouper_v" + grouperVersion + "_" + thisAppToRevert.name().toLowerCase() + "_patch_" + i;
7430       String key = keyBase + ".state";
7431 
7432       patchNumberToNameBase.put(i, keyBase);
7433       
7434       String value = patchesExistingProperties.getProperty(key);
7435 
7436       if (!GrouperInstallerUtils.isBlank(value)) {
7437         
7438         System.out.println("\n################ Checking patch " + keyBase);
7439 
7440         GrouperInstallerPatchStatus grouperInstallerPatchStatus = GrouperInstallerPatchStatus.valueOfIgnoreCase(value, true, true);
7441         
7442         switch (grouperInstallerPatchStatus) {
7443           case skippedPermanently:
7444             
7445             System.out.println("Patch: " + keyBase + ": was skipped permanently on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7446             continue;
7447 
7448           case skippedTemporarily:
7449 
7450             System.out.println("Patch: " + keyBase + ": was skipped termporarily on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7451             continue;
7452 
7453           case reverted:
7454 
7455             System.out.println("Patch: " + keyBase + ": was removed on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7456             continue;
7457 
7458           case error:
7459 
7460             System.out.println("Patch: " + keyBase + ": had an error installing on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7461             continue;
7462 
7463           case applied:
7464             
7465             System.out.println("Patch: " + keyBase + ": was applied on: " + patchesExistingProperties.getProperty(keyBase + ".date")  + "\n");
7466             this.patchesInstalled.add(keyBase);
7467             break;
7468 
7469           default:
7470             throw new RuntimeException("Not expecting: " + grouperInstallerPatchStatus);
7471         }
7472 
7473       } else {
7474         continue;
7475       }
7476 
7477       if (!this.patchesInstalled.contains(keyBase)) {
7478         System.out.println("\n");
7479         continue;
7480       }
7481 
7482       //lets see if it exists on the server
7483       File patchUntarredDir = downloadAndUnzipPatch(keyBase);
7484       
7485       //if no more patches
7486       if (patchUntarredDir == null) {
7487         System.out.print("Error: cant find directory for patch: " + keyBase + ", press <enter> to continue. ");
7488         readFromStdIn("grouperInstaller.autorun.continueAfterCantFindPatchDir");
7489         continue;
7490       }
7491 
7492       //lets get the description:
7493       //  # will show up on screen so user knows what it is
7494       //  description = This patch fixes GRP-1080: browse folders refresh button only works in chrome, not other browsers
7495       //
7496       //  # patches that this patch is dependant on (comma separated)
7497       //  dependencies = 
7498       //
7499       //  # low, medium, or high risk to applying the patch
7500       //  risk = low
7501       //
7502       //  # is this is a security patch (true or false)
7503       //  security = false
7504       //
7505       //  # if this patch requires restart of processes (true or false)
7506       //  requiresRestart = false
7507       Properties patchProperties = GrouperInstallerUtils.propertiesFromFile(new File(patchUntarredDir.getAbsoluteFile() + File.separator + keyBase + ".properties"));
7508 
7509       foundPatch = true;
7510 
7511       // check dependencies
7512       {
7513         List<String> dependencies = GrouperInstallerUtils.splitTrimToList(patchProperties.getProperty("dependencies"), ",");
7514         Set<String> dependenciesSet = new HashSet<String>(GrouperInstallerUtils.nonNull(dependencies));
7515         installedPatchDependencies.put(keyBase, dependenciesSet);
7516       }
7517 
7518       boolean securityRelated = GrouperInstallerUtils.booleanValue(patchProperties.getProperty("security"), false);
7519       boolean requiresRestart = GrouperInstallerUtils.booleanValue(patchProperties.getProperty("requiresRestart"), true);
7520 
7521       if (this.revertAllPatches == null) {
7522         System.out.println("Would you like to revert all patches (t|f)? [" + (this.revertAllPatchesDefault ? "t" : "f") + "]: ");
7523         this.revertAllPatches = readFromStdInBoolean(this.revertAllPatchesDefault, "grouperInstaller.autorun.revertAllPatches");
7524       }
7525       
7526       if (!this.revertAllPatches && this.revertCertainSpecifiedPatches == null) {
7527         System.out.println("Would you like to revert certain specified patches? (t|f)? [f]: ");
7528         this.revertCertainSpecifiedPatches = readFromStdInBoolean(false, "grouperInstaller.autorun.revertCertainSpecifiedPatches");
7529 
7530         if (this.revertCertainSpecifiedPatches) {
7531 
7532           System.out.println("What patches would you like to revert [comma-separated] (e.g. grouper_v2_3_0_api_patch_0, grouper_v2_3_0_api_patch_1, grouper_v2_3_0_ui_patch_0)? : ");
7533           this.revertCertainSpecifiedPatchesList = readFromStdIn("grouperInstaller.autorun.revertCertainSpecifiedPatchesList");
7534         }
7535       }
7536       if (this.revertCertainSpecifiedPatches == null) {
7537         this.revertCertainSpecifiedPatches = false;
7538       }
7539 
7540       //print description
7541       System.out.println("Patch " + keyBase + " is " + patchProperties.getProperty("risk") + " risk, "
7542           + (securityRelated ? "is a security patch" : "is not a security patch"));
7543       System.out.println(patchProperties.getProperty("description"));
7544       
7545       Boolean revertPatch = null;
7546       
7547       if (this.revertAllPatches) {
7548         revertPatch = true;
7549       } else if (this.revertCertainSpecifiedPatches) {
7550         if (revertPatch == null) {
7551           revertPatch = shouldRevertCertainSpecifiedPatches(keyBase);
7552         }
7553       } else {
7554         System.out.print("Would you like to revert patch " + keyBase + " (t|f)? [f]: ");
7555         revertPatch = readFromStdInBoolean(false, "grouperInstaller.autorun.revertPatch");
7556       }
7557 
7558 
7559       if (!revertPatch) {
7560         System.out.println("");
7561         continue;
7562       }
7563 
7564       //check dependencies
7565       for (String patchName : installedPatchDependencies.keySet()) {
7566         
7567         Set<String> dependencies = GrouperInstallerUtils.nonNull(installedPatchDependencies.get(patchName));
7568         
7569         if (dependencies.contains(keyBase)) {
7570           System.out.println("Error: cant revert " + keyBase + " because an installed patch is dependent on it: " + patchName);
7571           System.exit(1);
7572         }
7573       }
7574 
7575       if (requiresRestart && !this.grouperStopped) {
7576         System.out.print("This patch requires all processes that user Grouper to be stopped.\n  "
7577             + "Please stop these processes if they are running and press <enter> to continue... ");
7578         this.grouperStopped = true;
7579         readFromStdIn("grouperInstaller.autorun.continueAfterStoppingGrouperProcesses");
7580       }
7581       
7582       Map<String, String> patchDirToApplicationPath = new LinkedHashMap<String, String>();
7583       patchDirToApplicationPath.put("files", this.upgradeExistingApplicationDirectoryString);
7584       patchDirToApplicationPath.put("classes", this.upgradeExistingClassesDirectoryString);
7585       patchDirToApplicationPath.put("lib", this.upgradeExistingLibDirectoryString);
7586       patchDirToApplicationPath.put("bin", this.upgradeExistingBinDirectoryString);
7587 
7588       boolean patchHasProblem = false;
7589       
7590       //we are reverting this patch, lets see if the files are there...
7591       //this.upgradeExistingApplicationDirectoryString
7592       //patchUntarredDir
7593       File newDir = new File(patchUntarredDir.getAbsolutePath() + File.separator + "new");
7594       File oldDir = new File(patchUntarredDir.getAbsolutePath() + File.separator + "old");
7595       {
7596 
7597         for (String patchDir : patchDirToApplicationPath.keySet()) {
7598 
7599           String applicationPath = patchDirToApplicationPath.get(patchDir);
7600 
7601           File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
7602           File oldDirFiles = new File(oldDir.getAbsoluteFile() + File.separator + patchDir);
7603           
7604           if (newDirFiles.exists() && newDirFiles.isDirectory()) {
7605 
7606             // relative, e.g. WEB-INF/jsp/someFile.jsp
7607             Set<String> newFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(newDirFiles);
7608 
7609             for (String newFilePath : GrouperInstallerUtils.nonNull(newFileRelativePaths)) {
7610               File newFileInPatch = new File(newDirFiles.getAbsolutePath() + File.separator + newFilePath);
7611               File oldFileInPatch = new File(oldDirFiles.getAbsolutePath() + File.separator + newFilePath);
7612               
7613               if (revertPatchExcludes.contains(newFileInPatch.getName())) {
7614                 System.out.println("Skipping revert for file: " + newFileInPatch.getName());
7615                 continue;
7616               }
7617 
7618               File newFileInGrouper = new File(applicationPath + newFilePath);
7619 
7620               newFileInGrouper = fixLibraryFileIfFoundAndDifferent(newFileInGrouper, originalAppToUpgrade);
7621               
7622               if (!newFileInGrouper.exists() || !newFileInGrouper.isFile() 
7623                   || (!GrouperInstallerUtils.contentEquals(newFileInPatch, newFileInGrouper)
7624                       //its ok if the patch is already reverted?
7625                       && !GrouperInstallerUtils.contentEquals(oldFileInPatch, newFileInGrouper))) {
7626                 
7627                 // if it's just an example file and it didn't previously exist, then it's fine??
7628                 if (!newFileInGrouper.exists() && newFileInGrouper.getName().contains(".example.")) {
7629                   System.out.println("Grouper file " + newFileInGrouper.getAbsolutePath() + " doesn't exist.  Reverting patch anyways since this is an example file.");
7630                 } else {
7631                 
7632                   System.out.print("Problem reverting patch since this patch file:\n  " + newFileInPatch.getAbsolutePath() 
7633                       + "\n  is not the same as what the patch expects:\n  " + newFileInGrouper.getAbsolutePath()
7634                       + "\n  Do you want to force revert this patch (t|f)? [f]: ");
7635                   
7636                   boolean forceRevertPatch = readFromStdInBoolean(false, "grouperInstaller.autorun.forceRevertPatch");
7637                   
7638                   if (!forceRevertPatch) {
7639                     System.out.println("\nCannot revert patch since this patch file:\n  " + newFileInPatch.getAbsolutePath() 
7640                         + "\n  is not the same as what the patch expects:\n  " + newFileInGrouper.getAbsolutePath());
7641                     patchHasProblem = true;
7642                   }
7643                 }
7644               }
7645             }
7646           }
7647         }
7648       }
7649 
7650       {
7651         //deletes
7652         for (String patchDir : patchDirToApplicationPath.keySet()) {
7653 
7654           String applicationPath = patchDirToApplicationPath.get(patchDir);
7655 
7656           File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
7657           File oldDirFiles = new File(oldDir.getAbsoluteFile() + File.separator + patchDir);
7658           
7659           if (oldDirFiles.exists() && oldDirFiles.isDirectory()) {
7660 
7661             // relative, e.g. WEB-INF/jsp/someFile.jsp
7662             Set<String> oldFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(oldDirFiles);
7663 
7664             for (String oldFilePath : GrouperInstallerUtils.nonNull(oldFileRelativePaths)) {
7665               File newFileInPatch = new File(newDirFiles.getAbsolutePath() + File.separator + oldFilePath);
7666               File oldFileInPatch = new File(oldDirFiles.getAbsolutePath() + File.separator + oldFilePath);
7667 
7668               //if there is a new file, then its not a delete
7669               if (newFileInPatch.exists()) {
7670                 continue;
7671               }
7672               
7673               if (revertPatchExcludes.contains(newFileInPatch.getName())) {
7674                 System.out.println("Skipping revert for file: " + newFileInPatch.getName());
7675                 continue;
7676               }
7677               
7678               File newFileInGrouper = new File(applicationPath + oldFilePath);
7679               
7680               newFileInGrouper = fixLibraryFileIfFoundAndDifferent(newFileInGrouper, originalAppToUpgrade);
7681 
7682               if (newFileInGrouper.exists() && newFileInGrouper.isFile() 
7683                   && !GrouperInstallerUtils.contentEquals(oldFileInPatch, newFileInGrouper)) {
7684                 
7685                 System.out.print("Problem reverting patch since this patch file:\n  " + oldFileInPatch.getAbsolutePath() 
7686                     + "\n  is not the same as what the patch expects (shouldnt exist):\n  " + newFileInGrouper.getAbsolutePath()
7687                     + "\n  Do you want to force revert this patch (t|f)? [f]: ");
7688                 
7689                 boolean forceRevertPatch = readFromStdInBoolean(true, "grouperInstaller.autorun.forceRevertPatch");
7690                 
7691                 if (!forceRevertPatch) {
7692                   System.out.println("\nCannot revert patch since this patch file:\n  " + newFileInPatch.getAbsolutePath() 
7693                       + "\n  is not the same as what the patch expects:\n  " + newFileInGrouper.getAbsolutePath());
7694                   patchHasProblem = true;
7695                 }
7696               }
7697             }
7698           }
7699         }
7700       }
7701 
7702       if (patchHasProblem) {
7703         System.out.println("Cannot continue since patch has problem");
7704         System.exit(1);
7705       }
7706       
7707       //so far so good, all the new files are all good, revert the patch
7708       for (String patchDir : patchDirToApplicationPath.keySet()) {
7709         
7710         String applicationPath = patchDirToApplicationPath.get(patchDir);
7711 
7712         File oldDirFiles = new File(oldDir.getAbsoluteFile() + File.separator + patchDir);
7713         File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
7714         
7715         if (newDirFiles.exists() && newDirFiles.isDirectory()) {
7716         
7717           // relative, e.g. WEB-INF/jsp/someFile.jsp
7718           Set<String> newFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(newDirFiles);
7719           
7720           for (String newFilePath : GrouperInstallerUtils.nonNull(newFileRelativePaths)) {
7721 
7722             File oldFileInPatch = new File(oldDirFiles.getAbsolutePath() + File.separator + newFilePath);
7723 
7724             File newFileInGrouper = new File(applicationPath + newFilePath);
7725             
7726             if (revertPatchExcludes.contains(oldFileInPatch.getName())) {
7727               continue;
7728             }
7729             
7730             newFileInGrouper = fixLibraryFileIfFoundAndDifferent(newFileInGrouper, originalAppToUpgrade);
7731 
7732             if (oldFileInPatch.exists() && oldFileInPatch.isFile()) {
7733               System.out.println("Reverting file: " + newFileInGrouper.getAbsolutePath());
7734               GrouperInstallerUtils.copyFile(oldFileInPatch, newFileInGrouper, false);
7735             } else {
7736               System.out.println("Reverting (deleting) file: " + newFileInGrouper.getAbsolutePath());
7737               GrouperInstallerUtils.fileDelete(newFileInGrouper);
7738             }
7739           }
7740         }
7741       }
7742       
7743       //so far so good, revert the deletes
7744       for (String patchDir : patchDirToApplicationPath.keySet()) {
7745         
7746         String applicationPath = patchDirToApplicationPath.get(patchDir);
7747 
7748         File oldDirFiles = new File(oldDir.getAbsoluteFile() + File.separator + patchDir);
7749         File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
7750         
7751         if (oldDirFiles.exists() && oldDirFiles.isDirectory()) {
7752         
7753           // relative, e.g. WEB-INF/jsp/someFile.jsp
7754           Set<String> oldFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(oldDirFiles);
7755           
7756           for (String oldFilePath : GrouperInstallerUtils.nonNull(oldFileRelativePaths)) {
7757 
7758             File oldFileInPatch = new File(oldDirFiles.getAbsolutePath() + File.separator + oldFilePath);
7759             File newFileInPatch = new File(newDirFiles.getAbsolutePath() + File.separator + oldFilePath);
7760 
7761             if (newFileInPatch.exists()) {
7762               continue;
7763             }
7764             
7765             if (revertPatchExcludes.contains(oldFileInPatch.getName())) {
7766               continue;
7767             }
7768             
7769             File newFileInGrouper = new File(applicationPath + oldFilePath);
7770             
7771             newFileInGrouper = fixLibraryFileIfFoundAndDifferent(newFileInGrouper, originalAppToUpgrade);
7772 
7773             if (oldFileInPatch.exists() && oldFileInPatch.isFile()) {
7774               System.out.println("Reverting deleted file: " + newFileInGrouper.getAbsolutePath());
7775               GrouperInstallerUtils.copyFile(oldFileInPatch, newFileInGrouper, false);
7776             }
7777           }
7778         }
7779       }
7780       
7781       //fixLibDir(patchDirToApplicationPath.get("lib"), originalAppToUpgrade);
7782       
7783       this.patchesInstalled.remove(keyBase);
7784       installedPatchDependencies.remove(keyBase);
7785       System.out.println("Patch successfully reverted: " + keyBase);
7786 
7787       editPropertiesFile(patchExistingPropertiesFile, keyBase + ".date", 
7788           GrouperInstallerUtils.dateMinutesSecondsFormat.format(new Date()), false);
7789       editPropertiesFile(patchExistingPropertiesFile, keyBase + ".state", 
7790           GrouperInstallerPatchStatus.reverted.name(), false);
7791 
7792       System.out.println("");
7793     }
7794 
7795     if (!foundPatch) {
7796       System.out.println("There are no new " + thisAppToRevert + " patches to revert\n");
7797       return false;
7798     }
7799     
7800     return true;
7801       
7802   }
7803   
7804   /**
7805    * this makes sure libs are in the right spot, though might be risky so dont do it
7806    * @param libDirWithSlash
7807    * @param originalAppToUpgrade
7808    */
7809   private void fixLibDir(String libDirWithSlash, AppToUpgrade originalAppToUpgrade) {
7810     if (originalAppToUpgrade.isApiOrganized()) {
7811       FilenameFilter apiFilenameFilter = new FilenameFilter() {
7812         
7813         public boolean accept(File dir, String name) {
7814           
7815           // any jars in "lib"
7816           if (GrouperInstallerUtils.equals("lib", dir.getName()) && name.endsWith(".jar")) {
7817             return true;
7818           }
7819           return false;
7820         }
7821       };
7822       //make sure all libs have something between lib and grouper
7823       for (File file : new File(libDirWithSlash).listFiles(apiFilenameFilter)) {
7824         // move this to grouper dir
7825         final File newFile = new File(file.getParentFile().getAbsolutePath() + File.separator + "grouper" + File.separator + file.getName());
7826         GrouperInstallerUtils.fileMove(file, newFile);
7827         System.out.println("Moving jar: " + file.getAbsolutePath() + " to " + newFile.getAbsolutePath());
7828       }
7829     } else {
7830       for (File file : GrouperInstallerUtils.fileListRecursive(new File(libDirWithSlash))) {
7831         // any jars not in "lib" but parent dir of dir is lib
7832         if (file.getName().endsWith(".jar") && !GrouperInstallerUtils.equals("lib", file.getParentFile().getName()) && GrouperInstallerUtils.equals("lib", file.getParentFile().getParentFile().getName())) {
7833           // move this to grouper dir
7834           final File newFile = new File(file.getParentFile().getParentFile().getAbsolutePath() + File.separator + file.getName());
7835           GrouperInstallerUtils.fileMove(file, newFile);
7836           System.out.println("Moving jar: " + file.getAbsolutePath() + " to " + newFile.getAbsolutePath());
7837           
7838         }
7839       }
7840     }
7841   }
7842   
7843   /**
7844    * get the patches available to apply that are not already applied
7845    * @param thisAppToUpgrade app to upgrade to check
7846    * @param originalAppToUpgrade 
7847    * @return if patches were installed
7848    */
7849   private boolean downloadAndInstallPatches(AppToUpgrade thisAppToUpgrade, AppToUpgrade originalAppToUpgrade) {
7850 
7851     if (thisAppToUpgrade == AppToUpgrade.CLIENT) {
7852       throw new RuntimeException("Cant install patches for " + thisAppToUpgrade);
7853     }
7854     
7855     Properties patchesExistingProperties = patchExistingProperties();
7856 
7857     String grouperVersion = GrouperInstallerUtils.propertiesValue("grouper.version", true);
7858 
7859     grouperVersion = GrouperInstallerUtils.replace(grouperVersion, ".", "_");
7860 
7861     Map<Integer, String> patchNumberToNameBase = new LinkedHashMap<Integer, String>();
7862     
7863     boolean foundNewPatch = false;
7864     
7865     File patchExistingPropertiesFile = patchExistingPropertiesFile();
7866     
7867     OUTER: for (int i=0;i<1000;i++) {
7868       
7869       //grouper_v2_2_1_api_patch_0.state
7870       String keyBase = "grouper_v" + grouperVersion + "_" + thisAppToUpgrade.name().toLowerCase() + "_patch_" + i;
7871       System.out.println("\n################ Checking patch " + keyBase);
7872       String key = keyBase + ".state";
7873 
7874       patchNumberToNameBase.put(i, keyBase);
7875       
7876       String value = patchesExistingProperties.getProperty(key);
7877 
7878       if (!GrouperInstallerUtils.isBlank(value)) {
7879         
7880         GrouperInstallerPatchStatus grouperInstallerPatchStatus = GrouperInstallerPatchStatus.valueOfIgnoreCase(value, true, true);
7881         
7882         switch (grouperInstallerPatchStatus) {
7883           case applied:
7884             
7885             System.out.println("Patch: " + keyBase + ": was applied on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7886             this.patchesInstalled.add(keyBase);
7887             
7888             continue;
7889 
7890           case skippedPermanently:
7891             
7892             System.out.println("Patch: " + keyBase + ": was skipped permanently on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7893             continue;
7894 
7895           case skippedTemporarily:
7896 
7897             System.out.println("Patch: " + keyBase + ": was skipped termporarily on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7898 
7899             break;
7900 
7901           case reverted:
7902 
7903             System.out.println("Patch: " + keyBase + ": was reverted on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7904 
7905             break;
7906 
7907           case error:
7908 
7909             System.out.println("Patch: " + keyBase + ": had an error installing on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7910 
7911             break;
7912 
7913           default:
7914             throw new RuntimeException("Not expecting: " + grouperInstallerPatchStatus);
7915         }
7916         
7917       }
7918 
7919       //lets see if it exists on the server
7920       File patchUntarredDir = downloadAndUnzipPatch(keyBase);
7921       
7922       //if no more patches
7923       if (patchUntarredDir == null) {
7924         System.out.println("");
7925         break OUTER;
7926       }
7927       
7928       //lets get the description:
7929       //  # will show up on screen so user knows what it is
7930       //  description = This patch fixes GRP-1080: browse folders refresh button only works in chrome, not other browsers
7931       //
7932       //  # patches that this patch is dependant on (comma separated)
7933       //  dependencies = 
7934       //
7935       //  # low, medium, or high risk to applying the patch
7936       //  risk = low
7937       //
7938       //  # is this is a security patch (true or false)
7939       //  security = false
7940       //
7941       //  # if this patch requires restart of processes (true or false)
7942       //  requiresRestart = false
7943       Properties patchProperties = GrouperInstallerUtils.propertiesFromFile(new File(patchUntarredDir.getAbsoluteFile() + File.separator + keyBase + ".properties"));
7944 
7945       foundNewPatch = true;
7946 
7947       Boolean installPatch = null;
7948       
7949       if (this.installPatchesUpToACertainPatchLevel != null && this.installPatchesUpToACertainPatchLevel) {
7950         if (!GrouperInstallerUtils.isBlank(this.installPatchesUpToThesePatchLevels)) {
7951           
7952           installPatch = shouldInstallPatchUpToLevel(keyBase);
7953           
7954           if (!installPatch) {
7955             break OUTER;
7956           }
7957         }
7958       }
7959       if (this.installCertainSpecifiedPatches != null && this.installCertainSpecifiedPatches) {
7960         if (!GrouperInstallerUtils.isBlank(this.installCertainSpecifiedPatchesList)) {
7961           
7962           installPatch = shouldInstallCertainSpecifiedPatches(keyBase);
7963           
7964         }
7965       }
7966      
7967       // check dependencies
7968       if (installPatch == null || installPatch == true){
7969         String[] dependencies = GrouperInstallerUtils.splitTrim(patchProperties.getProperty("dependencies"), ",");
7970   
7971         boolean invalidDependency = false;
7972         for (String dependency : GrouperInstallerUtils.nonNull(dependencies, String.class)) {
7973           if (!this.patchesInstalled.contains(dependency)) {
7974             System.out.println("Cannot install patch " + keyBase + " since it is dependent on a patch which is not installed: " + dependency);
7975             invalidDependency = true;
7976           }
7977         }
7978         if (invalidDependency) {
7979           System.out.println("Press <enter> to continue. ");
7980           readFromStdIn("grouperInstaller.autorun.continueAfterPatchDependencyFails");
7981           continue OUTER;
7982         }
7983       }
7984       
7985       boolean securityRelated = GrouperInstallerUtils.booleanValue(patchProperties.getProperty("security"), false);
7986       boolean requiresRestart = GrouperInstallerUtils.booleanValue(patchProperties.getProperty("requiresRestart"), true);
7987       
7988       if (this.installAllPatches == null) {
7989         System.out.println("Would you like to install all patches (t|f)? [t]: ");
7990         this.installAllPatches = readFromStdInBoolean(true, "grouperInstaller.autorun.installAllPatches");
7991 
7992         if (!this.installAllPatches && this.installPatchesUpToACertainPatchLevel == null ) {
7993           System.out.println("Would you like to install patches up to a certain patch level? (t|f)? [f]: ");
7994           this.installPatchesUpToACertainPatchLevel = readFromStdInBoolean(false, "grouperInstaller.autorun.installPatchesUpToACertainPatchLevel");
7995           
7996           if (this.installPatchesUpToACertainPatchLevel) {
7997 
7998             System.out.println("What patch levels would you like to install up to and including [comma-separated] (e.g. grouper_v2_3_0_api_patch_9, grouper_v2_3_0_ui_patch_10, grouper_v2_3_0_ws_patch_5)? : ");
7999             this.installPatchesUpToThesePatchLevels = readFromStdIn("grouperInstaller.autorun.installPatchesUpToThesePatchLevels");
8000 
8001           }
8002           
8003         }
8004         
8005         if (this.installPatchesUpToACertainPatchLevel == null) {
8006           this.installPatchesUpToACertainPatchLevel = false;
8007         }
8008         
8009         if (!this.installAllPatches && !this.installPatchesUpToACertainPatchLevel && this.installCertainSpecifiedPatches == null) {
8010           System.out.println("Would you like to install certain specified patches? (t|f)? [f]: ");
8011           this.installCertainSpecifiedPatches = readFromStdInBoolean(false, "grouperInstaller.autorun.installCertainSpecifiedPatches");
8012 
8013           if (this.installCertainSpecifiedPatches) {
8014 
8015             System.out.println("What patches would you like to install [comma-separated] (e.g. grouper_v2_3_0_api_patch_0, grouper_v2_3_0_api_patch_1, grouper_v2_3_0_ui_patch_0)? : ");
8016             this.installCertainSpecifiedPatchesList = readFromStdIn("grouperInstaller.autorun.installCertainSpecifiedPatchesList");
8017           }
8018         }
8019         if (this.installCertainSpecifiedPatches == null) {
8020           this.installCertainSpecifiedPatches = false;
8021         }
8022       }
8023 
8024       //print description
8025       System.out.println("Patch " + keyBase + " is " + patchProperties.getProperty("risk") + " risk, "
8026           + (securityRelated ? "is a security patch" : "is not a security patch"));
8027       System.out.println(patchProperties.getProperty("description"));
8028 
8029       if (this.installAllPatches) {
8030         installPatch = true;
8031       } else if (this.installPatchesUpToACertainPatchLevel) {
8032         if (installPatch == null) {
8033           installPatch = shouldInstallPatchUpToLevel(keyBase);
8034         }
8035       } else if (this.installCertainSpecifiedPatches) {
8036         if (installPatch == null) {
8037           installPatch = shouldInstallCertainSpecifiedPatches(keyBase);
8038         }
8039       } else {
8040         System.out.println("Would you like to install patch " + keyBase + " (t|f)? [t]: ");
8041         installPatch = readFromStdInBoolean(true, "grouperInstaller.autorun.installPatch");
8042       }
8043 
8044       //keep track that we skipped this in the patch properties file
8045       editPropertiesFile(patchExistingPropertiesFile, keyBase + ".date", 
8046           GrouperInstallerUtils.dateMinutesSecondsFormat.format(new Date()), true);
8047 
8048       //if we arent installing the patch
8049       if (!installPatch) {
8050         
8051         boolean temporary = false;
8052         
8053         //if installing up to a patch level, and not specifying about next time, make it temporary
8054         if (this.installPatchesUpToACertainPatchLevel && GrouperInstallerUtils.isBlank(GrouperInstallerUtils.propertiesValue("grouperInstaller.autorun.promptAboutPatchNextTime", false))) {
8055           temporary = true;
8056         } else if (this.installCertainSpecifiedPatches && GrouperInstallerUtils.isBlank(GrouperInstallerUtils.propertiesValue("grouperInstaller.autorun.promptAboutPatchNextTime", false))) {
8057           temporary = true;
8058 
8059         } else {
8060           System.out.println("Would you like to be prompted about this patch next time? (t|f)? [t]: ");
8061 
8062           temporary = readFromStdInBoolean(true, "grouperInstaller.autorun.promptAboutPatchNextTime");
8063         }
8064 
8065         GrouperInstallerPatchStatus grouperInstallerPatchStatus = null;
8066 
8067         if (temporary) {
8068           grouperInstallerPatchStatus = GrouperInstallerPatchStatus.skippedTemporarily;
8069         } else {
8070           grouperInstallerPatchStatus = GrouperInstallerPatchStatus.skippedPermanently;
8071         }
8072 
8073         editPropertiesFile(patchExistingPropertiesFile, keyBase + ".state", 
8074             grouperInstallerPatchStatus.name(), true);
8075         System.out.println("");
8076         continue OUTER;
8077       }
8078 
8079       if (requiresRestart && !this.grouperStopped) {
8080         System.out.println("This patch requires all processes that user Grouper to be stopped.\n  "
8081             + "Please stop these processes if they are running and press <enter> to continue...");
8082         this.grouperStopped = true;
8083         readFromStdIn("grouperInstaller.autorun.continueAfterPatchStopProcesses");
8084       }
8085       
8086       Map<String, String> patchDirToApplicationPath = new LinkedHashMap<String, String>();
8087       patchDirToApplicationPath.put("files", this.upgradeExistingApplicationDirectoryString);
8088       patchDirToApplicationPath.put("classes", this.upgradeExistingClassesDirectoryString);
8089       patchDirToApplicationPath.put("lib", this.upgradeExistingLibDirectoryString);
8090       patchDirToApplicationPath.put("bin", this.upgradeExistingBinDirectoryString);
8091 
8092       boolean patchHasProblem = false;
8093       
8094       //we are installing this patch, lets see if the files are there...
8095       //this.upgradeExistingApplicationDirectoryString
8096       //patchUntarredDir
8097       File oldDir = new File(patchUntarredDir.getAbsolutePath() + File.separator + "old");
8098       File newDir = new File(patchUntarredDir.getAbsolutePath() + File.separator + "new");
8099       {
8100 
8101         for (String patchDir : patchDirToApplicationPath.keySet()) {
8102           
8103           String applicationPath = patchDirToApplicationPath.get(patchDir);
8104 
8105           File oldDirFiles = new File(oldDir.getAbsoluteFile() + File.separator + patchDir);
8106           File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
8107           
8108           if (oldDirFiles.exists() && oldDirFiles.isDirectory()) {
8109           
8110             // relative, e.g. WEB-INF/jsp/someFile.jsp
8111             Set<String> oldFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(oldDirFiles);
8112             
8113             for (String oldFilePath : GrouperInstallerUtils.nonNull(oldFileRelativePaths)) {
8114               File oldFileInPatch = new File(oldDirFiles.getAbsolutePath() + File.separator + oldFilePath);
8115               File newFileInPatch = new File(newDirFiles.getAbsolutePath() + File.separator + oldFilePath);
8116 
8117               oldFilePath = patchFixFilePath(applicationPath, patchDir, oldFilePath, originalAppToUpgrade);
8118 
8119               File oldFileInGrouper = new File(applicationPath + oldFilePath);
8120   
8121               oldFileInGrouper = fixLibraryFileIfFoundAndDifferent(oldFileInGrouper, originalAppToUpgrade);
8122 
8123               if (!oldFileInPatch.exists() || !oldFileInPatch.isFile()) {
8124                 throw new RuntimeException("Why does file not exist or not file??? " + oldFileInPatch.getAbsolutePath());
8125               }
8126               boolean deletedNewPatchFile = !newFileInPatch.exists();
8127               boolean deletedGrouperFile = !oldFileInGrouper.exists();
8128               //if both deleted thats ok
8129               if ((!deletedGrouperFile || !deletedNewPatchFile) &&
8130                  ( !oldFileInGrouper.exists() || !oldFileInGrouper.isFile() 
8131                   || (!GrouperInstallerUtils.contentEquals(oldFileInPatch, oldFileInGrouper)
8132                       //patch is already applied?  thats ok i guess
8133                       && !GrouperInstallerUtils.contentEquals(newFileInPatch, oldFileInGrouper)))) {
8134                 
8135                 System.out.println("Problem applying patch since this patch old file:\n  " + oldFileInPatch.getAbsolutePath() 
8136                     + "\n  is not the same as what the patch expects:\n  " + oldFileInGrouper.getAbsolutePath()
8137                     + "\n  Do you want to force install this patch (t|f)? [f]: ");
8138                 
8139                 boolean forceInstallPatch = readFromStdInBoolean(false, "grouperInstaller.autorun.forceInstallPatch");
8140                 
8141                 if (!forceInstallPatch) {
8142                   System.out.println("Cannot apply patch since this patch file:\n  " + oldFileInPatch.getAbsolutePath() 
8143                       + "\n  is not the same as what the patch expects:\n  " + oldFileInGrouper.getAbsolutePath());
8144                   patchHasProblem = true;
8145                 }
8146               }
8147             }
8148           }
8149         }
8150       }
8151 
8152       //lets make sure that files which are new which dont have an old version do not exist in the application
8153       for (String patchDir : patchDirToApplicationPath.keySet()) {
8154         
8155         String applicationPath = patchDirToApplicationPath.get(patchDir);
8156 
8157         File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
8158         File oldDirFiles = new File(oldDir.getAbsoluteFile() + File.separator + patchDir);
8159         
8160         if (newDirFiles.exists() && newDirFiles.isDirectory()) {
8161         
8162           // relative, e.g. WEB-INF/jsp/someFile.jsp
8163           Set<String> newFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(newDirFiles);
8164 
8165           Set<String> oldFileRelativePaths = (oldDirFiles.exists() && oldDirFiles.isDirectory()) ? 
8166               GrouperInstallerUtils.fileDescendantRelativePaths(oldDirFiles) : new HashSet<String>();
8167 
8168           for (String newFilePath : GrouperInstallerUtils.nonNull(newFileRelativePaths)) {
8169 
8170             File newFileInPatch = new File(newDirFiles.getAbsoluteFile() + File.separator + newFilePath);
8171             
8172             newFilePath = patchFixFilePath(applicationPath, patchDir, newFilePath, originalAppToUpgrade);
8173 
8174             File oldFileInGrouper = new File(applicationPath + newFilePath);
8175 
8176             oldFileInGrouper = fixLibraryFileIfFoundAndDifferent(oldFileInGrouper, originalAppToUpgrade);
8177 
8178             if (!newFileInPatch.isFile()) {
8179               continue;
8180             }
8181             
8182             //if there wasnt a corresponding old file path
8183             if (!oldFileRelativePaths.contains(newFilePath) && !GrouperInstallerUtils.contentEquals(oldFileInGrouper, newFileInPatch)) {
8184 
8185               //then the file shouldnt exist
8186               if (oldFileInGrouper.exists()) {
8187 
8188                 System.out.println("Problem applying patch since this file:\n  " + oldFileInGrouper.getAbsolutePath() 
8189                   + "\n  should not exist yet\n  Do you want to force install this patch (t|f)? [f]: ");
8190             
8191                 boolean forceInstallPatch = readFromStdInBoolean(false, "grouperInstaller.autorun.forceInstallPatch");
8192                 
8193                 if (!forceInstallPatch) {
8194                 
8195                 
8196                   System.out.println("Cannot apply patch since this patch file:\n  " + newFileInPatch.getAbsolutePath() 
8197                       + "\n  is supposed to be new, but it already exists:\n  " + oldFileInGrouper.getAbsolutePath());
8198                   patchHasProblem = true;
8199 
8200                 }
8201               }
8202             }
8203           }
8204         }
8205       }
8206 
8207       if (patchHasProblem) {
8208         editPropertiesFile(patchExistingPropertiesFile, keyBase + ".state", 
8209             GrouperInstallerPatchStatus.error.name(), true);
8210 
8211         continue OUTER;
8212       }
8213 
8214       //so far so good, all the old files are all good, install the patch
8215       for (String patchDir : patchDirToApplicationPath.keySet()) {
8216         
8217         String applicationPath = patchDirToApplicationPath.get(patchDir);
8218 
8219         File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
8220         
8221         if (newDirFiles.exists() && newDirFiles.isDirectory()) {
8222         
8223           // relative, e.g. WEB-INF/jsp/someFile.jsp
8224           Set<String> newFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(newDirFiles);
8225           
8226           for (String newFilePath : GrouperInstallerUtils.nonNull(newFileRelativePaths)) {
8227             // adjust for jars in web apps
8228             //patchDir (e.g. lib), newFilePath (e.g. something.jar or grouper/something.jar), originalAppToUpgrade (e.g. UI)
8229             File newFileInPatch = new File(newDirFiles.getAbsolutePath() + File.separator + newFilePath);
8230             if (!newFileInPatch.isFile()) {
8231               continue;
8232             }
8233             newFilePath = patchFixFilePath(applicationPath, patchDir, newFilePath, originalAppToUpgrade);
8234             File oldFileInGrouper = new File(applicationPath + newFilePath);
8235             oldFileInGrouper = fixLibraryFileIfFoundAndDifferent(oldFileInGrouper, originalAppToUpgrade);
8236 
8237             if (!oldFileInGrouper.exists() && !oldFileInGrouper.getParentFile().exists()) {
8238               GrouperInstallerUtils.mkdirs(oldFileInGrouper.getParentFile());
8239             }
8240             System.out.println("Applying file: " + oldFileInGrouper.getAbsolutePath());
8241             GrouperInstallerUtils.copyFile(newFileInPatch, oldFileInGrouper, false);
8242           }
8243         }
8244         
8245         File oldDirFiles = new File(oldDir.getAbsoluteFile() + File.separator + patchDir);
8246         
8247         if (oldDirFiles.exists() && oldDirFiles.isDirectory()) {
8248         
8249           // relative, e.g. WEB-INF/jsp/someFile.jsp
8250           Set<String> oldFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(oldDirFiles);
8251           
8252           for (String oldFilePath : GrouperInstallerUtils.nonNull(oldFileRelativePaths)) {
8253             File oldFileInPatch = new File(oldDirFiles.getAbsolutePath() + File.separator + oldFilePath);
8254             File newFileInPatch = new File(newDirFiles.getAbsolutePath() + File.separator + oldFilePath);
8255             File oldFileInGrouper = new File(applicationPath + oldFilePath);
8256             oldFileInGrouper = fixLibraryFileIfFoundAndDifferent(oldFileInGrouper, originalAppToUpgrade);
8257 
8258             if (oldFileInPatch.exists() && !newFileInPatch.exists() && oldFileInGrouper.exists() && oldFileInGrouper.isFile()) {
8259 
8260               System.out.println("Deleting file: " + oldFileInGrouper.getAbsolutePath());
8261               GrouperInstallerUtils.fileDelete(oldFileInGrouper);
8262               
8263             }
8264           }
8265         }
8266       }
8267       
8268       //fixLibDir(patchDirToApplicationPath.get("lib"), originalAppToUpgrade);
8269 
8270       this.patchesInstalled.add(keyBase);
8271       System.out.println("Patch successfully applied: " + keyBase);
8272       
8273       editPropertiesFile(patchExistingPropertiesFile, keyBase + ".state", 
8274           GrouperInstallerPatchStatus.applied.name(), true);
8275       System.out.println("");
8276     }
8277 
8278     if (!foundNewPatch) {
8279       System.out.println("There are no new " + thisAppToUpgrade + " patches to install\n");
8280       return false;
8281     } 
8282     return true;
8283   }
8284 
8285   /**
8286    * <pre>
8287    * patch file extra grouper prefix pattern
8288    * ^grouper[/\\][^/\\]+[/\\]([^/\\]+)$
8289    * starts with grouper, then a slash, then capture a dir and jar filename
8290    * </pre>
8291    */
8292   private static Pattern patchFileExtraGrouperPrefixPattern = Pattern.compile("^grouper[/\\\\]([^/\\\\]+[/\\\\][^/\\\\]+)$");
8293   
8294   /**
8295    * applicationPath (e.g. lib), newFilePath (e.g. something.jar or grouper/something.jar), originalAppToUpgrade (e.g. UI)
8296    * @param applicationPath
8297    * @param patchDir
8298    * @param newFilePath
8299    * @param originalAppToUpgrade
8300    * @return String of new newFilePath
8301    */
8302   public String patchFixFilePath(String applicationPath, String patchDir, String newFilePath, AppToUpgrade originalAppToUpgrade) {
8303 
8304     if ("lib".equals(patchDir)) {
8305       // if this is the api then there should be something between lib and the jar
8306       String jarName = newFilePath;
8307       {
8308 //        Matcher matcher = patchFileExtraGrouperPrefixPattern.matcher(newFilePath);
8309 //        if (matcher.matches()) {
8310 //          jarName = matcher.group(1);
8311 //        }
8312         jarName = GrouperInstallerUtils.suffixAfterChar(newFilePath.replace("\\", "/"), '/');
8313 
8314       }
8315       
8316 
8317       if (originalAppToUpgrade.isApiOrganized()) {
8318         
8319         String noSlashApplicationPath = GrouperInstallerUtils.stripLastSlashIfExists(applicationPath);
8320         // if the application path has "grouper" already, then just put jarname on it
8321         if (!noSlashApplicationPath.endsWith("lib")) {
8322           newFilePath = jarName;
8323         } else {
8324           //if application is just lib, then make sure there is something in front of the jarname
8325           if (GrouperInstallerUtils.equals(newFilePath, jarName)) {
8326             newFilePath = "grouper/" + jarName;
8327           }
8328         }
8329          
8330       } else {
8331         // if this is a UI or WS, then we only want the jar
8332         newFilePath = jarName;
8333       }
8334     }
8335     return newFilePath;
8336   }
8337 
8338   
8339   /**
8340    * @param keyBase
8341    * @return if should revert patch
8342    */
8343   private boolean shouldRevertCertainSpecifiedPatches(String keyBase) {
8344     List<String> revertUpToThesePatchLevelsList = GrouperInstallerUtils.splitTrimToList(this.revertCertainSpecifiedPatchesList, ",");
8345     return revertUpToThesePatchLevelsList.contains(keyBase);
8346   }
8347 
8348   /**
8349    * @param keyBase
8350    * @return if should install patch
8351    */
8352   private boolean shouldInstallCertainSpecifiedPatches(String keyBase) {
8353     
8354     List<String> installUpToThesePatchLevelsList = GrouperInstallerUtils.splitTrimToList(this.installCertainSpecifiedPatchesList, ",");
8355     return installUpToThesePatchLevelsList.contains(keyBase);
8356   }
8357   
8358   /**
8359    * @param keyBase
8360    * @return if should install patch
8361    */
8362   private boolean shouldInstallPatchUpToLevel(String keyBase) {
8363     boolean installPatch = false;
8364 
8365     //e.g. ^grouper_v(\\d+)_(\\d+)_(\\d+)_(api|ws|ui|psp|pspng)_patch_(\\d+)$
8366     Matcher patchNameMatcher = patchNamePattern.matcher(keyBase);
8367     if (!patchNameMatcher.matches()) {
8368       throw new RuntimeException("Invalid patch name: " + keyBase);
8369     }
8370     
8371     String grouperVersionInstallPatch = patchNameMatcher.group(1) + "." + patchNameMatcher.group(2) + "." + patchNameMatcher.group(3);
8372     String systemInstallPatch = patchNameMatcher.group(4);
8373     int numberInstallPatch = GrouperInstallerUtils.intValue(patchNameMatcher.group(5));
8374 
8375     
8376     String[] installUpToThesePatchLevels = GrouperInstallerUtils.splitTrim(this.installPatchesUpToThesePatchLevels, ",");
8377     for (String patchName : installUpToThesePatchLevels) {
8378 
8379       //e.g. ^grouper_v(\\d+)_(\\d+)_(\\d+)_(api|ws|ui|psp|pspng)_patch_(\\d+)$
8380       patchNameMatcher = patchNamePattern.matcher(patchName);
8381       if (!patchNameMatcher.matches()) {
8382         throw new RuntimeException("Invalid patch name: " + patchName);
8383       }
8384       
8385       String grouperVersionUpToPatch = patchNameMatcher.group(1) + "." + patchNameMatcher.group(2) + "." + patchNameMatcher.group(3);
8386       String systemUpToPatch = patchNameMatcher.group(4);
8387       int numberUpToPatch = GrouperInstallerUtils.intValue(patchNameMatcher.group(5));
8388 
8389       if (GrouperInstallerUtils.equals(systemInstallPatch, systemUpToPatch)
8390           && GrouperInstallerUtils.equals(grouperVersionInstallPatch, grouperVersionUpToPatch)
8391           && numberInstallPatch <= numberUpToPatch) {
8392         installPatch = true;
8393         break;
8394       }
8395       
8396     }
8397     return installPatch;
8398   }
8399   
8400   /**
8401    * fix the index file
8402    * @param thisAppToUpgrade app to upgrade to check
8403    */
8404   private void fixIndexFile(AppToUpgrade thisAppToUpgrade) {
8405 
8406     if (thisAppToUpgrade == AppToUpgrade.CLIENT) {
8407       throw new RuntimeException("Cant fix index file for " + thisAppToUpgrade);
8408     }
8409     
8410     Properties patchesExistingProperties = patchExistingProperties();
8411 
8412     String grouperVersion = this.grouperVersionOfJar().toString();
8413 
8414     grouperVersion = GrouperInstallerUtils.replace(grouperVersion, ".", "_");
8415 
8416     //lets download all patches
8417     int nextPatchIndex = downloadPatches(thisAppToUpgrade, grouperVersion);
8418     
8419     File patchExistingPropertiesFile = patchExistingPropertiesFile();
8420 
8421     Map<String, String> patchDirToApplicationPath = new LinkedHashMap<String, String>();
8422     patchDirToApplicationPath.put("files", this.upgradeExistingApplicationDirectoryString);
8423     patchDirToApplicationPath.put("classes", this.upgradeExistingClassesDirectoryString);
8424     patchDirToApplicationPath.put("lib", this.upgradeExistingLibDirectoryString);
8425     patchDirToApplicationPath.put("bin", this.upgradeExistingBinDirectoryString);
8426 
8427     //map of full patch file name to the patch number that is installed
8428     Map<String, Integer> fileInMoreRecentPatchMap = new HashMap<String, Integer>();
8429 
8430     boolean patchesOverallOk = true;
8431     
8432     //process patches from greatest to least
8433     for (int i=nextPatchIndex-1;i>=0;i--) {
8434       
8435       //grouper_v2_2_1_api_patch_0.state
8436       String patchName = "grouper_v" + grouperVersion + "_" + thisAppToUpgrade.name().toLowerCase() + "_patch_" + i;
8437 
8438       String key = patchName + ".state";
8439 
8440       //see what is already there
8441       String existingState = patchesExistingProperties.getProperty(key);
8442       
8443       GrouperInstallerPatchStatus grouperInstallerPatchStatus = GrouperInstallerPatchStatus.valueOfIgnoreCase(existingState, false, true);
8444 
8445       File patchUntarredDir = new File(this.grouperTarballDirectoryString + "patches" + File.separator + patchName);
8446 
8447       //keep track that we skipped this in the patch properties file
8448       //editPropertiesFile(patchExistingPropertiesFile, keyBase + ".date", 
8449       //    GrouperInstallerUtils.dateMinutesSecondsFormat.format(new Date()));
8450       //editPropertiesFile(patchExistingPropertiesFile, keyBase + ".state", 
8451       //    grouperInstallerPatchStatus.name());
8452 
8453       boolean patchHasProblem = false;
8454       boolean patchHasAtLeastOneFile = false;
8455       boolean patchHasAtLeastOneFileInAnotherPatch = false;
8456       Set<String> patchErrors = new LinkedHashSet<String>();
8457       
8458       //keep track of patch paths (full path in patch)
8459       Set<String> patchPaths = new HashSet<String>();
8460       
8461       //we are installing this patch, lets see if the files are there...
8462       //this.upgradeExistingApplicationDirectoryString
8463       //patchUntarredDir
8464       File newDir = new File(patchUntarredDir.getAbsolutePath() + File.separator + "new");
8465       //loop through lib, classes, files
8466       for (String patchDir : patchDirToApplicationPath.keySet()) {
8467 
8468         String applicationPath = patchDirToApplicationPath.get(patchDir);
8469 
8470         File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
8471 
8472         // relative, e.g. WEB-INF/jsp/someFile.jsp
8473         Set<String> newFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(newDirFiles);
8474         // go through all files of the patches in the new dir
8475         for (String newFilePath : GrouperInstallerUtils.nonNull(newFileRelativePaths)) {
8476 
8477           String patchPath = patchDir + File.separator + newFilePath;
8478           
8479           Integer existsInPatchVersion = fileInMoreRecentPatchMap.get(patchPath);
8480           
8481           //if this file was in a newer patch, then thats ok
8482           if (existsInPatchVersion != null) {
8483             //this file is ok, its in a more recent patch
8484             patchHasAtLeastOneFileInAnotherPatch = true;
8485             continue;
8486           }
8487 
8488           File newFileInGrouper = new File(applicationPath + newFilePath);
8489 
8490           File newFileInPatch = new File(newDirFiles.getAbsolutePath() + File.separator + newFilePath);
8491           
8492           //see if the contents of the patch match those in grouper
8493           if (!GrouperInstallerUtils.contentEquals(newFileInPatch, newFileInGrouper)) {
8494 
8495             patchErrors.add("Problem in patch:\n  " + newFileInPatch.getAbsolutePath() 
8496                 + "\n  is not the same as what the patch expects:\n  " + newFileInGrouper.getAbsolutePath());
8497             patchHasProblem = true;
8498           } else {
8499 
8500             patchPaths.add(patchPath);
8501 
8502             patchHasAtLeastOneFile = true;
8503           }
8504         }
8505       }
8506         
8507       //is any file installed?  or if there are only files in other patches... hmm
8508       if (patchHasAtLeastOneFile || (patchHasAtLeastOneFileInAnotherPatch && !patchHasProblem )) {
8509         
8510         //add files in this patch to the list
8511         for (String patchPath : patchPaths) {
8512           fileInMoreRecentPatchMap.put(patchPath, i);
8513         }
8514         
8515         //one or more of the files in the patch had a problem
8516         if (patchHasProblem) {
8517           for (String patchError: patchErrors) {
8518             System.out.println(patchError);
8519           }
8520           if (grouperInstallerPatchStatus == null || (grouperInstallerPatchStatus != GrouperInstallerPatchStatus.applied
8521               && grouperInstallerPatchStatus != GrouperInstallerPatchStatus.error)) {
8522             patchesOverallOk = false;
8523             editPropertiesFile(patchExistingPropertiesFile, patchName + ".date", 
8524                 GrouperInstallerUtils.dateMinutesSecondsFormat.format(new Date()), true);
8525             editPropertiesFile(patchExistingPropertiesFile, patchName + ".state", 
8526                 GrouperInstallerPatchStatus.applied.name(), true);
8527             System.out.println("Patch " + patchName + " was listed as " + grouperInstallerPatchStatus + " but was changed to applied (even though there are files missing)");
8528             
8529           }
8530           continue;          
8531         }
8532         
8533         if (grouperInstallerPatchStatus == null || grouperInstallerPatchStatus != GrouperInstallerPatchStatus.applied) {
8534           patchesOverallOk = false;
8535           editPropertiesFile(patchExistingPropertiesFile, patchName + ".date", 
8536               GrouperInstallerUtils.dateMinutesSecondsFormat.format(new Date()), true);
8537           editPropertiesFile(patchExistingPropertiesFile, patchName + ".state", 
8538               GrouperInstallerPatchStatus.applied.name(), true);
8539           System.out.println("Patch " + patchName + " was listed as " + grouperInstallerPatchStatus + " but was changed to applied");
8540           
8541         }
8542         
8543       } else {
8544         if (grouperInstallerPatchStatus == null || grouperInstallerPatchStatus != GrouperInstallerPatchStatus.applied) {
8545           continue;
8546         }
8547         
8548         patchesOverallOk = false;
8549         editPropertiesFile(patchExistingPropertiesFile, patchName + ".date", 
8550             GrouperInstallerUtils.dateMinutesSecondsFormat.format(new Date()), true);
8551         editPropertiesFile(patchExistingPropertiesFile, patchName + ".state", 
8552             GrouperInstallerPatchStatus.skippedTemporarily.name(), true);
8553         System.out.println("Patch " + patchName + " was listed as applied but was changed to skippedTemporarily");
8554         continue;
8555       }
8556 
8557     }
8558 
8559     //tell the properties file that we have fixed the index file now
8560     editPropertiesFile(patchExistingPropertiesFile, "grouperInstallerLastFixedIndexFile.date", 
8561         GrouperInstallerUtils.dateMinutesSecondsFormat.format(new Date()), true);
8562   
8563     if (patchesOverallOk) {
8564       System.out.println("Patches for " + thisAppToUpgrade + " for version " + grouperVersion + " were in the index file correctly");
8565     }
8566   }
8567   
8568   /**
8569    * get all patches
8570    * @param thisAppToUpgrade app to upgrade to check
8571    * @param grouperVersion
8572    * @return next patch index
8573    */
8574   private int downloadPatches(AppToUpgrade thisAppToUpgrade, String grouperVersion) {
8575 
8576     if (thisAppToUpgrade == AppToUpgrade.CLIENT) {
8577       throw new RuntimeException("Cant install patches for " + thisAppToUpgrade);
8578     }
8579     
8580     Map<Integer, String> patchNumberToNameBase = new LinkedHashMap<Integer, String>();
8581     
8582     int nextPatchIndex = 0;
8583 
8584     OUTER: for (int i=0;i<1000;i++) {
8585 
8586       //grouper_v2_2_1_api_patch_0.state
8587       String keyBase = "grouper_v" + grouperVersion + "_" + thisAppToUpgrade.name().toLowerCase() + "_patch_" + i;
8588 
8589       patchNumberToNameBase.put(i, keyBase);
8590 
8591       //lets see if it exists on the server
8592       File patchUntarredDir = downloadAndUnzipPatch(keyBase);
8593 
8594       //if no more patches
8595       if (patchUntarredDir == null) {
8596         System.out.println("");
8597         break OUTER;
8598       }
8599 
8600       nextPatchIndex = i+1;
8601     }
8602 
8603     return nextPatchIndex;
8604 
8605   }
8606   
8607   /**
8608    * 
8609    * @param patchName e.g. grouper_v2_2_1_api_patch_0.tar.gz
8610    * @return the directory of the unzipped patch
8611    */
8612   public File downloadAndUnzipPatch(String patchName) {
8613     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
8614     
8615     if (!urlToDownload.endsWith("/")) {
8616       urlToDownload += "/";
8617     }
8618     urlToDownload += "release/";
8619     
8620     //e.g. 2.2.2
8621     Matcher patchNameMatcher = patchNamePattern.matcher(patchName);
8622     if (!patchNameMatcher.matches()) {
8623       throw new RuntimeException("Invalid patch name: " + patchName);
8624     }
8625     
8626     //String grouperVersion = GrouperInstallerUtils.propertiesValue("grouper.version", true);
8627     String grouperVersion = patchNameMatcher.group(1) + "." + patchNameMatcher.group(2) + "." + patchNameMatcher.group(3);
8628     
8629     urlToDownload +=  grouperVersion + "/patches/" + patchName + ".tar.gz";
8630 
8631     File patchFile = new File(this.grouperTarballDirectoryString + "patches" + File.separator + patchName + ".tar.gz");
8632     
8633     if (GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.downloadPatches", true, false)) {
8634 
8635       boolean foundFile = downloadFile(urlToDownload, patchFile.getAbsolutePath(), true, "Patch doesnt exist yet (not an error): ", 
8636           "grouperInstaller.autorun.useLocalPatchIfExists");
8637       
8638       if (!foundFile) {
8639 
8640         //if we are doing test patches
8641         if (GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.useTestPatches", false, false)) {
8642           String testUrlToDownload = GrouperInstallerUtils.replace(urlToDownload, ".tar.gz", "_test.tar.gz");
8643           
8644           //its a test url, but download to the same file name
8645           foundFile = downloadFile(testUrlToDownload, patchFile.getAbsolutePath(), true, "Patch doesnt exist yet (not an error): ", 
8646               "grouperInstaller.autorun.useLocalPatchIfExists");
8647         }
8648 
8649         if (!foundFile) {
8650           return null;
8651         }
8652       }
8653     } else {
8654       if (!patchFile.exists()) {
8655         return null;
8656       }
8657     }
8658     
8659     //####################################
8660     //unzip/untar the patch file
8661     
8662     File unzippedFile = unzip(patchFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalPatchIfExists");
8663     File untarredDir = untar(unzippedFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalPatchIfExists", null);
8664     return untarredDir;
8665   }
8666 
8667   /**
8668    * 
8669    * @param branchName
8670    * @return the directory of the unzipped source repo
8671    */
8672   public File downloadAndUnzipGrouperSource(String branchName) {
8673     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.source.url", false);
8674     
8675     if (GrouperInstallerUtils.isBlank(urlToDownload)) {
8676       urlToDownload = "https://github.com/Internet2/grouper/archive/$BRANCH_NAME$.zip";
8677     }
8678     
8679     urlToDownload = GrouperInstallerUtils.replace(urlToDownload, "$BRANCH_NAME$", branchName);
8680 
8681     String fileToDownload = GrouperInstallerUtils.substringAfterLast(urlToDownload, "/");
8682     
8683     File sourceFile = new File(this.grouperTarballDirectoryString + fileToDownload);
8684     
8685     if (GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.downloadSource", true, false)) {
8686 
8687       downloadFile(urlToDownload, sourceFile.getAbsolutePath(), "grouperInstaller.autorun.createPatchDownloadSourceUseLocalIfExist");
8688       
8689     } else {
8690       if (!sourceFile.exists()) {
8691         throw new RuntimeException("Cant find grouper source");
8692       }
8693     }
8694     
8695     //####################################
8696     //unzip/untar the source file
8697     File unzippedDir = unzipFromZip(sourceFile.getAbsolutePath(), "grouperInstaller.autorun.createPatchDownloadSourceUseLocalIfExist");
8698     return unzippedDir;
8699   }
8700 
8701   /**
8702    * 
8703    * @param branchName
8704    * @return the directory of the unzipped source repo
8705    */
8706   public File downloadAndUnzipPspSource(String branchName) {
8707     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.pspSource.url", false);
8708     
8709     if (GrouperInstallerUtils.isBlank(urlToDownload)) {
8710       urlToDownload = "https://github.com/Internet2/grouper-psp/archive/$BRANCH_NAME$.zip";
8711     }
8712     
8713     urlToDownload = GrouperInstallerUtils.replace(urlToDownload, "$BRANCH_NAME$", branchName);
8714 
8715     String fileToDownload = GrouperInstallerUtils.substringAfterLast(urlToDownload, "/");
8716     
8717     File sourceFile = new File(this.grouperTarballDirectoryString + fileToDownload);
8718     
8719     if (GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.downloadSource", true, false)) {
8720 
8721       downloadFile(urlToDownload, sourceFile.getAbsolutePath(), "grouperInstaller.autorun.createPatchDownloadSourceUseLocalIfExist");
8722       
8723     } else {
8724       if (!sourceFile.exists()) {
8725         throw new RuntimeException("Cant find grouper psp source");
8726       }
8727     }
8728     
8729     //####################################
8730     //unzip/untar the source file
8731     File unzippedDir = unzipFromZip(sourceFile.getAbsolutePath(), "grouperInstaller.autorun.createPatchDownloadSourceUseLocalIfExist");
8732     return unzippedDir;
8733   }
8734 
8735   /**
8736    * 
8737    */
8738   public static enum GrouperInstallerPatchStatus {
8739 
8740     /**
8741      * patch was applied
8742      */
8743     applied, 
8744     
8745     /**
8746      * patch was removed
8747      */
8748     reverted, 
8749     
8750     /**
8751      * patch was skipped temporarily, prompt again
8752      */
8753     skippedTemporarily, 
8754 
8755     /**
8756      * patch had an error applying
8757      */
8758     error, 
8759 
8760     /**
8761      * patch was skipped permanently, dont prompt again
8762      */
8763     skippedPermanently;
8764 
8765     /**
8766      * 
8767      * @param string
8768      * @param exceptionIfNotFound
8769      * @param exceptionIfInvalid
8770      * @return the patch status
8771      */
8772     public static GrouperInstallerPatchStatus valueOfIgnoreCase(String string, boolean exceptionIfNotFound, boolean exceptionIfInvalid) {
8773       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerPatchStatus.class, string, exceptionIfNotFound, exceptionIfInvalid);
8774     }
8775     
8776   }
8777 
8778   /**
8779    * patch status api
8780    */
8781   private void patchStatusApi() {
8782     this.patchStatus(AppToUpgrade.API);
8783   }
8784 
8785 
8786   /**
8787    * patch the api
8788    */
8789   private void patchApi() {
8790     this.downloadAndInstallPatches(AppToUpgrade.API, AppToUpgrade.API);
8791   }
8792 
8793   /**
8794    * fix index file api
8795    */
8796   private void fixIndexFileApi() {
8797     this.fixIndexFile(AppToUpgrade.API);
8798   }
8799 
8800   /**
8801    * fix index file ui
8802    */
8803   private void fixIndexFileUi() {
8804     this.fixIndexFile(AppToUpgrade.UI);
8805     this.fixIndexFile(AppToUpgrade.API);
8806   }
8807 
8808   /**
8809    * fix index file ws
8810    */
8811   private void fixIndexFileWs() {
8812     this.fixIndexFile(AppToUpgrade.WS);
8813     this.fixIndexFile(AppToUpgrade.API);
8814   }
8815 
8816   /**
8817    * fix index file psp
8818    */
8819   private void fixIndexFilePsp() {
8820     this.fixIndexFile(AppToUpgrade.PSP);
8821     this.fixIndexFile(AppToUpgrade.API);
8822   }
8823 
8824   /**
8825    * fix index file psp
8826    */
8827   private void fixIndexFilePspng() {
8828     this.fixIndexFile(AppToUpgrade.PSPNG);
8829     this.fixIndexFile(AppToUpgrade.API);
8830   }
8831 
8832   /**
8833    * patch status ui
8834    */
8835   private void patchStatusUi() {
8836     this.patchStatus(AppToUpgrade.API);
8837     this.patchStatus(AppToUpgrade.UI);
8838   }
8839 
8840   /**
8841    * patch status ws
8842    */
8843   private void patchStatusWs() {
8844     this.patchStatus(AppToUpgrade.API);
8845     this.patchStatus(AppToUpgrade.WS);
8846   }
8847 
8848   /**
8849    * patch status psp
8850    */
8851   private void patchStatusPsp() {
8852     this.patchStatus(AppToUpgrade.API);
8853     this.patchStatus(AppToUpgrade.PSP);
8854   }
8855 
8856   /**
8857    * patch status pspng
8858    */
8859   private void patchStatusPspng() {
8860     this.patchStatus(AppToUpgrade.API);
8861     this.patchStatus(AppToUpgrade.PSPNG);
8862   }
8863 
8864 
8865   /**
8866    * patch the client
8867    */
8868   private void patchUi() {
8869     this.downloadAndInstallPatches(AppToUpgrade.API, AppToUpgrade.UI);
8870     boolean patchesApplied = this.downloadAndInstallPatches(AppToUpgrade.UI, AppToUpgrade.UI);
8871     if (patchesApplied && (this.grouperInstallerMainFunction == GrouperInstallerMainFunction.patch 
8872         || this.grouperInstallerMainFunction == GrouperInstallerMainFunction.upgrade)) {
8873       System.out.print("Since patches were applied, you should delete files in your app server work directory,"
8874           + "\n  in tomcat it is named 'work'.  Hit <enter> to continue: ");
8875       readFromStdIn("grouperInstaller.autorun.continueAfterDeleteUiWorkDirectory");
8876     }
8877   }
8878   
8879   /**
8880    * patch the client
8881    */
8882   private void patchWs() {
8883     this.downloadAndInstallPatches(AppToUpgrade.API, AppToUpgrade.WS);
8884     boolean patchesApplied = this.downloadAndInstallPatches(AppToUpgrade.WS, AppToUpgrade.WS);
8885     if (patchesApplied && (this.grouperInstallerMainFunction == GrouperInstallerMainFunction.patch 
8886         || this.grouperInstallerMainFunction == GrouperInstallerMainFunction.upgrade)) {
8887       System.out.print("Since patches were applied, you should delete files in your app server work directory,"
8888           + "\n  in tomcat it is named 'work'.  Hit <enter> to continue: ");
8889       readFromStdIn("grouperInstaller.autorun.continueAfterDeleteWsWorkDirectory");
8890     }
8891   }
8892   
8893   /**
8894    * patch the psp
8895    */
8896   private void patchPsp() {
8897     this.downloadAndInstallPatches(AppToUpgrade.API, AppToUpgrade.PSP);
8898     this.downloadAndInstallPatches(AppToUpgrade.PSP, AppToUpgrade.PSP);
8899   }
8900 
8901   /**
8902    * patch the pspng
8903    */
8904   private void patchPspng() {
8905     this.downloadAndInstallPatches(AppToUpgrade.API, AppToUpgrade.PSPNG);
8906     this.downloadAndInstallPatches(AppToUpgrade.PSPNG, AppToUpgrade.PSPNG);
8907   }
8908 
8909   /**
8910    * revert patch the client
8911    */
8912   private void patchRevertApi() {
8913     this.revertPatches(AppToUpgrade.API, AppToUpgrade.API);
8914   }
8915 
8916   /**
8917    * revert patch the client
8918    */
8919   private void patchRevertUi() {
8920     this.revertPatches(AppToUpgrade.UI, AppToUpgrade.UI);
8921     boolean patchesReverted = this.revertPatches(AppToUpgrade.API, AppToUpgrade.UI);
8922     if (patchesReverted && (this.grouperInstallerMainFunction == GrouperInstallerMainFunction.patch 
8923         || this.grouperInstallerMainFunction == GrouperInstallerMainFunction.upgrade)) {
8924       System.out.print("Since patches were reverted, you should delete files in your app server work directory,"
8925           + "\n  in tomcat it is named 'work'.  Hit <enter> to continue: ");
8926       readFromStdIn("grouperInstaller.autorun.continueAfterDeleteUiWorkDirectory");
8927     }
8928   }
8929   
8930   /**
8931    * revert patch the client
8932    */
8933   private void patchRevertWs() {
8934     this.revertPatches(AppToUpgrade.WS,AppToUpgrade.WS);
8935     boolean patchesReverted = this.revertPatches(AppToUpgrade.API, AppToUpgrade.WS);
8936     if (patchesReverted && (this.grouperInstallerMainFunction == GrouperInstallerMainFunction.patch 
8937         || this.grouperInstallerMainFunction == GrouperInstallerMainFunction.upgrade)) {
8938       System.out.print("Since patches were reverted, you should delete files in your app server work directory,"
8939           + "\n  in tomcat it is named 'work'.  Hit <enter> to continue: ");
8940       readFromStdIn("grouperInstaller.autorun.continueAfterDeleteWsWorkDirectory");
8941     }
8942   }
8943   
8944   /**
8945    * revert patch the psp
8946    */
8947   private void patchRevertPsp() {
8948     this.revertPatches(AppToUpgrade.PSP, AppToUpgrade.PSP);
8949     this.revertPatches(AppToUpgrade.API, AppToUpgrade.PSP);
8950   }
8951 
8952   /**
8953    * revert patch the pspng
8954    */
8955   private void patchRevertPspng() {
8956     this.revertPatches(AppToUpgrade.PSPNG, AppToUpgrade.PSPNG);
8957     this.revertPatches(AppToUpgrade.API, AppToUpgrade.PSPNG);
8958   }
8959 
8960   /**
8961    * owasp csrf guard file
8962    */
8963   private File owaspCsrfGuardFile;
8964   
8965   /**
8966    * owasp csrf guard base file
8967    */
8968   private File owaspCsrfGuardBaseFile;
8969   
8970   /**
8971    * on an upgrade, compare a new jar and an existing jar and see if needs to be updated, and if so, update it
8972    * @param existingJarFile
8973    * @param newJarFile
8974    * @param printResultIfNotUpgrade
8975    * @param toDir if file not there, copy here.  If null, then go to upgrade existing lib directory string
8976    * @return true if upgraded, false if not
8977    */
8978   private boolean compareAndReplaceJar(File existingJarFile, File newJarFile, boolean printResultIfNotUpgrade, File toDir) {
8979     
8980     if (toDir == null) {
8981       toDir = new File(this.upgradeExistingLibDirectoryString);
8982     }
8983     
8984     if (existingJarFile == null || !existingJarFile.exists()) {
8985       System.out.println(newJarFile.getName() + " is a new file and is being copied to the application lib dir");
8986       existingJarFile = new File(toDir.getAbsoluteFile() + File.separator + newJarFile.getName());
8987       GrouperInstallerUtils.copyFile(newJarFile, existingJarFile, true);
8988       return true;
8989     }
8990 
8991     String existingJarFilePath = existingJarFile.getAbsolutePath();
8992     if (!GrouperInstallerUtils.filePathStartsWith(existingJarFilePath,this.upgradeExistingApplicationDirectoryString)) {
8993       throw new RuntimeException("Why does existing path not start with upgrade path??? " + existingJarFilePath + ", " + this.upgradeExistingApplicationDirectoryString);
8994     }
8995     
8996     String bakJarFileString = this.grouperBaseBakDir + existingJarFilePath.substring(this.upgradeExistingApplicationDirectoryString.length());
8997     File bakJarFile = new File(bakJarFileString);
8998     
8999     String existingVersion = GrouperInstallerUtils.jarVersion(existingJarFile);
9000     String newVersion = GrouperInstallerUtils.jarVersion(newJarFile);
9001     
9002     long existingSize = existingJarFile.length();
9003     long newSize = newJarFile.length();
9004     
9005     if (!GrouperInstallerUtils.equals(existingVersion, newVersion) || existingSize != newSize) {
9006 
9007       //make sure parents exist
9008       GrouperInstallerUtils.createParentDirectories(bakJarFile);
9009       
9010       System.out.println(existingJarFile.getName() + " had version " + existingVersion + " and size " + existingSize + " bytes and is being upgraded to version "
9011           + newVersion + " and size " + newSize + " bytes.\n  It is backed up to " + bakJarFile);
9012 
9013       GrouperInstallerUtils.fileMove(existingJarFile, bakJarFile);
9014       
9015       GrouperInstallerUtils.copyFile(newJarFile, existingJarFile, true);
9016       
9017       return true;
9018     }
9019     
9020     if (printResultIfNotUpgrade) {
9021       System.out.println(existingJarFile.getName() + " is up to date");
9022     }
9023     return false;
9024   }
9025 
9026   /**
9027    * on an upgrade, compare a new file and an existing file and see if needs to be updated, and if so, update it
9028    * @param existingFile
9029    * @param newFile
9030    * @param printResultIfNotUpgrade
9031    * @param toDir if file not there, copy here.  If null, then go to upgrade existing lib directory string
9032    * @return true if upgraded, false if not
9033    */
9034   private boolean compareAndCopyFile(File existingFile, File newFile, boolean printResultIfNotUpgrade, File toDir) {
9035     
9036     if (toDir == null) {
9037       throw new RuntimeException("Which dir to copy to??? " + newFile + ", " + existingFile);
9038     }
9039     
9040     if (existingFile == null || !existingFile.exists()) {
9041       System.out.println(newFile.getName() + " is a new file and is being copied to the application dir: " + toDir.getAbsolutePath());
9042       existingFile = new File(toDir.getAbsoluteFile() + File.separator + newFile.getName());
9043       GrouperInstallerUtils.copyFile(newFile, existingFile, true);
9044       return true;
9045     }
9046 
9047     String existingFilePath = existingFile.getAbsolutePath();
9048     if (!GrouperInstallerUtils.filePathStartsWith(existingFilePath,this.upgradeExistingApplicationDirectoryString)) {
9049       throw new RuntimeException("Why does existing path not start with upgrade path??? " + existingFilePath + ", " + this.upgradeExistingApplicationDirectoryString);
9050     }
9051     
9052     String bakFileString = this.grouperBaseBakDir + existingFilePath.substring(this.upgradeExistingApplicationDirectoryString.length());
9053     File bakFile = new File(bakFileString);
9054     
9055     String existingChecksum = GrouperInstallerUtils.fileSha1(existingFile);
9056     String newChecksum = GrouperInstallerUtils.fileSha1(newFile);
9057     
9058     long existingSize = existingFile.length();
9059     long newSize = newFile.length();
9060     
9061     if (!GrouperInstallerUtils.equals(existingChecksum, newChecksum) || existingSize != newSize) {
9062 
9063       //make sure parents exist
9064       GrouperInstallerUtils.createParentDirectories(bakFile);
9065       
9066       System.out.println(existingFile.getName() + " had checksum " + existingChecksum + " and size " + existingSize + " bytes and is being upgraded to checksum "
9067           + newChecksum + " and size " + newSize + " bytes.\n  It is backed up to " + bakFile);
9068 
9069       GrouperInstallerUtils.fileMove(existingFile, bakFile);
9070       
9071       GrouperInstallerUtils.copyFile(newFile, existingFile, true);
9072       
9073       return true;
9074     }
9075     
9076     if (printResultIfNotUpgrade) {
9077       System.out.println(existingFile.getName() + " is up to date");
9078     }
9079     return false;
9080   }
9081 
9082   /**
9083    * grouper.client.properties
9084    */
9085   private File grouperClientPropertiesFile;
9086   
9087   /**
9088    * grouper.client.base.properties
9089    */
9090   private File grouperClientBasePropertiesFile;
9091   
9092   /**
9093    * grouper.client.example.properties
9094    */
9095   private File grouperClientExamplePropertiesFile;
9096 
9097   /**
9098    * grouperClient.jar
9099    */
9100   private File grouperClientJar;
9101 
9102   /**
9103    * grouper.properties
9104    */
9105   private File grouperPropertiesFile;
9106   
9107   /**
9108    * grouper.base.properties
9109    */
9110   private File grouperBasePropertiesFile;
9111   
9112   /**
9113    * subject.properties
9114    */
9115   private File subjectPropertiesFile;
9116   
9117   /**
9118    * subject.base.properties
9119    */
9120   private File subjectBasePropertiesFile;
9121   
9122   /**
9123    * grouperUtf8.txt
9124    */
9125   private File grouperUtf8File;
9126   
9127   /**
9128    * GSHFileLoad.properties
9129    */
9130   private File gshFileLoadPropertiesFile;
9131   
9132   /**
9133    * groovysh.profile
9134    */
9135   private File groovyshProfileFile;
9136   
9137   /**
9138    * grouper.client.usage.example.txt
9139    */
9140   private File grouperClientUsageExampleFile;
9141   
9142   /**
9143    * grouper.example.properties
9144    */
9145   private File grouperExamplePropertiesFile;
9146 
9147   /**
9148    * grouper.hibernate.properties
9149    */
9150   private File grouperHibernatePropertiesFile;
9151   
9152   /**
9153    * grouper.hibernate.base.properties
9154    */
9155   private File grouperHibernateBasePropertiesFile;
9156   
9157   /**
9158    * grouper.hibernate.example.properties
9159    */
9160   private File grouperHibernateExamplePropertiesFile;
9161 
9162   /**
9163    * grouper-ws.properties
9164    */
9165   private File grouperWsPropertiesFile;
9166   
9167   /**
9168    * grouper-ws.example.properties
9169    */
9170   private File grouperWsBasePropertiesFile;
9171 
9172   /**
9173    * grouper-ws.base.properties
9174    */
9175   private File grouperWsExamplePropertiesFile;
9176   
9177   /**
9178    * ehcache.xml
9179    */
9180   private File ehcacheFile;
9181 
9182   /**
9183    * ehcache.example.xml
9184    */
9185   private File ehcacheExampleFile;
9186   
9187   /**
9188    * grouper-loader.properties
9189    */
9190   private File grouperLoaderPropertiesFile;
9191   
9192   /**
9193    * grouper-loader.base.properties
9194    */
9195   private File grouperLoaderBasePropertiesFile;
9196   
9197   /**
9198    * grouper.cache.properties
9199    */
9200   private File grouperCachePropertiesFile;
9201   
9202   /**
9203    * grouper.cache.base.properties
9204    */
9205   private File grouperCacheBasePropertiesFile;
9206   
9207   /**
9208    * grouper-loader.example.properties
9209    */
9210   private File grouperLoaderExamplePropertiesFile;
9211 
9212   /**
9213    * grouper.jar
9214    */
9215   private File grouperJar;
9216 
9217   
9218   /**
9219    * find a classpath file on classpath by resourceName
9220    * @param resourceName resource name of file
9221    * @param exceptionIfNotFound 
9222    * @return the file or null if not exception if not found
9223    */
9224   private File findClasspathFile(String resourceName, boolean exceptionIfNotFound) {
9225     
9226     Set<String> fileNamesTried = new LinkedHashSet<String>();
9227     
9228     File file = new File(this.upgradeExistingApplicationDirectoryString + "classes" + File.separator + resourceName);
9229     if (file.exists()) {
9230       return file;
9231     }
9232     
9233     fileNamesTried.add(file.getAbsolutePath());
9234     
9235     file = new File(this.upgradeExistingApplicationDirectoryString + "conf" + File.separator + resourceName);
9236     if (file.exists()) {
9237       return file;
9238     }
9239 
9240     fileNamesTried.add(file.getAbsolutePath());
9241 
9242     //these could be in this location
9243     if (GrouperInstallerUtils.equals("nav.properties", resourceName) 
9244         || GrouperInstallerUtils.equals("media.properties", resourceName)) {
9245       file = new File(this.upgradeExistingApplicationDirectoryString + "WEB-INF" + File.separator 
9246           + "classes" + File.separator + "resources" + File.separator + "grouper" + File.separator + resourceName);
9247       if (file.exists()) {
9248         return file;
9249       }
9250       
9251       fileNamesTried.add(file.getAbsolutePath());
9252     }
9253     
9254     file = new File(this.upgradeExistingApplicationDirectoryString + "WEB-INF" + File.separator + "classes" 
9255         + File.separator + resourceName);
9256     if (file.exists()) {
9257       return file;
9258     }
9259 
9260     fileNamesTried.add(file.getAbsolutePath());
9261     
9262     file = new File(this.upgradeExistingApplicationDirectoryString + resourceName);
9263     if (file.exists()) {
9264       return file;
9265     }
9266 
9267     fileNamesTried.add(file.getAbsolutePath());
9268     
9269     if (exceptionIfNotFound) {
9270       throw new RuntimeException("Cant find file, looked in: " + GrouperInstallerUtils.join(fileNamesTried.iterator(), ", "));
9271     }
9272     
9273     return null;
9274   }
9275 
9276   /**
9277    * lib dirs where libs might be in this.upgradeExistingApplicationDirectoryString
9278    */
9279   private static List<String> libDirs = GrouperInstallerUtils.toList(
9280       "lib" + File.separator, 
9281       "WEB-INF" + File.separator + "lib" + File.separator,
9282       "lib" + File.separator + "grouper" + File.separator,
9283       "lib" + File.separator + "custom" + File.separator,
9284       "lib" + File.separator + "jdbcSamples" + File.separator,
9285       "dist" + File.separator + "lib" + File.separator,
9286       "");
9287 
9288   /**
9289    * get all library files
9290    * @param appDir 
9291    * @return the list of files
9292    */
9293   private List<File> findAllLibraryFiles(String appDir) {
9294     
9295     if (!appDir.endsWith("/") && !appDir.endsWith("\\")) {
9296       appDir = appDir + File.separator;
9297     }
9298     
9299     List<File> result = new ArrayList<File>();
9300     for (String libDir : libDirs) {
9301 
9302       File dir = new File(appDir + libDir);
9303       if (dir.exists() && dir.isDirectory()) {
9304         for (File file : dir.listFiles()) {
9305           if (file.getName().endsWith(".jar")) {
9306             result.add(file);
9307           }
9308         }
9309       }
9310       
9311     }
9312     return result;
9313   }
9314 
9315   /**
9316    * if file is there, return it.  if not a jar, return original
9317    * find a library file on lib dir by lib name, if there return it
9318    * otherwise just return the original file name.  fix the name if not correct
9319    * @param originalThoughtLocation 
9320    * @param originalAppToUpgrade
9321    * @return the file or null if not exception if not found
9322    */
9323   private File fixLibraryFileIfFoundAndDifferent(File originalThoughtLocation, AppToUpgrade originalAppToUpgrade) {
9324     
9325     if (originalThoughtLocation == null || (originalThoughtLocation.exists() && originalThoughtLocation.isFile())) {
9326       return originalThoughtLocation;
9327     }
9328     
9329     if (!originalThoughtLocation.getAbsolutePath().endsWith(".jar")) {
9330       return originalThoughtLocation;
9331     }
9332     
9333     File foundLibraryFile = findLibraryFile(originalThoughtLocation.getName(), false);
9334     if (foundLibraryFile != null && foundLibraryFile.exists() && foundLibraryFile.isFile()) {
9335       return foundLibraryFile;
9336     }
9337     
9338     if (!originalAppToUpgrade.isApiOrganized()) {
9339       
9340       // is in WEB-INF/lib/something.jar
9341       if (GrouperInstallerUtils.equals("lib", originalThoughtLocation.getParentFile().getName())) {
9342         return originalThoughtLocation;
9343       }
9344       
9345       if (GrouperInstallerUtils.equals("lib", originalThoughtLocation.getParentFile().getParentFile().getName())) {
9346         return new File(originalThoughtLocation.getParentFile().getParentFile().getAbsoluteFile() + File.separator + originalThoughtLocation.getName());
9347       }
9348       
9349       return originalThoughtLocation;
9350     }
9351     
9352     //is api
9353     if (!GrouperInstallerUtils.equals("lib", originalThoughtLocation.getParentFile().getName())
9354         && GrouperInstallerUtils.equals("lib", originalThoughtLocation.getParentFile().getParentFile().getName())) {
9355       return originalThoughtLocation;
9356     }
9357       
9358     if (GrouperInstallerUtils.equals("lib", originalThoughtLocation.getParentFile().getName())) {
9359       return new File(originalThoughtLocation.getParentFile().getAbsoluteFile() + File.separator + "grouper" + File.separator + originalThoughtLocation.getName());
9360     }
9361     
9362     // not sure what to do here
9363     return originalThoughtLocation;
9364   }
9365 
9366   /**
9367    * find a library file on lib dir by libName
9368    * @param libName lib name of file
9369    * @param exceptionIfNotFound 
9370    * @return the file or null if not exception if not found
9371    */
9372   private File findLibraryFile(String libName, boolean exceptionIfNotFound) {
9373     
9374     Set<String> fileNamesTried = new LinkedHashSet<String>();
9375 
9376     for (String libDir : libDirs) {
9377 
9378       File file = new File(this.upgradeExistingApplicationDirectoryString + libDir + libName);
9379       if (file.exists()) {
9380         return file;
9381       }
9382       
9383       fileNamesTried.add(file.getAbsolutePath());
9384       
9385     }
9386     
9387     if (exceptionIfNotFound) {
9388       throw new RuntimeException("Cant find file, looked in: " + GrouperInstallerUtils.join(fileNamesTried.iterator(), ", "));
9389     }
9390     
9391     return null;
9392   }
9393   
9394   /**
9395    * 
9396    */
9397   private void mainInstallContainerLogic() {
9398     
9399     System.out.print("The Grouper Installer will install a \"maturity level 0\" Grouper environment using Docker. You do not have to use Docker to run Grouper, but you need Docker for this Grouper Installer. Do you have Docker installed and running? (t|f) [t]: ");
9400     
9401     boolean dockerInstalledAndRunning = readFromStdInBoolean(true, "");
9402     
9403     if (!dockerInstalledAndRunning) {
9404       System.out.println("Please install and run docker before proceeding. Thanks! ");
9405       return;
9406     }
9407     
9408     boolean validBaseDirectoryFound = false;
9409     String path = null;
9410     do {
9411       File grouperContainerBaseDirectory = new File(new File("").getAbsolutePath());
9412       
9413       System.out.print("Where do you want your host grouper container base directory (e.g. /opt/grouperContainer)? ["+grouperContainerBaseDirectory.getAbsolutePath()+"]: ");
9414       String localGrouperContainerBaseDirectoryString = readFromStdIn("Placeholder");
9415       if (!GrouperInstallerUtils.isBlank(localGrouperContainerBaseDirectoryString)) {
9416         File grouperContainerBaseDirectoryFile = new File(localGrouperContainerBaseDirectoryString);
9417         if (!grouperContainerBaseDirectoryFile.exists() || !grouperContainerBaseDirectoryFile.isDirectory()) { 
9418           System.out.println("Error: cant find directory: '" + grouperContainerBaseDirectoryFile.getAbsolutePath() + "'");
9419         } else {
9420           path = grouperContainerBaseDirectoryFile.getAbsolutePath();
9421           validBaseDirectoryFound = true;
9422         }
9423       } else {
9424         path = grouperContainerBaseDirectory.getAbsolutePath();
9425         validBaseDirectoryFound = true;
9426       }
9427       
9428     } while (validBaseDirectoryFound == false);
9429     
9430     
9431     // create README.txt file at the path
9432     File readmeFile = new File(path + File.separator + "README.txt");
9433     if (readmeFile.exists()) {
9434       String newFileName = "README_" + new Date().toString().replace(" ", "_") + ".txt";
9435       System.out.println("README.txt already exists. Going to rename to "+newFileName);
9436       readmeFile.renameTo(new File(path + File.separator + newFileName));
9437       readmeFile = new File(path + File.separator + "README.txt");
9438     }
9439         
9440     GrouperInstallerUtils.fileCreate(readmeFile);
9441     
9442     // create logs directory
9443     StringBuilder contentToWrite = new StringBuilder();
9444     contentToWrite.append("Create logs directory in "+path);
9445     contentToWrite.append("\n\n");
9446     contentToWrite.append("\n\n");
9447     try {      
9448       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
9449     } catch (Exception e) {
9450       System.out.println("Could not write to README.txt file.");
9451     }
9452     
9453     File logsDirectory = new File(path+File.separator+"logs"+File.separator+"nothing");
9454     GrouperInstallerUtils.createParentDirectories(logsDirectory);
9455     
9456     File logsDirectoryOnly = new File(path+File.separator+"logs");
9457     
9458     // run chmod o+w for logs directory
9459     contentToWrite = new StringBuilder();
9460     contentToWrite.append("Run chmod o+w for "+logsDirectoryOnly.getAbsolutePath());
9461     contentToWrite.append("\n\n");
9462     contentToWrite.append("\n\n");
9463     try {      
9464       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
9465     } catch (Exception e) {
9466       System.out.println("Could not write to README.txt file.");
9467     }
9468     
9469     List<String> openWriteCommands = GrouperInstallerUtils.toList("chmod", "o+w", 
9470         logsDirectoryOnly.getAbsolutePath() + File.separator);
9471     
9472     System.out.println("Making logs directory o+w so that logs can be written from inside the container: " + convertCommandsIntoCommand(openWriteCommands) + "\n");
9473 
9474     String errorMessageOnChangingLogsDirectoryPermissions = "";
9475     boolean errorOnChangingLogsDirectoryPermissions = false;
9476     try {
9477       CommandResult openWriteCommandResult = GrouperInstallerUtils.execCommand(
9478           GrouperInstallerUtils.toArray(openWriteCommands, String.class), true, true, null, 
9479           new File("."), null, true);
9480       
9481       if (openWriteCommandResult.getExitCode() != 0) {
9482         errorMessageOnChangingLogsDirectoryPermissions = openWriteCommandResult.getErrorText();
9483         errorOnChangingLogsDirectoryPermissions = true;
9484       } 
9485       
9486     } catch (Throwable e) {
9487       errorOnChangingLogsDirectoryPermissions = true;
9488     }
9489     
9490     if (errorOnChangingLogsDirectoryPermissions) {
9491       System.out.println("Could not change permissions on logs directory at "+logsDirectoryOnly.getAbsolutePath());
9492       if (GrouperInstallerUtils.isNotBlank(errorMessageOnChangingLogsDirectoryPermissions)) {
9493         System.out.println("Received error message: "+errorMessageOnChangingLogsDirectoryPermissions+ " ");
9494       }
9495       return;
9496     }
9497     
9498     // create log4j.properties file in <path>/slashRoot/opt/grouper/grouperWebapp/WEB-INF/classes
9499     File classesDir = new File(path+File.separator+"slashRoot"+File.separator+"opt"+File.separator+"grouper"
9500         +File.separator+"grouperWebapp"+File.separator+"WEB-INF"+File.separator+"classes");
9501     
9502     File log4jPropertiesFile = new File(classesDir.getAbsolutePath()+File.separator+"log4j.properties");
9503     
9504     contentToWrite = new StringBuilder();
9505     contentToWrite.append("Create log4j.properties file in "+log4jPropertiesFile.getAbsolutePath());
9506     contentToWrite.append("\n\n");
9507     contentToWrite.append("Copy the content from https://spaces.at.internet2.edu/display/Grouper/Install+the+Grouper+v2.5+container+with+maturity+level+0+using+installer");
9508     contentToWrite.append("\n\n");
9509     contentToWrite.append("\n\n");
9510     try {      
9511       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
9512     } catch (Exception e) {
9513       System.out.println("Could not write to README.txt file.");
9514     }
9515     
9516     GrouperInstallerUtils.createParentDirectories(log4jPropertiesFile);
9517     boolean reuseLog4jPropertiesFile = false;
9518     while(true) {
9519       if (log4jPropertiesFile.exists()) {
9520         System.out.print("log4j.properties already exists at '"+log4jPropertiesFile.getParent()+"' ");
9521         System.out.print("Do you want to reuse it (t|f) [t]: ");
9522         reuseLog4jPropertiesFile = readFromStdInBoolean(true, "Placeholder");
9523         if (reuseLog4jPropertiesFile) {
9524           System.out.println("Going to reuse existing log4j.properties file. ");
9525           break;
9526         } else {
9527           System.out.print("Delete log4j.properties and press <return> to continue ");
9528           readFromStdIn("nothing");
9529           log4jPropertiesFile = new File(path+File.separator+"conf"+File.separator+"log4j.properties");
9530           continue;
9531         }
9532       } else {
9533         GrouperInstallerUtils.fileCreate(log4jPropertiesFile);
9534         break;
9535       }
9536     }
9537     
9538     try {
9539       InputStream in = getClass().getResourceAsStream("/log4j.sample.properties"); 
9540       BufferedReader reader = new BufferedReader(new InputStreamReader(in));
9541       StringBuilder log4jContent = new StringBuilder();
9542       String line;
9543       while( (line = reader.readLine()) != null) {
9544         log4jContent.append(line);
9545         log4jContent.append("\n");
9546       }
9547       Files.write(Paths.get(log4jPropertiesFile.getAbsolutePath()), log4jContent.toString().getBytes(), StandardOpenOption.APPEND);
9548     } catch (Exception e) {
9549       System.out.println("Could not write content to "+log4jPropertiesFile.getAbsolutePath());
9550       System.out.println("Go to https://spaces.at.internet2.edu/display/Grouper/Install+the+Grouper+v2.5+container+with+maturity+level+0+using+installer to copy the log4j.properties section manually. ");
9551       System.out.print("press <return> to continue ");
9552       readFromStdIn("Placeholder");
9553     }
9554     
9555     
9556     String dockerImageVersion = GrouperInstallerUtils.propertiesValue("grouperInstaller.docker.image.version", false);
9557     
9558     if (GrouperInstallerUtils.isBlank(dockerImageVersion)) {
9559       dockerImageVersion = getClass().getPackage().getImplementationVersion();
9560     }
9561     
9562     if (GrouperInstallerUtils.isBlank(dockerImageVersion)) {
9563       dockerImageVersion = GrouperInstallerUtils.propertiesValue("grouper.version", true);
9564     }
9565     
9566     contentToWrite = new StringBuilder("Make sure docker is installed and running. Run the following command to check if docker is installed.");
9567     contentToWrite.append("\n\n");
9568     contentToWrite.append("which docker");
9569     contentToWrite.append("\n\n");
9570     contentToWrite.append("If docker is not installed, go to: https://docs.docker.com/install/ and select the correct platform and follow the instructions. ");
9571     contentToWrite.append("\n\n");
9572     contentToWrite.append("\n\n");
9573     
9574     contentToWrite.append("Run the following command to check if docker is running");
9575     contentToWrite.append("\n\n");
9576     contentToWrite.append("docker info");
9577     
9578     contentToWrite.append("\n\n");
9579     contentToWrite.append("\n\n");
9580     contentToWrite.append("Run the following command to start docker if it's not running already. Command might vary based on the platform.");
9581     contentToWrite.append("\n\n");
9582     contentToWrite.append("sudo service docker start");
9583     
9584     contentToWrite.append("\n\n");
9585     contentToWrite.append("\n\n");
9586     
9587     try {      
9588       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
9589     } catch (Exception e) {
9590       System.out.println("Could not write to README.txt file.");
9591     }
9592     
9593     List<String> commands = new ArrayList<String>();
9594     commands.add(shCommand());
9595     commands.add("-c");
9596     commands.add("which docker");
9597     
9598     String dockerLocation = null;
9599     boolean errorDetectingDocker = false;
9600     String errorMessageDetectingDocker = null;
9601     
9602     try {
9603       CommandResult commandResult = GrouperInstallerUtils.execCommand(
9604           GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
9605           new File("."), null, false, false, true);
9606       
9607       if (commandResult.getExitCode() != 0) {
9608         errorMessageDetectingDocker = commandResult.getErrorText();
9609         errorDetectingDocker = true;
9610       } else {
9611         dockerLocation = commandResult.getOutputText();
9612       }
9613       
9614     } catch (Throwable e) {
9615       errorDetectingDocker = true;
9616     }
9617     
9618     if (errorDetectingDocker) {
9619       System.out.println("Could not detect if docker is installed with command 'which docker' ");
9620       if (GrouperInstallerUtils.isNotBlank(errorMessageDetectingDocker)) {
9621         System.out.println("Received error message: "+errorMessageDetectingDocker+ " ");
9622       }
9623       
9624       System.out.println("Make sure you are running the installer as the user that runs docker ");
9625       
9626       System.out.print("If you have docker installed, enter 't' otherwise enter 'f': ");
9627       boolean isDockerInstalled = readFromStdInBoolean(null, "Placeholder");
9628       
9629       if (isDockerInstalled) {
9630         System.out.print("Please enter the full absolute path to docker. eg: /usr/local/bin/docker ");
9631         dockerLocation = readFromStdIn("Placeholder");
9632         
9633         while (true) {
9634           if (GrouperInstallerUtils.isBlank(dockerLocation) || !new File(dockerLocation).exists()) {
9635             System.out.print("Path is invalid. Please try again. ");
9636             dockerLocation = readFromStdIn("Placeholder");
9637           } else {
9638             break;
9639           }
9640         }
9641       } else {
9642         System.out.print("Please install docker first and try again. ");
9643         return;
9644       }
9645     } else {
9646       // docker is installed
9647       System.out.println("We detected docker is installed at: "+dockerLocation);
9648       System.out.print("Is the path above correct? (t|f) [t]: ");
9649       boolean correctDockerLocationDetected = readFromStdInBoolean(true, "Placeholder");
9650       
9651       if (correctDockerLocationDetected == false) {
9652         System.out.print("Please enter the full absolute path to docker. eg: /usr/local/bin/docker : ");
9653         dockerLocation = readFromStdIn("Placeholder");
9654         
9655         while (true) {
9656           if (GrouperInstallerUtils.isBlank(dockerLocation) || !new File(dockerLocation).exists()) {
9657             System.out.print("Path is invalid. Please try again. ");
9658             dockerLocation = readFromStdIn("Placeholder");
9659           } else {
9660             break;
9661           }
9662         }
9663       }
9664     }
9665     
9666     
9667     dockerLocation = dockerLocation.trim();
9668     
9669     // check if docker is running or not. 
9670     
9671     System.out.println("Going to check if docker is running. ");
9672     boolean dockerIsRunning = false;
9673     CommandResult commandResult = null;
9674     while (true) {
9675       try {
9676         commandResult = GrouperInstallerUtils.execCommand(
9677             new String[] {shCommand(), "-c", dockerLocation + " info"}, true, true, null, 
9678             new File("."), null, false, false, true);
9679         if (commandResult.getExitCode() == 0) {
9680           dockerIsRunning = true;
9681         }
9682       } catch (Exception e) {}
9683       
9684       if (dockerIsRunning == false) {
9685         
9686         System.out.print("Could not determine if docker is running. You can run 'docker info' to check if docker is running. Do you have docker running? (t|f): ");
9687         boolean isDockerRunning = readFromStdInBoolean(null, "Placeholder");
9688         
9689         if (isDockerRunning) {
9690           break;
9691         } else {
9692           System.out.print("Start docker and press <return> to continue ");
9693           readFromStdIn("Placeholder");
9694           continue;
9695         }
9696         
9697       } else {
9698         System.out.println("docker is running. ");
9699         System.out.println(commandResult.getOutputText());
9700         break;
9701       }
9702     }
9703     
9704     
9705     // if there's a container with name gsh, ws, ui, etc is already running. ask user to stop them first
9706     
9707     contentToWrite = new StringBuilder();
9708     contentToWrite.append("Run the following command to view the containers names");
9709     contentToWrite.append("\n\n");
9710     contentToWrite.append("docker ps --all --format \"{{.Names}}\" ");
9711     contentToWrite.append("\n");
9712     contentToWrite.append("If you have gsh, ws, grouper or ui containers already there. Please stop them, remove them and then continue.");
9713     contentToWrite.append("\n");
9714     
9715     contentToWrite.append("To stop a running container, run the following command. ");
9716     contentToWrite.append("\n");
9717     contentToWrite.append("docker kill <container name>");
9718     contentToWrite.append("\n");
9719     contentToWrite.append("You might want to add -f flag to docker kill command if unable to stop.");
9720     contentToWrite.append("\n");
9721     contentToWrite.append("To remove the container, run the following command.");
9722     contentToWrite.append("\n");
9723     contentToWrite.append("docker rm <container name>");
9724     contentToWrite.append("\n");
9725     contentToWrite.append("You might want to add -f flag to docker rm command if unable to remove.");
9726     contentToWrite.append("\n\n");
9727     contentToWrite.append("\n\n");
9728     
9729     try {      
9730       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
9731     } catch (Exception e) {
9732       System.out.println("Could not write to README.txt file.");
9733     }
9734     
9735     System.out.println("Going to check if gsh, ws, grouper-ui, or ui containers already exist.");
9736     List<String> conflictingNames = new ArrayList<String>();
9737     boolean conflictingNamesRanSuccessfully = false;
9738     try {
9739       commandResult = GrouperInstallerUtils.execCommand(
9740           new String[] {shCommand(), "-c", dockerLocation + " ps --all --format \"{{.Names}}\""}, true, true, null, 
9741           new File("."), null, false, false, true);
9742       if (commandResult.getExitCode() == 0) {
9743         conflictingNamesRanSuccessfully = true;
9744         String containerNamesString = commandResult.getOutputText();
9745         if (GrouperInstallerUtils.isNotBlank(containerNamesString)) {
9746           String[] containerNames = containerNamesString.split("\n");
9747           
9748           List<String> containersThatCanCauseConflict = Arrays.asList("gsh", "ui", "ws", "grouper", "grouper-ui");
9749           
9750           for (String containerName: containerNames) {
9751             if (containersThatCanCauseConflict.contains(containerName)) {
9752               conflictingNames.add(containerName);
9753             }
9754           }
9755         }
9756       }
9757     } catch (Exception e) {}
9758     
9759     if (conflictingNamesRanSuccessfully == false) {
9760       System.out.println("There was an error trying to figure out if gsh, ui, or ws containers already exist.");
9761       System.out.println("Run 'docker ps --all --format \"{{.Names}}\"' to see if gsh, ws, grouper, grouper-ui, or ui already exist. Please delete them before proceeding. ");
9762       System.out.println("Run docker rm -f <container name> to force remove a docker container");
9763     }
9764     
9765     if (conflictingNames.size() > 0) {
9766       System.out.println("We found that containers with names "+String.join(", ", conflictingNames) + " already exist. Please delete them before proceeding. ");
9767       System.out.println("Command to delete a docker container is 'docker rm <container name>'. Use -f flag to force remove. ");
9768       System.out.print("press <return> once you have deleted the conflicting containers. ");
9769       readFromStdIn("Placeholder");
9770     }
9771     if (conflictingNamesRanSuccessfully && conflictingNames.size() == 0) {
9772       System.out.println("No conflicting containers found. ");
9773     }
9774     
9775     // pull grouper docker image
9776     contentToWrite = new StringBuilder();
9777     contentToWrite.append("Pull grouper docker image by running the following command. ");
9778     contentToWrite.append("\n\n");
9779     contentToWrite.append("docker pull i2incommon/grouper:"+dockerImageVersion);
9780     contentToWrite.append("\n\n");
9781     contentToWrite.append("\n\n");
9782     
9783     try {      
9784       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
9785     } catch (Exception e) {
9786       System.out.println("Could not write to README.txt file.");
9787     }
9788     
9789     System.out.println("Going to pull grouper docker image: i2incommon/grouper:"+dockerImageVersion);
9790     boolean pulledDockerImage = false;
9791     try {
9792       
9793       String dockerPullCommand = dockerLocation + " pull i2incommon/grouper:"+dockerImageVersion;
9794       
9795       commandResult = GrouperInstallerUtils.execCommand(
9796           new String[] {shCommand(), "-c", dockerPullCommand}, true, true, null, 
9797           new File("."), null, false, true, true);
9798       
9799       if (commandResult.getExitCode() == 0) {
9800         pulledDockerImage = true;
9801       }
9802       
9803       if (commandResult.getOutputText() != null) {
9804         System.out.println(commandResult.getOutputText());
9805       }
9806       
9807     } catch (Exception e) {
9808       // TODO: handle exception
9809     }
9810     
9811     if(pulledDockerImage == false) {
9812       System.out.println("Could not pull grouper docker image. Pull it manually by running: docker pull i2incommon/grouper:"+dockerImageVersion);
9813       System.out.print("press <return> when done ");
9814       readFromStdIn("Placeholder");
9815     }
9816     
9817     // create slashRoot directory
9818     contentToWrite = new StringBuilder();
9819     contentToWrite.append("Create slashRoot directory in "+path);
9820     contentToWrite.append("\n\n");
9821     try {      
9822       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
9823     } catch (Exception e) {
9824       System.out.println("Could not write to README.txt file.");
9825     }
9826     
9827     // create morphString.properties file in slashRoot/opt/grouper/grouperWebapp/WEB-INF/classes/
9828     contentToWrite = new StringBuilder();
9829     contentToWrite.append("Create morphString.properties file in "+path+"/slashRoot/opt/grouper/grouperWebapp/WEB-INF/classes/");
9830     contentToWrite.append("\n");
9831     contentToWrite.append("Add the following lines to morphString.properties file. Replace the placeholders below with actual values");
9832     contentToWrite.append("\n");
9833     contentToWrite.append("encrypt.key = <random alphanumeric key with minimum 8 characters>");
9834     contentToWrite.append("\n\n");
9835     contentToWrite.append("\n\n");
9836     
9837     try {      
9838       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
9839     } catch (Exception e) {
9840       System.out.println("Could not write to README.txt file.");
9841     }
9842     
9843     File morphStringPropertiesFile = new File(classesDir+File.separator+"morphString.properties");
9844     
9845     GrouperInstallerUtils.createParentDirectories(morphStringPropertiesFile);
9846     boolean reuseMorphStringPropertiesFile = false;
9847     while(true) {
9848       if (morphStringPropertiesFile.exists()) {
9849         System.out.println("morphString.properties already exists at "+morphStringPropertiesFile.getParent()+" ");
9850         System.out.print("Do you want to reuse it (t|f) [t]: ");
9851         reuseMorphStringPropertiesFile = readFromStdInBoolean(true, "Placeholder");
9852         if (reuseMorphStringPropertiesFile) {
9853           System.out.println("Going to reuse existing morphString.properties file. ");
9854           break;
9855         } else {
9856           System.out.print("Delete morphString.properties and press <return> to continue ");
9857           readFromStdIn("nothing");
9858           morphStringPropertiesFile = new File(path+File.separator+"conf"+File.separator+"morphString.properties");
9859           continue;
9860         }
9861       } else {
9862         GrouperInstallerUtils.fileCreate(morphStringPropertiesFile);
9863         break;
9864       }
9865     }
9866     
9867     if (reuseMorphStringPropertiesFile == false) {
9868       
9869       String validCharactersMorphString = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
9870       SecureRandom sr = new SecureRandom();
9871       
9872       StringBuilder morphStringBuilder = new StringBuilder(20);
9873       for( int i = 0; i < 20; i++ ) {
9874         morphStringBuilder.append( validCharactersMorphString.charAt(sr.nextInt(validCharactersMorphString.length())));
9875       }
9876       
9877       System.out.print("Do you want to use the randomly generated morphString key? (" +  morphStringBuilder.toString()+") (t|f) [t]: ");
9878       boolean useAutoGeneratedMorphKey = readFromStdInBoolean(true, "Placeholder");
9879       String morphStringPasswd = null;
9880       if (useAutoGeneratedMorphKey == false) {
9881         System.out.print("Enter morphString key. Minimum 8 characters required: ");
9882         while (true) {          
9883           String manualMorphKey = readFromStdIn("Placeholder");
9884           if (GrouperInstallerUtils.isNotBlank(manualMorphKey) && manualMorphKey.trim().length() >= 8) {
9885             morphStringPasswd = manualMorphKey.trim();
9886             break;
9887           } else {
9888             System.out.print("morphString key is invalid. Minimum 8 characters required. Please try again: ");
9889             continue;
9890           } 
9891         }
9892         
9893       } else {
9894         morphStringPasswd = morphStringBuilder.toString();
9895       }
9896       
9897       editPropertiesFile(morphStringPropertiesFile, "encrypt.key", morphStringPasswd, false);
9898     }
9899     
9900     Properties morphStringProperties = GrouperInstallerUtils.propertiesFromFile(morphStringPropertiesFile);
9901     
9902     // create grouper.hibernate.properties file
9903     contentToWrite = new StringBuilder();
9904     contentToWrite.append("Create grouper.hibernate.properties file in " +path+"/" + classesDir.getAbsolutePath());
9905     contentToWrite.append("\n");
9906     contentToWrite.append("Add the following lines to grouper.hibernate.properties file. Replace the placeholders below with actual values");
9907     contentToWrite.append("\n");
9908     contentToWrite.append("hibernate.connection.url = <db url> eg: jdbc:mysql://localhost:3306/grouper");
9909     contentToWrite.append("\n");
9910     contentToWrite.append("hibernate.connection.username = <user> eg: root");
9911     contentToWrite.append("\n");
9912     contentToWrite.append("hibernate.connection.password = <morph string encrypted password> eg: 86asd9f87a9sdf87a9s78df97");
9913     contentToWrite.append("\n");
9914     
9915     contentToWrite.append("grouper.is.ui.basicAuthn = true");
9916     contentToWrite.append("\n");
9917     
9918     contentToWrite.append("grouper.is.ws.basicAuthn = true");
9919     contentToWrite.append("\n");
9920     
9921     contentToWrite.append("grouper.is.scim.basicAuthn = true");
9922     contentToWrite.append("\n");
9923     contentToWrite.append("\n");
9924     contentToWrite.append("\n");
9925     
9926     try {      
9927       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
9928     } catch (Exception e) {
9929       System.out.println("Could not write to README.txt file.");
9930     }
9931     
9932     System.out.println("Going to create grouper.hibernate.properties file in " + path + File.separator+ classesDir.getAbsolutePath());
9933     
9934     File grouperHibernatePropertiesFile = new File(classesDir+File.separator+"grouper.hibernate.properties");
9935     boolean reuseHibernatePropertiesFile = false;
9936     while(true) {
9937       GrouperInstallerUtils.createParentDirectories(grouperHibernatePropertiesFile);
9938       if (grouperHibernatePropertiesFile.exists()) {
9939         System.out.println("grouper.hibernate.properties already exists at "+grouperHibernatePropertiesFile.getParent()+" ");
9940         System.out.print("Do you want to reuse it (t|f) [t]: ");
9941         reuseHibernatePropertiesFile = readFromStdInBoolean(true, "Placeholder");
9942         if (reuseHibernatePropertiesFile) {
9943           System.out.println("Going to reuse existing grouper.hibernate.properties file. ");
9944           break;
9945         } else {
9946           System.out.print("Delete grouper.hibernate.properties and press <return> to continue ");
9947           readFromStdIn("nothing");
9948           grouperHibernatePropertiesFile = new File(classesDir+File.separator+"grouper.hibernate.properties");
9949           continue;
9950         }
9951       } else {
9952         GrouperInstallerUtils.fileCreate(grouperHibernatePropertiesFile);
9953         break;
9954       }
9955     }
9956     
9957     // create a blank grouper.client.properties
9958     contentToWrite = new StringBuilder();
9959     contentToWrite.append("Create a blank grouper.client.properties file in " +path+File.separator+classesDir.getAbsolutePath());
9960     contentToWrite.append("\n");
9961     contentToWrite.append("\n");
9962     contentToWrite.append("\n");
9963     
9964     try {      
9965       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
9966     } catch (Exception e) {
9967       System.out.println("Could not write to README.txt file.");
9968     }
9969     File grouperClientPropertiesFile = new File(classesDir+File.separator+"grouper.client.properties");
9970     boolean reuseClientPropertiesFile = false;
9971     while(true) {
9972       GrouperInstallerUtils.createParentDirectories(grouperClientPropertiesFile);
9973       if (grouperClientPropertiesFile.exists()) {
9974         System.out.println("grouper.client.properties already exists at "+grouperClientPropertiesFile.getParent()+" ");
9975         System.out.print("Do you want to reuse it (t|f) [t]: ");
9976         reuseClientPropertiesFile = readFromStdInBoolean(true, "Placeholder");
9977         if (reuseClientPropertiesFile) {
9978           System.out.println("Going to reuse existing grouper.client.properties file. ");
9979           break;
9980         } else {
9981           System.out.print("Delete grouper.client.properties and press <return> to continue ");
9982           readFromStdIn("nothing");
9983           grouperClientPropertiesFile = new File(classesDir+File.separator+"grouper.client.properties");
9984           continue;
9985         }
9986       } else {
9987         GrouperInstallerUtils.fileCreate(grouperClientPropertiesFile);
9988         break;
9989       }
9990     }
9991     
9992     // create a blank subject.properties
9993     contentToWrite = new StringBuilder();
9994     contentToWrite.append("Create a blank subject.properties file in " +path+File.separator+classesDir.getAbsolutePath());
9995     contentToWrite.append("\n");
9996     contentToWrite.append("\n");
9997     contentToWrite.append("\n");
9998     
9999     try {      
10000       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
10001     } catch (Exception e) {
10002       System.out.println("Could not write to README.txt file.");
10003     }
10004     File grouperSubjectPropertiesFile = new File(classesDir+File.separator+"subject.properties");
10005     boolean reuseSubjectPropertiesFile = false;
10006     while(true) {
10007       GrouperInstallerUtils.createParentDirectories(grouperSubjectPropertiesFile);
10008       if (grouperSubjectPropertiesFile.exists()) {
10009         System.out.println("subject.properties already exists at "+grouperSubjectPropertiesFile.getParent()+" ");
10010         System.out.print("Do you want to reuse it (t|f) [t]: ");
10011         reuseSubjectPropertiesFile = readFromStdInBoolean(true, "Placeholder");
10012         if (reuseSubjectPropertiesFile) {
10013           System.out.println("Going to reuse existing subject.properties file. ");
10014           break;
10015         } else {
10016           System.out.print("Delete subject.properties and press <return> to continue ");
10017           readFromStdIn("nothing");
10018           grouperSubjectPropertiesFile = new File(classesDir+File.separator+"subject.properties");
10019           continue;
10020         }
10021       } else {
10022         GrouperInstallerUtils.fileCreate(grouperSubjectPropertiesFile);
10023         break;
10024       }
10025     }
10026     
10027     //####################################
10028     //ask about database
10029     if (reuseHibernatePropertiesFile == false) {
10030       System.out.print("Database setup");
10031 
10032       System.out.println("\n##################################\n");
10033       System.out.println("Example mysql URL: jdbc:mysql://1.2.3.4:3306/grouper?useSSL=false");
10034       System.out.println("Example oracle URL: jdbc:oracle:thin:@server.school.edu:1521:sid");
10035       System.out.println("Example postgres URL: jdbc:postgresql://1.2.3.4:5432/database");
10036       System.out.print("\nEnter the database URL: ");
10037       String newDbUrl = readFromStdIn("grouperInstaller.autorun.dbUrl");
10038       if (!GrouperInstallerUtils.isBlank(newDbUrl)) {
10039         this.dbUrl = newDbUrl;
10040         if (newDbUrl.contains("postgresql")) {
10041           System.out.println("Note: you need to change the search sql in the jdbc source in the grouperApi/conf/sources.xml... the change is in the comments in that file");
10042           for (int i=0;i<3;i++) {
10043             System.out.print("Ready to continue? (t|f)? [t] ");
10044             boolean shouldContinue = readFromStdInBoolean(true, "grouperInstaller.autorun.dbContinueAfterChangeSourcesXmlForPostgresSqlServer");
10045             if (shouldContinue) {
10046               break;
10047             }
10048           }
10049         }
10050         
10051         if (newDbUrl.contains("oracle")) {
10052           
10053           System.out.print("Have you reviewed and agreed to the Oracle's terms https://www.oracle.com/downloads/licenses/distribution-license.html (t|f)? [t] ");
10054           boolean oracleTermsAgreed = readFromStdInBoolean(true, "Placeholder");
10055           File oracleLibPath = new File(path+File.separator+"slashRoot"+File.separator+"opt"+
10056               File.separator+"grouper"+File.separator+"grouperWebapp"+File.separator+"WEB-INF"+File.separator+"lib");
10057           if (!oracleTermsAgreed) {
10058             System.out.print("Place the oracle jdbc jar in " + oracleLibPath.getAbsolutePath() + " and press <return> to continue ");
10059             readFromStdIn("Placeholder");
10060           } else {
10061             System.out.print("Do you want the installer to install the oracle jar (t|f)? [t] ");
10062             boolean shouldContinue = readFromStdInBoolean(true, "Placeholder");
10063             if (shouldContinue) {
10064               
10065               File oracleLibJarPath = new File(path+File.separator+"slashRoot"+File.separator+"opt"+
10066                   File.separator+"grouper"+File.separator+"grouperWebapp"+File.separator+"WEB-INF"+File.separator+"lib"+File.separator+"ojdbc8-19.3.0.0.jar");
10067               
10068               GrouperInstallerUtils.createParentDirectories(oracleLibJarPath);
10069               
10070               downloadFile("https://repo1.maven.org/maven2/com/oracle/ojdbc/ojdbc8/19.3.0.0/ojdbc8-19.3.0.0.jar", oracleLibJarPath.getAbsolutePath(), "");
10071             }
10072           }
10073           
10074           
10075         }
10076         
10077       }
10078       System.out.print("Database user: ");
10079       String newDbUser = readFromStdIn("grouperInstaller.autorun.dbUser");
10080       if (!GrouperInstallerUtils.isBlank(newDbUser)) {
10081         this.dbUser = newDbUser;
10082       }
10083       System.out.print("Database password (note, you aren't setting the pass here, you are using an existing pass, this will be echoed back) [" 
10084           + GrouperInstallerUtils.defaultIfEmpty(this.dbPass, "<blank>") + "]: ");
10085       String newDbPass = readFromStdIn("grouperInstaller.autorun.dbPass");
10086       
10087       String encryptedDbPassword =  "";
10088       
10089       if (GrouperInstallerUtils.isNotBlank(newDbPass)) {
10090         encryptedDbPassword = new Crypto(morphStringProperties.getProperty("encrypt.key") + "w").encrypt(newDbPass);
10091       }
10092 
10093       editPropertiesFile(grouperHibernatePropertiesFile, "hibernate.connection.url", this.dbUrl, false);
10094       editPropertiesFile(grouperHibernatePropertiesFile, "hibernate.connection.username", this.dbUser, false);
10095       editPropertiesFile(grouperHibernatePropertiesFile, "hibernate.connection.password", encryptedDbPassword, false);
10096       
10097       editPropertiesFile(grouperHibernatePropertiesFile, "grouper.is.ui.basicAuthn", "true", false);
10098       editPropertiesFile(grouperHibernatePropertiesFile, "grouper.is.ws.basicAuthn", "true", false);
10099       editPropertiesFile(grouperHibernatePropertiesFile, "grouper.is.scim.basicAuthn", "true", false);
10100     } 
10101     
10102     System.out.print("Do you want to init the database and auto-upgrade for subsequent containers of the same major and minor version of Grouper (t|f)? [t] ");
10103     boolean autoInitDatabase = readFromStdInBoolean(true, "Placeholder");
10104     boolean initDbDocker = false;
10105     if (autoInitDatabase) {
10106       String versionWithAnyPatch = dockerImageVersion.substring(0, dockerImageVersion.lastIndexOf("."));
10107       versionWithAnyPatch = versionWithAnyPatch + ".*";
10108       editPropertiesFile(grouperHibernatePropertiesFile, "registry.auto.ddl.upToVersion", versionWithAnyPatch, false);
10109       initDbDocker = true;
10110     } else {
10111       System.out.print("Do you want to init the database one time now (t|f)? [t] ");
10112       boolean initDatabaseOneTime = readFromStdInBoolean(true, "Placeholder");
10113       if (initDatabaseOneTime == true) {
10114         initDbDocker = true;
10115       }
10116     }
10117     
10118     contentToWrite = new StringBuilder();
10119     contentToWrite.append("Run the following command to init the database. It is not a required step.");
10120     contentToWrite.append("\n");
10121     
10122     StringBuilder buildInitCommand = new StringBuilder();
10123     buildInitCommand.append("docker run --detach ");
10124     buildInitCommand.append("--mount type=bind,src=");
10125     buildInitCommand.append(path+File.separator);
10126     buildInitCommand.append("logs,dst=/opt/grouper/logs ");
10127     buildInitCommand.append("--mount type=bind,src=");
10128     buildInitCommand.append(path+File.separator);
10129     buildInitCommand.append("slashRoot,dst=/opt/grouper/slashRoot ");
10130     buildInitCommand.append("--name gsh ");
10131     buildInitCommand.append("i2incommon/grouper:"+dockerImageVersion );
10132     buildInitCommand.append(" gsh -registry -check -runscript -noprompt" );
10133     
10134     contentToWrite.append(buildInitCommand.toString());
10135     contentToWrite.append("\n\n");
10136     contentToWrite.append("\n\n");
10137     
10138     try {      
10139       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
10140     } catch (Exception e) {
10141       System.out.println("Could not write to README.txt file.");
10142     }
10143     
10144     boolean removeDockerGshContainer = false;
10145     if (initDbDocker) {
10146       boolean dbInitialized = false;
10147       try {
10148         commands = new ArrayList<String>();
10149         commands.add(shCommand());
10150         commands.add("-c");
10151         
10152         commands.add(buildInitCommand.toString());
10153         
10154         CommandResult dockerDbInitCommandResult = GrouperInstallerUtils.execCommand(
10155             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10156             new File("."), null, false, false, true);
10157         
10158         if (dockerDbInitCommandResult.getExitCode() == 0) {
10159           dbInitialized = true;
10160         }
10161         
10162       } catch (Exception e) {}
10163       
10164       if (dbInitialized == false) {
10165         System.out.println("Could not initialize db. Run the following command manually in another terminal window/tab.");
10166         System.out.println(buildInitCommand.toString());
10167         System.out.print("Press <return> to continue once the command has been run: ");
10168         readFromStdIn("Placeholder");
10169       } else {
10170         removeDockerGshContainer = true;
10171         StringBuilder dockerPsCommand = new StringBuilder();
10172         dockerPsCommand.append("docker ps -a --filter \"name=gsh\" --format \"{{.Status}}\" ");
10173         
10174         for (int i=0; i<100; i++) {
10175           System.out.println("Waiting for docker command to finish.");
10176           GrouperInstallerUtils.sleep(4000);
10177           
10178           commands = new ArrayList<String>();
10179           commands.add(shCommand());
10180           commands.add("-c");
10181           
10182           commands.add(dockerPsCommand.toString());
10183           
10184           CommandResult dockerPsCommandResult = GrouperInstallerUtils.execCommand(
10185               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10186               new File("."), null, false, false, true);
10187           
10188           if (dockerPsCommandResult.getExitCode() == 0) {
10189             
10190             String output = dockerPsCommandResult.getOutputText();
10191             if (output.contains("Exited")) {
10192               
10193               GrouperInstallerUtils.sleep(20000);
10194               
10195               commands = new ArrayList<String>();
10196               commands.add(shCommand());
10197               commands.add("-c");
10198               
10199               StringBuilder dockerLogsCommand = new StringBuilder();
10200               dockerLogsCommand.append("docker logs gsh ");
10201               
10202               commands.add(dockerLogsCommand.toString());
10203               
10204               CommandResult dockerLogsCommandResult = GrouperInstallerUtils.execCommand(
10205                   GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10206                   new File("."), null, false, false, true);
10207               
10208               if (dockerLogsCommandResult.getExitCode() == 0) {
10209                 String logs = dockerLogsCommandResult.getOutputText();
10210                 
10211                 File dbInitLogsFile = new File(path+File.separator+"docker_logs_init_db_"+new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS").format(new Date())+".log");
10212                 GrouperInstallerUtils.fileCreate(dbInitLogsFile);
10213                 
10214                 try {      
10215                   Files.write(Paths.get(dbInitLogsFile.getAbsolutePath()), logs.getBytes(), StandardOpenOption.APPEND);
10216                 } catch (Exception e) {
10217                   System.out.println("Could not write logs to "+dbInitLogsFile.getAbsolutePath()+" file. ");
10218                 }
10219                 
10220                 System.out.println("docker database initialization logs are at: "+dbInitLogsFile.getAbsolutePath());
10221                 
10222                 if (logs.contains("Script was executed successfully")
10223                     && logs.contains("SQL statements executed successfully")
10224                     && !logs.contains("Exception") && !logs.contains("exception")) {
10225                   System.out.println("From the logs: Script was executed successfully");
10226                   System.out.print("Press <return> to continue. ");
10227                   readFromStdIn("Placeholder");
10228                 } else {
10229                   System.out.println("Could not find success in docker logs. Look in the logs at the location above and make sure there are no exceptions. ");
10230                   System.out.print("Press <return> to continue. ");
10231                   readFromStdIn("Placeholder");
10232                 }
10233                 
10234                 try (BufferedReader reader = new BufferedReader(new FileReader(dbInitLogsFile))) {
10235                   StringBuilder logContent = new StringBuilder();
10236                   String line;
10237                   int lineNumber =0;
10238                   while( (line = reader.readLine()) != null) {
10239                     logContent.append(line);
10240                     logContent.append("\n");
10241                     lineNumber++;
10242                     
10243                     if (lineNumber > 24) {
10244                       break;
10245                     }
10246                   }
10247                   System.out.println("First 25 lines of logs are below: ");
10248                   System.out.println(logContent);
10249                 } catch (Exception e) {}
10250                 
10251               } else {
10252                 System.out.println("Could not retrieve logs for 'gsh' container. Please run 'docker logs gsh' manually and make sure there are no exceptions before proceeding. ");
10253                 System.out.print("Press <return> to continue. ");
10254                 readFromStdIn("Placeholder");
10255               }
10256               
10257               break;
10258             } 
10259             
10260           } else {
10261             System.out.println("Could not detect the status of the docker command. Please run '" +dockerPsCommand+ "' manually in another terminal window and once the status shows Exited, press <return> to continue. ");
10262             readFromStdIn("Placeholder");
10263             break;
10264           }
10265           
10266         }
10267         
10268       }
10269       
10270     }
10271     
10272     // remove gsh docker container
10273     contentToWrite = new StringBuilder();
10274     contentToWrite.append("Run 'docker rm -f gsh' to remove the gsh container.");
10275     contentToWrite.append("\n\n");
10276     contentToWrite.append("\n\n");
10277     try {
10278       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
10279     } catch (Exception e) {
10280       System.out.println("Could not write to README.txt file.");
10281     }
10282     
10283     if (removeDockerGshContainer) {
10284       commands = new ArrayList<String>();
10285       commands.add(shCommand());
10286       commands.add("-c");
10287       commands.add("docker rm -f gsh");
10288       
10289       boolean dockerRemovedGshContainer = false;
10290       String dockerRmCommandError = null;
10291       try {
10292         CommandResult dockerRmCommandResult = GrouperInstallerUtils.execCommand(
10293             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10294             new File("."), null, false, false, true);
10295         if (dockerRmCommandResult.getExitCode() == 0) {
10296           dockerRemovedGshContainer = true;
10297         } else if (GrouperInstallerUtils.isNotBlank(dockerRmCommandResult.getErrorText())) {
10298           dockerRmCommandError = dockerRmCommandResult.getErrorText();
10299         }
10300       } catch (Exception e) {
10301         dockerRemovedGshContainer = false;
10302       }
10303       
10304       if (dockerRemovedGshContainer) {
10305         System.out.println("Removed 'gsh' container successfully. ");
10306       } else if (dockerRmCommandError != null) {
10307         System.out.println("Could not remove docker gsh container. Please run 'docker rm -f gsh' in another terminal window before proceeding. ");
10308       }
10309       
10310       System.out.print("Press <return> to continue ");
10311       readFromStdIn("Placeholder");
10312     }
10313     
10314     // Add passwords for grouper ui
10315     contentToWrite = new StringBuilder();    
10316     contentToWrite.append("If you want to use grouper basic authentication for UI, follow the instructions below.");
10317     contentToWrite.append("\n");
10318     contentToWrite.append("Create createGrouperSystemPasswordUi.gsh file in "+path+File.separator+"slashRoot"+File.separator+"opt"+
10319         File.separator+"grouper"+File.separator+"grouperWebapp"+File.separator+"WEB-INF"+File.separator+"bin");
10320     contentToWrite.append("\n");
10321     contentToWrite.append("Add the following lines to createGrouperSystemPasswordUi.gsh. Replace placeholder with actual values below.");
10322     contentToWrite.append("\n");
10323     contentToWrite.append("GrouperSession grouperSession = GrouperSession.startRootSession();");
10324     contentToWrite.append("\n");
10325     contentToWrite.append("GrouperPasswordSave grouperPasswordSave = new GrouperPasswordSave();");
10326     contentToWrite.append("\n");
10327     contentToWrite.append("grouperPasswordSave.assignUsername(\"GrouperSystem\");");
10328     contentToWrite.append("\n");
10329     contentToWrite.append("grouperPasswordSave.assignEntityType(\"username\");");
10330     contentToWrite.append("\n");
10331     contentToWrite.append("grouperPasswordSave.assignPassword(\"<password>\");");
10332     contentToWrite.append("\n");
10333     contentToWrite.append("grouperPasswordSave.assignApplication(GrouperPassword.Application.UI);");
10334     contentToWrite.append("\n");
10335     contentToWrite.append("new Authentication().assignUserPassword(grouperPasswordSave);");
10336     contentToWrite.append("\n\n");
10337     contentToWrite.append("\n\n");
10338     
10339     try {
10340       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
10341     } catch (Exception e) {
10342       System.out.println("Could not write to README.txt file.");
10343     }
10344     
10345     //System.out.print("Do you want to use grouper authenitcation for UI? (t|f)? [t]: ");
10346     //boolean useGrouperAuthenticationUi = readFromStdInBoolean(true, "grouperInstaller.autorun.useBuiltInGrouperAuthenticationUI");
10347     boolean useGrouperAuthenticationUi = true;
10348     
10349     if (useGrouperAuthenticationUi) {
10350       System.out.print("Enter the password for user 'GrouperSystem' for grouper UI: ");
10351       String uiPassword = readFromStdIn("Placeholder");
10352       
10353       while (true) {
10354         if (uiPassword == null || uiPassword.trim().length() < 4 ) {
10355           System.out.print("Invalid password. Minimum 4 characters required. Please try again: ");
10356           uiPassword = readFromStdIn("Placeholder");
10357         } else {
10358           break;
10359         }
10360       }
10361       
10362       File grouperUiSystemPasswordSh = new File(path+File.separator+"slashRoot"+File.separator+"opt"+
10363           File.separator+"grouper"+File.separator+"grouperWebapp"+File.separator+"WEB-INF"+File.separator+"bin"+File.separator+"createGrouperSystemPasswordUi.gsh");
10364       GrouperInstallerUtils.createParentDirectories(grouperUiSystemPasswordSh);
10365       boolean reuseCreateGrouperSystemPasswordUiFile = false;
10366       while(true) {
10367         if (grouperUiSystemPasswordSh.exists()) {
10368           System.out.println("createGrouperSystemPasswordUi.gsh already exists at "+grouperUiSystemPasswordSh.getParent()+" ");
10369           System.out.print("Do you want to reuse it (t|f) [t]: ");
10370           reuseCreateGrouperSystemPasswordUiFile = readFromStdInBoolean(true, "Placeholder");
10371           if (reuseCreateGrouperSystemPasswordUiFile) {
10372             System.out.println("Going to reuse existing createGrouperSystemPasswordUi.gsh file. ");
10373             break;
10374           } else {
10375             System.out.print("Delete createGrouperSystemPasswordUi.sh and press <return> to continue ");
10376             readFromStdIn("nothing");
10377             grouperUiSystemPasswordSh = new File(path+File.separator+"slashRoot"+File.separator+"opt"+
10378                 File.separator+"grouper"+File.separator+"grouperWebapp"+File.separator+"WEB-INF"+File.separator+"bin"+File.separator+"createGrouperSystemPasswordUi.gsh");
10379             continue;
10380           }
10381         } else {
10382           GrouperInstallerUtils.fileCreate(grouperUiSystemPasswordSh);
10383           break;
10384         }
10385       }
10386       
10387       if (reuseCreateGrouperSystemPasswordUiFile == false) {
10388         StringBuilder createPasswordCommands = new StringBuilder();
10389         createPasswordCommands.append("GrouperSession grouperSession = GrouperSession.startRootSession(); \n");
10390         createPasswordCommands.append("GrouperPasswordSave grouperPasswordSave = new GrouperPasswordSave(); \n");
10391         createPasswordCommands.append("grouperPasswordSave.assignUsername(\"GrouperSystem\"); \n");
10392         createPasswordCommands.append("grouperPasswordSave.assignEntityType(\"username\"); \n");
10393         createPasswordCommands.append("grouperPasswordSave.assignPassword(\""+uiPassword+"\");");
10394         createPasswordCommands.append("\n");
10395         
10396         createPasswordCommands.append("grouperPasswordSave.assignApplication(GrouperPassword.Application.UI); \n");
10397         createPasswordCommands.append("new Authentication().assignUserPassword(grouperPasswordSave); \n");
10398         
10399         GrouperInstallerUtils.writeStringToFile(grouperUiSystemPasswordSh, createPasswordCommands.toString());
10400       }
10401       
10402       // run docker command to add UI password to grouper
10403       contentToWrite = new StringBuilder();
10404       contentToWrite.append("Run the following command to add UI password to grouper.");
10405       contentToWrite.append("\n");
10406       StringBuilder uiPasswordDockerCommand = new StringBuilder();
10407       uiPasswordDockerCommand.append("docker run --detach ");
10408       uiPasswordDockerCommand.append("--mount type=bind,src=");
10409       uiPasswordDockerCommand.append(path+File.separator);
10410       uiPasswordDockerCommand.append("logs,dst=/opt/grouper/logs ");
10411       uiPasswordDockerCommand.append("--mount type=bind,src=");
10412       uiPasswordDockerCommand.append(path+File.separator);
10413       uiPasswordDockerCommand.append("slashRoot,dst=/opt/grouper/slashRoot ");
10414       uiPasswordDockerCommand.append("--name gsh ");
10415       uiPasswordDockerCommand.append("i2incommon/grouper:"+dockerImageVersion );
10416       uiPasswordDockerCommand.append(" gsh /opt/grouper/grouperWebapp/WEB-INF/bin/createGrouperSystemPasswordUi.gsh");
10417       contentToWrite.append(uiPasswordDockerCommand.toString());
10418       contentToWrite.append("\n\n");
10419       contentToWrite.append("\n\n");
10420       try {
10421         Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
10422       } catch (Exception e) {
10423         System.out.println("Could not write to README.txt file.");
10424       }
10425       
10426       removeDockerGshContainer = false;
10427       boolean uiPasswordCreated = false;
10428       try {
10429         commands = new ArrayList<String>();
10430         commands.add(shCommand());
10431         commands.add("-c");
10432         commands.add(uiPasswordDockerCommand.toString());
10433         commandResult = GrouperInstallerUtils.execCommand(
10434             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10435             new File("."), null, false, false, true);
10436         
10437         if (commandResult.getExitCode() == 0) {
10438           uiPasswordCreated = true;
10439         }
10440         
10441       } catch (Exception e) {}
10442       
10443       if (uiPasswordCreated == false) {
10444         System.out.println("Could not create password for grouper UI. Run the following command manually. ");
10445         System.out.println(uiPasswordDockerCommand.toString());
10446         System.out.print("Press <return> to continue ");
10447         readFromStdIn("Placeholder");
10448       } else {
10449         
10450         removeDockerGshContainer = true;
10451         StringBuilder dockerPsCommand = new StringBuilder();
10452         dockerPsCommand.append("docker ps -a --filter \"name=gsh\" --format \"{{.Status}}\" ");
10453         
10454         for (int i=0; i<100; i++) {
10455           System.out.println("Waiting for docker command to finish.");
10456           GrouperInstallerUtils.sleep(4000);
10457           
10458           commands = new ArrayList<String>();
10459           commands.add(shCommand());
10460           commands.add("-c");
10461           
10462           commands.add(dockerPsCommand.toString());
10463           
10464           CommandResult dockerPsCommandResult = GrouperInstallerUtils.execCommand(
10465               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10466               new File("."), null, false, false, true);
10467           
10468           if (dockerPsCommandResult.getExitCode() == 0) {
10469             
10470             String output = dockerPsCommandResult.getOutputText();
10471             if (output.contains("Exited")) {
10472               
10473               GrouperInstallerUtils.sleep(20000);
10474               
10475               commands = new ArrayList<String>();
10476               commands.add(shCommand());
10477               commands.add("-c");
10478               
10479               StringBuilder dockerLogsCommand = new StringBuilder();
10480               dockerLogsCommand.append("docker logs gsh ");
10481               
10482               commands.add(dockerLogsCommand.toString());
10483               
10484               CommandResult dockerLogsCommandResult = GrouperInstallerUtils.execCommand(
10485                   GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10486                   new File("."), null, false, false, true);
10487               
10488               if (dockerLogsCommandResult.getExitCode() == 0) {
10489                 String logs = dockerLogsCommandResult.getOutputText();
10490                 
10491                 File uiPasswordLogsFile = new File(path+File.separator+"docker_logs_ui_password_"+new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS").format(new Date())+".log");
10492                 GrouperInstallerUtils.fileCreate(uiPasswordLogsFile);
10493                 
10494                 try {      
10495                   Files.write(Paths.get(uiPasswordLogsFile.getAbsolutePath()), logs.getBytes(), StandardOpenOption.APPEND);
10496                 } catch (Exception e) {
10497                   System.out.println("Could not write logs to "+uiPasswordLogsFile.getAbsolutePath()+" file. ");
10498                 }
10499                 
10500                 System.out.println("docker ui password setup logs are at: "+uiPasswordLogsFile.getAbsolutePath());
10501                 
10502                 if (logs.contains("===> null\n" + 
10503                     "groovy:000> :exit") && !logs.contains("Exception") && !logs.contains("exception")) {
10504                   System.out.println("Password was created successfully.");
10505                   System.out.print("Press <return> to continue. ");
10506                   readFromStdIn("Placeholder");
10507                 } else {
10508                   System.out.println("Could not find success in docker logs. Look in the logs at the location above and make sure there are no exceptions. ");
10509                   System.out.print("Press <return> to continue. ");
10510                   readFromStdIn("Placeholder");
10511                 }
10512                 
10513                 try (BufferedReader reader = new BufferedReader(new FileReader(uiPasswordLogsFile))) {
10514                   StringBuilder logContent = new StringBuilder();
10515                   String line;
10516                   int lineNumber =0;
10517                   while( (line = reader.readLine()) != null) {
10518                     logContent.append(line);
10519                     logContent.append("\n");
10520                     lineNumber++;
10521                     
10522                     if (lineNumber > 24) {
10523                       break;
10524                     }
10525                   }
10526                   System.out.println("First 25 lines of logs are below: ");
10527                   System.out.println(logContent);
10528                 } catch (Exception e) {}
10529                 
10530               } else {
10531                 System.out.println("Could not retrieve logs for 'gsh' container. Please run 'docker logs gsh' manually and make sure there are no exceptions before proceeding. ");
10532                 System.out.print("Press <return> to continue. ");
10533                 readFromStdIn("Placeholder");
10534               }
10535               
10536               break;
10537             } 
10538             
10539           } else {
10540             System.out.println("Could not detect the status of the docker command. Please run '" +dockerPsCommand+ "' manually in another terminal window and once the status shows Exited, press <return> to continue. ");
10541             readFromStdIn("Placeholder");
10542             break;
10543           }
10544           
10545         }
10546         
10547       }
10548       
10549       if (removeDockerGshContainer) {
10550         commands = new ArrayList<String>();
10551         commands.add(shCommand());
10552         commands.add("-c");
10553         commands.add("docker rm -f gsh");
10554         
10555         boolean dockerRemovedGshContainer = false;
10556         String dockerRmCommandError = null;
10557         try {
10558           CommandResult dockerRmCommandResult = GrouperInstallerUtils.execCommand(
10559               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10560               new File("."), null, false, false, true);
10561           if (dockerRmCommandResult.getExitCode() == 0) {
10562             dockerRemovedGshContainer = true;
10563           } else if (GrouperInstallerUtils.isNotBlank(dockerRmCommandResult.getErrorText())) {
10564             dockerRmCommandError = dockerRmCommandResult.getErrorText();
10565           }
10566         } catch (Exception e) {
10567           dockerRemovedGshContainer = false;
10568         }
10569         
10570         if (dockerRemovedGshContainer) {
10571           System.out.println("Removed gsh container successfully. ");
10572         } else if (dockerRmCommandError != null) {
10573           System.out.println("Could not remove docker gsh container. Please run 'docker rm -f gsh' in another terminal window before proceeding. ");
10574         }
10575         
10576         System.out.print("Press <return> to continue ");
10577         readFromStdIn("Placeholder");
10578       }
10579         
10580       // remove createGrouperSystemPasswordUi.sh file
10581       contentToWrite = new StringBuilder();    
10582       contentToWrite.append("Delete createGrouperSystemPasswordUi.gsh file from "+path+File.separator+"slashRoot"+File.separator+"opt"+
10583           File.separator+"grouper"+File.separator+"grouperWebapp"+File.separator+"WEB-INF"+File.separator+"bin");
10584       contentToWrite.append(" because it contains password in plain text.");
10585       contentToWrite.append("\n\n");
10586       try {
10587         Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
10588       } catch (Exception e) {
10589         System.out.println("Could not write to README.txt file.");
10590       }
10591       
10592       grouperUiSystemPasswordSh.delete();
10593       
10594     }
10595     
10596     // Add passwords for grouper ws
10597     contentToWrite = new StringBuilder();    
10598     contentToWrite.append("If you want to use grouper basic authentication for grouper web services, follow the instructions below.");
10599     contentToWrite.append("\n");
10600     contentToWrite.append("Create createGrouperSystemPasswordWs.gsh file in "+path+File.separator+"slashRoot"+File.separator+"opt"+
10601         File.separator+"grouper"+File.separator+"grouperWebapp"+File.separator+"WEB-INF"+File.separator+"bin");
10602     contentToWrite.append("\n");
10603     contentToWrite.append("Add the following lines to createGrouperSystemPasswordWs.gsh. Replace placeholder with actual values below.");
10604     contentToWrite.append("\n");
10605     contentToWrite.append("GrouperSession grouperSession = GrouperSession.startRootSession();");
10606     contentToWrite.append("\n");
10607     contentToWrite.append("GrouperPasswordSave grouperPasswordSave = new GrouperPasswordSave();");
10608     contentToWrite.append("\n");
10609     contentToWrite.append("grouperPasswordSave.assignUsername(\"GrouperSystem\");");
10610     contentToWrite.append("\n");
10611     contentToWrite.append("grouperPasswordSave.assignEntityType(\"username\");");
10612     contentToWrite.append("\n");
10613     contentToWrite.append("grouperPasswordSave.assignPassword(\"<password>\");");
10614     contentToWrite.append("\n");
10615     contentToWrite.append("grouperPasswordSave.assignApplication(GrouperPassword.Application.WS);");
10616     contentToWrite.append("\n");
10617     contentToWrite.append("new Authentication().assignUserPassword(grouperPasswordSave);");
10618     contentToWrite.append("\n\n");
10619     contentToWrite.append("\n\n");
10620     
10621     try {
10622       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
10623     } catch (Exception e) {
10624       System.out.println("Could not write to README.txt file.");
10625     }
10626 //    System.out.print("Do you want to use grouper authenitcation for WS? (t|f)? [t]: ");
10627 //    boolean useGrouperAuthenticationWs = readFromStdInBoolean(true, "grouperInstaller.autorun.useBuiltInGrouperAuthenticationWS");
10628     boolean useGrouperAuthenticationWs = true;
10629     
10630     if (useGrouperAuthenticationWs) {
10631       System.out.print("Please enter the password for user 'GrouperSystem' for grouper web services: ");
10632       String wsPassword = readFromStdIn("Placeholder");
10633       
10634       while (true) {
10635         if (wsPassword == null || wsPassword.trim().length() < 4 ) {
10636           System.out.print("Invalid password. Minimum 4 characters required. Please try again: ");
10637           wsPassword = readFromStdIn("Placeholder");
10638         } else {
10639           break;
10640         }
10641       }
10642       
10643       File grouperWsSystemPasswordSh = new File(path+File.separator+"slashRoot"+File.separator+"opt"+
10644           File.separator+"grouper"+File.separator+"grouperWebapp"+File.separator+"WEB-INF"+File.separator+"bin"+File.separator+"createGrouperSystemPasswordWs.gsh");
10645       GrouperInstallerUtils.createParentDirectories(grouperWsSystemPasswordSh);
10646       boolean reuseCreateGrouperSystemPasswordWsFile = false;
10647       while(true) {
10648         if (grouperWsSystemPasswordSh.exists()) {
10649           System.out.println("createGrouperSystemPasswordWs.gsh already exists at "+grouperWsSystemPasswordSh.getParent()+" ");
10650           System.out.print("Do you want to reuse it (t|f) [t]: ");
10651           reuseCreateGrouperSystemPasswordWsFile = readFromStdInBoolean(true, "Placeholder");
10652           if (reuseCreateGrouperSystemPasswordWsFile) {
10653             System.out.println("Going to reuse existing createGrouperSystemPasswordWs.gsh file. ");
10654             break;
10655           } else {
10656             System.out.print("Delete createGrouperSystemPasswordWs.gsh and press <return> to continue ");
10657             readFromStdIn("nothing");
10658             grouperWsSystemPasswordSh = new File(path+File.separator+"slashRoot"+File.separator+"opt"+
10659                 File.separator+"grouper"+File.separator+"grouperWebapp"+File.separator+"WEB-INF"+File.separator+"bin"+File.separator+"createGrouperSystemPasswordWs.gsh");
10660             continue;
10661           }
10662         } else {
10663           GrouperInstallerUtils.fileCreate(grouperWsSystemPasswordSh);
10664           break;
10665         }
10666       }
10667       
10668       if (reuseCreateGrouperSystemPasswordWsFile == false) {
10669         StringBuilder createPasswordCommands = new StringBuilder();
10670         createPasswordCommands.append("GrouperSession grouperSession = GrouperSession.startRootSession(); \n");
10671         createPasswordCommands.append("GrouperPasswordSave grouperPasswordSave = new GrouperPasswordSave(); \n");
10672         createPasswordCommands.append("grouperPasswordSave.assignUsername(\"GrouperSystem\"); \n");
10673         createPasswordCommands.append("grouperPasswordSave.assignEntityType(\"username\"); \n");
10674         createPasswordCommands.append("grouperPasswordSave.assignPassword(\""+wsPassword+"\"); \n");
10675         createPasswordCommands.append("\n");
10676         
10677         createPasswordCommands.append("grouperPasswordSave.assignApplication(GrouperPassword.Application.WS); \n");
10678         createPasswordCommands.append("new Authentication().assignUserPassword(grouperPasswordSave); \n");
10679         
10680         GrouperInstallerUtils.writeStringToFile(grouperWsSystemPasswordSh, createPasswordCommands.toString());
10681       }
10682       
10683    // run docker command to add WS password to grouper
10684       contentToWrite = new StringBuilder();
10685       contentToWrite.append("Run the following command to add WS password to grouper.");
10686       contentToWrite.append("\n");
10687       StringBuilder wsPasswordDockerCommand = new StringBuilder();
10688       wsPasswordDockerCommand.append("docker run --detach ");
10689       wsPasswordDockerCommand.append("--mount type=bind,src=");
10690       wsPasswordDockerCommand.append(path+File.separator);
10691       wsPasswordDockerCommand.append("logs,dst=/opt/grouper/logs ");
10692       wsPasswordDockerCommand.append("--mount type=bind,src=");
10693       wsPasswordDockerCommand.append(path+File.separator);
10694       wsPasswordDockerCommand.append("slashRoot,dst=/opt/grouper/slashRoot ");
10695       wsPasswordDockerCommand.append("--name gsh ");
10696       wsPasswordDockerCommand.append("i2incommon/grouper:"+dockerImageVersion );
10697       wsPasswordDockerCommand.append(" gsh /opt/grouper/grouperWebapp/WEB-INF/bin/createGrouperSystemPasswordWs.gsh");
10698       contentToWrite.append(wsPasswordDockerCommand.toString());
10699       contentToWrite.append("\n\n");
10700       contentToWrite.append("\n\n");
10701       try {
10702         Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
10703       } catch (Exception e) {
10704         System.out.println("Could not write to README.txt file.");
10705       }
10706       
10707       boolean wsPasswordCreated = false;
10708       removeDockerGshContainer = false;
10709       try {
10710         commands = new ArrayList<String>();
10711         commands.add(shCommand());
10712         commands.add("-c");
10713         
10714         commands.add(wsPasswordDockerCommand.toString());
10715         
10716         commandResult = GrouperInstallerUtils.execCommand(
10717             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10718             new File("."), null, false, false, true);
10719         
10720         if (commandResult.getExitCode() == 0) {
10721           wsPasswordCreated = true;
10722         }
10723         
10724       } catch (Exception e) {}
10725       
10726       if (wsPasswordCreated == false) {
10727         System.out.println("Could not create password for WS. Run the following command manually.");
10728         System.out.println(wsPasswordDockerCommand.toString());
10729         System.out.print("Press <return> to continue");
10730         readFromStdIn("Placeholder");
10731       } else {
10732 
10733         removeDockerGshContainer = true;
10734         StringBuilder dockerPsCommand = new StringBuilder();
10735         dockerPsCommand.append("docker ps -a --filter \"name=gsh\" --format \"{{.Status}}\" ");
10736         
10737         for (int i=0; i<100; i++) {
10738           System.out.println("Waiting for docker command to finish.");
10739           GrouperInstallerUtils.sleep(4000);
10740           
10741           commands = new ArrayList<String>();
10742           commands.add(shCommand());
10743           commands.add("-c");
10744           
10745           commands.add(dockerPsCommand.toString());
10746           
10747           CommandResult dockerPsCommandResult = GrouperInstallerUtils.execCommand(
10748               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10749               new File("."), null, false, false, true);
10750           
10751           if (dockerPsCommandResult.getExitCode() == 0) {
10752             
10753             String output = dockerPsCommandResult.getOutputText();
10754             if (output.contains("Exited")) {
10755               
10756               GrouperInstallerUtils.sleep(20000);
10757               
10758               commands = new ArrayList<String>();
10759               commands.add(shCommand());
10760               commands.add("-c");
10761               
10762               StringBuilder dockerLogsCommand = new StringBuilder();
10763               dockerLogsCommand.append("docker logs gsh ");
10764               
10765               commands.add(dockerLogsCommand.toString());
10766               
10767               CommandResult dockerLogsCommandResult = GrouperInstallerUtils.execCommand(
10768                   GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10769                   new File("."), null, false, false, true);
10770               
10771               if (dockerLogsCommandResult.getExitCode() == 0) {
10772                 String logs = dockerLogsCommandResult.getOutputText();
10773                 
10774                 File wsPasswordLogsFile = new File(path+File.separator+"docker_logs_ws_password_"+new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS").format(new Date())+".log");
10775                 GrouperInstallerUtils.fileCreate(wsPasswordLogsFile);
10776                 
10777                 try {      
10778                   Files.write(Paths.get(wsPasswordLogsFile.getAbsolutePath()), logs.getBytes(), StandardOpenOption.APPEND);
10779                 } catch (Exception e) {
10780                   System.out.println("Could not write logs to "+wsPasswordLogsFile.getAbsolutePath()+" file. ");
10781                 }
10782                 
10783                 System.out.println("docker ws password setup logs are at: "+wsPasswordLogsFile.getAbsolutePath());
10784                 
10785                 if (logs.contains("===> null\n" + 
10786                     "groovy:000> :exit") && !logs.contains("Exception") && !logs.contains("exception")) {
10787                   System.out.println("Password was created successfully.");
10788                   System.out.print("Press <return> to continue. ");
10789                   readFromStdIn("Placeholder");
10790                 } else {
10791                   System.out.println("Could not find success in docker logs. Look in the logs at the location above and make sure there are no exceptions. ");
10792                   System.out.print("Press <return> to continue. ");
10793                   readFromStdIn("Placeholder");
10794                 }
10795                 
10796                 try (BufferedReader reader = new BufferedReader(new FileReader(wsPasswordLogsFile))) {
10797                   StringBuilder logContent = new StringBuilder();
10798                   String line;
10799                   int lineNumber = 0;
10800                   while( (line = reader.readLine()) != null) {
10801                     logContent.append(line);
10802                     logContent.append("\n");
10803                     lineNumber++;
10804                     
10805                     if (lineNumber > 24) {
10806                       break;
10807                     }
10808                   }
10809                   System.out.println("First 25 lines of logs are below: ");
10810                   System.out.println(logContent);
10811                 } catch (Exception e) {}
10812                 
10813               } else {
10814                 System.out.println("Could not retrieve logs for 'gsh' container. Please run 'docker logs gsh' manually and make sure there are no exceptions before proceeding. ");
10815                 System.out.print("Press <return> to continue. ");
10816                 readFromStdIn("Placeholder");
10817               }
10818               
10819               break;
10820             } 
10821             
10822           } else {
10823             System.out.println("Could not detect the status of the docker command. Please run '" +dockerPsCommand+ "' manually in another terminal window and once the status shows Exited, press <return> to continue. ");
10824             readFromStdIn("Placeholder");
10825             break;
10826           }
10827           
10828         }
10829       
10830       }
10831       
10832       if (removeDockerGshContainer) {
10833         commands = new ArrayList<String>();
10834         commands.add(shCommand());
10835         commands.add("-c");
10836         commands.add("docker rm -f gsh");
10837         
10838         boolean dockerRemovedGshContainer = false;
10839         String dockerRmCommandError = null;
10840         try {
10841           CommandResult dockerRmCommandResult = GrouperInstallerUtils.execCommand(
10842               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10843               new File("."), null, false, false, true);
10844           if (dockerRmCommandResult.getExitCode() == 0) {
10845             dockerRemovedGshContainer = true;
10846           } else if (GrouperInstallerUtils.isNotBlank(dockerRmCommandResult.getErrorText())) {
10847             dockerRmCommandError = dockerRmCommandResult.getErrorText();
10848           }
10849         } catch (Exception e) {
10850           dockerRemovedGshContainer = false;
10851         }
10852         
10853         if (dockerRemovedGshContainer) {
10854           System.out.println("Removed 'gsh' container successfully. ");
10855         } else if (dockerRmCommandError != null) {
10856           System.out.println("Could not remove docker gsh container. Please run 'docker rm -f gsh' in another terminal window before proceeding. ");
10857         }
10858         
10859         System.out.print("Press <return> to continue ");
10860         readFromStdIn("Placeholder");
10861       }
10862       
10863       // remove createGrouperSystemPasswordWs.sh file
10864       contentToWrite = new StringBuilder();    
10865       contentToWrite.append("Delete createGrouperSystemPasswordWs.gsh file from "+path+File.separator+"slashRoot"+File.separator+"opt"+
10866           File.separator+"grouper"+File.separator+"grouperWebapp"+File.separator+"WEB-INF"+File.separator+"bin");
10867       contentToWrite.append(" because it contains password in plain text.");
10868       contentToWrite.append("\n\n");
10869       try {
10870         Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
10871       } catch (Exception e) {
10872         System.out.println("Could not write to README.txt file.");
10873       }
10874       
10875       grouperWsSystemPasswordSh.delete();
10876       
10877     }
10878     
10879     Integer portNumberInt = 8080;
10880     System.out.print("Please provide the host HTTP (not HTTPS) port number to run the UI container (default 8080): ");
10881     while (true) {
10882       String portNumber = readFromStdIn("Placeholder");
10883       if (GrouperInstallerUtils.isNotBlank(portNumber)) {
10884         try {
10885           portNumberInt = Integer.valueOf(portNumber);
10886           break;
10887         } catch (Exception e) {
10888           System.out.print("Invalid port number '"+portNumber+"'. Please try again. (default 8080): ");
10889         }
10890       } else {
10891         break;
10892       }
10893     }
10894     
10895     // start the container
10896     contentToWrite = new StringBuilder();
10897     contentToWrite.append("Run the following command to start the container.");
10898     contentToWrite.append("\n");
10899     StringBuilder grouperContainerStartDockerCommand = new StringBuilder();
10900     grouperContainerStartDockerCommand.append("docker run");
10901     //grouperContainerStartDockerCommand.append(" -e GROUPER_UI='true' -e GROUPER_WS='true' -e GROUPER_DAEMON='true' -e GROUPER_SCIM='true' ");
10902     //grouperContainerStartDockerCommand.append("-e RUN_APACHE='true' -e RUN_SHIB_SP='false' -e RUN_TOMEE='true' ");
10903     grouperContainerStartDockerCommand.append(" --detach --publish "+portNumberInt.toString()+":8080 ");
10904     grouperContainerStartDockerCommand.append("--mount type=bind,src=");
10905     grouperContainerStartDockerCommand.append(path+File.separator);
10906     grouperContainerStartDockerCommand.append("logs,dst=/opt/grouper/logs ");
10907     grouperContainerStartDockerCommand.append("--mount type=bind,src=");
10908     grouperContainerStartDockerCommand.append(path+File.separator);
10909     grouperContainerStartDockerCommand.append("slashRoot,dst=/opt/grouper/slashRoot ");
10910     grouperContainerStartDockerCommand.append("--restart always --name grouper-ui ");
10911     grouperContainerStartDockerCommand.append("i2incommon/grouper:"+dockerImageVersion );
10912     grouperContainerStartDockerCommand.append(" ui" );
10913     contentToWrite.append(grouperContainerStartDockerCommand.toString());
10914     contentToWrite.append("\n\n");
10915     contentToWrite.append("\n\n");
10916     try {
10917       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
10918     } catch (Exception e) {
10919       System.out.println("Could not write to README.txt file.");
10920     }
10921     
10922     
10923     System.out.print("Press <return> to start the UI container: ");
10924     readFromStdIn("Placeholder");
10925     boolean dockerGrouperContainerStarted = false;
10926     try {
10927       commands = new ArrayList<String>();
10928       commands.add(shCommand());
10929       commands.add("-c");
10930       
10931       commands.add(grouperContainerStartDockerCommand.toString());
10932       
10933       commandResult = GrouperInstallerUtils.execCommand(
10934           GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10935           new File("."), null, false, false, true);
10936       
10937       if (commandResult.getExitCode() == 0) {
10938         dockerGrouperContainerStarted = true;
10939       }
10940       
10941     } catch (Exception e) {}
10942     
10943     if (dockerGrouperContainerStarted == false) {
10944       System.out.println("Could not start grouper container. Run the following command manually.");
10945       System.out.println(grouperContainerStartDockerCommand.toString());
10946     } else {
10947       System.out.println("Inside container grouper runs on port 8080");
10948     }
10949     
10950     System.out.println("Logs are at: "+new File(path+File.separator+"logs").getAbsolutePath());
10951     System.out.println("Command history and documentation are in "+ readmeFile.getAbsolutePath());
10952     System.out.println("Grouper UI is running at : http://localhost:"+portNumberInt+"/grouper/");
10953     System.out.print("Press <return> to exit ");
10954     readFromStdIn("Placeholder");
10955   }
10956     
10957   /**
10958    * 
10959    */
10960   private void mainBuildContainerLogic() {
10961     
10962     //Find out the working directory.  This ends in a file separator
10963     this.grouperTarballDirectoryString = grouperUpgradeTempDirectory();
10964     
10965     this.version = GrouperInstallerUtils.propertiesValue("grouper.version", false);
10966     
10967     if (GrouperInstallerUtils.isBlank(this.version)) {
10968       this.version = getClass().getPackage().getImplementationVersion();
10969     }
10970     
10971     System.out.println("Installing grouper version: " + this.version);
10972     
10973     downloadAndUnzipMaven();
10974     
10975     //####################################
10976     //download apache tomee
10977     File tomeeDir = downloadTomee();
10978     File unzippedTomeeFile = unzip(tomeeDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
10979     this.untarredTomeeDir = untar(unzippedTomeeFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc", null);
10980     
10981     // download grouper tag from github
10982     File grouperSourceCodeDir = downloadGrouperSourceTagFromGithub();
10983     File unzippedGrouperSourceCodeFile = unzip(grouperSourceCodeDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
10984     File untarredGrouperSourceCodeDir = untar(unzippedGrouperSourceCodeFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc", null);
10985     
10986     
10987     String grouperUntarredReleaseDir = untarredGrouperSourceCodeDir.getAbsolutePath().substring(0, untarredGrouperSourceCodeDir.getAbsolutePath().lastIndexOf(File.separator));
10988     grouperUntarredReleaseDir = grouperUntarredReleaseDir + File.separator + "grouper-" + untarredGrouperSourceCodeDir.getName() ;
10989     
10990     // now create an output directory (webapp) and tomee
10991     String containerDirString = grouperContainerDirectory();
10992     File containerTomeeDir = new File(containerDirString + "tomee");
10993     containerTomeeDir.mkdirs();
10994     
10995     File webAppDir = new File(containerDirString + "webapp");
10996     webAppDir.mkdirs();
10997         
10998     // let's start with grouper-ui/webapp directory. We will copy everything else later
10999     GrouperInstallerUtils.copyDirectory(new File(grouperUntarredReleaseDir + File.separator + "grouper-ui" + File.separator+"webapp"), webAppDir);
11000     
11001     File webInfDir = new File(webAppDir+File.separator+"WEB-INF");
11002     webInfDir.mkdirs(); // should already be there
11003     File webInfConfDir = new File(webInfDir+File.separator+"conf");
11004     webInfConfDir.mkdirs();
11005     
11006     File libDir = new File(webInfDir+File.separator+"lib");
11007     libDir.mkdirs();
11008     
11009     File libUiAndDaemonDir = new File(webInfDir+File.separator+"libUiAndDaemon");
11010     libUiAndDaemonDir.mkdirs();
11011     
11012     File libWsDir = new File(webInfDir+File.separator+"libWs");
11013     libWsDir.mkdirs();
11014     
11015     File libScimDir = new File(webInfDir+File.separator+"libScim");
11016     libScimDir.mkdirs();
11017     
11018     File modulesDir = new File(webInfDir+File.separator+"modules");
11019     modulesDir.mkdirs();
11020     File servicesDir = new File(webInfDir+File.separator+"services");
11021     servicesDir.mkdirs();
11022     File classesDir = new File(webInfDir+File.separator+"classes");
11023     classesDir.mkdirs();
11024     File binDir = new File(webInfDir+File.separator+"bin");
11025     binDir.mkdirs();
11026     
11027     // go in grouper-container and run mvn dependency:copy-dependencies
11028     Map<File, File> projectDirToOutputLibDir = new LinkedHashMap<File, File>();
11029     projectDirToOutputLibDir.put(new File(grouperUntarredReleaseDir + File.separator + "grouper-container" + File.separator + "grouper-api-container"), libDir);
11030     projectDirToOutputLibDir.put(new File(grouperUntarredReleaseDir + File.separator + "grouper-container" + File.separator + "grouper-uiDaemon-container"), libUiAndDaemonDir);
11031     projectDirToOutputLibDir.put(new File(grouperUntarredReleaseDir + File.separator + "grouper-container" + File.separator + "grouper-ws-container"), libWsDir);
11032     projectDirToOutputLibDir.put(new File(grouperUntarredReleaseDir + File.separator + "grouper-container" + File.separator + "grouper-scim-container"), libScimDir);
11033     
11034     List<String> commands = new ArrayList<String>();
11035     addMavenCommands(commands);
11036     
11037     commands.add("-DincludeScope=runtime");
11038     commands.add("-Dgrouper.version="+this.version);
11039  
11040     commands.add("dependency:copy-dependencies");
11041           
11042     for (File file: projectDirToOutputLibDir.keySet()) {
11043       System.out.println("\n##################################");
11044       System.out.println("Downloading third party jars for "+ file.getName()+" with command:\n" 
11045           + convertCommandsIntoCommand(commands) + "\n");
11046       
11047       CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
11048           true, true, null, new File(file.getAbsolutePath()), null, true);
11049       
11050       if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
11051         System.out.println("stderr: " + commandResult.getErrorText());
11052       }
11053       if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
11054         System.out.println("stdout: " + commandResult.getOutputText());
11055       }
11056     }
11057     
11058     // now copy all dependency jars into corresponding container/webapp/WEB-INF/lib{""|UiAndDaemon|Ws|Scim|}
11059     try {
11060       Set<String> allGrouperApiJars = new HashSet<String>();
11061       for (File file: projectDirToOutputLibDir.keySet()) {        
11062         File jarsDirectory = new File(file.getAbsolutePath()+File.separator+"target"+File.separator+"dependency");
11063         
11064         if (allGrouperApiJars.size() == 0) { // we are relying on the first item in projectDirToOutputLibDir being the grouper api 
11065           for (File jarFile: findAllLibraryFiles(jarsDirectory.getAbsolutePath())) {
11066             allGrouperApiJars.add(jarFile.getName());
11067           }
11068           // copy the grouper api jars into container/webapp/WEB-INF/lib
11069           GrouperInstallerUtils.copyDirectory(jarsDirectory, projectDirToOutputLibDir.get(file), null, true);
11070           continue;
11071         }
11072         
11073         // make sure the jar file is already not there
11074         for (File jarFile: findAllLibraryFiles(jarsDirectory.getAbsolutePath())) {
11075           if (allGrouperApiJars.contains(jarFile.getName()) == false) {
11076             File destFile = new File(projectDirToOutputLibDir.get(file).getAbsolutePath() + File.separator + jarFile.getName());
11077             GrouperInstallerUtils.copyFile(jarFile, destFile);
11078           }
11079         }
11080         
11081       }
11082     } catch (Exception e) {
11083       throw new RuntimeException("Could not copy jars from dependency directories ", e);
11084     }
11085     
11086     // delete all grouper snapshots jars from WEB-INF/lib
11087 //    List<File> allLibraryJars = findAllLibraryFiles(libDir.getAbsolutePath());
11088 //    
11089 //    for (File file: allLibraryJars) {
11090 //      if (file.getName().contains("grouper") && file.getName().contains("SNAPSHOT")) {
11091 //        GrouperInstallerUtils.fileDelete(file);
11092 //      }
11093 //    }
11094     
11095     // now copy grouper/conf, grouper-ws/conf, grouper-ui/conf and grouperClient/conf to classesDir
11096     
11097     List<File> projectsToGetConfFrom = new ArrayList<File>();
11098     projectsToGetConfFrom.add(new File(grouperUntarredReleaseDir + File.separator + "grouper"));
11099     projectsToGetConfFrom.add(new File(grouperUntarredReleaseDir + File.separator + "grouper-ws"+File.separator+"grouper-ws"));
11100     projectsToGetConfFrom.add(new File(grouperUntarredReleaseDir + File.separator + "grouper-ui"));
11101     projectsToGetConfFrom.add(new File(grouperUntarredReleaseDir + File.separator + "grouper-misc"+File.separator+"grouperClient"));
11102     
11103     try {
11104       for (File file: projectsToGetConfFrom) {
11105         File confDir = new File(file.getAbsolutePath()+File.separator+"conf");
11106         if (confDir.exists()) {
11107           GrouperInstallerUtils.copyDirectory(confDir, classesDir, null, true);
11108         }
11109       }
11110     } catch (Exception e) {
11111       throw new RuntimeException("Could not copy files from conf directory to classes directory", e);
11112     }
11113     
11114     // now copy all misc/*.example.properties into classes directory and rename them to not have example in the filename
11115     
11116     File miscDir = new File(grouperUntarredReleaseDir + File.separator + "grouper" + File.separator + "misc");
11117     File[] filesToBeCopied = miscDir.listFiles(new FilenameFilter() {
11118       
11119       @Override
11120       public boolean accept(File dir, String name) {
11121         return name.endsWith("example.properties") && !name.equals("grouper.text.en.us.example.properties");
11122       }
11123     });
11124     
11125     for (File miscFileToBeCopied: filesToBeCopied) {
11126       String newFileName = miscFileToBeCopied.getName().replace(".example", "");
11127       File destFile = new File(classesDir.getAbsolutePath() + File.separator + newFileName);
11128       GrouperInstallerUtils.copyFile(miscFileToBeCopied, destFile);
11129     }
11130     
11131     // now copy grouper.text.en.us.example.properties into classes/grouperText
11132     File textFileToBeCopied = new File(grouperUntarredReleaseDir + File.separator + "grouper" + 
11133         File.separator + "misc" + File.separator + "grouper.text.en.us.example.properties");
11134     
11135     File textDestFile = new File(classesDir.getAbsolutePath() + File.separator + "grouperText" + 
11136         File.separator + "grouper.text.en.us.properties");
11137     GrouperInstallerUtils.copyFile(textFileToBeCopied, textDestFile);
11138     
11139     
11140     // now copy all grouper-ws/webapp specific files into outputDir/webapp
11141     File grouperWsWebinfDir = new File(grouperUntarredReleaseDir+File.separator+"grouper-ws"+File.separator+
11142         "grouper-ws"+File.separator+"webapp"+File.separator+"WEB-INF");
11143     
11144     GrouperInstallerUtils.copyDirectory(new File(grouperWsWebinfDir.getAbsolutePath()+File.separator+"modules"), modulesDir);
11145     GrouperInstallerUtils.copyDirectory(new File(grouperWsWebinfDir.getAbsolutePath()+File.separator+"services"), servicesDir);
11146     GrouperInstallerUtils.copyDirectory(new File(grouperWsWebinfDir.getAbsolutePath()+File.separator+"conf"), webInfConfDir);
11147     
11148     // now copy grouper/bin contents into outputDir/webapp/WEB-INF/bin
11149     GrouperInstallerUtils.copyDirectory(new File(grouperUntarredReleaseDir + File.separator + "grouper" + File.separator + "bin"), binDir);
11150     
11151     // make gsh.sh executable
11152     commands = GrouperInstallerUtils.toList("chmod", "+x", binDir.getAbsolutePath() + File.separator + "gsh.sh");
11153 
11154     System.out.println("Making sure gsh.sh is executable with command: " + convertCommandsIntoCommand(commands) + "\n");
11155 
11156     CommandResult commandResult = GrouperInstallerUtils.execCommand(
11157         GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
11158         binDir, null, true);
11159     
11160     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
11161       System.out.println("stderr: " + commandResult.getErrorText());
11162     }
11163     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
11164       System.out.println("stdout: " + commandResult.getOutputText());
11165     }
11166     
11167     // now download all the grouper project jars
11168     downloadGrouperJarsIntoLibDirectory(webInfDir);
11169     
11170     // delete slf4j related jars from all the lib dirs
11171     deleteJarsFromLibDirs(webInfDir);
11172     
11173     // take care of conflicting jars
11174     reportOnConflictingJars(libDir.getAbsolutePath());
11175     reportOnConflictingJars(libUiAndDaemonDir.getAbsolutePath());
11176     reportOnConflictingJars(libWsDir.getAbsolutePath());
11177     reportOnConflictingJars(libScimDir.getAbsolutePath());
11178     
11179     // copy apache-tomee-webprofile-x.y.z to tomee
11180     // why can't uncompressed directory has the same name??? :((
11181     File tomeeUntarredDir = new File(this.grouperTarballDirectoryString + File.separator + "apache-tomee-webprofile-" + TOMEE_VERSION);
11182     try {      
11183       GrouperInstallerUtils.copyDirectory(tomeeUntarredDir, containerTomeeDir, null, true);
11184     } catch (Exception e) {
11185       throw new RuntimeException("Could not copy untarred tomee into container/tomee", e);
11186     }
11187     
11188     // put logging related jars in tomee/bin directory
11189     File tomeeBinDir = new File(containerTomeeDir + File.separator + "bin");
11190     
11191 //    downloadFile("https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-core/2.13.1/log4j-core-2.13.1.jar", tomeeBinDir.getAbsolutePath() + File.separator + "log4j-core-2.13.1.jar", "");
11192 //    downloadFile("https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-jul/2.13.1/log4j-jul-2.13.1.jar", tomeeBinDir.getAbsolutePath() + File.separator + "log4j-jul-2.13.1.jar", "");
11193 //    downloadFile("https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-api/2.13.1/log4j-api-2.13.1.jar", tomeeBinDir.getAbsolutePath() + File.separator + "log4j-api-2.13.1.jar", "");
11194 //    
11195 //    // put slf4j in lib dir
11196 //    downloadFile("https://repo1.maven.org/maven2/org/slf4j/slf4j-log4j12/1.7.21/slf4j-log4j12-1.7.21.jar", libDir + File.separator + "slf4j-log4j12-1.7.21.jar", "");
11197     
11198     // point tomee to downloaded webpp
11199     configureTomeeGrouperUberWebapp(containerTomeeDir, webAppDir);
11200     
11201     // copy slf4j from tomee/lib to web-inf/lib
11202 //    File tomeeLibDir = new File(containerTomeeDir + File.separator + "lib");
11203 //    GrouperInstallerUtils.copyFile(new File(tomeeLibDir.getAbsolutePath() + File.separator + "slf4j-jdk14-1.7.21.jar"), new File(libDir.getAbsolutePath() + File.separator + "slf4j-jdk14-1.7.21.jar"));
11204 //    GrouperInstallerUtils.copyFile(new File(tomeeLibDir.getAbsolutePath() + File.separator + "slf4j-api-1.7.21.jar"), new File(libDir.getAbsolutePath() + File.separator + "slf4j-api-1.7.21.jar"));
11205     
11206   }
11207   
11208   /**
11209    * 
11210    */
11211   //TODO delete everything related to install
11212   @Deprecated
11213   private void mainInstallLogic() {
11214     
11215     //####################################
11216     //Find out what directory to install to.  This ends in a file separator
11217     this.grouperInstallDirectoryString = grouperInstallDirectory();
11218 
11219     //Find out what directory to upgrade to.  This ends in a file separator
11220     this.grouperTarballDirectoryString = grouperUpgradeTempDirectory();
11221 
11222     //####################################
11223     //get default ip address
11224     System.out.print("Enter the default IP address for checking ports (just hit enter to accept the default unless on a machine with no network, might want to change to 127.0.0.1): [0.0.0.0]: ");
11225     this.defaultIpAddress = readFromStdIn("grouperInstaller.autorun.defaultIpAddressForPorts");
11226     
11227     if (GrouperInstallerUtils.isBlank(this.defaultIpAddress)) {
11228       this.defaultIpAddress = "0.0.0.0";
11229     }
11230 
11231     if (!GrouperInstallerUtils.equals("0.0.0.0", this.defaultIpAddress)) {
11232       System.out.println("Note, you will probably need to change the tomcat server.xml IP addresses...");
11233     }
11234     
11235     //####################################
11236     //System.out.println("Grouper install directory is: " + grouperInstallDirectoryFile.getAbsolutePath());
11237 
11238     this.version = GrouperInstallerUtils.propertiesValue("grouper.version", true);
11239     System.out.println("Installing grouper version: " + this.version);
11240     //see if it is already downloaded
11241     
11242     //download api and set executable and dos2unix etc
11243     downloadAndConfigureApi();
11244 
11245     //####################################
11246     //ask about database
11247 
11248     File localGrouperHibernatePropertiesFile = new File(this.untarredApiDir.getAbsoluteFile() + File.separator + "conf" 
11249         + File.separator + "grouper.hibernate.properties");
11250 
11251     Properties grouperHibernateProperties = GrouperInstallerUtils.propertiesFromFile(localGrouperHibernatePropertiesFile);
11252 
11253     this.dbUrl = GrouperInstallerUtils.defaultString(grouperHibernateProperties.getProperty("hibernate.connection.url"));
11254     this.dbUser = GrouperInstallerUtils.defaultString(grouperHibernateProperties.getProperty("hibernate.connection.username"));
11255     this.dbPass = GrouperInstallerUtils.defaultString(grouperHibernateProperties.getProperty("hibernate.connection.password"));
11256 
11257     System.out.println("\n##################################\n");
11258     System.out.println("Example mysql URL: jdbc:mysql://localhost:3306/grouper");
11259     System.out.println("Example oracle URL: jdbc:oracle:thin:@server.school.edu:1521:sid");
11260     System.out.println("Example postgres URL: jdbc:postgresql://localhost:5432/database");
11261     System.out.println("Example mssql URL: jdbc:sqlserver://localhost:3280;databaseName=grouper");
11262     System.out.print("\nEnter the database URL [" + this.dbUrl + "]: ");
11263     String newDbUrl = readFromStdIn("grouperInstaller.autorun.dbUrl");
11264     if (!GrouperInstallerUtils.isBlank(newDbUrl)) {
11265       this.dbUrl = newDbUrl;
11266       if (newDbUrl.contains("postgresql") || newDbUrl.contains("sqlserver")) {
11267         System.out.println("Note: you need to change the search sql in the jdbc source in the grouperApi/conf/sources.xml... the change is in the comments in that file");
11268         for (int i=0;i<3;i++) {
11269           System.out.print("Ready to continue? (t|f)? [t] ");
11270           boolean shouldContinue = readFromStdInBoolean(true, "grouperInstaller.autorun.dbContinueAfterChangeSourcesXmlForPostgresSqlServer");
11271           if (shouldContinue) {
11272             break;
11273           }
11274         }
11275       }
11276     }
11277     System.out.print("Database user [" + this.dbUser + "]: ");
11278     String newDbUser = readFromStdIn("grouperInstaller.autorun.dbUser");
11279     if (!GrouperInstallerUtils.isBlank(newDbUser)) {
11280       this.dbUser = newDbUser;
11281     }
11282     System.out.print("Database password (note, you aren't setting the pass here, you are using an existing pass, this will be echoed back) [" 
11283         + GrouperInstallerUtils.defaultIfEmpty(this.dbPass, "<blank>") + "]: ");
11284     String newDbPass = readFromStdIn("grouperInstaller.autorun.dbPass");
11285     if (!GrouperInstallerUtils.isBlank(newDbPass)) {
11286       this.dbPass = newDbPass;
11287     }
11288 
11289     this.giDbUtils = new GiDbUtils(this.dbUrl, this.dbUser, this.dbPass);
11290     this.giDbUtils.registerDriverOnce(this.grouperInstallDirectoryString);
11291 
11292     //####################################
11293     //change the config file
11294     //get the config file
11295 
11296     //lets edit the three properties:
11297     System.out.println("Editing " + localGrouperHibernatePropertiesFile.getAbsolutePath() + ": ");
11298     editPropertiesFile(localGrouperHibernatePropertiesFile, "hibernate.connection.url", this.dbUrl, false);
11299     editPropertiesFile(localGrouperHibernatePropertiesFile, "hibernate.connection.username", this.dbUser, false);
11300     editPropertiesFile(localGrouperHibernatePropertiesFile, "hibernate.connection.password", this.dbPass, false);
11301 
11302     //####################################
11303     //check to see if listening on port?
11304 
11305     //#####################################
11306     //add driver to classpath
11307     //note, we are note really doing this now, we are using drivers already on classpath since this doesnt work
11308     //this.addDriverJarToClasspath();
11309 
11310     //####################################
11311     //check connection to database
11312     checkDatabaseConnection();
11313     
11314     //####################################
11315     // patch the API
11316     this.upgradeExistingApplicationDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath());
11317     this.upgradeExistingClassesDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath())
11318         + "conf" + File.separator;
11319     this.upgradeExistingLibDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath())
11320         + "lib" + File.separator;
11321     this.upgradeExistingBinDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath())
11322             + "bin" + File.separator;
11323     patchApi();
11324 
11325     //make sure log4j is debugging sql statements
11326     log4jDebugSql(this.upgradeExistingClassesDirectoryString + "log4j.properties");
11327     
11328     //####################################
11329     //ask then init the DB
11330     initDb();
11331     addQuickstartSubjects();
11332     addQuickstartData();
11333     
11334     //####################################
11335     //download and configure ui
11336     System.out.print("Do you want to install the user interface (t|f)? [t]: ");
11337     boolean installUi = readFromStdInBoolean(true, "grouperInstaller.autorun.installUi");
11338     if (installUi) {
11339       downloadAndConfigureUi();
11340     }
11341     
11342     //####################################
11343     //get ant
11344     downloadAndUnzipAnt();
11345     
11346     //####################################
11347     //look for or ask or download tomcat
11348     File tomcatDir = downloadTomcat();
11349     File unzippedTomcatFile = unzip(tomcatDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
11350     this.untarredTomcatDir = untar(unzippedTomcatFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc", 
11351         new File(this.grouperInstallDirectoryString));
11352 
11353     //####################################
11354     //ask for tomcat port
11355     configureTomcat();
11356 
11357     File apiPatchStatusFile = new File(GrouperInstallerUtils.fileAddLastSlashIfNotExists(
11358         this.untarredApiDir.getAbsolutePath()) + "grouperPatchStatus.properties");
11359 
11360     //####################################
11361     //build UI
11362     if (installUi) {
11363 
11364       buildUi(true);
11365 
11366       //####################################
11367       //configureTomcatUiWebapp
11368       configureTomcatUiWebapp();
11369   
11370       //####################################
11371       //copy api patch level to ui
11372       File uiPatchStatusFile = new File(GrouperInstallerUtils.fileAddLastSlashIfNotExists(
11373           this.grouperUiBuildToDirName()) + "WEB-INF" + File.separator + "grouperPatchStatus.properties");
11374       System.out.println("Copying applied API patch status to UI:");
11375       System.out.println("  - from: "  + apiPatchStatusFile.getAbsolutePath());
11376       System.out.println("  - to: "  + uiPatchStatusFile.getAbsolutePath());
11377       GrouperInstallerMergePatchFiles.mergePatchFiles(
11378           apiPatchStatusFile, uiPatchStatusFile, true);
11379 
11380 
11381       //####################################
11382       // patch the ui
11383       this.upgradeExistingApplicationDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperUiBuildToDirName());
11384       this.upgradeExistingClassesDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperUiBuildToDirName())
11385           + "WEB-INF" + File.separator + "classes" + File.separator ;
11386       this.upgradeExistingLibDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperUiBuildToDirName())
11387           + "WEB-INF" + File.separator + "lib" + File.separator;
11388       this.upgradeExistingBinDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperUiBuildToDirName())
11389           + "WEB-INF" + File.separator + "bin" + File.separator ;
11390 
11391       //####################################
11392       // temp fix for 2.4 full install - the api and ui /bin dirs differ in 2.4; this step
11393       // will sync by renaming ui files that differ and then copy the api versions
11394       String apiBinSource = GrouperInstallerUtils.fileAddLastSlashIfNotExists(
11395               this.untarredApiDir.getAbsolutePath()) + "bin" + File.separator;
11396       String targetBinSouce = GrouperInstallerUtils.fileAddLastSlashIfNotExists(
11397               this.grouperUiBuildToDirName()) + "WEB-INF" + File.separator + "bin" + File.separator;
11398       String[] filesToCopyFromApiBin = new String[]{"gsh.sh", "gsh.bat", "gsh", "README.txt", "setenv.example.bat", "setenv.example.sh"};
11399       this.grouperBaseBakDir = this.grouperTarballDirectoryString + "bak_UI_"
11400               + new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS").format(new Date()) + File.separator;
11401 
11402       System.out.println("Reconciling differences between API and UI /bin directories...");
11403       syncFilesInDirWithBackup(apiBinSource, targetBinSouce, filesToCopyFromApiBin);
11404       this.grouperBaseBakDir = null;
11405 
11406       this.patchUi();
11407     }
11408     
11409     //####################################
11410     //set the GrouperSystem password
11411     tomcatConfigureGrouperSystem();
11412 
11413     if (installUi) {
11414       //####################################
11415       //bounce tomcat
11416       tomcatBounce("restart");
11417       
11418       //####################################
11419       //tell user to go to url
11420       System.out.println("##################################\n");
11421       System.out.println("Go here for the Grouper UI (change hostname if on different host): http://localhost:" + this.tomcatHttpPort + "/" + this.tomcatUiPath + "/");
11422       System.out.println("\n##################################\n");
11423     }
11424     
11425     System.out.print("Do you want to install web services (t|f)? [t]: ");
11426     boolean installWs = readFromStdInBoolean(true, "grouperInstaller.autorun.installWs");
11427     
11428     if (installWs) {
11429       this.downloadAndUntarWs();
11430       
11431       //#################################### 
11432       //configure Ws
11433       this.configureWs();
11434       
11435       //####################################
11436       //build WS
11437       buildWs(true);
11438       
11439       //####################################
11440       //copy to tomcat
11441       configureTomcatWsWebapp();
11442   
11443       //####################################
11444       //copy api patch level to ui
11445       File wsPatchStatusFile = new File(GrouperInstallerUtils.fileAddLastSlashIfNotExists(
11446           this.grouperWsBuildToDirName()) + "WEB-INF" + File.separator + "grouperPatchStatus.properties");
11447       System.out.println("Copying applied API patch status to WS:");
11448       System.out.println("  - from: "  + apiPatchStatusFile.getAbsolutePath());
11449       System.out.println("  - to: "  + wsPatchStatusFile.getAbsolutePath());
11450       GrouperInstallerMergePatchFiles.mergePatchFiles(
11451           apiPatchStatusFile, wsPatchStatusFile, true);
11452 
11453       //####################################
11454       // patch the ws
11455       this.upgradeExistingApplicationDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperWsBuildToDirName());
11456       this.upgradeExistingClassesDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperWsBuildToDirName())
11457           + "WEB-INF" + File.separator + "classes" + File.separator ;
11458       this.upgradeExistingLibDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperWsBuildToDirName())
11459           + "WEB-INF" + File.separator + "lib" + File.separator;
11460       this.upgradeExistingBinDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperWsBuildToDirName())
11461               + "WEB-INF" + File.separator + "bin" + File.separator ;
11462 
11463       //####################################
11464       // temp fix for 2.4 full install - the api and ws /bin dirs differ in 2.4; this step
11465       // will sync by renaming ws files that differ and then copy the api versions
11466       String apiBinSource = GrouperInstallerUtils.fileAddLastSlashIfNotExists(
11467               this.untarredApiDir.getAbsolutePath()) + "bin" + File.separator;
11468       String targetBinSouce = GrouperInstallerUtils.fileAddLastSlashIfNotExists(
11469               this.grouperWsBuildToDirName()) + "WEB-INF" + File.separator + "bin" + File.separator;
11470       String[] filesToCopyFromApiBin = new String[]{"gsh.sh", "gsh.bat", "gsh", "README.txt", "setenv.example.bat", "setenv.example.sh"};
11471       this.grouperBaseBakDir = this.grouperTarballDirectoryString + "bak_WS_"
11472               + new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS").format(new Date()) + File.separator;
11473 
11474       System.out.println("Reconciling differences between API and WS /bin directories...");
11475       syncFilesInDirWithBackup(apiBinSource, targetBinSouce, filesToCopyFromApiBin);
11476       this.grouperBaseBakDir = null;
11477 
11478       this.patchWs();
11479   
11480       //####################################
11481       //bounce tomcat
11482       tomcatBounce("restart");
11483   
11484       //####################################
11485       //tell user to go to url
11486       System.out.println("This is the Grouper WS URL (change hostname if on different host): http://localhost:" + this.tomcatHttpPort + "/" + this.tomcatWsPath + "/");
11487     }
11488     
11489     System.out.print("Do you want to install the web services client (t|f)? [t]: ");
11490     boolean installClient = readFromStdInBoolean(true, "grouperInstaller.autorun.installClient");
11491     
11492     if (installClient) {
11493       //download and build client
11494       this.downloadAndBuildClient();
11495   
11496       //####################################
11497       //configure where WS is
11498       this.configureClient();
11499       
11500       if (installWs) {
11501         //####################################
11502         //add grouper system to WS group
11503         this.addGrouperSystemWsGroup();
11504         
11505         //####################################
11506         //run a client command
11507         this.runClientCommand();
11508       }
11509     }
11510 
11511     //####################################
11512     //install pspng
11513     System.out.print("Do you want to install the provisioning service provider next generation (t|f)? [t]: ");
11514     boolean installPspng = readFromStdInBoolean(true, "grouperInstaller.autorun.installPspng");
11515     if (installPspng) {
11516       downloadAndBuildPspng();  
11517       
11518       //copy jars
11519       GrouperInstallerUtils.copyDirectory(new File(this.untarredPspngDir.getAbsolutePath() + File.separator + "lib" + File.separator + "custom"), 
11520           new File(this.untarredApiDir.getAbsolutePath() + File.separator + "lib" + File.separator + "custom"));
11521       GrouperInstallerUtils.copyDirectory(new File(this.untarredPspngDir.getAbsolutePath() + File.separator + "dist"), 
11522           new File(this.untarredApiDir.getAbsolutePath() + File.separator + "lib" + File.separator + "custom"));
11523 
11524       //####################################
11525       // patch the PSP
11526       this.upgradeExistingApplicationDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath());
11527       this.upgradeExistingClassesDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath())
11528           + "conf" + File.separator;
11529       this.upgradeExistingLibDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath())
11530           + "lib" + File.separator;
11531       patchPspng();
11532             
11533     }
11534 
11535 
11536     if (!installPspng) {
11537       //####################################
11538       //install psp
11539       System.out.print("Do you want to install the provisioning service provider (t|f)? [t]: ");
11540       if (readFromStdInBoolean(true, "grouperInstaller.autorun.installPsp")) {
11541         downloadAndBuildPsp();              
11542         GrouperInstallerUtils.copyDirectory(this.untarredPspDir, this.untarredApiDir);
11543   
11544         //####################################
11545         // patch the PSP
11546         this.upgradeExistingApplicationDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath());
11547         this.upgradeExistingClassesDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath())
11548             + "conf" + File.separator;
11549         this.upgradeExistingLibDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath())
11550             + "lib" + File.separator;
11551         patchPsp();
11552               
11553       }
11554     }
11555     
11556     reportOnConflictingJars(this.upgradeExistingApplicationDirectoryString);
11557 
11558     //#####################################
11559     //start the loader
11560     startLoader(true);
11561     
11562     //prompt and install ws scim
11563     installWsScim();
11564     
11565     //prompt and install rabbitmq messaging
11566     installMessagingRabbitMq();
11567     
11568     //prompt and install aws sqs messaging
11569     installMessagingAwsSqs();
11570     
11571     //prompt and install activemq messaging
11572     installMessagingActiveMq();
11573     
11574     //prompt and install activeMq messaging
11575     //installMessagingRabbitMq();
11576     
11577 
11578     //#####################################
11579     //success
11580     System.out.println("\n##################################\n");
11581 
11582     System.out.println("\nInstallation success!");
11583 
11584 
11585     System.out.println("\nRun the installer's 'admin' function to get information and manage about your installation (db, tomcat, logs, etc)");
11586     
11587     if (installUi) {
11588       System.out.println("\nGo here for the Grouper UI (change hostname if on different host): http://localhost:" + this.tomcatHttpPort + "/" + this.tomcatUiPath + "/");
11589       
11590     }
11591     if (installWs) {
11592       System.out.println("\nThis is the Grouper WS URL (change hostname if on different host): http://localhost:" + this.tomcatHttpPort + "/" + this.tomcatWsPath + "/");
11593     }
11594     System.out.println("\n##################################\n");
11595 
11596   }
11597 
11598   /**
11599    * 
11600    */
11601   private void installWsScim() {
11602     //#####################################
11603     // Install Grouper WS Scim Tier API
11604     //####################################
11605     System.out.print("Do you want to install the grouper ws scim (t|f)? [t]: ");
11606     boolean installWsScim = readFromStdInBoolean(true, "grouperInstaller.autorun.installGrouperWsScim");
11607     if (installWsScim) {
11608       downloadAndUntarWs();
11609       
11610       //####################################
11611       //get maven
11612       // NOTE: we dont need maven, ship the binary
11613       //downloadAndUnzipMaven();
11614       
11615       //####################################
11616       //look for or ask or download apache tomee
11617       File tomeeDir = downloadTomee();
11618       File unzippedTomeeFile = unzip(tomeeDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
11619       this.untarredTomeeDir = untar(unzippedTomeeFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc", 
11620           new File(this.grouperInstallDirectoryString));
11621 
11622       //####################################
11623       //ask for tomee port
11624       configureTomee();
11625 
11626       //####################################
11627       //build grouper ws scim
11628       // NOTE: we dont need to build it, ship the binary
11629       //buildWsScim();
11630 
11631       //####################################
11632       //configureTomeeGrouperWsScimWebapp
11633       configureTomeeGrouperWsScimWebapp();
11634       
11635       //####################################
11636       //set the GrouperSystem password
11637       tomeeConfigureGrouperSystem();
11638 
11639       //####################################
11640       //bounce tomcat
11641       tomeeBounce("restart");
11642       
11643       //####################################
11644       //tell user to go to url
11645       System.out.println("##################################\n");
11646       System.out.println("Go here for the Grouper WS Scim (change hostname if on different host): http://localhost:" + this.tomeeHttpPort + "/" + "grouper-ws-scim" + "/");
11647       System.out.println("\n##################################\n");
11648     }
11649   }
11650 
11651   /**
11652    * 
11653    */
11654   private void installMessagingRabbitMq() {
11655     //#####################################
11656     // Install Grouper Messaging RabbitMQ
11657     //####################################
11658     System.out.print("Do you want to install grouper rabbitMQ messaging (t|f)? [f]: ");
11659     boolean installRabbitMqMessaging = readFromStdInBoolean(false, "grouperInstaller.autorun.installGrouperRabbitMqMessaging");
11660     if (installRabbitMqMessaging) {
11661       
11662       String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
11663       
11664       if (!urlToDownload.endsWith("/")) {
11665         urlToDownload += "/";
11666       }
11667 
11668       urlToDownload += "release/";
11669       String rabbitMqFileName = "grouper.rabbitMq-" + this.version + ".tar.gz";
11670       urlToDownload += this.version + "/" + rabbitMqFileName;
11671 
11672       File rabbitMqFile = new File(this.grouperTarballDirectoryString + rabbitMqFileName);
11673       
11674       downloadFile(urlToDownload, rabbitMqFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalRabbitMqDownloadTarEtc");
11675 
11676       File unzippedRabbitMqFile = unzip(rabbitMqFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalRabbitMqDownloadTarEtc");
11677       File unzippedRabbitMqDir = untar(unzippedRabbitMqFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalRabbitMqDownloadTarEtc", 
11678           new File(this.grouperInstallDirectoryString));
11679 
11680       File rabbitMqInstallDirectoryFile = null;
11681       boolean success = false;
11682       for (int i=0;i<10;i++) {
11683 
11684         System.out.print("Where do you want the Grouper RabbitMQ messaging connector installed? ");
11685         String rabbitMqInstallDirectoryFileString = readFromStdIn("grouperInstaller.autorun.rabbitMqWhereInstalled");
11686         rabbitMqInstallDirectoryFile = new File(rabbitMqInstallDirectoryFileString);
11687         if (!rabbitMqInstallDirectoryFile.exists() || !rabbitMqInstallDirectoryFile.isDirectory()) {
11688           System.out.println("Error: cant find directory: '" + rabbitMqInstallDirectoryFile.getAbsolutePath() + "'");
11689           continue;
11690         }
11691 
11692         //make sure directory is where the app is
11693         
11694         List<File> grouperClientFiles = GrouperInstallerUtils.jarFindJar(rabbitMqInstallDirectoryFile, "grouperClient.jar");
11695         
11696         if (GrouperInstallerUtils.length(grouperClientFiles) == 0) {
11697           System.out.println("Cant find grouperClient.jar in a subdir of the install dir, please try again!");
11698           continue;
11699         }
11700         
11701         
11702         if (GrouperInstallerUtils.length(grouperClientFiles) > 1) {
11703           System.out.println("Found more than one grouperClient.jar in a subdir of the install dir, must only be one, please try again!");
11704           continue;
11705         }
11706 
11707         //ok, we know where the jars go
11708         File dirWhereFilesGo = grouperClientFiles.get(0).getParentFile();
11709         
11710         List<File> jarFiles = GrouperInstallerUtils.fileListRecursive(new File(unzippedRabbitMqDir.getAbsolutePath() + File.separatorChar 
11711             + "lib" + File.separatorChar));
11712       
11713         for (File jarFile : jarFiles) {
11714           
11715           String fileName = jarFile.getName();
11716           
11717           if (!fileName.endsWith(".jar")) {
11718             continue;
11719           }
11720           
11721           String sourceFileName = unzippedRabbitMqDir.getAbsolutePath() + File.separatorChar 
11722               + "lib" + File.separatorChar + fileName;
11723           
11724           File sourceFile = new File(sourceFileName);
11725           
11726           String destFileName = dirWhereFilesGo.getAbsolutePath() + File.separatorChar + fileName;
11727           
11728           File destFile = new File(destFileName);
11729           
11730           copyJarFileIfNotExists(sourceFile, destFile, false, false);
11731 
11732         }
11733 
11734         success = true;
11735         break;
11736       }        
11737       
11738       if (!success) {
11739         System.exit(1);
11740       }
11741       
11742       //####################################
11743       //tell user to configure
11744       System.out.println("##################################\n");
11745       
11746       System.out.println("Configure your grouper.client.properties based on this file " 
11747           + unzippedRabbitMqDir.getAbsoluteFile() + File.separator 
11748           + "grouper.client.rabbitMq.example.properties");
11749       System.out.println("\n##################################\n");
11750     }
11751   }
11752   
11753   /**
11754    * 
11755    */
11756   private void installMessagingAwsSqs() {
11757 
11758     //#####################################
11759     // Install Grouper Messaging AWS SQS
11760     //####################################
11761     System.out.print("Do you want to install grouper AWS SQS messaging (t|f)? [f]: ");
11762     boolean installAwsMessaging = readFromStdInBoolean(false, "grouperInstaller.autorun.installGrouperAwsSqsMessaging");
11763     if (installAwsMessaging) {
11764       
11765       String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
11766       
11767       if (!urlToDownload.endsWith("/")) {
11768         urlToDownload += "/";
11769       }
11770 
11771       urlToDownload += "release/";
11772       String awsFileName = "grouper.aws-" + this.version + ".tar.gz";
11773       urlToDownload += this.version + "/" + awsFileName;
11774 
11775       File awsFile = new File(this.grouperTarballDirectoryString + awsFileName);
11776       
11777       downloadFile(urlToDownload, awsFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalAwsSqsDownloadTarEtc");
11778 
11779       File unzippedAwsFile = unzip(awsFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalAwsSqsDownloadTarEtc");
11780       File unzippedAwsDir = untar(unzippedAwsFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalAwsSqsDownloadTarEtc", 
11781           new File(this.grouperInstallDirectoryString));
11782 
11783       File awsInstallDirectoryFile = null;
11784       boolean success = false;
11785       for (int i=0;i<10;i++) {
11786 
11787         System.out.print("Where do you want the Grouper AWS SQS messaging connector installed? ");
11788         String awsInstallDirectoryFileString = readFromStdIn("grouperInstaller.autorun.AwsSqsWhereInstalled");
11789         awsInstallDirectoryFile = new File(awsInstallDirectoryFileString);
11790         if (!awsInstallDirectoryFile.exists() || !awsInstallDirectoryFile.isDirectory()) {
11791           System.out.println("Error: cant find directory: '" + awsInstallDirectoryFile.getAbsolutePath() + "'");
11792           continue;
11793         }
11794 
11795         //make sure directory is where the app is
11796         
11797         List<File> grouperClientFiles = GrouperInstallerUtils.jarFindJar(awsInstallDirectoryFile, "grouperClient.jar");
11798         
11799         if (GrouperInstallerUtils.length(grouperClientFiles) == 0) {
11800           System.out.println("Cant find grouperClient.jar in a subdir of the install dir, please try again!");
11801           continue;
11802         }
11803         
11804         
11805         if (GrouperInstallerUtils.length(grouperClientFiles) > 1) {
11806           System.out.println("Found more than one grouperClient.jar in a subdir of the install dir, must only be one, please try again!");
11807           continue;
11808         }
11809 
11810         //ok, we know where the jars go
11811         File dirWhereFilesGo = grouperClientFiles.get(0).getParentFile();
11812         
11813         List<File> jarFiles = GrouperInstallerUtils.fileListRecursive(new File(unzippedAwsDir.getAbsolutePath() + File.separatorChar 
11814               + "lib" + File.separatorChar));
11815         
11816         for (File jarFile : jarFiles) {
11817           
11818           String fileName = jarFile.getName();
11819           
11820           if (!fileName.endsWith(".jar")) {
11821             continue;
11822           }
11823           
11824           String sourceFileName = unzippedAwsDir.getAbsolutePath() + File.separatorChar 
11825               + "lib" + File.separatorChar + fileName;
11826           
11827           File sourceFile = new File(sourceFileName);
11828           
11829           String destFileName = dirWhereFilesGo.getAbsolutePath() + File.separatorChar + fileName;
11830           
11831           File destFile = new File(destFileName);
11832           
11833           copyJarFileIfNotExists(sourceFile, destFile, false, false);
11834 
11835         }
11836 
11837         success = true;
11838         break;
11839       }        
11840       
11841       if (!success) {
11842         System.exit(1);
11843       }
11844       
11845       //####################################
11846       //tell user to configure
11847       System.out.println("##################################\n");
11848       
11849       System.out.println("Configure your grouper.client.properties based on this file " 
11850           + unzippedAwsDir.getAbsoluteFile() + File.separator 
11851           + "grouper.client.aws.example.properties");
11852       System.out.println("\n##################################\n");
11853     }
11854   
11855   }
11856   
11857   /**
11858    * 
11859    */
11860   private void installMessagingActiveMq() {
11861 
11862     //#####################################
11863     // Install Grouper Messaging ActiveMq
11864     //####################################
11865     System.out.print("Do you want to install grouper activeMq messaging (t|f)? [f]: ");
11866     boolean installActiveMqMessaging = readFromStdInBoolean(false, "grouperInstaller.autorun.installGrouperActiveMqMessaging");
11867     if (installActiveMqMessaging) {
11868       
11869       String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
11870       
11871       if (!urlToDownload.endsWith("/")) {
11872         urlToDownload += "/";
11873       }
11874 
11875       urlToDownload += "release/";
11876       String activeMqFileName = "grouper.activeMq-" + this.version + ".tar.gz";
11877       urlToDownload += this.version + "/" + activeMqFileName;
11878 
11879       File activeMqFile = new File(this.grouperTarballDirectoryString + activeMqFileName);
11880       
11881       downloadFile(urlToDownload, activeMqFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalActiveMqDownloadTarEtc");
11882 
11883       File unzippedActiveMqFile = unzip(activeMqFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalActiveMqDownloadTarEtc");
11884       File unzippedActiveMqDir = untar(unzippedActiveMqFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalActiveMqDownloadTarEtc", 
11885           new File(this.grouperInstallDirectoryString));
11886 
11887       File activeMqInstallDirectoryFile = null;
11888       boolean success = false;
11889       for (int i=0;i<10;i++) {
11890 
11891         System.out.print("Where do you want the Grouper ActiveMq messaging connector installed? ");
11892         String activeMqInstallDirectoryFileString = readFromStdIn("grouperInstaller.autorun.activeMqWhereInstalled");
11893         activeMqInstallDirectoryFile = new File(activeMqInstallDirectoryFileString);
11894         if (!activeMqInstallDirectoryFile.exists() || !activeMqInstallDirectoryFile.isDirectory()) {
11895           System.out.println("Error: cant find directory: '" + activeMqInstallDirectoryFile.getAbsolutePath() + "'");
11896           continue;
11897         }
11898 
11899         //make sure directory is where the app is
11900         
11901         List<File> grouperClientFiles = GrouperInstallerUtils.jarFindJar(activeMqInstallDirectoryFile, "grouperClient.jar");
11902         
11903         if (GrouperInstallerUtils.length(grouperClientFiles) == 0) {
11904           System.out.println("Cant find grouperClient.jar in a subdir of the install dir, please try again!");
11905           continue;
11906         }
11907         
11908         
11909         if (GrouperInstallerUtils.length(grouperClientFiles) > 1) {
11910           System.out.println("Found more than one grouperClient.jar in a subdir of the install dir, must only be one, please try again!");
11911           continue;
11912         }
11913 
11914         //ok, we know where the jars go
11915         File dirWhereFilesGo = grouperClientFiles.get(0).getParentFile();
11916         
11917         List<File> jarFiles = GrouperInstallerUtils.fileListRecursive(new File(unzippedActiveMqDir.getAbsolutePath() + File.separatorChar 
11918             + "lib" + File.separatorChar));
11919       
11920         for (File jarFile : jarFiles) {
11921           
11922           String fileName = jarFile.getName();
11923           
11924           if (!fileName.endsWith(".jar")) {
11925             continue;
11926           }
11927           
11928           String sourceFileName = unzippedActiveMqDir.getAbsolutePath() + File.separatorChar 
11929               + "lib" + File.separatorChar + fileName;
11930           
11931           File sourceFile = new File(sourceFileName);
11932           
11933           String destFileName = dirWhereFilesGo.getAbsolutePath() + File.separatorChar + fileName;
11934           
11935           File destFile = new File(destFileName);
11936           
11937           copyJarFileIfNotExists(sourceFile, destFile, false, false);
11938 
11939         }
11940 
11941         success = true;
11942         break;
11943       }        
11944       
11945       if (!success) {
11946         System.exit(1);
11947       }
11948       
11949       //####################################
11950       //tell user to configure
11951       System.out.println("##################################\n");
11952       
11953       System.out.println("Configure your grouper.client.properties based on this file " 
11954           + unzippedActiveMqDir.getAbsoluteFile() + File.separator 
11955           + "grouper.client.activeMq.example.properties");
11956       System.out.println("\n##################################\n");
11957     }
11958   
11959   }
11960 
11961   /**
11962    * 
11963    */
11964   private void downloadAndBuildPsp() {
11965     File pspDir = downloadPsp();
11966     File unzippedPspFile = unzip(pspDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalPspDownloadTarEtc");
11967     this.untarredPspDir = untar(unzippedPspFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalPspDownloadTarEtc", 
11968         null);
11969 
11970   }
11971   
11972   /**
11973    * 
11974    */
11975   private void downloadAndBuildPspng() {
11976     File pspngDir = downloadPspng();
11977     File unzippedPspngFile = unzip(pspngDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalPspngDownloadTarEtc");
11978     this.untarredPspngDir = untar(unzippedPspngFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalPspngDownloadTarEtc", 
11979         null);
11980 
11981   }
11982   
11983   /** untarred psp dir file */
11984   private File untarredPspDir;
11985   
11986   /** untarred pspng dir file */
11987   private File untarredPspngDir;
11988   
11989   /**
11990    * 
11991    */
11992   public void downloadAndUnzipAnt() {
11993     File antDir = downloadAnt();
11994     File unzippedAntFile = unzip(antDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
11995     this.untarredAntDir = untar(unzippedAntFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc", null);
11996   }
11997 
11998   /**
11999    * 
12000    */
12001   public void downloadAndUnzipMaven() {
12002     File mavenDir = downloadMaven();
12003     File unzippedMavenFile = unzip(mavenDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
12004     this.untarredMavenDir = untar(unzippedMavenFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc", null);
12005   }
12006 
12007   /**
12008    * 
12009    */
12010   public void downloadAndUntarWs() {
12011 
12012     //####################################
12013     //download the ws
12014     File wsDir = downloadWs();
12015 
12016     //####################################
12017     //unzip/untar the ws file
12018     File unzippedWsFile = unzip(wsDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalWsDownloadTarEtc");
12019     System.out.println("Unzipped Ws file is "+unzippedWsFile);
12020     this.untarredWsDir = untar(unzippedWsFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalWsDownloadTarEtc", 
12021         new File(this.grouperInstallDirectoryString));
12022 
12023   }
12024 
12025   /**
12026    * 
12027    */
12028   public void downloadAndConfigureUi() {
12029     //####################################
12030     //get UI
12031     File uiDir = downloadUi();
12032     
12033     //####################################
12034     //unzip/untar the ui file
12035     File unzippedUiFile = unzip(uiDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalUiDownloadTarEtc");
12036     this.untarredUiDir = untar(unzippedUiFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalUiDownloadTarEtc", 
12037         new File(this.grouperInstallDirectoryString));
12038 
12039     //####################################
12040     //configure UI
12041     configureUi();
12042   }
12043 
12044   /**
12045    * 
12046    */
12047   public void downloadAndConfigureApi() {
12048     File apiFile = downloadApi();
12049     
12050     //####################################
12051     //unzip/untar the api file
12052     
12053     File unzippedApiFile = unzip(apiFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalApiDownloadTarEtc");
12054     File theUntarredApiDir = untar(unzippedApiFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalApiDownloadTarEtc", 
12055         new File(this.grouperInstallDirectoryString));
12056     
12057     File theGrouperJar = new File(GrouperInstallerUtils.fileAddLastSlashIfNotExists(theUntarredApiDir.getAbsolutePath())
12058         + "dist" + File.separator + "lib" + File.separator + "grouper.jar");
12059     
12060     gshExcutableAndDos2Unix(theUntarredApiDir.getAbsolutePath() + File.separator + "bin" + File.separator);
12061 
12062     //these might be set from UI or WS
12063     if (this.untarredApiDir == null) {
12064       this.untarredApiDir = theUntarredApiDir;
12065     }
12066     
12067     if (this.grouperJar == null) {
12068       this.grouperJar = theGrouperJar;
12069     }
12070   }
12071 
12072   /**
12073    * @param binDirLocation which includes trailing slash
12074    */
12075   public void gshExcutableAndDos2Unix(String binDirLocation) {
12076     gshExcutableAndDos2Unix(binDirLocation, null);
12077   }
12078 
12079   /**
12080    * run dos2unix on a file
12081    * @param file
12082    * @param fileNameInPrompt 
12083    * @param configSuffixAutorun 
12084    */
12085   public static void dos2unix(File file, String fileNameInPrompt, String configSuffixAutorun) {
12086     dos2unix(GrouperInstallerUtils.toSet(file), fileNameInPrompt, configSuffixAutorun);
12087   }
12088 
12089   /**
12090    * run dos2unix on a file
12091    * @param files
12092    * @param fileNameInPrompt e.g. gsh.sh
12093    * @param configSuffixAutorun suffix after grouperInstaller.autorun.dos2unix in properties file
12094    */
12095   public static void dos2unix(Collection<File> files, String fileNameInPrompt, String configSuffixAutorun) {
12096 
12097     if (!GrouperInstallerUtils.isWindows()) {
12098 
12099       System.out.print("Do you want to run dos2unix on " + fileNameInPrompt + " (t|f)? [t]: ");
12100       boolean dos2unixRunOnFile = readFromStdInBoolean(true, "grouperInstaller.autorun.dos2unix" + configSuffixAutorun);
12101       
12102       if (dos2unixRunOnFile) {
12103 
12104         for (File file : files) {
12105           
12106           if (!file.exists()) {
12107             continue;
12108           }
12109           
12110           List<String> commands = GrouperInstallerUtils.toList("dos2unix", 
12111               file.getAbsolutePath());
12112     
12113           System.out.println("Making sure " + file.getName() + " is in unix format: " + convertCommandsIntoCommand(commands) + "\n");
12114           String error = null;
12115           CommandResult commandResult = null;
12116           boolean didntWork = false;
12117           Throwable throwable = null;
12118           try {
12119             commandResult = GrouperInstallerUtils.execCommand(
12120                 GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
12121                 file.getParentFile(), null, false, true, false);
12122 
12123             if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
12124               System.out.println("stderr: " + commandResult.getErrorText());
12125             }
12126             if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
12127               System.out.println("stdout: " + commandResult.getOutputText());
12128             }
12129             continue;
12130           } catch (Throwable t) {
12131             didntWork = true;
12132             error = t.getMessage();
12133             throwable = t;
12134           }
12135           
12136           if (didntWork) {
12137             try {
12138               //lets try the java way?
12139               String fileContents = GrouperInstallerUtils.readFileIntoString(file);
12140               if (fileContents.contains("\r\n")) {
12141                 System.out.println("Problem with command 'dos2unix'.   Is it installed?  Converting to unix via java replacing \\r\\n with \\n: " + file.getAbsolutePath());
12142                 fileContents = fileContents.replaceAll("\r\n", "\n");
12143                 GrouperInstallerUtils.saveStringIntoFile(file, fileContents);
12144               }
12145               continue;
12146             } catch (Throwable t) {
12147               t.printStackTrace();
12148             }
12149           }
12150           
12151           if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
12152             System.out.println("stderr: " + commandResult.getErrorText());
12153           }
12154           if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
12155             System.out.println("stdout: " + commandResult.getOutputText());
12156           }
12157           if (!GrouperInstallerUtils.isBlank(error)) {
12158             if (throwable != null) {
12159               throwable.printStackTrace();
12160             }
12161             System.out.println("Error: " + error);
12162             System.out.println("NOTE: you might need to run this to convert newline characters to mac/unix:\n\n" +
12163                 "cat " + file.getAbsolutePath()
12164                 + " | col -b > " + file.getAbsolutePath() + "\n");
12165           }
12166         }
12167       }
12168 
12169     }
12170   }
12171   
12172   /**
12173    * @param binDirLocation which includes trailing slash
12174    * @param specify if specifying location
12175    */
12176   public void gshExcutableAndDos2Unix(String binDirLocation, String specify) {
12177     //lts make sure gsh is executable and in unix format
12178 
12179     if (!GrouperInstallerUtils.isWindows()) {
12180 
12181       specify = GrouperInstallerUtils.trimToEmpty(specify);
12182       
12183       if (specify.length() > 0) {
12184         specify += " ";
12185       }
12186       
12187       System.out.print("Do you want to set " + specify + "gsh script to executable (t|f)? [t]: ");
12188       boolean setGshFile = readFromStdInBoolean(true, "grouperInstaller.autorun.setGshScriptsToExecutable");
12189       
12190       if (setGshFile) {
12191       
12192         binDirLocation = GrouperInstallerUtils.fileAddLastSlashIfNotExists(binDirLocation);
12193         
12194         List<String> commands = GrouperInstallerUtils.toList("chmod", "+x", 
12195             binDirLocation + "gsh.sh");
12196   
12197         System.out.println("Making sure gsh.sh is executable with command: " + convertCommandsIntoCommand(commands) + "\n");
12198   
12199         CommandResult commandResult = GrouperInstallerUtils.execCommand(
12200             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
12201             new File(binDirLocation), null, true);
12202         
12203         if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
12204           System.out.println("stderr: " + commandResult.getErrorText());
12205         }
12206         if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
12207           System.out.println("stdout: " + commandResult.getOutputText());
12208         }
12209 
12210         if (new File(binDirLocation + "gsh").exists()) {
12211           commands = GrouperInstallerUtils.toList("chmod", "+x", 
12212               binDirLocation + "gsh");
12213     
12214           System.out.println("Making sure gsh is executable with command: " + convertCommandsIntoCommand(commands) + "\n");
12215     
12216           commandResult = GrouperInstallerUtils.execCommand(
12217               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
12218               new File(binDirLocation), null, true);
12219           
12220           if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
12221             System.out.println("stderr: " + commandResult.getErrorText());
12222           }
12223           if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
12224             System.out.println("stdout: " + commandResult.getOutputText());
12225           }
12226         }
12227         
12228         dos2unix(GrouperInstallerUtils.toSet(new File(binDirLocation + "gsh.sh"), new File(binDirLocation + "gsh")), "gsh.sh", "OnGsh");
12229 
12230       }
12231       
12232     }
12233   }
12234   
12235   /**
12236    * if we are debugging sql in log4j
12237    */
12238   private Boolean log4jDebugSql = null;
12239   
12240   /**
12241    * if this file has been taken care of for a while
12242    */
12243   private Set<File> log4jDebugDone = new HashSet<File>();
12244   
12245   /**
12246    * if this file has been taken care of for a while
12247    */
12248   private Set<File> removeLegacyHibernatePropertiesDone = new HashSet<File>();
12249   
12250   /**
12251    * @param hibernateFileLocation
12252    */
12253   public void removeLegacyHibernateProperties(String hibernateFileLocation) {
12254 
12255     //if not a file dont worry about it
12256     File hibernateFile = new File(hibernateFileLocation);
12257 
12258     if (this.removeLegacyHibernatePropertiesDone.contains(hibernateFile)) {
12259       return;
12260     }
12261 
12262     this.removeLegacyHibernatePropertiesDone.add(hibernateFile);
12263 
12264     if (!hibernateFile.exists()) {
12265       System.out.println("Cant find grouper.hibernate.properties: " + hibernateFileLocation);
12266       return;
12267     }
12268     
12269     //see if its there
12270     Properties hibernateProperties = GrouperInstallerUtils.propertiesFromFile(hibernateFile);
12271     String current = GrouperInstallerUtils.propertiesValue(hibernateProperties, "hibernate.cache.region.factory_class");
12272     
12273     if (current == null) {
12274       //not there, we're good
12275       return;
12276     }
12277 
12278 
12279     removeRedundantProperties(hibernateFile, GrouperInstallerUtils.toSet("hibernate.cache.region.factory_class"));
12280     System.out.println("File " + hibernateFile.getAbsolutePath() + " has property hibernate.cache.region.factory_class set to \"" + current + "\".  Removing since this is now in the grouper.hibernate.base.properties file.");
12281   }
12282   
12283   /**
12284    * @param log4jLocation
12285    */
12286   public void log4jDebugSql(String log4jLocation) {
12287 
12288     //if not a file dont worry about it
12289     File log4jFile = new File(log4jLocation);
12290 
12291     if (this.log4jDebugDone.contains(log4jFile)) {
12292       return;
12293     }
12294 
12295     this.log4jDebugDone.add(log4jFile);
12296 
12297     if (!log4jFile.exists()) {
12298       System.out.println("Cant find log4j.properties: " + log4jLocation);
12299       return;
12300     }
12301     
12302     //see if its already there
12303     Properties log4jProperties = GrouperInstallerUtils.propertiesFromFile(log4jFile);
12304     String currentAntEntry = GrouperInstallerUtils.propertiesValue(log4jProperties, "log4j.logger.org.apache.tools.ant");
12305     
12306     if (GrouperInstallerUtils.equalsIgnoreCase(GrouperInstallerUtils.trimToEmpty(currentAntEntry), "DEBUG")
12307         || GrouperInstallerUtils.equalsIgnoreCase(GrouperInstallerUtils.trimToEmpty(currentAntEntry), "INFO")
12308         || GrouperInstallerUtils.equalsIgnoreCase(GrouperInstallerUtils.trimToEmpty(currentAntEntry), "WARN")) {
12309       //we are already there
12310       return;
12311     }
12312 
12313     if (this.log4jDebugSql == null) {
12314       System.out.print("Do you want add log4j.logger.org.apache.tools.ant = WARN to " + log4jFile.getAbsolutePath() + " (recommended so you can see progress of SQL scripts) (t|f)? [t]: ");
12315       this.log4jDebugSql = readFromStdInBoolean(true, "grouperInstaller.autorun.log4jDebugSql");
12316     }
12317 
12318     if (this.log4jDebugSql) {
12319 
12320       editPropertiesFile(log4jFile, "log4j.logger.org.apache.tools.ant", "WARN", false);
12321 
12322     }
12323   }
12324 
12325   /**
12326    * 
12327    */
12328   public void downloadAndBuildClient() {
12329     //####################################
12330     //download the client
12331     File clientDir = downloadClient();
12332 
12333     //####################################
12334     //unzip/untar the client file
12335     File unzippedClientFile = unzip(clientDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalClientDownloadTarEtc");
12336     this.untarredClientDir = untar(unzippedClientFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalClientDownloadTarEtc", 
12337         new File(this.grouperInstallDirectoryString));
12338     
12339   }
12340 
12341   /**
12342    * 
12343    */
12344   private int tomcatHttpPort = -1;
12345   
12346   /**
12347    * 
12348    */
12349   private int tomeeHttpPort = -1;
12350   
12351   
12352   /**
12353    * 
12354    */
12355   private void configureTomcat() {
12356     
12357     System.out.print("Do you want to set the tomcat memory limit (t|f)? [t]: ");
12358     boolean setTomcatMemory = readFromStdInBoolean(true, "grouperInstaller.autorun.setTomcatMemoryLimit");
12359     
12360     if (setTomcatMemory) {
12361       
12362       {
12363         File catalinaBatFile = new File(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin" + File.separator + "catalina.bat");
12364         
12365         System.out.println("Editing file: " + catalinaBatFile.getAbsolutePath());
12366         
12367         Boolean edited = editFile(catalinaBatFile, "^\\s*set\\s+\"JAVA_OPTS\\s*=.*-Xmx([0-9mMgG]+)", null, null, "512M", "max memory");
12368         if (edited == null) {
12369           addToFile(catalinaBatFile, "\nset \"JAVA_OPTS=-server -Xmx512M -XX:MaxPermSize=256M\"\n", 65, "max memory");
12370         }
12371         if (null == editFile(catalinaBatFile, "^\\s*set\\s+\"JAVA_OPTS\\s*=.*-XX:MaxPermSize=([0-9mMgG]+)", null, null, "256M", "permgen memory")) {
12372           throw new RuntimeException("Why not edit permgen in file " + catalinaBatFile);
12373         }
12374       }
12375       
12376       {
12377         File catalinaShFile = new File(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin" + File.separator + "catalina.sh");
12378         
12379         System.out.println("Editing file: " + catalinaShFile.getAbsolutePath());
12380 
12381         Boolean edited = editFile(catalinaShFile, "^\\s*JAVA_OPTS\\s*=\".*-Xmx([0-9mMgG]+)", null, null, "512M", "max memory");
12382         if (edited == null) {
12383           addToFile(catalinaShFile, "\nJAVA_OPTS=\"-server -Xmx512M -XX:MaxPermSize=256M\"\n", 65, "max memory");
12384         }
12385         if (null == editFile(catalinaShFile, "^\\s*JAVA_OPTS\\s*=\".*-XX:MaxPermSize=([0-9mMgG]+)", null, null, "256M", "permgen memory")) {
12386           throw new RuntimeException("Why not edit permgen in file " + catalinaShFile);
12387         }
12388       }
12389     }      
12390     
12391     
12392     if (!GrouperInstallerUtils.isWindows()) {
12393 
12394       System.out.print("Do you want to set tomcat scripts to executable (t|f)? [t]: ");
12395       boolean setTomcatFiles = readFromStdInBoolean(true, "grouperInstaller.autorun.setTomcatScriptsToExecutable");
12396       
12397       //GrouperInstallerUtils.toSet("catalina.sh", "startup.sh", "shutdown.sh");
12398       Set<String> shFileNames = new HashSet<String>();
12399 
12400       File binDir = new File(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin");
12401 
12402       //get all sh files, doing wildcards doesnt work
12403       for (File file : binDir.listFiles()) {
12404         String fileName = GrouperInstallerUtils.defaultString(file.getName());
12405         if (file.isFile() && fileName.endsWith(".sh")) {
12406           shFileNames.add(fileName);
12407         }
12408       }
12409 
12410       if (setTomcatFiles) {
12411       
12412         for (String command : shFileNames) {
12413           List<String> commands = new ArrayList<String>();
12414           
12415           commands.add("chmod");
12416           commands.add("+x");
12417           //have to do * since all the  sh files need chmod
12418           commands.add(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin" + File.separator + command);
12419     
12420           System.out.println("Making tomcat file executable with command: " + convertCommandsIntoCommand(commands) + "\n");
12421     
12422           CommandResult commandResult = GrouperInstallerUtils.execCommand(
12423               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
12424               new File(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin"), null, true);
12425           
12426           if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
12427             System.out.println("stderr: " + commandResult.getErrorText());
12428           }
12429           if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
12430             System.out.println("stdout: " + commandResult.getOutputText());
12431           }
12432         }
12433       }
12434       
12435       Set<File> shFiles = new LinkedHashSet<File>();
12436       for (String shFileName : shFileNames) {
12437         shFiles.add(new File(shFileName));
12438       }
12439       
12440       dos2unix(shFiles, "tomcat sh files", "OnTomcatFiles");
12441 
12442     }
12443       
12444     //see what the current ports are
12445     this.tomcatHttpPort = -1;
12446     
12447     File serverXmlFile = new File(this.untarredTomcatDir.getAbsolutePath() + File.separator + "conf" + File.separator + "server.xml");
12448     
12449     int shutdownPort = GrouperInstallerUtils.xpathEvaluateAttributeInt(serverXmlFile, "/Server", "port", -1);
12450     
12451     int originalShutdownPort = shutdownPort;
12452     
12453     //  /Server/Service/Connector <Connector port="8080" protocol="HTTP/1.1" 
12454     this.tomcatHttpPort = GrouperInstallerUtils.xpathEvaluateAttributeInt(serverXmlFile, "/Server/Service/Connector[@protocol='HTTP/1.1']", "port", -1);
12455 
12456     int originalTomcatHttpPort = this.tomcatHttpPort;
12457 
12458     // <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
12459     int jkPort = GrouperInstallerUtils.xpathEvaluateAttributeInt(serverXmlFile, "/Server/Service/Connector[@protocol='AJP/1.3']", "port", -1);
12460 
12461     int originalJkPort = jkPort;
12462     
12463     String portsCommaSeparated = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.tomcatPorts", false);
12464     if (!GrouperInstallerUtils.isBlank(portsCommaSeparated)) {
12465       
12466       String[] portsStrings = GrouperInstallerUtils.splitTrim(portsCommaSeparated, ",");
12467       
12468       if (portsStrings.length != 3) {
12469         throw new RuntimeException("Why is grouperInstaller.default.tomcatPorts from grouper.installer.properties not 3 ints comma separated? " + portsCommaSeparated);
12470       }
12471       
12472       this.tomcatHttpPort = GrouperInstallerUtils.intValue(portsStrings[0]);
12473       jkPort = GrouperInstallerUtils.intValue(portsStrings[1]);
12474       shutdownPort = GrouperInstallerUtils.intValue(portsStrings[2]);
12475       
12476     }
12477     
12478     while(true) {
12479       System.out.print("What ports do you want tomcat to run on (HTTP, JK, shutdown): [" + this.tomcatHttpPort + ", " + jkPort + ", " + shutdownPort + "]: ");
12480       
12481       String ports = readFromStdIn("grouperInstaller.autorun.tomcatPorts");
12482       
12483       if (GrouperInstallerUtils.isBlank(ports)) {
12484         if (this.tomcatHttpPort == originalTomcatHttpPort && jkPort == originalJkPort && shutdownPort == originalShutdownPort) {
12485           break;
12486         }
12487       } else {
12488         String[] portsArray = GrouperInstallerUtils.splitTrim(ports, ",");
12489         if (GrouperInstallerUtils.length(portsArray) == 3) {
12490           for (String portString : portsArray) {
12491             try {
12492               GrouperInstallerUtils.intValue(portString);
12493             } catch (Exception e) {
12494               continue;
12495             }
12496           }
12497         } else {
12498           continue;
12499         }
12500         //ok, we have three integer entries
12501         this.tomcatHttpPort = GrouperInstallerUtils.intValue(portsArray[0]);
12502         jkPort = GrouperInstallerUtils.intValue(portsArray[1]);
12503         shutdownPort = GrouperInstallerUtils.intValue(portsArray[2]);
12504       }
12505       
12506       if (!GrouperInstallerUtils.portAvailable(this.tomcatHttpPort, this.defaultIpAddress)) {
12507         System.out.print("The tomcat HTTP port is in use or unavailable: " + this.tomcatHttpPort + ", do you want to pick different ports? (t|f): ");
12508         boolean pickDifferentPorts = readFromStdInBoolean(null, "grouperInstaller.autorun.pickDifferentPortIfInUse");
12509         if (pickDifferentPorts) {
12510           continue;
12511         }
12512       }
12513       if (!GrouperInstallerUtils.portAvailable(jkPort, this.defaultIpAddress)) {
12514         System.out.print("The tomcat JK port is in use or unavailable: " + this.tomcatHttpPort + ", do you want to pick different ports? (t|f): ");
12515         boolean pickDifferentPorts = readFromStdInBoolean(null, "grouperInstaller.autorun.pickDifferentPortIfInUse");
12516         if (pickDifferentPorts) {
12517           continue;
12518         }
12519       }
12520       
12521       System.out.println("Editing tomcat config file: " + serverXmlFile.getAbsolutePath());
12522       //lets edit the file
12523       //<Connector port="8080" protocol="HTTP/1.1" 
12524       editFile(serverXmlFile, "port=\"([\\d]+)\"", new String[]{"<Connector", "protocol=\"HTTP/1.1\""}, 
12525           new String[]{"SSLEnabled=\"true\""}, Integer.toString(this.tomcatHttpPort), "tomcat HTTP port");
12526       //<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
12527       editFile(serverXmlFile, "port=\"([\\d]+)\"", new String[]{"<Connector", "protocol=\"AJP/1.3\""}, null, Integer.toString(jkPort), "tomcat JK port");
12528       //<Server port="8005" shutdown="SHUTDOWN">
12529       editFile(serverXmlFile, "port=\"([\\d]+)\"", new String[]{"<Server", "shutdown=\"SHUTDOWN\""}, null, Integer.toString(shutdownPort), "tomcat shutdown port");
12530       break;
12531     }
12532 
12533     configureTomcatUriEncoding(serverXmlFile);
12534     
12535   }
12536   
12537   /**
12538    * 
12539    */
12540   private void configureTomee() {
12541     
12542     System.out.print("Do you want to set the tomee memory limit (t|f)? [t]: ");
12543     boolean setTomeeMemory = readFromStdInBoolean(true, "grouperInstaller.autorun.setTomeeMemoryLimit");
12544     
12545     if (setTomeeMemory) {
12546       
12547       {
12548         File catalinaBatFile = new File(this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin" + File.separator + "catalina.bat");
12549         
12550         System.out.println("Editing file: " + catalinaBatFile.getAbsolutePath());
12551         
12552         Boolean edited = editFile(catalinaBatFile, "^\\s*set\\s+\"JAVA_OPTS\\s*=.*-Xmx([0-9mMgG]+)", null, null, "512M", "max memory");
12553         if (edited == null) {
12554           addToFile(catalinaBatFile, "\nset \"JAVA_OPTS=-server -Xmx512M -XX:MaxPermSize=256M\"\n", 65, "max memory");
12555         }
12556         if (null == editFile(catalinaBatFile, "^\\s*set\\s+\"JAVA_OPTS\\s*=.*-XX:MaxPermSize=([0-9mMgG]+)", null, null, "256M", "permgen memory")) {
12557           throw new RuntimeException("Why not edit permgen in file " + catalinaBatFile);
12558         }
12559       }
12560       
12561       {
12562         File catalinaShFile = new File(this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin" + File.separator + "catalina.sh");
12563         
12564         System.out.println("Editing file: " + catalinaShFile.getAbsolutePath());
12565 
12566         Boolean edited = editFile(catalinaShFile, "^\\s*JAVA_OPTS\\s*=\".*-Xmx([0-9mMgG]+)", null, null, "512M", "max memory");
12567         if (edited == null) {
12568           addToFile(catalinaShFile, "\nJAVA_OPTS=\"-server -Xmx512M -XX:MaxPermSize=256M\"\n", 65, "max memory");
12569         }
12570         if (null == editFile(catalinaShFile, "^\\s*JAVA_OPTS\\s*=\".*-XX:MaxPermSize=([0-9mMgG]+)", null, null, "256M", "permgen memory")) {
12571           throw new RuntimeException("Why not edit permgen in file " + catalinaShFile);
12572         }
12573       }
12574     }      
12575     
12576     
12577     if (!GrouperInstallerUtils.isWindows()) {
12578 
12579       System.out.print("Do you want to set tomee scripts to executable (t|f)? [t]: ");
12580       boolean setTomeeFiles = readFromStdInBoolean(true, "grouperInstaller.autorun.setTomeeScriptsToExecutable");
12581       
12582       //GrouperInstallerUtils.toSet("catalina.sh", "startup.sh", "shutdown.sh");
12583       Set<String> shFileNames = new HashSet<String>();
12584 
12585       File binDir = new File(this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin");
12586 
12587       //get all sh files, doing wildcards doesnt work
12588       for (File file : binDir.listFiles()) {
12589         String fileName = GrouperInstallerUtils.defaultString(file.getName());
12590         if (file.isFile() && fileName.endsWith(".sh")) {
12591           shFileNames.add(fileName);
12592         }
12593       }
12594 
12595       if (setTomeeFiles) {
12596       
12597         for (String command : shFileNames) {
12598           List<String> commands = new ArrayList<String>();
12599           
12600           commands.add("chmod");
12601           commands.add("+x");
12602           //have to do * since all the  sh files need chmod
12603           commands.add(this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin" + File.separator + command);
12604     
12605           System.out.println("Making tomee file executable with command: " + convertCommandsIntoCommand(commands) + "\n");
12606     
12607           CommandResult commandResult = GrouperInstallerUtils.execCommand(
12608               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
12609               new File(this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin"), null, true);
12610           
12611           if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
12612             System.out.println("stderr: " + commandResult.getErrorText());
12613           }
12614           if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
12615             System.out.println("stdout: " + commandResult.getOutputText());
12616           }
12617         }
12618       }
12619       
12620       Set<File> shFiles = new LinkedHashSet<File>();
12621       for (String shFileName : shFileNames) {
12622         shFiles.add(new File(shFileName));
12623       }
12624       
12625       dos2unix(shFiles, "tomee sh files", "OnTomeeFiles");
12626 
12627     }
12628       
12629     //see what the current ports are
12630     this.tomeeHttpPort = -1;
12631     
12632     File serverXmlFile = new File(this.untarredTomeeDir.getAbsolutePath() + File.separator + "conf" + File.separator + "server.xml");
12633     
12634     int shutdownPort = GrouperInstallerUtils.xpathEvaluateAttributeInt(serverXmlFile, "/Server", "port", -1);
12635     
12636     int originalShutdownPort = shutdownPort;
12637     
12638     //  /Server/Service/Connector <Connector port="8080" protocol="HTTP/1.1" 
12639     this.tomeeHttpPort = GrouperInstallerUtils.xpathEvaluateAttributeInt(serverXmlFile, "/Server/Service/Connector[@protocol='HTTP/1.1']", "port", -1);
12640 
12641     int originalTomeeHttpPort = this.tomeeHttpPort;
12642 
12643     // <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
12644     int jkPort = GrouperInstallerUtils.xpathEvaluateAttributeInt(serverXmlFile, "/Server/Service/Connector[@protocol='AJP/1.3']", "port", -1);
12645 
12646     int originalJkPort = jkPort;
12647     
12648     String portsCommaSeparated = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.tomeePorts", false);
12649     if (!GrouperInstallerUtils.isBlank(portsCommaSeparated)) {
12650       
12651       String[] portsStrings = GrouperInstallerUtils.splitTrim(portsCommaSeparated, ",");
12652       
12653       if (portsStrings.length != 3) {
12654         throw new RuntimeException("Why is grouperInstaller.default.tomeePorts from grouper.installer.properties not 3 ints comma separated? " + portsCommaSeparated);
12655       }
12656       
12657       this.tomeeHttpPort = GrouperInstallerUtils.intValue(portsStrings[0]);
12658       jkPort = GrouperInstallerUtils.intValue(portsStrings[1]);
12659       shutdownPort = GrouperInstallerUtils.intValue(portsStrings[2]);
12660       
12661     }
12662     
12663     while(true) {
12664       System.out.print("What ports do you want tomee to run on (HTTP, JK, shutdown): [" + this.tomeeHttpPort + ", " + jkPort + ", " + shutdownPort + "]: ");
12665       
12666       String ports = readFromStdIn("grouperInstaller.autorun.tomeePorts");
12667       
12668       if (GrouperInstallerUtils.isBlank(ports)) {
12669         if (this.tomeeHttpPort == originalTomeeHttpPort && jkPort == originalJkPort && shutdownPort == originalShutdownPort) {
12670           break;
12671         }
12672       } else {
12673         String[] portsArray = GrouperInstallerUtils.splitTrim(ports, ",");
12674         if (GrouperInstallerUtils.length(portsArray) == 3) {
12675           for (String portString : portsArray) {
12676             try {
12677               GrouperInstallerUtils.intValue(portString);
12678             } catch (Exception e) {
12679               continue;
12680             }
12681           }
12682         } else {
12683           continue;
12684         }
12685         //ok, we have three integer entries
12686         this.tomeeHttpPort = GrouperInstallerUtils.intValue(portsArray[0]);
12687         jkPort = GrouperInstallerUtils.intValue(portsArray[1]);
12688         shutdownPort = GrouperInstallerUtils.intValue(portsArray[2]);
12689       }
12690       
12691       if (!GrouperInstallerUtils.portAvailable(this.tomeeHttpPort, this.defaultIpAddress)) {
12692         System.out.print("The tomee HTTP port is in use or unavailable: " + this.tomeeHttpPort + ", do you want to pick different ports? (t|f): ");
12693         boolean pickDifferentPorts = readFromStdInBoolean(null, "grouperInstaller.autorun.pickDifferentPortIfInUse");
12694         if (pickDifferentPorts) {
12695           continue;
12696         }
12697       }
12698       if (!GrouperInstallerUtils.portAvailable(jkPort, this.defaultIpAddress)) {
12699         System.out.print("The tomee JK port is in use or unavailable: " + this.tomeeHttpPort + ", do you want to pick different ports? (t|f): ");
12700         boolean pickDifferentPorts = readFromStdInBoolean(null, "grouperInstaller.autorun.pickDifferentPortIfInUse");
12701         if (pickDifferentPorts) {
12702           continue;
12703         }
12704       }
12705       
12706       System.out.println("Editing tomee config file: " + serverXmlFile.getAbsolutePath());
12707       //lets edit the file
12708       //<Connector port="8080" protocol="HTTP/1.1" 
12709       editFile(serverXmlFile, "port=\"([\\d]+)\"", new String[]{"<Connector", "protocol=\"HTTP/1.1\""}, 
12710           new String[]{"SSLEnabled=\"true\""}, Integer.toString(this.tomcatHttpPort), "tomee HTTP port");
12711       //<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
12712       editFile(serverXmlFile, "port=\"([\\d]+)\"", new String[]{"<Connector", "protocol=\"AJP/1.3\""}, null, Integer.toString(jkPort), "tomee JK port");
12713       //<Server port="8005" shutdown="SHUTDOWN">
12714       editFile(serverXmlFile, "port=\"([\\d]+)\"", new String[]{"<Server", "shutdown=\"SHUTDOWN\""}, null, Integer.toString(shutdownPort), "tomee shutdown port");
12715       break;
12716     }
12717 
12718     configureTomcatUriEncoding(serverXmlFile);
12719     
12720   }
12721   /**
12722    * @param serverXmlFile
12723    */
12724   public void configureTomcatUriEncoding(File serverXmlFile) {
12725     //set encoding for connectors
12726     //  /Server/Service/Connector <Connector port="8080" protocol="HTTP/1.1" 
12727     String uriEncodingHttp = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
12728         "/Server/Service/Connector[@protocol='HTTP/1.1']", "URIEncoding");
12729     
12730     // <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
12731     String uriEncodingAjp = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
12732         "/Server/Service/Connector[@protocol='AJP/1.3']", "URIEncoding");
12733 
12734     if (!GrouperInstallerUtils.equals(uriEncodingAjp, "UTF-8") || !GrouperInstallerUtils.equals(uriEncodingHttp, "UTF-8")) {
12735 
12736       boolean defaultSetUriEncoding = GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.ui.setTomcatUriEncoding", true, false);
12737       System.out.print("Do you want to set URIEncoding to UTF-8 in tomcat server.xml <Connector> elements (t|f)? [" 
12738           + (defaultSetUriEncoding ? "t" : "f") + "]: ");
12739       boolean assignUriEncoding = readFromStdInBoolean(defaultSetUriEncoding, "grouperInstaller.autorun.setUriEncodingToUtf8inServerXml");
12740 
12741       if (assignUriEncoding) {
12742         
12743         if (!GrouperInstallerUtils.equals(uriEncodingAjp, "UTF-8")) {
12744           editFile(serverXmlFile, "URIEncoding=\"([^\"]+)\"", new String[]{"<Connector", "protocol=\"AJP/1.3\""}, 
12745               new String[]{"SSLEnabled=\"true\""}, "UTF-8", "tomcat URIEncoding attribute for element <Connector AJP", true, "URIEncoding");
12746           
12747         }
12748         
12749         if (!GrouperInstallerUtils.equals(uriEncodingHttp, "UTF-8")) {
12750           editFile(serverXmlFile, "URIEncoding=\"([^\"]+)\"", new String[]{"<Connector", "protocol=\"HTTP/1.1\""}, 
12751               new String[]{"SSLEnabled=\"true\""}, "UTF-8", "tomcat URIEncoding attribute for element <Connector HTTP", true, "URIEncoding");
12752           
12753         }
12754       }
12755 
12756     }
12757   }
12758 
12759   /**
12760    * 
12761    * @param newEhcacheExampleFile
12762    * @param existingEhcacheExampleFile
12763    * @param existingEhcacheFile
12764    */
12765   public static void mergeEhcacheXmlFiles(File newEhcacheExampleFile, File existingEhcacheExampleFile, File existingEhcacheFile) {
12766     
12767     try {
12768       //lets get the differences of the existing ehcache file and the existing ehcache example file
12769       DocumentBuilderFactory domFactory = GrouperInstallerUtils.xmlDocumentBuilderFactory();
12770       DocumentBuilder builder = domFactory.newDocumentBuilder();
12771       Document existingEhcacheDoc = builder.parse(existingEhcacheFile);
12772       Document existingEhcacheExampleDoc = builder.parse(existingEhcacheExampleFile);
12773 
12774       Element existingDocumentElement = existingEhcacheDoc.getDocumentElement();
12775       Element existingExampleDocumentElement = existingEhcacheExampleDoc.getDocumentElement();
12776 
12777       Map<String, String> diskStoreDifferences = null;
12778       
12779       {
12780         Element existingDiskStoreElement = (Element)existingDocumentElement.getElementsByTagName("diskStore").item(0);
12781         Element existingExampleDiskStoreElement = (Element)existingExampleDocumentElement.getElementsByTagName("diskStore").item(0);
12782         diskStoreDifferences = xmlNodeAttributeDifferences(existingExampleDiskStoreElement, existingDiskStoreElement);
12783       }
12784 
12785       Map<String, String> defaultCacheDifferences = null;
12786 
12787       {
12788         Element existingDefaultCacheElement = (Element)existingDocumentElement.getElementsByTagName("defaultCache").item(0);
12789         Element existingExampleDefaultCacheElement = (Element)existingExampleDocumentElement.getElementsByTagName("defaultCache").item(0);
12790         defaultCacheDifferences = xmlNodeAttributeDifferences(existingExampleDefaultCacheElement, existingDefaultCacheElement);
12791         
12792       }
12793 
12794       XPath xpath = XPathFactory.newInstance().newXPath();
12795       
12796       //map of cache name to the differences in the attributes of the cache
12797       Map<String, Map<String, String>> cacheDifferencesByCacheName = new LinkedHashMap<String, Map<String, String>>();
12798       
12799       {
12800         NodeList existingCacheNodeList = existingDocumentElement.getElementsByTagName("cache");
12801         
12802         //loop through all the caches
12803         for (int i=0;i<existingCacheNodeList.getLength(); i++) {
12804           
12805           Element existingCacheElement = (Element)existingCacheNodeList.item(i);
12806 
12807           String cacheName = existingCacheElement.getAttribute("name");
12808 
12809           //find the example cache
12810           XPathExpression expr = xpath.compile("cache[@name='" + cacheName + "']");
12811           Element existingExampleCacheElement = (Element)expr.evaluate(existingExampleDocumentElement, XPathConstants.NODE);
12812 
12813           //see if they differ
12814           Map<String, String> differences = xmlNodeAttributeDifferences(existingExampleCacheElement, existingCacheElement);
12815           
12816           if (differences != null) {
12817             cacheDifferencesByCacheName.put(cacheName, differences);
12818           }
12819         }
12820         
12821         //note, dont worry if there were caches in the example that werent in the configured one
12822         
12823       }      
12824       
12825       //lets see if there are any other nodes
12826       Set<Element> otherNodes = new LinkedHashSet<Element>();
12827       {
12828         NodeList nodeList = existingDocumentElement.getChildNodes();
12829         
12830         for (int i=0;i<nodeList.getLength();i++) {
12831           Node node = nodeList.item(i);
12832           if (node instanceof Element) {
12833             Element nodeElement = (Element)node;
12834             String nodeName = nodeElement.getNodeName();
12835             if (!GrouperInstallerUtils.equals(nodeName, "cache")
12836                 && !GrouperInstallerUtils.equals(nodeName, "defaultCache")
12837                 && !GrouperInstallerUtils.equals(nodeName, "diskStore")) {
12838               otherNodes.add(nodeElement);
12839             }
12840           }
12841         }
12842       }
12843       
12844       //lets copy the new example to both the example and the configured ehcache file
12845       //assume this is already backed up
12846       GrouperInstallerUtils.copyFile(newEhcacheExampleFile, existingEhcacheExampleFile, true);
12847       GrouperInstallerUtils.copyFile(newEhcacheExampleFile, existingEhcacheFile, true);
12848 
12849       //now lets do our edits
12850       if (GrouperInstallerUtils.length(diskStoreDifferences) > 0) {
12851         
12852         for (String attributeName : diskStoreDifferences.keySet()) {
12853 
12854           String attributeValue = diskStoreDifferences.get(attributeName);
12855 
12856           editXmlFileAttribute(existingEhcacheFile, "diskStore", null, attributeName, attributeValue, 
12857               "ehcache diskStore attribute '" + attributeName + "'");
12858           
12859         }
12860       }
12861       
12862       if (GrouperInstallerUtils.length(defaultCacheDifferences) > 0) {
12863         
12864         for (String attributeName : defaultCacheDifferences.keySet()) {
12865           
12866           String attributeValue = defaultCacheDifferences.get(attributeName);
12867 
12868           editXmlFileAttribute(existingEhcacheFile, "defaultCache", null, attributeName, attributeValue, 
12869               "ehcache defaultCache attribute '" + attributeName + "'");
12870 
12871         }
12872       }
12873 
12874       if (GrouperInstallerUtils.length(cacheDifferencesByCacheName) > 0) {
12875 
12876         existingEhcacheDoc = builder.parse(existingEhcacheFile);
12877         existingDocumentElement = existingEhcacheDoc.getDocumentElement();
12878         
12879         for (String cacheName : cacheDifferencesByCacheName.keySet()) {
12880 
12881           //see if the name exists
12882           //find the example cache
12883           XPathExpression expr = xpath.compile("cache[@name='" + cacheName + "']");
12884 
12885           Element existingCacheElement = (Element)expr.evaluate(existingDocumentElement, XPathConstants.NODE);
12886 
12887           Map<String, String> attributeMap = cacheDifferencesByCacheName.get(cacheName);
12888 
12889           //it exists
12890           if (existingCacheElement != null) {
12891 
12892             Map<String, String> expectedAttribute = new HashMap<String, String>();
12893 
12894             expectedAttribute.put("name", cacheName);
12895             
12896             for (String attributeName : attributeMap.keySet()) {
12897 
12898               String attributeValue = attributeMap.get(attributeName);
12899 
12900               editXmlFileAttribute(existingEhcacheFile, "cache", expectedAttribute, attributeName, attributeValue, 
12901                   "ehcache cache name=" + cacheName + " attribute '" + attributeName + "'");
12902             }
12903           } else {
12904 
12905               String fileContents = GrouperInstallerUtils.readFileIntoString(existingEhcacheFile);
12906 
12907               String newline = GrouperInstallerUtils.newlineFromFile(fileContents);
12908 
12909               int lastTagStart = fileContents.lastIndexOf("</ehcache>");
12910               
12911               if (lastTagStart == -1) {
12912                 throw new RuntimeException("Why is </ehcache> not found???? " + fileContents);
12913               }
12914 
12915               String tag = GrouperInstallerUtils.xmlElementToXml("cache", null, attributeMap);
12916 //              sdf
12917               String newFileContents = fileContents.substring(0, lastTagStart) + tag + newline 
12918                   + fileContents.substring(lastTagStart, fileContents.length());
12919 
12920               System.out.println(" - adding ehcache cache " + cacheName);
12921 
12922               GrouperInstallerUtils.saveStringIntoFile(existingEhcacheFile, newFileContents);
12923 
12924           }
12925 
12926         }
12927       }
12928 
12929       if (GrouperInstallerUtils.length(otherNodes) > 0) {
12930         String fileContents = GrouperInstallerUtils.readFileIntoString(existingEhcacheFile);
12931         
12932         String newline = GrouperInstallerUtils.newlineFromFile(fileContents);
12933 
12934         StringBuilder otherNodesStringBuilder = new StringBuilder();
12935         for (Element element : otherNodes) {
12936           String elementString = GrouperInstallerUtils.xmlToString(element);
12937           // take out the xml header: <?xml version="1.0" encoding="UTF-8"?>
12938           
12939           int elementStart = elementString.indexOf("<" + element.getNodeName());
12940           
12941           elementString = elementString.substring(elementStart);
12942           
12943           otherNodesStringBuilder.append(elementString).append(newline);
12944           System.out.println(" - adding element " + element.getTagName());
12945         }
12946 
12947         int lastTagStart = fileContents.lastIndexOf("</ehcache>");
12948         
12949         if (lastTagStart == -1) {
12950           throw new RuntimeException("Why is </ehcache> not found???? " + fileContents);
12951         }
12952 
12953         String newFileContents = fileContents.substring(0, lastTagStart) + otherNodesStringBuilder.toString()
12954             + fileContents.substring(lastTagStart, fileContents.length());
12955 
12956         GrouperInstallerUtils.saveStringIntoFile(existingEhcacheFile, newFileContents);
12957 
12958       }
12959 
12960 
12961       // test the new file, look for things
12962       existingEhcacheDoc = builder.parse(existingEhcacheFile);
12963       existingDocumentElement = existingEhcacheDoc.getDocumentElement();
12964 
12965       if (GrouperInstallerUtils.length(diskStoreDifferences) > 0) {
12966         Element existingDiskStoreElement = (Element)existingDocumentElement.getElementsByTagName("diskStore").item(0);
12967         for (String attributeName : diskStoreDifferences.keySet()) {
12968           String attributeValue = diskStoreDifferences.get(attributeName);
12969           if (!GrouperInstallerUtils.equals(attributeValue, existingDiskStoreElement.getAttribute(attributeName))) {
12970             throw new RuntimeException("Why is diskStore attribute " + attributeName + " not '" + attributeValue + "'" 
12971                 + existingEhcacheFile.getAbsolutePath());
12972           }
12973         }
12974       }
12975       
12976       if (GrouperInstallerUtils.length(defaultCacheDifferences) > 0) {
12977         Element existingDefaultCacheElement = (Element)existingDocumentElement.getElementsByTagName("defaultCache").item(0);
12978         for (String attributeName : defaultCacheDifferences.keySet()) {
12979           String attributeValue = defaultCacheDifferences.get(attributeName);
12980           if (!GrouperInstallerUtils.equals(attributeValue, existingDefaultCacheElement.getAttribute(attributeName))) {
12981             throw new RuntimeException("Why is defaultCache attribute " + attributeName + " not '" + attributeValue + "'" 
12982                 + existingEhcacheFile.getAbsolutePath());
12983           }
12984         }
12985       }
12986 
12987       if (GrouperInstallerUtils.length(cacheDifferencesByCacheName) > 0) {
12988         for (String cacheName : cacheDifferencesByCacheName.keySet()) {
12989 
12990           //see if the name exists
12991           //find the example cache
12992           XPathExpression expr = xpath.compile("cache[@name='" + cacheName + "']");
12993           Element existingCacheElement = (Element)expr.evaluate(existingDocumentElement, XPathConstants.NODE);
12994 
12995           Map<String, String> attributeMap = cacheDifferencesByCacheName.get(cacheName);
12996           
12997           for (String attributeName : attributeMap.keySet()) {
12998             
12999             String attributeValue = attributeMap.get(attributeName);
13000 
13001             if (!GrouperInstallerUtils.equals(attributeValue, existingCacheElement.getAttribute(attributeName))) {
13002               throw new RuntimeException("Why is cache " + cacheName + " attribute " + attributeName + " not '" + attributeValue + "'" 
13003                   + existingEhcacheFile.getAbsolutePath());
13004             }
13005             
13006           }
13007         }
13008       }
13009 
13010       if (GrouperInstallerUtils.length(otherNodes) > 0) {
13011         for (Element element : otherNodes) {
13012           
13013           NodeList nodeList = existingDocumentElement.getElementsByTagName(element.getNodeName());
13014           if (nodeList == null || nodeList.getLength() == 0 ) {
13015             throw new RuntimeException("Why is new element not there? " + element.getTagName() + ", "
13016                 + existingEhcacheFile.getAbsolutePath());
13017           }
13018         }
13019       }
13020 
13021     } catch (Exception e) {
13022       throw new RuntimeException(e.getMessage(), e);
13023     }
13024   }
13025 
13026   /**
13027    * 
13028    * @param newEhcacheExampleFile
13029    * @param existingEhcacheExampleFile
13030    * @param existingEhcacheFile
13031    * @return hasMerging
13032    */
13033   @SuppressWarnings("unused")
13034   private static boolean mergeEhcacheXmlFiles_XML_NOT_USED(File newEhcacheExampleFile, File existingEhcacheExampleFile, 
13035       File existingEhcacheFile) {
13036     
13037     boolean hasMerging = false;
13038     
13039     try {
13040       //lets get the differences of the existing ehcache file and the existing ehcache example file
13041       DocumentBuilderFactory domFactory = GrouperInstallerUtils.xmlDocumentBuilderFactory();
13042       DocumentBuilder builder = domFactory.newDocumentBuilder();
13043       Document existingEhcacheDoc = builder.parse(existingEhcacheFile);
13044       Document existingEhcacheExampleDoc = builder.parse(existingEhcacheExampleFile);
13045 
13046       Element existingDocumentElement = existingEhcacheDoc.getDocumentElement();
13047       Element existingExampleDocumentElement = existingEhcacheExampleDoc.getDocumentElement();
13048 
13049       Map<String, String> diskStoreDifferences = null;
13050       
13051       {
13052         Element existingDiskStoreElement = (Element)existingDocumentElement.getElementsByTagName("diskStore").item(0);
13053         Element existingExampleDiskStoreElement = (Element)existingExampleDocumentElement.getElementsByTagName("diskStore").item(0);
13054         diskStoreDifferences = xmlNodeAttributeDifferences(existingExampleDiskStoreElement, existingDiskStoreElement);
13055       }
13056 
13057       Map<String, String> defaultCacheDifferences = null;
13058 
13059       {
13060         Element existingDefaultCacheElement = (Element)existingDocumentElement.getElementsByTagName("defaultCache").item(0);
13061         Element existingExampleDefaultCacheElement = (Element)existingExampleDocumentElement.getElementsByTagName("defaultCache").item(0);
13062         defaultCacheDifferences = xmlNodeAttributeDifferences(existingExampleDefaultCacheElement, existingDefaultCacheElement);
13063         
13064       }
13065 
13066       XPath xpath = XPathFactory.newInstance().newXPath();
13067       
13068       //map of cache name to the differences in the attributes of the cache
13069       Map<String, Map<String, String>> cacheDifferencesByCacheName = new LinkedHashMap<String, Map<String, String>>();
13070       
13071       {
13072         NodeList existingCacheNodeList = existingDocumentElement.getElementsByTagName("cache");
13073         
13074         //loop through all the caches
13075         for (int i=0;i<existingCacheNodeList.getLength(); i++) {
13076           
13077           Element existingCacheElement = (Element)existingCacheNodeList.item(i);
13078 
13079           String cacheName = existingCacheElement.getAttribute("name");
13080 
13081           //find the example cache
13082           XPathExpression expr = xpath.compile("cache[@name='" + cacheName + "']");
13083           Element existingExampleCacheElement = (Element)expr.evaluate(existingExampleDocumentElement, XPathConstants.NODE);
13084 
13085           //see if they differ
13086           Map<String, String> differences = xmlNodeAttributeDifferences(existingExampleCacheElement, existingCacheElement);
13087           
13088           if (differences != null) {
13089             cacheDifferencesByCacheName.put(cacheName, differences);
13090           }
13091         }
13092         
13093         //note, dont worry if there were caches in the example that werent in the configured one
13094         
13095       }      
13096       
13097       //lets see if there are any other nodes
13098       Set<Element> otherNodes = new LinkedHashSet<Element>();
13099       {
13100         NodeList nodeList = existingDocumentElement.getChildNodes();
13101         
13102         for (int i=0;i<nodeList.getLength();i++) {
13103           Node node = nodeList.item(i);
13104           if (node instanceof Element) {
13105             Element nodeElement = (Element)node;
13106             String nodeName = nodeElement.getNodeName();
13107             if (!GrouperInstallerUtils.equals(nodeName, "cache")
13108                 && !GrouperInstallerUtils.equals(nodeName, "defaultCache")
13109                 && !GrouperInstallerUtils.equals(nodeName, "diskStore")) {
13110               otherNodes.add(nodeElement);
13111             }
13112           }
13113         }
13114       }
13115       
13116       //lets copy the new example to both the example and the configured ehcache file
13117       //assume this is already backed up
13118       GrouperInstallerUtils.copyFile(newEhcacheExampleFile, existingEhcacheExampleFile, true);
13119 
13120       //this is the new existing ehcache file
13121       existingEhcacheExampleDoc = builder.parse(existingEhcacheExampleFile);
13122       existingExampleDocumentElement = existingEhcacheExampleDoc.getDocumentElement();
13123 
13124       //now lets do our edits
13125       if (GrouperInstallerUtils.length(diskStoreDifferences) > 0) {
13126         
13127         hasMerging = true;
13128 
13129         Element existingExampleDiskStoreElement = (Element)existingExampleDocumentElement.getElementsByTagName("diskStore").item(0);
13130 
13131         for (String attributeName : diskStoreDifferences.keySet()) {
13132 
13133           String attributeValue = diskStoreDifferences.get(attributeName);
13134 
13135           existingExampleDiskStoreElement.setAttribute(attributeName, attributeValue);
13136         }
13137       }
13138       
13139       if (GrouperInstallerUtils.length(defaultCacheDifferences) > 0) {
13140 
13141         hasMerging = true;
13142 
13143         Element existingExampleDefaultCacheElement = (Element)existingExampleDocumentElement.getElementsByTagName("defaultCache").item(0);
13144         
13145         for (String attributeName : defaultCacheDifferences.keySet()) {
13146           
13147           String attributeValue = defaultCacheDifferences.get(attributeName);
13148 
13149           existingExampleDefaultCacheElement.setAttribute(attributeName, attributeValue);
13150           
13151         }
13152       }
13153 
13154       if (GrouperInstallerUtils.length(cacheDifferencesByCacheName) > 0) {
13155         hasMerging = true;
13156         for (String cacheName : cacheDifferencesByCacheName.keySet()) {
13157 
13158           //see if the name exists
13159           //find the example cache
13160           XPathExpression expr = xpath.compile("cache[@name='" + cacheName + "']");
13161           Element existingExampleCacheElement = (Element)expr.evaluate(existingExampleDocumentElement, XPathConstants.NODE);
13162 
13163           Map<String, String> attributeMap = cacheDifferencesByCacheName.get(cacheName);
13164           
13165           //it exists
13166           if (existingExampleCacheElement != null) {
13167             
13168             for (String attributeName : attributeMap.keySet()) {
13169               
13170               String attributeValue = attributeMap.get(attributeName);
13171               existingExampleCacheElement.setAttribute(attributeName, attributeValue);
13172               
13173             }
13174           } else {
13175             
13176             Element existingCacheElement = (Element)expr.evaluate(existingDocumentElement, XPathConstants.NODE);
13177             //move a cache from one document to another
13178             existingExampleDocumentElement.appendChild(existingCacheElement.cloneNode(true));
13179             
13180           }
13181           
13182         }
13183       }
13184 
13185       if (GrouperInstallerUtils.length(otherNodes) > 0) {
13186         hasMerging = true;
13187         for (Element element : otherNodes) {
13188           
13189           //move a cache from one document to another
13190           existingExampleDocumentElement.appendChild(element.cloneNode(true));
13191         }
13192       }
13193 
13194 //      System.out.println("Compare you old ehcache.xml with your new ehcache.xml file: " 
13195 //          + "\n  Old file: "
13196 //          + backedUpEhcacheFile.getAbsolutePath()
13197 //          + "\n  New file: " + existingEhcacheFile.getAbsolutePath());
13198 
13199       // save to file
13200       String xml = GrouperInstallerUtils.xmlToString(existingEhcacheExampleDoc);
13201       GrouperInstallerUtils.saveStringIntoFile(existingEhcacheFile, xml);
13202       
13203       // test the new file, look for things
13204       existingEhcacheDoc = builder.parse(existingEhcacheFile);
13205       existingDocumentElement = existingEhcacheDoc.getDocumentElement();
13206 
13207       if (GrouperInstallerUtils.length(diskStoreDifferences) > 0) {
13208         Element existingDiskStoreElement = (Element)existingDocumentElement.getElementsByTagName("diskStore").item(0);
13209         for (String attributeName : diskStoreDifferences.keySet()) {
13210           String attributeValue = diskStoreDifferences.get(attributeName);
13211           if (!GrouperInstallerUtils.equals(attributeValue, existingDiskStoreElement.getAttribute(attributeName))) {
13212             throw new RuntimeException("Why is diskStore attribute " + attributeName + " not '" + attributeValue + "'" 
13213                 + existingEhcacheFile.getAbsolutePath());
13214           }
13215         }
13216       }
13217       
13218       if (GrouperInstallerUtils.length(defaultCacheDifferences) > 0) {
13219         Element existingDefaultCacheElement = (Element)existingDocumentElement.getElementsByTagName("defaultCache").item(0);
13220         for (String attributeName : defaultCacheDifferences.keySet()) {
13221           String attributeValue = defaultCacheDifferences.get(attributeName);
13222           if (!GrouperInstallerUtils.equals(attributeValue, existingDefaultCacheElement.getAttribute(attributeName))) {
13223             throw new RuntimeException("Why is defaultCache attribute " + attributeName + " not '" + attributeValue + "'" 
13224                 + existingEhcacheFile.getAbsolutePath());
13225           }
13226         }
13227       }
13228 
13229       if (GrouperInstallerUtils.length(cacheDifferencesByCacheName) > 0) {
13230         for (String cacheName : cacheDifferencesByCacheName.keySet()) {
13231 
13232           //see if the name exists
13233           //find the example cache
13234           XPathExpression expr = xpath.compile("cache[@name='" + cacheName + "']");
13235           Element existingCacheElement = (Element)expr.evaluate(existingDocumentElement, XPathConstants.NODE);
13236 
13237           Map<String, String> attributeMap = cacheDifferencesByCacheName.get(cacheName);
13238           
13239           for (String attributeName : attributeMap.keySet()) {
13240             
13241             String attributeValue = attributeMap.get(attributeName);
13242 
13243             if (!GrouperInstallerUtils.equals(attributeValue, existingCacheElement.getAttribute(attributeName))) {
13244               throw new RuntimeException("Why is cache " + cacheName + " attribute " + attributeName + " not '" + attributeValue + "'" 
13245                   + existingEhcacheFile.getAbsolutePath());
13246             }
13247             
13248           }
13249         }
13250       }
13251 
13252       if (GrouperInstallerUtils.length(otherNodes) > 0) {
13253         for (Element element : otherNodes) {
13254           
13255           NodeList nodeList = existingDocumentElement.getElementsByTagName(element.getNodeName());
13256           if (nodeList == null || nodeList.getLength() == 0 ) {
13257             throw new RuntimeException("Why is new element not there? " + element.getTagName() 
13258                 + existingEhcacheFile.getAbsolutePath());
13259           }
13260         }
13261       }
13262 
13263     } catch (Exception e) {
13264       throw new RuntimeException(e.getMessage(), e);
13265     }
13266     return hasMerging;
13267   }
13268   /**
13269    * 
13270    * @param baseElement
13271    * @param configuredElement
13272    * @return the map of differences
13273    */
13274   public static Map<String, String> xmlNodeAttributeDifferences(Element baseElement, Element configuredElement) {
13275     NamedNodeMap configuredNamedNodeMap = configuredElement.getAttributes();
13276     
13277     Map<String, String> result = null;
13278     
13279     //see which attributes are new or changed
13280     for (int i=0;i<configuredNamedNodeMap.getLength();i++) {
13281       Node configuredAttribute = configuredNamedNodeMap.item(i);
13282       Node baseAttribute = baseElement == null ? null : baseElement.getAttributeNode(configuredAttribute.getNodeName());
13283 
13284       String configuredValue = configuredAttribute.getNodeValue();
13285       String baseValue = baseAttribute == null ? null : baseAttribute.getNodeValue();
13286       
13287       if (!GrouperInstallerUtils.equals(configuredValue, baseValue)) {
13288         if (result == null) {
13289           result = new LinkedHashMap<String, String>();
13290         }
13291         result.put(configuredAttribute.getNodeName(), configuredValue);
13292       }
13293     }
13294     
13295     //see which ones are missing
13296     NamedNodeMap baseNamedNodeMap = baseElement == null ? null : baseElement.getAttributes();
13297     
13298     //see which attributes are new or changed
13299     for (int i=0;i<(baseNamedNodeMap == null ? 0 : baseNamedNodeMap.getLength());i++) {
13300       
13301       Node baseAttribute = configuredNamedNodeMap.item(0);
13302       Node configuredAttribute = configuredElement.getAttributeNode(baseAttribute.getNodeName());
13303 
13304       String baseValue = baseAttribute.getNodeValue();
13305       String configuredValue = configuredAttribute == null ? null : configuredAttribute.getNodeValue();
13306       
13307       if (configuredValue == null && !GrouperInstallerUtils.equals(configuredValue, baseValue)) {
13308         if (result == null) {
13309           result = new LinkedHashMap<String, String>();
13310         }
13311         result.put(baseAttribute.getNodeName(), configuredValue);
13312       }
13313     }
13314     
13315     return result;
13316   }
13317   
13318   /**
13319    * 
13320    * @return the file of the directory of API
13321    */
13322   private File downloadApi() {
13323     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
13324     
13325     if (!urlToDownload.endsWith("/")) {
13326       urlToDownload += "/";
13327     }
13328     urlToDownload += "release/";
13329     String apiFileName = "grouper.apiBinary-" + this.version + ".tar.gz";
13330     urlToDownload += this.version + "/" + apiFileName;
13331 
13332     File apiFile = new File(this.grouperTarballDirectoryString + apiFileName);
13333     
13334     downloadFile(urlToDownload, apiFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalApiDownloadTarEtc");
13335 
13336     return apiFile;
13337   }
13338 
13339   /**
13340    * 
13341    * @return the file of the directory of UI
13342    */
13343   private File downloadUi() {
13344     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
13345     
13346     if (!urlToDownload.endsWith("/")) {
13347       urlToDownload += "/";
13348     }
13349     urlToDownload += "release/";
13350 
13351     String uiFileName = "grouper.ui-" + this.version + ".tar.gz";
13352     urlToDownload += this.version + "/" + uiFileName;
13353 
13354     File uiFile = new File(this.grouperTarballDirectoryString + uiFileName);
13355     
13356     downloadFile(urlToDownload, uiFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalUiDownloadTarEtc");
13357 
13358     return uiFile;
13359   }
13360   
13361   /**
13362    * 
13363    * @return the file of the directory of WS
13364    */
13365   private File downloadWs() {
13366     
13367     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
13368     
13369     //String urlToDownload = "http://localhost:8085/grouper/grouperExternal/public/assets/dojo/dijit/themes/claro/images";
13370     
13371     if (!urlToDownload.endsWith("/")) {
13372       urlToDownload += "/";
13373     }
13374     urlToDownload += "release/";
13375 
13376     String wsFileName = "grouper.ws-" + this.version + ".tar.gz";
13377     urlToDownload += this.version + "/" + wsFileName;
13378 
13379     File wsFile = new File(this.grouperTarballDirectoryString + wsFileName);
13380     
13381     System.out.println("wsFile path is "+wsFile.getAbsolutePath());
13382     downloadFile(urlToDownload, wsFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalWsDownloadTarEtc");
13383 
13384     return wsFile;
13385   }
13386 
13387   /**
13388    * 
13389    * @return the file of the directory of ant
13390    */
13391   private File downloadAnt() {
13392     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
13393     
13394     if (!urlToDownload.endsWith("/")) {
13395       urlToDownload += "/";
13396     }
13397 
13398     urlToDownload += "downloads/tools/apache-ant-1.8.2-bin.tar.gz";
13399     
13400     File antFile = new File(this.grouperTarballDirectoryString + "apache-ant-1.8.2-bin.tar.gz");
13401     
13402     downloadFile(urlToDownload, antFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
13403 
13404     return antFile;
13405   }
13406 
13407   /**
13408    * 
13409    * @return the file of the directory of maven
13410    */
13411   private File downloadMaven() {
13412     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
13413     
13414     if (!urlToDownload.endsWith("/")) {
13415       urlToDownload += "/";
13416     }
13417 
13418     urlToDownload += "downloads/tools/apache-maven-3.6.3-bin.tar.gz";
13419     
13420     File mavenFile = new File(this.grouperTarballDirectoryString + "apache-maven-3.6.3-bin.tar.gz");
13421     
13422     downloadFile(urlToDownload, mavenFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
13423 
13424     return mavenFile;
13425   }
13426 
13427   /**
13428    * tomcat version
13429    */
13430   private String tomcatVersion = "8.5.42";
13431   
13432   /**
13433    * 
13434    * @return tomcat version
13435    */
13436   private String tomcatVersion() {
13437     
13438     // this is now hardcoded
13439     if (this.tomcatVersion == null) {
13440       
13441       String defaultTomcatVersion = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.tomcat.version", false);
13442       defaultTomcatVersion = GrouperInstallerUtils.defaultIfBlank(defaultTomcatVersion, "8.5.42");
13443       
13444       System.out.print("Enter the tomcat version (8.5.42 or 8.5.12 or 6.0.35) [" + defaultTomcatVersion + "]: ");
13445       this.tomcatVersion = readFromStdIn("grouperInstaller.autorun.tomcat.version");
13446       
13447       this.tomcatVersion = GrouperInstallerUtils.defaultIfBlank(this.tomcatVersion, defaultTomcatVersion);
13448       
13449       if (!GrouperInstallerUtils.equals(this.tomcatVersion, "8.5.42") && !GrouperInstallerUtils.equals(this.tomcatVersion, "6.0.35")) {
13450         System.out.print("Warning: this *should* be 8.5.42 or 8.5.12 or 6.0.35, hit <Enter> to continue: ");
13451         readFromStdIn("grouperInstaller.autorun.tomcat.version.mismatch");
13452       }
13453       
13454     }
13455     
13456     return this.tomcatVersion;
13457 
13458   }
13459   
13460   /**
13461    * Copy a jar file to another file.  this perserves the file date
13462    * 
13463    * @param sourceFile
13464    * @param destinationFile
13465    * @param onlyIfDifferentContents true if only saving due to different contents.  Note, this is only for non-binary files!
13466    * @param ignoreWhitespace true to ignore whitespace in comparisons
13467    * @return true if contents were saved (thus different if param set)
13468    */
13469   public static boolean copyJarFileIfNotExists(File sourceFile, File destinationFile, boolean onlyIfDifferentContents, boolean ignoreWhitespace) {
13470     
13471     if (!sourceFile.isFile() || !sourceFile.exists()) {
13472       throw new RuntimeException("Why does this not exist???? " + sourceFile.getAbsolutePath());
13473     }
13474     
13475     if (destinationFile.isFile() && destinationFile.exists() && 
13476         GrouperInstallerUtils.equals(GrouperInstallerUtils.fileSha1(destinationFile), GrouperInstallerUtils.fileSha1(sourceFile))) {
13477       System.out.println("Skipping file that exists in destination: " + destinationFile.getAbsolutePath());
13478       return false;
13479     }
13480 
13481     if (onlyIfDifferentContents) {
13482       String sourceContents = GrouperInstallerUtils.readFileIntoString(sourceFile);
13483       return GrouperInstallerUtils.saveStringIntoFile(destinationFile, sourceContents, 
13484           onlyIfDifferentContents, ignoreWhitespace);
13485     }
13486     
13487     File destinationFolder = destinationFile.getParentFile();
13488     
13489     Set<String> relatedBaseNames = GrouperInstallerUtils.jarFileBaseNames(destinationFile.getName());
13490 
13491     boolean hasConflict = false;
13492     for (File destinationCandidateFile : destinationFolder.listFiles()) {
13493       if (!destinationCandidateFile.getName().endsWith(".jar")) {
13494         continue;
13495       }
13496       Set<String> relatedCandidateBaseNames = GrouperInstallerUtils.jarFileBaseNames(destinationCandidateFile.getName());
13497       if (GrouperInstallerUtils.containsAny(relatedBaseNames, relatedCandidateBaseNames)) {
13498         
13499         hasConflict = true;
13500       }
13501     }
13502     
13503     if (hasConflict) {
13504       List<File> relatedFiles = GrouperInstallerUtils.jarFindJar(destinationFolder, sourceFile.getName());
13505       
13506       if (GrouperInstallerUtils.length(relatedFiles) == 1) {
13507         File relatedFile = relatedFiles.iterator().next();
13508         File newerVersion = GrouperInstallerUtils.jarNewerVersion(relatedFile, sourceFile);
13509         if (newerVersion != null) {
13510           
13511           if (newerVersion.equals(sourceFile)) {
13512             System.out.println("There is a conflicting jar: " + sourceFile.getAbsolutePath());
13513             System.out.println("Deleting older jar: " + relatedFile.getAbsolutePath());
13514             GrouperInstallerUtils.fileDelete(relatedFile);
13515             System.out.println("Copying " + sourceFile.getAbsolutePath() + " to " + destinationFile.getAbsolutePath());
13516             GrouperInstallerUtils.copyFile(sourceFile, destinationFile);
13517             return true;
13518           }
13519           System.out.println("There is a conflicting jar for source: " + sourceFile.getAbsolutePath());
13520           System.out.println("Not copying to dest due to this jar is newer: " + relatedFile.getAbsolutePath());
13521           return false;
13522         }
13523         System.out.println("There is a conflicting jar, source jar: " + sourceFile.getAbsolutePath());
13524         System.out.println("Destination jar: " + destinationFile.getAbsolutePath());
13525         System.out.print("Unable to resolve conflict, resolve manually, press <enter> to continue... ");
13526         readFromStdIn("grouperInstaller.autorun.conflictingJarContinue");
13527         return false;
13528       }
13529 
13530     }
13531     
13532     System.out.println("Copying " + sourceFile.getAbsolutePath() + " to " + destinationFile.getAbsolutePath());
13533     GrouperInstallerUtils.copyFile(sourceFile, destinationFile);
13534     return true;
13535   }
13536 
13537   /**
13538    * 
13539    * @return the file of the directory of tomcat
13540    */
13541   private File downloadTomcat() {
13542     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
13543     
13544     if (!urlToDownload.endsWith("/")) {
13545       urlToDownload += "/";
13546     }
13547 
13548     urlToDownload += "downloads/tools/apache-tomcat-" + this.tomcatVersion() + ".tar.gz";
13549     
13550     File tomcatFile = new File(this.grouperTarballDirectoryString + "apache-tomcat-" + this.tomcatVersion() + ".tar.gz");
13551     
13552     downloadFile(urlToDownload, tomcatFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
13553 
13554     return tomcatFile;
13555   }
13556   
13557   public static final String TOMEE_VERSION = "7.0.9";
13558   
13559   /**
13560    * 
13561    * @return the file of the directory of tomee
13562    */
13563   private File downloadTomee() {
13564     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
13565     
13566     //String urlToDownload = "http://localhost:8085/grouper/grouperExternal/public/assets/dojo/dijit/themes/claro/images";
13567     
13568     if (!urlToDownload.endsWith("/")) {
13569       urlToDownload += "/";
13570     }
13571 
13572     urlToDownload += "downloads/tools/apache-tomee-" + TOMEE_VERSION + "-webprofile.tar.gz";
13573     
13574     File tomeeFile = new File(this.grouperTarballDirectoryString + "apache-tomee-" + TOMEE_VERSION + "-webprofile.tar.gz");
13575     
13576     downloadFile(urlToDownload, tomeeFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
13577 
13578     return tomeeFile;
13579   }
13580   
13581   private File downloadGrouperSourceTagFromGithub() {
13582     
13583     File grouperSourceCodeFile = new File(this.grouperTarballDirectoryString + "GROUPER_RELEASE_"+this.version+".tar.gz");
13584     downloadFile("https://github.com/Internet2/grouper/archive/GROUPER_RELEASE_"+this.version+".tar.gz", grouperSourceCodeFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
13585     return grouperSourceCodeFile;
13586   }
13587   
13588   private void deleteJarsFromLibDirs(File webInfDir) {
13589     
13590     List<File> allJarsToBeDeleted = new ArrayList<File>();
13591     
13592     List<File> libDirs = new ArrayList<File>();
13593     libDirs.add(new File(webInfDir+File.separator+"lib"));
13594     libDirs.add(new File(webInfDir+File.separator+"libUiAndDaemon"));
13595     libDirs.add(new File(webInfDir+File.separator+"libWs"));
13596     libDirs.add(new File(webInfDir+File.separator+"libScim"));
13597     
13598 //    for (File libDir: libDirs) {
13599 //      File[] filesFromLibToBeDeleted = libDir.listFiles(new FilenameFilter() {
13600 //        
13601 //        @Override
13602 //        public boolean accept(File dir, String name) {
13603 //          if (name.startsWith("slf4j-api") && name.endsWith("jar")) {
13604 //            return true;
13605 //          }
13606 //          
13607 //          if (name.startsWith("slf4j-log4j12") && name.endsWith("jar")) {
13608 //            return true;
13609 //          }
13610 //                    
13611 //          return false;
13612 //        }
13613 //      });
13614 //      allJarsToBeDeleted.addAll(Arrays.asList(filesFromLibToBeDeleted));
13615 //    }
13616 //    
13617 //    for (File jarToBeDeleted: allJarsToBeDeleted) {
13618 //      GrouperInstallerUtils.fileDelete(jarToBeDeleted);
13619 //    }
13620     
13621   }
13622   
13623   private void downloadGrouperJarsIntoLibDirectory(File webInfDir) {
13624     String basePath = "https://oss.sonatype.org/service/local/repositories/releases/content/edu/internet2/middleware/grouper/";
13625     
13626     {
13627       File libDir = new File(webInfDir+File.separator+"lib");
13628       
13629       List<String> urlsToDownload = new ArrayList<String>();
13630       urlsToDownload.add(basePath+"grouper/"+this.version+"/grouper-"+this.version+".jar");
13631       urlsToDownload.add(basePath+"grouperClient/"+this.version+"/grouperClient-"+this.version+".jar");
13632       
13633       for (String urlToDownload: urlsToDownload) {
13634         String fileName = urlToDownload.substring(urlToDownload.lastIndexOf(File.separator)+1, urlToDownload.length());
13635         downloadFile(urlToDownload, libDir.getAbsolutePath() + File.separator+ fileName, "grouperInstaller.autorun.buildContainerUseExistingJarIfExists");
13636       }
13637     }
13638     
13639     {
13640       File libUiAndDaemonDir = new File(webInfDir+File.separator+"libUiAndDaemon");
13641       List<String> urlsToDownload = new ArrayList<String>();
13642       urlsToDownload.add(basePath+"grouper-messaging-aws/"+this.version+"/grouper-messaging-aws-"+this.version+".jar");
13643       urlsToDownload.add(basePath+"google-apps-provisioner/"+this.version+"/google-apps-provisioner-"+this.version+".jar");
13644       urlsToDownload.add(basePath+"grouper-messaging-rabbitmq/"+this.version+"/grouper-messaging-rabbitmq-"+this.version+".jar");
13645       urlsToDownload.add(basePath+"grouper-messaging-activemq/"+this.version+"/grouper-messaging-activemq-"+this.version+".jar");
13646       urlsToDownload.add(basePath+"grouper-ui/"+this.version+"/grouper-ui-"+this.version+".jar");
13647       urlsToDownload.add(basePath+"grouper-pspng/"+this.version+"/grouper-pspng-"+this.version+".jar");
13648       urlsToDownload.add(basePath+"grouper-box/"+this.version+"/grouper-box-"+this.version+".jar");
13649       urlsToDownload.add(basePath+"grouper-duo/"+this.version+"/grouper-duo-"+this.version+".jar");
13650       urlsToDownload.add(basePath+"grouper-azure-provisioner/"+this.version+"/grouper-azure-provisioner-"+this.version+".jar");
13651       for (String urlToDownload: urlsToDownload) {
13652         String fileName = urlToDownload.substring(urlToDownload.lastIndexOf(File.separator)+1, urlToDownload.length());
13653         downloadFile(urlToDownload, libUiAndDaemonDir.getAbsolutePath() + File.separator+ fileName, "grouperInstaller.autorun.buildContainerUseExistingJarIfExists");
13654       }
13655     }
13656     
13657     {
13658       File libWsDir = new File(webInfDir+File.separator+"libWs");
13659       String wsUrlToDownload = basePath+"grouper-ws/"+this.version+"/grouper-ws-"+this.version+".jar";
13660       String wsJarfileName = wsUrlToDownload.substring(wsUrlToDownload.lastIndexOf(File.separator)+1, wsUrlToDownload.length());
13661       downloadFile(wsUrlToDownload, libWsDir.getAbsolutePath() + File.separator+ wsJarfileName, "grouperInstaller.autorun.buildContainerUseExistingJarIfExists");
13662     }
13663     
13664     {
13665       File libScimDir = new File(webInfDir+File.separator+"libScim");
13666       String scimUrlToDownload = basePath+"grouper-ws-scim/"+this.version+"/grouper-ws-scim-"+this.version+".jar";
13667       String scimJarfileName = scimUrlToDownload.substring(scimUrlToDownload.lastIndexOf(File.separator)+1, scimUrlToDownload.length());
13668       downloadFile(scimUrlToDownload, libScimDir.getAbsolutePath() + File.separator+ scimJarfileName, "grouperInstaller.autorun.buildContainerUseExistingJarIfExists");
13669     }
13670     
13671   }
13672 
13673   /**
13674    * add quick start subjects
13675    */
13676   private void addQuickstartSubjects() {
13677     
13678     System.out.print("Do you want to add quickstart subjects to DB (t|f)? [t]: ");
13679     boolean addQuickstartSubjects = readFromStdInBoolean(true, "grouperInstaller.autorun.addQuickstartSubjectsToDb");
13680     
13681     if (addQuickstartSubjects) {
13682 
13683       String url = GrouperInstallerUtils.propertiesValue("download.server.url", true);
13684       
13685       if (!url.endsWith("/")) {
13686         url += "/";
13687       }
13688       url += "release/" + this.version + "/subjects.sql";
13689 
13690       String subjectsSqlFileName = this.untarredApiDir.getParent() + File.separator + "subjects.sql";
13691       File subjectsSqlFile = new File(subjectsSqlFileName);
13692       downloadFile(url, subjectsSqlFileName, "grouperInstaller.autorun.useLocalApiDownloadTarEtc");
13693 
13694       List<String> commands = new ArrayList<String>();
13695       
13696       addGshCommands(commands);
13697       commands.add("-registry");
13698       commands.add("-runsqlfile");
13699       commands.add(subjectsSqlFile.getAbsolutePath());
13700       commands.add("-noprompt");
13701       
13702       System.out.println("\n##################################");
13703       System.out.println("Adding sample subjects with command: " + convertCommandsIntoCommand(commands) + "\n");
13704       
13705       CommandResult commandResult = GrouperInstallerUtils.execCommand(
13706           GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
13707           this.untarredApiDir, null, true);
13708       
13709       if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
13710         System.out.println("stderr: " + commandResult.getErrorText());
13711       }
13712       if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
13713         System.out.println("stdout: " + commandResult.getOutputText());
13714       }
13715       
13716       System.out.println("\nEnd adding sample subjects");
13717       System.out.println("##################################\n");
13718 
13719     }
13720   }
13721 
13722   /**
13723    * add quick start subjects
13724    */
13725   private void addQuickstartData() {
13726 
13727     System.out.print("Do you want to add quickstart data to registry (t|f)? [t] ");
13728     boolean addQuickstartData = readFromStdInBoolean(true, "grouperInstaller.autorun.addQuickstartData");
13729 
13730     if (addQuickstartData) {
13731       String url = GrouperInstallerUtils.propertiesValue("download.server.url", true);
13732       
13733       if (!url.endsWith("/")) {
13734         url += "/";
13735       }
13736       url += "release/" + this.version + "/quickstart.xml";
13737       String quickstartFileName = this.untarredApiDir.getParent() + File.separator + "quickstart.xml";
13738       
13739       File quickstartFile = new File(quickstartFileName);
13740       downloadFile(url, quickstartFileName, "grouperInstaller.autorun.useLocalApiDownloadTarEtc");
13741 
13742       List<String> commands = new ArrayList<String>();
13743       
13744       addGshCommands(commands);
13745       commands.add("-xmlimportold");
13746       commands.add("GrouperSystem");
13747       commands.add(quickstartFile.getAbsolutePath());
13748       commands.add("-noprompt");
13749       
13750       System.out.println("\n##################################");
13751       System.out.println("Adding quickstart data with command: " + convertCommandsIntoCommand(commands) + "\n");
13752       
13753       CommandResult commandResult = GrouperInstallerUtils.execCommand(
13754           GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
13755           this.untarredApiDir, null, true);
13756       
13757       if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
13758         System.out.println("stderr: " + commandResult.getErrorText());
13759       }
13760       if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
13761 
13762         System.out.println("stdout: " + commandResult.getOutputText());
13763       }
13764       System.out.println("\nEnd adding quickstart data");
13765       System.out.println("##################################\n");
13766 
13767     }
13768   }
13769   
13770   /**
13771    * if commands have spaces, put quotes around...
13772    * @param commands
13773    * @return the command
13774    */
13775   private static String convertCommandsIntoCommand(List<String> commands) {
13776     StringBuilder result = new StringBuilder();
13777     for (int i=0;i<GrouperInstallerUtils.length(commands); i++) {
13778       String command = GrouperInstallerUtils.defaultString(commands.get(i));
13779       
13780       //if there is a space, put quotes around command
13781       if (command.contains(" ")) {
13782         result.append("\"").append(command).append("\"");
13783       } else {
13784         result.append(command);
13785       }
13786       if (i != GrouperInstallerUtils.length(commands)-1) {
13787         result.append(" ");
13788       }
13789     }
13790     return result.toString();
13791   }
13792   
13793   /**
13794    * 
13795    */
13796   private void initDb() {
13797     System.out.print("Do you want to init the database (delete all existing grouper tables, add new ones) (t|f)? ");
13798     boolean initdb = readFromStdInBoolean(null, "grouperInstaller.autorun.deleteAndInitDatabase");
13799     
13800     if (initdb) {
13801       List<String> commands = new ArrayList<String>();
13802       
13803       addGshCommands(commands);
13804       commands.add("-registry");
13805       commands.add("-drop");
13806       commands.add("-runscript");
13807       commands.add("-noprompt");
13808       
13809       System.out.println("\n##################################");
13810       System.out.println("Initting DB with command: " + convertCommandsIntoCommand(commands) + "\n");
13811       
13812       CommandResult commandResult = GrouperInstallerUtils.execCommand(
13813           GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
13814           this.untarredApiDir, null, true);
13815       
13816       if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
13817         System.out.println("stderr: " + commandResult.getErrorText());
13818       }
13819       if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
13820 
13821         System.out.println("stdout: " + commandResult.getOutputText());
13822       }
13823       System.out.println("\nEnd Initting DB");
13824       System.out.println("##################################\n");
13825       
13826       
13827     }
13828 
13829   }
13830   
13831   /**
13832    * @param prompt
13833    */
13834   private void startLoader(boolean prompt) {
13835     
13836     boolean startLoader = true;
13837     
13838     if (prompt) {
13839       System.out.print("Do you want to start the Grouper loader (daemons)?\n  (note, if it is already running, you need to stop it now, check " 
13840           + (GrouperInstallerUtils.isWindows() ? "the task manager for java.exe" : "ps -ef | grep gsh | grep loader") + ") (t|f)? [f]: ");
13841       startLoader = readFromStdInBoolean(false, "grouperInstaller.autorun.startGrouperDaemons");
13842     }
13843     
13844     if (startLoader) {
13845       final List<String> commands = new ArrayList<String>();
13846       
13847       addGshCommands(commands);
13848       commands.add("-loader");
13849       
13850       if (!GrouperInstallerUtils.isWindows()) {
13851         
13852         //let this database run forever
13853         commands.add(0, "nohup");
13854         //run in new process
13855         commands.add("> /dev/null 2>&1 &");
13856         
13857         String fullCommand = GrouperInstallerUtils.join(commands.iterator(), ' ');
13858         commands.clear();
13859         commands.add(shCommand());
13860         commands.add("-c");
13861         commands.add(fullCommand);
13862         
13863       }
13864       System.out.println("\n##################################");
13865       System.out.println("Starting the Grouper loader (daemons): " + convertCommandsIntoCommand(commands) + "\n");
13866 
13867       //start in new thread
13868       Thread thread = new Thread(new Runnable() {
13869         
13870         @Override
13871         public void run() {
13872           GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
13873               true, true, null, GrouperInstaller.this.untarredApiDir, 
13874               GrouperInstaller.this.grouperInstallDirectoryString + "grouperLoader", false);
13875         }
13876       });
13877       thread.setDaemon(true);
13878       thread.start();
13879       
13880       System.out.println("\nEnd starting the Grouper loader (daemons)");
13881       System.out.println("##################################\n");
13882       
13883     }
13884 
13885   }
13886 
13887   /**
13888    * gsh command fully qualified
13889    */
13890   private String gshCommand;
13891   
13892   /**
13893    * 
13894    * @return the gsh command
13895    */
13896   private String gshCommand() {
13897 
13898     if (this.gshCommand == null) {
13899 
13900       String gshDir = GrouperInstallerUtils.defaultIfBlank(this.upgradeExistingApplicationDirectoryString, 
13901           this.untarredApiDir.getAbsolutePath() + File.separator);
13902       
13903       String gsh = gshDir + "bin" + File.separator 
13904           + (GrouperInstallerUtils.isWindows() ? "gsh.bat" : "gsh.sh");
13905       
13906       if (new File(gsh).exists()) {
13907         this.gshCommand = gsh;
13908         return gsh;
13909       }
13910 
13911       gsh = gshDir + "WEB-INF" + File.separator + "bin" + File.separator 
13912           + (GrouperInstallerUtils.isWindows() ? "gsh.bat" : "gsh.sh");
13913 
13914       if (new File(gsh).exists()) {
13915         this.gshCommand = gsh;
13916         return gsh;
13917       }
13918       
13919       throw new RuntimeException("Cant find gsh: " + gshDir);
13920     }
13921 
13922     return this.gshCommand;
13923   }
13924 
13925   /**
13926    * 
13927    */
13928   private void checkDatabaseConnection() {
13929     System.out.println("Checking database with query: " + this.giDbUtils.checkConnectionQuery());
13930     Exception exception = this.giDbUtils.checkConnection();
13931     if (exception == null) {
13932       System.out.println("Successfully tested database connection");
13933     } else {
13934       if (GrouperInstallerUtils.getFullStackTrace(exception).contains(ClassNotFoundException.class.getName())) {
13935         System.out.println("Cannot check connection since driver is not in classpath of installer, this is fine but not sure if connection details work or not");
13936       } else {
13937         System.out.println("Error: could not connect to the database: ");
13938         exception.printStackTrace();
13939       }
13940     }
13941   }
13942 
13943   /** gi db utils */
13944   private GiDbUtils giDbUtils = null;
13945     
13946   /**
13947    * 
13948    */
13949   private void configureTomeeGrouperWsScimWebapp() {
13950     
13951     File serverXmlFile = new File(this.untarredTomeeDir.getAbsolutePath() 
13952         + File.separator + "conf" + File.separator + "server.xml");
13953     
13954     //C:\mchyzer\grouper\trunk\grouper-installer\grouper.ui-2.0.2\dist\grouper
13955     //
13956     //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
13957     //Server
13958     //Service
13959     //Engine
13960     //Host
13961 
13962     System.out.print("Enter the URL path for the Grouper WS Scim [grouper-ws-scim]: ");
13963     this.tomeeWsScimPath = readFromStdIn("grouperInstaller.autorun.urlPathForGropuerWsScim");
13964     
13965     if (GrouperInstallerUtils.isBlank(this.tomeeWsScimPath)) {
13966       this.tomeeWsScimPath = "grouper-ws-scim";
13967     }
13968 
13969     if (this.tomeeWsScimPath.endsWith("/") || this.tomeeWsScimPath.endsWith("\\")) {
13970       this.tomeeWsScimPath = this.tomeeWsScimPath.substring(0, this.tomeeWsScimPath.length()-1);
13971     }
13972     if (this.tomeeWsScimPath.startsWith("/") || this.tomeeWsScimPath.startsWith("\\")) {
13973       this.tomeeWsScimPath = this.tomeeWsScimPath.substring(1, this.tomeeWsScimPath.length());
13974     }
13975     
13976     String currentDocBase = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
13977         "Server/Service/Engine/Host/Context[@path='/" + this.tomeeWsScimPath + "']", "docBase");
13978 
13979     String shouldBeDocBase = this.untarredWsDir.getAbsolutePath() + File.separator + "grouper-ws-scim" + File.separator + "targetBuiltin" + File.separator + "grouper-ws-scim";
13980 
13981     System.out.println("Editing tomee config file: " + serverXmlFile.getAbsolutePath());
13982     
13983     if (GrouperInstallerUtils.isBlank(currentDocBase)) {
13984 
13985       //need to add it
13986       //<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">
13987       //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
13988       addToXmlFile(serverXmlFile, ">", new String[]{"<Host "}, 
13989           "<Context docBase=\"" + shouldBeDocBase + "\" path=\"/" + this.tomeeWsScimPath + "\" reloadable=\"false\"/>", "tomee context for Grouper WS Scim");
13990 
13991     } else {
13992 
13993       if (!GrouperInstallerUtils.equals(currentDocBase, shouldBeDocBase)) {
13994         
13995         //lets edit the file
13996         //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
13997         editFile(serverXmlFile, "docBase=\"([^\"]+)\"", new String[]{"<Context", "path=\"/" + this.tomeeWsScimPath + "\""}, 
13998             null, shouldBeDocBase, "tomee context for Grouper WS Scim");
13999 
14000       } else {
14001         
14002         System.out.println("  - Context is already set for Grouper WS Scim");
14003         
14004       }
14005       
14006       
14007     }
14008     
14009     currentDocBase = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
14010         "Server/Service/Engine/Host/Context[@path='/" + this.tomeeWsScimPath + "']", "docBase");
14011     
14012     if (!GrouperInstallerUtils.equals(currentDocBase, shouldBeDocBase)) {
14013       System.out.println("Tried to edit server.xml but it didnt work, should have context of: '" 
14014           + shouldBeDocBase + "', but was: '" + currentDocBase + "'");
14015     }
14016     
14017     File[] allFiles = new File(this.untarredApiDir + File.separator + "conf").listFiles(new FilenameFilter() {
14018       
14019       @Override
14020       public boolean accept(File file, String name) {
14021         return name.endsWith(".properties") || name.endsWith(".xml") || name.endsWith(".txt");
14022       }
14023     });
14024     
14025     
14026     for (File fileToCopyFrom : allFiles) {
14027       if (fileToCopyFrom.isFile()) {
14028         File destFile = new File(shouldBeDocBase + File.separator + "WEB-INF" + File.separator + "classes" + File.separator + fileToCopyFrom.getName());
14029         if (!destFile.exists()) {
14030           GrouperInstallerUtils.fileCreate(destFile);
14031         }
14032         GrouperInstallerUtils.copyFile(fileToCopyFrom, destFile, false);
14033       }
14034     }
14035     
14036   }
14037   
14038   /**
14039    * 
14040    */
14041   private void configureTomeeGrouperUberWebapp(File tommeDir, File webAppDir) {
14042     
14043     //GrouperInstallerUtils.toSet("catalina.sh", "startup.sh", "shutdown.sh");
14044     Set<String> shFileNames = new HashSet<String>();
14045 
14046     File binDir = new File(tommeDir.getAbsolutePath() + File.separator + "bin");
14047 
14048     //get all sh files, doing wildcards doesnt work
14049     for (File file : binDir.listFiles()) {
14050       String fileName = GrouperInstallerUtils.defaultString(file.getName());
14051       if (file.isFile() && fileName.endsWith(".sh")) {
14052         shFileNames.add(fileName);
14053       }
14054     }
14055 
14056     for (String command : shFileNames) {
14057       List<String> commands = new ArrayList<String>();
14058       
14059       commands.add("chmod");
14060       commands.add("+x");
14061       //have to do * since all the  sh files need chmod
14062       commands.add(tommeDir.getAbsolutePath() + File.separator + "bin" + File.separator + command);
14063 
14064       System.out.println("Making tomee file executable with command: " + convertCommandsIntoCommand(commands) + "\n");
14065 
14066       CommandResult commandResult = GrouperInstallerUtils.execCommand(
14067           GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
14068           new File(tommeDir.getAbsolutePath() + File.separator + "bin"), null, true);
14069       
14070       if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
14071         System.out.println("stderr: " + commandResult.getErrorText());
14072       }
14073       if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
14074         System.out.println("stdout: " + commandResult.getOutputText());
14075       }
14076     }
14077     
14078     Set<File> shFiles = new LinkedHashSet<File>();
14079     for (String shFileName : shFileNames) {
14080       shFiles.add(new File(shFileName));
14081     }
14082 // do this in container since its too dynamic
14083 //    for (String path : new String[] {"grouper", "grouper-ws", "grouper-ws-scim"}) {
14084 //      // create grouper.xml in conf/Catalina/localhost/grouper.xml
14085 //      File tomeeGrouperFile = new File(tommeDir.getAbsolutePath() + File.separator + "conf" + File.separator +
14086 //          "Catalina" + File.separator + "localhost" + File.separator + path + ".xml");
14087 //      
14088 //      GrouperInstallerUtils.createParentDirectories(tomeeGrouperFile);
14089 //      GrouperInstallerUtils.fileCreate(tomeeGrouperFile);
14090 //      String cookiesFalse = "";
14091 //      if ("grouper-ws".equals(path) || "grouper-ws-scim".equals(path)) {
14092 //        cookiesFalse = " cookies=\"false\" ";
14093 //      }
14094 //      String contentToWrite = "<Context docBase=\"/opt/grouper/grouperWebapp/\" path=\"/" + path + "\" reloadable=\"false\"" 
14095 //            + cookiesFalse + ">\n" + 
14096 //          "<Resources allowLinking=\"true\" />\n" + 
14097 //          "</Context>";
14098 //    
14099 //      try {      
14100 //        Files.write(Paths.get(tomeeGrouperFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
14101 //      } catch (Exception e) {
14102 //        System.out.println("Could not write to grouper.xml file.");
14103 //      }
14104 //    }
14105     
14106 //    // edit server.xml in tomee/conf dir
14107 //    File serverXmlFile = new File(tommeDir.getAbsolutePath()
14108 //        + File.separator + "conf" + File.separator + "server.xml");
14109 //    
14110 //    Map<String, String> expectedAttribute = new HashMap<String, String>();
14111 //
14112 //    expectedAttribute.put("port", "8009");
14113 //    expectedAttribute.put("protocol", "AJP/1.3");
14114 //
14115 //    editXmlFileAttribute(serverXmlFile, "Connector", expectedAttribute, "tomcatAuthentication", "false", 
14116 //         "Set tomcatAuthentication to false");
14117 //    editXmlFileAttribute(serverXmlFile, "Connector", expectedAttribute, "URIEncoding", "UTF-8", 
14118 //        "Set URIEncoding to UTF-8");
14119 //    editXmlFileAttribute(serverXmlFile, "Connector", expectedAttribute, "scheme", "https", 
14120 //        "Set scheme to https");
14121 //    editXmlFileAttribute(serverXmlFile, "Connector", expectedAttribute, "secure", "true", 
14122 //        "Set secure to true");
14123     
14124   }
14125   
14126 
14127   /**
14128    * 
14129    */
14130   private void configureTomcatUiWebapp() {
14131     
14132     File serverXmlFile = new File(this.untarredTomcatDir.getAbsolutePath() 
14133         + File.separator + "conf" + File.separator + "server.xml");
14134     
14135     //C:\mchyzer\grouper\trunk\grouper-installer\grouper.ui-2.0.2\dist\grouper
14136     //
14137     //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
14138     //Server
14139     //Service
14140     //Engine
14141     //Host
14142 
14143     System.out.print("Enter the URL path for the UI [grouper]: ");
14144     this.tomcatUiPath = readFromStdIn("grouperInstaller.autorun.urlPathForUi");
14145     
14146     if (GrouperInstallerUtils.isBlank(this.tomcatUiPath)) {
14147       this.tomcatUiPath = "grouper";
14148     }
14149 
14150     if (this.tomcatUiPath.endsWith("/") || this.tomcatUiPath.endsWith("\\")) {
14151       this.tomcatUiPath = this.tomcatUiPath.substring(0, this.tomcatUiPath.length()-1);
14152     }
14153     if (this.tomcatUiPath.startsWith("/") || this.tomcatUiPath.startsWith("\\")) {
14154       this.tomcatUiPath = this.tomcatUiPath.substring(1, this.tomcatUiPath.length());
14155     }
14156     
14157     String currentDocBase = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
14158         "Server/Service/Engine/Host/Context[@path='/" + this.tomcatUiPath + "']", "docBase");
14159 
14160     String shouldBeDocBase = grouperUiBuildToDirName();
14161 
14162     System.out.println("Editing tomcat config file: " + serverXmlFile.getAbsolutePath());
14163     
14164     if (GrouperInstallerUtils.isBlank(currentDocBase)) {
14165 
14166       //need to add it
14167       //<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">
14168       //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
14169       addToXmlFile(serverXmlFile, ">", new String[]{"<Host "}, 
14170           "<Context docBase=\"" + shouldBeDocBase + "\" path=\"/" + this.tomcatUiPath + "\" reloadable=\"false\"/>", "tomcat context for UI");
14171 
14172     } else {
14173 
14174       if (!GrouperInstallerUtils.equals(currentDocBase, shouldBeDocBase)) {
14175         
14176         //lets edit the file
14177         //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
14178         editFile(serverXmlFile, "docBase=\"([^\"]+)\"", new String[]{"<Context", "path=\"/" + this.tomcatUiPath + "\""}, 
14179             null, shouldBeDocBase, "tomcat context for UI");
14180 
14181       } else {
14182         
14183         System.out.println("  - Context is already set for Grouper UI");
14184         
14185       }
14186       
14187       
14188     }
14189     
14190     currentDocBase = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
14191         "Server/Service/Engine/Host/Context[@path='/" + this.tomcatUiPath + "']", "docBase");
14192     
14193     if (!GrouperInstallerUtils.equals(currentDocBase, shouldBeDocBase)) {
14194       System.out.println("Tried to edit server.xml but it didnt work, should have context of: '" 
14195           + shouldBeDocBase + "', but was: '" + currentDocBase + "'");
14196     }
14197     
14198   }
14199 
14200   /**
14201    * 
14202    * @return grouper ui build to name
14203    */
14204   private String grouperUiBuildToDirName() {
14205     return this.untarredUiDir.getAbsolutePath() + File.separator + "dist" + File.separator + "grouper";
14206   }
14207 
14208   /**
14209    * @param isInstallNotUpgrade
14210    */
14211   private void buildWs(boolean isInstallNotUpgrade) {
14212     
14213     File grouperWsBuildToDir = new File(this.grouperWsBuildToDirName());
14214     
14215     if (grouperWsBuildToDir.exists()) {
14216 
14217       boolean rebuildWs = true;
14218       
14219       boolean defaultRebuild = GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.ws.rebuildIfBuilt", true, false);
14220       System.out.print("The Grouper WS has been built in the past, do you want it rebuilt? (t|f) [" 
14221           + (defaultRebuild ? "t" : "f") + "]: ");
14222       rebuildWs = readFromStdInBoolean(defaultRebuild, "grouperInstaller.autorun.rebuildWsIfBuiltAlready");
14223   
14224       if (!rebuildWs) {
14225         return;
14226       }
14227     }
14228     
14229     if (isInstallNotUpgrade) {
14230       //stop tomcat
14231       try {
14232         tomcatBounce("stop");
14233       } catch (Throwable e) {
14234         System.out.println("Couldnt stop tomcat, ignoring...");
14235       }
14236     }
14237     
14238     List<String> commands = new ArrayList<String>();
14239     
14240     addAntCommands(commands);
14241     commands.add("dist");
14242     
14243     System.out.println("\n##################################");
14244     System.out.println("Building WS with command:\n" + this.untarredWsDir.getAbsolutePath() + File.separator + "grouper-ws" + "> " 
14245         + convertCommandsIntoCommand(commands) + "\n");
14246     
14247     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
14248         true, true, null, new File(this.untarredWsDir.getAbsolutePath() + File.separator + "grouper-ws"), null, true);
14249     
14250     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
14251       System.out.println("stderr: " + commandResult.getErrorText());
14252     }
14253     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
14254       System.out.println("stdout: " + commandResult.getOutputText());
14255     }
14256 
14257     if (isInstallNotUpgrade) {
14258       System.out.print("Do you want to set the log dir of WS (t|f)? [t]: ");
14259       boolean setLogDir = readFromStdInBoolean(true, "grouperInstaller.autorun.setWsLogDir");
14260       
14261       if (setLogDir) {
14262         
14263         ////set the log dir
14264         //C:\apps\grouperInstallerTest\grouper.ws-2.0.2\grouper-ws\build\dist\grouper-ws\WEB-INF\classes\log4j.properties
14265         //
14266         //${grouper.home}logs
14267   
14268         String defaultLogDir = this.untarredTomcatDir.getAbsolutePath() + File.separator + "logs" + File.separator + "grouperWs";
14269         System.out.print("Enter the WS log dir: [" + defaultLogDir + "]: ");
14270         String logDir = readFromStdIn("grouperInstaller.autorun.wsLogDir");
14271         logDir = GrouperInstallerUtils.defaultIfBlank(logDir, defaultLogDir);
14272         
14273         //lets replace \\ with /
14274         logDir = GrouperInstallerUtils.replace(logDir, "\\\\", "/");
14275         //lets replace \ with /
14276         logDir = GrouperInstallerUtils.replace(logDir, "\\", "/");
14277   
14278         File log4jFile = new File(grouperWsBuildToDirName() + File.separator + "WEB-INF" + File.separator + "classes"
14279             + File.separator + "log4j.properties");
14280         
14281         System.out.println("Editing file: " + log4jFile.getAbsolutePath());
14282         
14283         editFile(log4jFile, "log4j\\.\\S+\\.File\\s*=\\s*([^\\s]+logs)/grouper_[^\\s]+\\.log", null, 
14284             null, logDir, "WS log directory");
14285         
14286         File logDirFile = new File(defaultLogDir);
14287         if (!logDirFile.exists()) {
14288           System.out.println("Creating log directory: " + logDirFile.getAbsolutePath());
14289           GrouperInstallerUtils.mkdirs(logDirFile);
14290         }
14291         //test log dir
14292         File testLogDirFile = new File(logDirFile.getAbsolutePath() + File.separator + "testFile" + GrouperInstallerUtils.uniqueId() + ".txt");
14293         GrouperInstallerUtils.saveStringIntoFile(testLogDirFile, "test");
14294         if (!testLogDirFile.delete()) {
14295           throw new RuntimeException("Cant delete file: " +  testLogDirFile.getAbsolutePath());
14296         }
14297         System.out.println("Created and deleted a test file successfully in dir: " + logDirFile.getAbsolutePath());
14298       }
14299     }
14300     
14301     System.out.println("\nEnd building Ws");
14302     System.out.println("##################################\n");
14303   
14304     
14305   }
14306 
14307   /**
14308    * 
14309    */
14310   private void configureTomcatWsWebapp() {
14311     
14312     File serverXmlFile = new File(this.untarredTomcatDir.getAbsolutePath() 
14313         + File.separator + "conf" + File.separator + "server.xml");
14314     
14315     //C:\mchyzer\grouper\trunk\grouper-installer\grouper.ui-2.0.2\dist\grouper
14316     //
14317     //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
14318     //Server
14319     //Service
14320     //Engine
14321     //Host
14322   
14323     System.out.print("Enter the URL path for the WS [grouper-ws]: ");
14324     this.tomcatWsPath = readFromStdIn("grouperInstaller.autorun.wsUrlPath");
14325     
14326     if (GrouperInstallerUtils.isBlank(this.tomcatWsPath)) {
14327       this.tomcatWsPath = "grouper-ws";
14328     }
14329   
14330     if (this.tomcatWsPath.endsWith("/") || this.tomcatWsPath.endsWith("\\")) {
14331       this.tomcatWsPath = this.tomcatWsPath.substring(0, this.tomcatWsPath.length()-1);
14332     }
14333     if (this.tomcatWsPath.startsWith("/") || this.tomcatWsPath.startsWith("\\")) {
14334       this.tomcatWsPath = this.tomcatWsPath.substring(1);
14335     }
14336     
14337     String currentDocBase = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
14338         "Server/Service/Engine/Host/Context[@path='/" + this.tomcatWsPath + "']", "docBase");
14339   
14340     //grouper.ws-2.0.2\grouper-ws\build\dist\grouper-ws
14341     
14342     String shouldBeDocBase = grouperWsBuildToDirName();
14343   
14344     System.out.println("Editing tomcat config file: " + serverXmlFile.getAbsolutePath());
14345     
14346     if (GrouperInstallerUtils.isBlank(currentDocBase)) {
14347   
14348       //need to add it
14349       //<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">
14350       //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
14351       addToXmlFile(serverXmlFile, ">", new String[]{"<Host "}, 
14352           "<Context docBase=\"" + shouldBeDocBase + "\" path=\"/" + this.tomcatWsPath + "\" reloadable=\"false\"/>", "tomcat context for WS");
14353   
14354     } else {
14355   
14356       if (!GrouperInstallerUtils.equals(currentDocBase, shouldBeDocBase)) {
14357         
14358         //lets edit the file
14359         //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
14360         editFile(serverXmlFile, "docBase=\"([^\"]+)\"", new String[]{"<Context", "path=\"/" + this.tomcatWsPath + "\""}, 
14361             null, shouldBeDocBase, "tomcat context for WS");
14362   
14363       } else {
14364         
14365         System.out.println("  - Context is already set for Grouper WS");
14366         
14367       }
14368       
14369       
14370     }
14371     
14372     currentDocBase = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
14373         "Server/Service/Engine/Host/Context[@path='/" + this.tomcatWsPath + "']", "docBase");
14374     
14375     if (!GrouperInstallerUtils.equals(currentDocBase, shouldBeDocBase)) {
14376       System.out.println("Tried to edit server.xml but it didnt work, should have context of: '" 
14377           + shouldBeDocBase + "', but was: '" + currentDocBase + "'");
14378     }
14379     
14380   }
14381 
14382   /**
14383    * @return grouper ws build to dir name
14384    */
14385   private String grouperWsBuildToDirName() {
14386     return this.untarredWsDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator 
14387       + "build" + File.separator + "dist" + File.separator + "grouper-ws";
14388   }
14389   
14390   /**
14391      * 
14392      */
14393     private void configureClient() {
14394       //properties file
14395       File localGrouperClientPropertiesFile = new File(this.untarredClientDir.getAbsolutePath() + File.separator 
14396           + "grouper.client.properties");
14397       
14398       //set the grouper property
14399       System.out.println("Editing " + localGrouperClientPropertiesFile.getAbsolutePath() + ": ");
14400       editPropertiesFile(localGrouperClientPropertiesFile, "grouperClient.webService.url", "http://localhost:" 
14401           + this.tomcatHttpPort + "/" + this.tomcatWsPath + "/servicesRest", false);
14402       editPropertiesFile(localGrouperClientPropertiesFile, "grouperClient.webService.login", "GrouperSystem", false);
14403       editPropertiesFile(localGrouperClientPropertiesFile, "grouperClient.webService.password", this.grouperSystemPassword, false);
14404       
14405       
14406 //      grouperClient.webService.url = http://localhost:8200/grouper-ws/servicesRest
14407 //
14408 //      # kerberos principal used to connect to web service
14409 //      grouperClient.webService.login = GrouperSystem
14410 //
14411 //      # password for shared secret authentication to web service
14412 //      # or you can put a filename with an encrypted password
14413 //      grouperClient.webService.password = myNewPass
14414 
14415       
14416     }
14417 
14418   /**
14419    * 
14420    * @return the file of the directory of WS
14421    */
14422   private File downloadClient() {
14423     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
14424     
14425     if (!urlToDownload.endsWith("/")) {
14426       urlToDownload += "/";
14427     }
14428     urlToDownload += "release/";
14429   
14430     String clientFileName = "grouper.clientBinary-" + this.version + ".tar.gz";
14431     urlToDownload += this.version + "/" + clientFileName;
14432   
14433     File clientFile = new File(this.grouperTarballDirectoryString + clientFileName);
14434     
14435     downloadFile(urlToDownload, clientFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalClientDownloadTarEtc");
14436 
14437     return clientFile;
14438   }
14439 
14440   /**
14441    * 
14442    */
14443   private void addGrouperSystemWsGroup() {
14444 
14445     //C:\mchyzer\grouper\trunk\grouper-installer\grouper.apiBinary-2.0.2\bin>gsh -runarg "grouperSession = GrouperSession.startRootSession();\nwsGroup = new GroupSave(grouperSession).assignName(\"etc:webServiceClientUsers\").assignCreateParentStemsIfNotExist(true).save();\nwsGroup.addMember(SubjectFinder.findRootSubject(), false);"
14446 
14447     //running with command on command line doenst work on linux since the args with whitespace translate to 
14448     //save the commands to a file, and runt he file
14449     StringBuilder gshCommands = new StringBuilder();
14450     gshCommands.append("grouperSession = GrouperSession.startRootSession();\n");
14451     gshCommands.append("wsGroup = new GroupSave(grouperSession).assignName(\"etc:webServiceClientUsers\").assignCreateParentStemsIfNotExist(true).save();\n");
14452     gshCommands.append("wsGroup.addMember(SubjectFinder.findRootSubject(), false);\n");
14453     
14454     File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "gshAddGrouperSystemWsGroup.gsh");
14455     GrouperInstallerUtils.saveStringIntoFile(gshFile, gshCommands.toString());
14456     
14457     List<String> commands = new ArrayList<String>();
14458 
14459     addGshCommands(commands);
14460     commands.add(gshFile.getAbsolutePath());
14461 
14462     System.out.println("\n##################################");
14463     System.out.println("Adding user GrouperSystem to grouper-ws users group with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
14464 
14465     CommandResult commandResult = GrouperInstallerUtils.execCommand(
14466         GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
14467         this.untarredApiDir, null, true);
14468 
14469     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
14470       System.out.println("stderr: " + commandResult.getErrorText());
14471     }
14472     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
14473       System.out.println("stdout: " + commandResult.getOutputText());
14474     }
14475 
14476 
14477   }
14478 
14479   /**
14480    * 
14481    */
14482   private void runChangeLogTempToChangeLog() {
14483 
14484     boolean defaultBoolean = GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.api.runChangeLogToChangeLogTemp", true, false);
14485     System.out.print("Is it ok to run a script that copies change log temp records to the change log (recommended) (t|f)? [" 
14486         + (defaultBoolean ? "t" : "f") + "]: ");
14487     boolean runScript = readFromStdInBoolean(defaultBoolean, "grouperInstaller.autorun.runChangeLogTempToChangeLog");
14488 
14489     
14490     if (!runScript) {
14491       return;
14492     }
14493     
14494     //running with command on command line doenst work on linux since the args with whitespace translate to 
14495     //save the commands to a file, and runt he file
14496     StringBuilder gshCommands = new StringBuilder();
14497     gshCommands.append("grouperSession = GrouperSession.startRootSession();\n");
14498     gshCommands.append("loaderRunOneJob(\"CHANGE_LOG_changeLogTempToChangeLog\");\n");
14499     
14500     File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "gshChangeLogTempToChangeLog.gsh");
14501     GrouperInstallerUtils.saveStringIntoFile(gshFile, gshCommands.toString());
14502     
14503     List<String> commands = new ArrayList<String>();
14504 
14505     addGshCommands(commands);
14506     commands.add(gshFile.getAbsolutePath());
14507 
14508     System.out.println("\n##################################");
14509     System.out.println("Copying records from change log temp to change log with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
14510 
14511     CommandResult commandResult = GrouperInstallerUtils.execCommand(
14512         GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
14513        new File(this.gshCommand()).getParentFile(), null, true);
14514 
14515     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
14516       System.out.println("stderr: " + commandResult.getErrorText());
14517     }
14518     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
14519       System.out.println("stdout: " + commandResult.getOutputText());
14520     }
14521 
14522 
14523   }
14524 
14525   /**
14526    * 
14527    */
14528   private void runClientCommand() {
14529     System.out.println("##################################");
14530     System.out.println("Running client command:");
14531     System.out.println(this.untarredClientDir.getAbsolutePath() + "> " + getJavaCommand()
14532         + " -jar grouperClient.jar --operation=getMembersWs --groupNames=etc:webServiceClientUsers");
14533     
14534     try {
14535       final List<String> command = new ArrayList<String>();
14536   
14537       command.add(getJavaCommand());
14538       command.add("-jar");
14539       command.addAll(GrouperInstallerUtils.splitTrimToList(
14540           "grouperClient.jar --operation=getMembersWs --groupNames=etc:webServiceClientUsers", " "));
14541               
14542       CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(command, String.class), 
14543           true, true, null, this.untarredClientDir, null, true);
14544   
14545       if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
14546         System.out.println("stderr: " + commandResult.getErrorText());
14547       }
14548       if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
14549         System.out.println("stdout: " + commandResult.getOutputText());
14550       }
14551       System.out.println("Success running client command:");
14552     } catch (Exception e) {
14553       System.out.println("Exception running Grouper client");
14554       e.printStackTrace();
14555       System.out.print("Press <enter> to continue: ");
14556       readFromStdIn("grouperInstaller.autorun.grouperClientErrorContinue");
14557     }
14558     System.out.println("##################################");
14559 
14560   }
14561 
14562   /**
14563    * edit a property in a property file
14564    * @param file
14565    * @param valueRegex 
14566    * @param lineMustHaveRegexes 
14567    * @param lineCantHaveRegexes 
14568    * @param newValue 
14569    * @param description of change for sys out print
14570    * @return true if edited file, or false if not but didnt need to, null if not found
14571    */
14572   public static Boolean editFile(File file, String valueRegex, String[] lineMustHaveRegexes, 
14573       String[] lineCantHaveRegexes, String newValue, String description) {
14574     return editFile(file, valueRegex, lineMustHaveRegexes, lineCantHaveRegexes, newValue, description, false, null);
14575   }
14576 
14577   /**
14578    * edit a property in a property file
14579    * @param file
14580    * @param valueRegex 
14581    * @param lineMustHaveRegexes 
14582    * @param lineCantHaveRegexes 
14583    * @param newValue 
14584    * @param description of change for sys out print
14585    * @param addAttributeIfNotExists if attribute isnt there, then if true, then add the attribute
14586    * @param newAttributeName if adding new attribute, this is the name
14587    * @return true if edited file, or false if not but didnt need to, null if not found
14588    */
14589   public static Boolean editFile(File file, String valueRegex, String[] lineMustHaveRegexes, 
14590       String[] lineCantHaveRegexes, String newValue, String description, boolean addAttributeIfNotExists, String newAttributeName) {
14591     
14592     if (!GrouperInstallerUtils.isBlank(newAttributeName) != addAttributeIfNotExists) {
14593       throw new RuntimeException("newAttributeName cant be null if addAttributeIfNotExists, and must be null if not addAttributeIfNotExists");
14594     }
14595     
14596     if (!file.exists() || file.length() == 0) {
14597       throw new RuntimeException("Why does " + file.getName() + " not exist and have contents? " 
14598           + file.getAbsolutePath());
14599     }
14600     
14601     String fileContents = GrouperInstallerUtils.readFileIntoString(file);
14602     
14603     String newline = GrouperInstallerUtils.newlineFromFile(fileContents);
14604     
14605     String[] lines = GrouperInstallerUtils.splitLines(fileContents);
14606     
14607     Pattern pattern = Pattern.compile(valueRegex);
14608     
14609     Pattern[] lineMustHavePatterns = new Pattern[GrouperInstallerUtils.length(lineMustHaveRegexes)];
14610     
14611     {
14612       int index = 0;
14613       for (String lineMustHaveRegex : GrouperInstallerUtils.nonNull(lineMustHaveRegexes, String.class)) {
14614         Pattern lineMustHavePattern = Pattern.compile(lineMustHaveRegex);
14615         lineMustHavePatterns[index] = lineMustHavePattern;
14616         
14617         index++;
14618       }
14619     }    
14620   
14621     Pattern[] lineCantHavePatterns = new Pattern[GrouperInstallerUtils.length(lineCantHaveRegexes)];
14622     
14623     {
14624       int index = 0;
14625       for (String lineCantHaveRegex : GrouperInstallerUtils.nonNull(lineCantHaveRegexes, String.class)) {
14626         Pattern lineCantHavePattern = Pattern.compile(lineCantHaveRegex);
14627         lineCantHavePatterns[index] = lineCantHavePattern;
14628         
14629         index++;
14630       }
14631     }    
14632   
14633     StringBuilder newfile = new StringBuilder();
14634     
14635     boolean madeChange = false;
14636     boolean noChangeNeeded = false;
14637     
14638     OUTER: for (String line : lines) {
14639       line = GrouperInstallerUtils.defaultString(line);
14640       
14641       //lets see if it satisfies all
14642       for (Pattern lineMustHavePattern : lineMustHavePatterns) {
14643         if (!lineMustHavePattern.matcher(line).find()) {
14644           newfile.append(line).append(newline);
14645           continue OUTER;
14646         }
14647       }
14648       
14649       //lets see if it doesnt have these
14650       for (Pattern lineCantHavePattern : lineCantHavePatterns) {
14651         if (lineCantHavePattern.matcher(line).find()) {
14652           newfile.append(line).append(newline);
14653           continue OUTER;
14654         }
14655       }
14656       
14657       //see if satisfies current
14658       Matcher matcher = pattern.matcher(line);
14659       if (!matcher.find()) {
14660         
14661         if (addAttributeIfNotExists) {
14662           
14663           System.out.println(" - adding " + description + " with value: '" + newValue + "'");
14664           
14665           line = GrouperInstallerUtils.trimEnd(line);
14666           
14667           boolean endsWithCloseTag = false;
14668           boolean endElement = false;
14669           
14670           if (line.endsWith("/>")) {
14671             line = line.substring(0, line.length()-2);
14672             line = GrouperInstallerUtils.trimEnd(line);
14673             endsWithCloseTag = true;
14674           } else if (line.endsWith(">")) {
14675             line = line.substring(0, line.length()-1);
14676             line = GrouperInstallerUtils.trimEnd(line);
14677             endElement = true;
14678           }
14679           
14680           newfile.append(line).append(" ").append(newAttributeName).append("=\"").append(newValue).append("\"");
14681           
14682           if (endsWithCloseTag) {
14683             newfile.append(" />");
14684           } else if (endElement) {
14685             newfile.append(" >");
14686           }
14687           
14688           newfile.append(newline);
14689           madeChange = true;
14690           
14691         } else {
14692         
14693           newfile.append(line).append(newline);
14694         }
14695         continue;
14696       }
14697       
14698       String oldValue = matcher.group(1);
14699       if (GrouperInstallerUtils.equals(newValue, oldValue)) {
14700         System.out.println(" - old " + description + " value is same as new value: " + newValue);
14701         noChangeNeeded = true;
14702         newfile.append(line).append(newline);
14703         continue;
14704       }
14705       
14706       //we need to change the value
14707       System.out.println(" - changing " + description + " from: '" + oldValue + "' to: '" + newValue + "'");
14708       newfile.append(line.substring(0, matcher.start(1)));
14709       newfile.append(newValue);
14710       newfile.append(line.substring(matcher.end(1), line.length()));
14711       newfile.append(newline);
14712       madeChange = true;
14713       continue;
14714     }
14715     
14716     if (!madeChange) {
14717       //true if edited file, or false if not but didnt need to, null if not found
14718       if (noChangeNeeded) {
14719         return false;
14720       }
14721       return null;
14722     }
14723     
14724     GrouperInstallerUtils.writeStringToFile(file, newfile.toString());
14725     
14726     return true;
14727   }
14728 
14729   /**
14730    * add a line to a file.  will replace \n with whatever newline is
14731    * @param file
14732    * @param line (not ending in newline)
14733    * @param lineNumber 1 indexed.  If not exist, add to end of file
14734    * @param description is a description of what was just changed
14735    */
14736   public static void addToFile(File file, String line, int lineNumber, String description) {
14737     if (!file.exists() || file.length() == 0) {
14738       throw new RuntimeException("Why does " + file.getName() + " not exist and have contents? " 
14739           + file.getAbsolutePath());
14740     }
14741     
14742     String fileContents = GrouperInstallerUtils.readFileIntoString(file);
14743     
14744     String newline = GrouperInstallerUtils.newlineFromFile(fileContents);
14745     
14746     String[] lines = GrouperInstallerUtils.splitLines(fileContents);
14747 
14748     line = GrouperInstallerUtils.replace(line, "\n", newline);
14749     
14750     line += newline;
14751     
14752     StringBuilder newfile = new StringBuilder();
14753     
14754     boolean madeChange = false;
14755     
14756     int index = 0;
14757     
14758     for (String fileLine : lines) {
14759       fileLine = GrouperInstallerUtils.defaultString(fileLine);
14760       newfile.append(fileLine).append(newline);
14761       index++;
14762       
14763       if (index >= lineNumber  && !madeChange) {
14764 
14765         System.out.println("Adding " + description + " to file at line number: " + lineNumber);        
14766         
14767         newfile.append(line);
14768         madeChange = true;
14769       }
14770     }
14771     
14772     if (!madeChange) {
14773       System.out.println("Appending " + description + " to end of file");        
14774       newfile.append(line);
14775     }
14776     
14777     GrouperInstallerUtils.writeStringToFile(file, newfile.toString());
14778     
14779   }
14780 
14781   /** tomcat ui path */
14782   private String tomcatUiPath = null;
14783 
14784   /** tomcat ws path */
14785   private String tomcatWsPath = null;
14786   
14787   /** tomee grouper ws scim path */
14788   private String tomeeWsScimPath = null;
14789 
14790   /** untarred dir, this does NOT end in file.separator */
14791   private File untarredClientDir;
14792 
14793   /**
14794    * see if install, upgrade, patch, etc
14795    * @return true if install, or false if upgrade
14796    */
14797   private GrouperInstallerMainFunction grouperInstallerMainFunction() {
14798 
14799     GrouperInstallerMainFunction grouperInstallerMainFunctionLocal = 
14800         (GrouperInstallerMainFunction)promptForEnum(
14801             "Do you want to install ('installContainer') a new grouper container , 'upgrade' an existing installation,\n"
14802                 + "  'patch' an existing installation, 'admin' utilities, 'buildContainer', 'installContainer', or 'createPatch' for Grouper developers\n" 
14803                 + "  (enter: 'installContainer', 'upgrade', 'patch', 'admin', 'createPatch', 'buildContainer', or blank for the default) ",
14804             "grouperInstaller.autorun.actionEgInstallUpgradePatch", GrouperInstallerMainFunction.class, 
14805             GrouperInstallerMainFunction.installContainer, "grouperInstaller.default.installOrUpgrade");
14806     return grouperInstallerMainFunctionLocal;
14807   }
14808 
14809 
14810   
14811   /**
14812    * 
14813    * @return install directory
14814    */
14815   private static String grouperInstallDirectory() {
14816     String grouperInstallDirectoryString;
14817     {
14818       File grouperInstallDirectoryFile = new File("");
14819       String defaultDirectory = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.installDirectory", false);
14820       System.out.print("Enter in the Grouper install directory (note: better if no spaces or special chars) [" 
14821           + (GrouperInstallerUtils.isBlank(defaultDirectory) ? grouperInstallDirectoryFile.getAbsolutePath() : defaultDirectory) + "]: ");
14822       String input = readFromStdIn("grouperInstaller.autorun.installDirectory");
14823       if (!GrouperInstallerUtils.isBlank(input)) {
14824         grouperInstallDirectoryFile = new File(input);
14825         if (!grouperInstallDirectoryFile.exists() || !grouperInstallDirectoryFile.isDirectory()) {
14826           System.out.println("Error: cant find directory: '" + input + "'");
14827           System.exit(1);
14828         }
14829       } else {
14830         if (!GrouperInstallerUtils.isBlank(defaultDirectory)) {
14831           grouperInstallDirectoryFile = new File(defaultDirectory);
14832           if (!grouperInstallDirectoryFile.exists() || !grouperInstallDirectoryFile.isDirectory()) {
14833             System.out.println("Error: cant find directory: '" + input + "'");
14834             System.exit(1);
14835           }
14836         }
14837       }
14838       grouperInstallDirectoryString = grouperInstallDirectoryFile.getAbsolutePath();
14839       if (!grouperInstallDirectoryString.endsWith(File.separator)) {
14840         grouperInstallDirectoryString += File.separator;
14841       }
14842     }
14843     return grouperInstallDirectoryString;
14844   }
14845   
14846   /**
14847    * 
14848    * @return upgrade directory
14849    */
14850   private String grouperUpgradeTempDirectory() {
14851     String localGrouperInstallDirectoryString = null;
14852     {
14853       File grouperInstallDirectoryFile = new File(new File("").getAbsolutePath() + File.separator + "tarballs");
14854       if (!GrouperInstallerUtils.isBlank(this.grouperInstallDirectoryString)) {
14855         grouperInstallDirectoryFile = new File(this.grouperInstallDirectoryString + "tarballs");
14856       }
14857       String defaultDirectory = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.tarballDirectory", false);
14858       if (GrouperInstallerUtils.isBlank(defaultDirectory)) {
14859         defaultDirectory = grouperInstallDirectoryFile.getAbsolutePath();
14860       }
14861       System.out.print("Enter in a Grouper temp directory to download tarballs (note: better if no spaces or special chars) [" 
14862           + defaultDirectory + "]: ");
14863       localGrouperInstallDirectoryString = readFromStdIn("grouperInstaller.autorun.tarballDirectory");
14864       if (!GrouperInstallerUtils.isBlank(localGrouperInstallDirectoryString)) {
14865         grouperInstallDirectoryFile = new File(localGrouperInstallDirectoryString);
14866         if (!grouperInstallDirectoryFile.exists() || !grouperInstallDirectoryFile.isDirectory()) {
14867           System.out.println("Error: cant find directory: '" + grouperInstallDirectoryFile.getAbsolutePath() + "'");
14868           System.exit(1);
14869         }
14870       } else {
14871         localGrouperInstallDirectoryString = defaultDirectory;
14872       }
14873       if (!localGrouperInstallDirectoryString.endsWith(File.separator)) {
14874         localGrouperInstallDirectoryString += File.separator;
14875       }
14876     }
14877     return localGrouperInstallDirectoryString;
14878   }
14879   
14880   /**
14881    * 
14882    * @return grouper container directory
14883    */
14884   private String grouperContainerDirectory() {
14885     String localGrouperContainerDirectoryString = null;
14886     {
14887       File grouperContainerDirectoryFile = new File(new File("").getAbsolutePath() + File.separator + "container");
14888       if (!GrouperInstallerUtils.isBlank(this.grouperInstallDirectoryString)) {
14889         grouperContainerDirectoryFile = new File(this.grouperInstallDirectoryString + "container");
14890       }
14891       String defaultDirectory = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.buildContainerDirectory", false);
14892       if (GrouperInstallerUtils.isBlank(defaultDirectory)) {
14893         defaultDirectory = grouperContainerDirectoryFile.getAbsolutePath();
14894       }
14895       System.out.print("Enter in a directory for output (note: better if no spaces or special chars) [" 
14896           + defaultDirectory + "]: ");
14897       localGrouperContainerDirectoryString = readFromStdIn("grouperInstaller.autorun.buildContainerDirectory");
14898       if (!GrouperInstallerUtils.isBlank(localGrouperContainerDirectoryString)) {
14899         grouperContainerDirectoryFile = new File(localGrouperContainerDirectoryString);
14900         if (!grouperContainerDirectoryFile.exists() || !grouperContainerDirectoryFile.isDirectory()) {
14901           System.out.println("Error: cant find directory: '" + grouperContainerDirectoryFile.getAbsolutePath() + "'");
14902           System.exit(1);
14903         }
14904       } else {
14905         localGrouperContainerDirectoryString = defaultDirectory;
14906       }
14907       if (!localGrouperContainerDirectoryString.endsWith(File.separator)) {
14908         localGrouperContainerDirectoryString += File.separator;
14909       }
14910     }
14911     return localGrouperContainerDirectoryString;
14912   }
14913   
14914   /**
14915    * 
14916    * @return see if operating on a source directory or deployed directory
14917    */
14918   @SuppressWarnings("unused")
14919   private GrouperInstallType sourceOrDeployed() {
14920 
14921     if (this.grouperDirectories.getGrouperInstallType() == null) {
14922              
14923     }
14924     
14925     return this.grouperDirectories.getGrouperInstallType();
14926   }
14927 
14928 
14929   
14930   /**
14931    * 
14932    * @return directory where existing installation exists
14933    */
14934   private String upgradeExistingDirectory() {
14935 
14936     //get the directory where the existing installation is
14937     String tempUpgradeExistingApplicationDirectoryString = this.upgradeExistingApplicationDirectoryString;
14938 
14939     String errorMessage = "Cant find Grouper " + this.appToUpgrade.name() + " properties files or libs, looked in the directory, "
14940         + "/classes/ , /conf/ , /WEB-INF/classes/ , /lib/ , /WEB-INF/lib/ , /lib/grouper/ , /dist/lib/ ";
14941 
14942     try {
14943       String upgradeExistingDirectoryString = null;
14944       for (int i=0;i<10;i++) {
14945         File grouperInstallDirectoryFile = new File("");
14946         String defaultDirectory = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.existingInstalledDirectory", false);
14947         System.out.print("Where is the grouper " + this.appToUpgrade.name() + " installed? " +
14948           (GrouperInstallerUtils.isBlank(defaultDirectory) ? "" : ("[" + defaultDirectory + "]: ")));
14949         upgradeExistingDirectoryString = readFromStdIn("grouperInstaller.autorun.grouperWhereInstalled");
14950         if (!GrouperInstallerUtils.isBlank(upgradeExistingDirectoryString)) {
14951           grouperInstallDirectoryFile = new File(upgradeExistingDirectoryString);
14952           if (!grouperInstallDirectoryFile.exists() || !grouperInstallDirectoryFile.isDirectory()) {
14953             System.out.println("Error: cant find directory: '" + grouperInstallDirectoryFile.getAbsolutePath() + "'");
14954             continue;
14955           }
14956         } else {
14957           upgradeExistingDirectoryString = defaultDirectory;
14958         }
14959         upgradeExistingDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(upgradeExistingDirectoryString);
14960 
14961         this.upgradeExistingApplicationDirectoryString = upgradeExistingDirectoryString;
14962 
14963         //make sure directory is where the app is
14964         if (!this.appToUpgrade.validateExistingDirectory(this)) {
14965           System.out.println(errorMessage);
14966           continue;
14967         }
14968         //find the resources dir
14969         {
14970           File resourcesDirFile = new File(this.upgradeExistingApplicationDirectoryString + "classes" + File.separator);
14971           if (resourcesDirFile.exists()) {
14972             this.upgradeExistingClassesDirectoryString = resourcesDirFile.getAbsolutePath();
14973           } else {
14974             resourcesDirFile = new File(this.upgradeExistingApplicationDirectoryString + "conf" + File.separator);
14975             if (resourcesDirFile.exists()) {
14976               this.upgradeExistingClassesDirectoryString = resourcesDirFile.getAbsolutePath();
14977             } else {
14978               resourcesDirFile = new File(this.upgradeExistingApplicationDirectoryString + "WEB-INF" + File.separator + "classes" + File.separator);
14979               if (resourcesDirFile.exists()) {
14980                 this.upgradeExistingClassesDirectoryString = resourcesDirFile.getAbsolutePath();
14981               } else {
14982                 this.upgradeExistingClassesDirectoryString = this.upgradeExistingApplicationDirectoryString;
14983               }            
14984             }
14985           }
14986           this.upgradeExistingClassesDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.upgradeExistingClassesDirectoryString);
14987         }
14988         
14989         //find the lib dir
14990         {
14991           File libDirFile = new File(this.upgradeExistingApplicationDirectoryString + "lib" + File.separator + "grouper" + File.separator);
14992           if (libDirFile.exists()) {
14993             this.upgradeExistingLibDirectoryString = libDirFile.getAbsolutePath();
14994           } else {
14995             libDirFile = new File(this.upgradeExistingApplicationDirectoryString + "WEB-INF" + File.separator + "lib" + File.separator);
14996             if (libDirFile.exists()) {
14997               this.upgradeExistingLibDirectoryString = libDirFile.getAbsolutePath();
14998             } else {
14999               libDirFile = new File(this.upgradeExistingApplicationDirectoryString + "lib" + File.separator);
15000               if (libDirFile.exists()) {
15001                 this.upgradeExistingLibDirectoryString = libDirFile.getAbsolutePath();
15002               } else {
15003                 this.upgradeExistingLibDirectoryString = this.upgradeExistingApplicationDirectoryString;
15004               }            
15005             }
15006           }
15007           this.upgradeExistingLibDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.upgradeExistingLibDirectoryString);
15008         }
15009 
15010         //find the bin dir
15011         {
15012           File binDirFile = new File(this.upgradeExistingApplicationDirectoryString + "bin" + File.separator);
15013           if (binDirFile.exists()) {
15014             this.upgradeExistingBinDirectoryString = binDirFile.getAbsolutePath();
15015           } else {
15016             binDirFile = new File(this.upgradeExistingApplicationDirectoryString + "WEB-INF" + File.separator + "bin" + File.separator);
15017             if (binDirFile.exists()) {
15018               this.upgradeExistingBinDirectoryString = binDirFile.getAbsolutePath();
15019             } else {
15020               this.upgradeExistingBinDirectoryString = this.upgradeExistingApplicationDirectoryString;
15021             }
15022           }
15023           this.upgradeExistingBinDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.upgradeExistingBinDirectoryString);
15024         }
15025 
15026 
15027         return upgradeExistingDirectoryString;
15028       }
15029       
15030       throw new RuntimeException(errorMessage);
15031       
15032     } finally {
15033       //set this back
15034       this.upgradeExistingApplicationDirectoryString = tempUpgradeExistingApplicationDirectoryString;
15035     }
15036   }
15037 
15038   /**
15039    * where classes are in the upgrade directory, ends in file separator
15040    */
15041   private String upgradeExistingClassesDirectoryString;
15042   
15043   /**
15044    * where jars are in the upgrade directory, ends in file separator
15045    */
15046   private String upgradeExistingLibDirectoryString;
15047 
15048   /**
15049    * where bin files (gsh) are in the upgrade directory, ends in file separator
15050    */
15051   private String upgradeExistingBinDirectoryString;
15052   /**
15053    * 
15054    * @param action upgrade or patch
15055    * @return what we are upgrading
15056    */
15057   private AppToUpgrade grouperAppToUpgradeOrPatch(String action) {
15058 
15059     AppToUpgrade appToUpgradeLocal = 
15060         (AppToUpgrade)promptForEnum(
15061             "What do you want to " + action + "?  api, ui, ws, pspng, or psp? ",
15062             "grouperInstaller.autorun.appToUpgrade", AppToUpgrade.class, AppToUpgrade.API, "grouperInstaller.default.appToUpgrade");
15063     return appToUpgradeLocal;
15064   }
15065 
15066   /**
15067    * add something to an xml file
15068    * @param file
15069    * @param addAfterThisRegex 
15070    * @param mustPassTheseRegexes 
15071    * @param newValue 
15072    * @param description of change for sys out print
15073    */
15074   public static void addToXmlFile(File file, String addAfterThisRegex, String[] mustPassTheseRegexes, String newValue, String description) {
15075     if (!file.exists() || file.length() == 0) {
15076       throw new RuntimeException("Why does " + file.getName() + " not exist and have contents? " 
15077           + file.getAbsolutePath());
15078     }
15079     
15080     String fileContents = GrouperInstallerUtils.readFileIntoString(file);
15081     
15082     String newline = GrouperInstallerUtils.newlineFromFile(fileContents);
15083     
15084     Pattern pattern = Pattern.compile(addAfterThisRegex);
15085 
15086     String[] lines = GrouperInstallerUtils.splitLines(fileContents);
15087     
15088     Pattern[] lineMustPassThesePatterns = new Pattern[GrouperInstallerUtils.length(mustPassTheseRegexes)];
15089     
15090     boolean[] hasPassedTheseRegexes = new boolean[lineMustPassThesePatterns.length];
15091     
15092     for (int i=0;i<hasPassedTheseRegexes.length;i++) {
15093       hasPassedTheseRegexes[i] = false;
15094     }
15095     
15096     {
15097       int index = 0;
15098       for (String lineMustHaveRegex : GrouperInstallerUtils.nonNull(mustPassTheseRegexes, String.class)) {
15099         Pattern lineMustHavePattern = Pattern.compile(lineMustHaveRegex);
15100         lineMustPassThesePatterns[index] = lineMustHavePattern;
15101         
15102         index++;
15103       }
15104     }    
15105 
15106     StringBuilder newfile = new StringBuilder();
15107 
15108     boolean madeChange = false;
15109 
15110     OUTER: for (String line : lines) {
15111       line = GrouperInstallerUtils.defaultString(line);
15112       
15113       //lets see if it satisfies all
15114       for (int i=0;i<lineMustPassThesePatterns.length;i++) {
15115         Pattern lineMustHavePattern = lineMustPassThesePatterns[i];
15116         if (lineMustHavePattern.matcher(line).find()) {
15117           hasPassedTheseRegexes[i] = true;
15118         }
15119       }
15120       
15121       //see if we have passed all the prefixes
15122       for (int i=0;i<hasPassedTheseRegexes.length;i++) {
15123         if (!hasPassedTheseRegexes[i]) {
15124           newfile.append(line).append(newline);
15125           continue OUTER;
15126         }
15127       }
15128       
15129       //see if satisfies current, and only add once
15130       Matcher matcher = pattern.matcher(line);
15131       if (!matcher.find() || madeChange) {
15132         newfile.append(line).append(newline);
15133         continue;
15134       }
15135 
15136       //we need to change the value
15137       System.out.println(" - adding " + description + " line: '" + newValue + "'");
15138       newfile.append(line);
15139       newfile.append(newline);
15140       newfile.append(newValue);
15141       newfile.append(newline);
15142       madeChange = true;
15143     }
15144     if (!madeChange) {
15145       throw new RuntimeException("Couldnt find place to add to server.xml!  Are there newlines that werent there before or something?");
15146     }
15147     
15148     GrouperInstallerUtils.writeStringToFile(file, newfile.toString());
15149     
15150   }
15151 
15152   private static String removeElConfigFromPropertiesFile(String fileContents, String propertyName) {
15153     
15154     if (propertyName.endsWith(".elConfig")) {
15155       return fileContents;
15156     }
15157 
15158     String elConfigPropertyName = propertyName + ".elConfig";
15159     
15160     //lets look for property in file
15161     //this is a newline or form feed then some optional whitespace, and the property name
15162     //then some optional whitespace then an equals, then optional whitespace, then some text
15163     String elConfigPropertyNameRegex = GrouperInstallerUtils.replace(elConfigPropertyName, ".", "\\.");
15164     String regex = "[\\n\\r][ \\t]*(" + elConfigPropertyNameRegex + "[ \\t]*=[ \\t]*[^\\n\\r]*)";
15165     Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
15166     Matcher matcher = pattern.matcher(fileContents);
15167 
15168     if (matcher.find()) {
15169       String previousValue = matcher.group(1);
15170             
15171       int startIndex = matcher.start(1);
15172       
15173       int endIndex = matcher.end(1);
15174       
15175       String newContents = fileContents.substring(0, startIndex);
15176       
15177       //if not the last char
15178       if (endIndex < fileContents.length()-1) {
15179         newContents += fileContents.substring(endIndex, fileContents.length());
15180       }
15181 
15182       //if there is another match, there is a problem
15183       if (matcher.find()) {
15184         throw new RuntimeException("Why are there multiple matches for " + propertyName + " in propertyFile??????");
15185       }
15186 
15187       System.out.println(" - removed property: " 
15188           + elConfigPropertyName);
15189       return newContents;
15190     }
15191     return fileContents;
15192   }
15193   /**
15194    * edit a property in a property file
15195    * @param file
15196    * @param propertyName
15197    * @param propertyValue
15198    * @param createFileIfNotExist 
15199    */
15200   public static void editPropertiesFile(File file, String propertyName, String propertyValue, boolean createFileIfNotExist) {
15201     if (!file.exists()) {
15202       if (createFileIfNotExist) {
15203         System.out.println("Creating file: " + (file == null ? null : file.getAbsolutePath()));
15204         GrouperInstallerUtils.fileCreate(file);
15205       } else {
15206         throw new RuntimeException("Why does " + file.getName() + " not exist and have contents? " 
15207             + file.getAbsolutePath());
15208       }
15209     }
15210     
15211     propertyValue = GrouperInstallerUtils.defaultString(propertyValue);
15212     
15213     String fileContents = GrouperInstallerUtils.readFileIntoString(file);
15214     
15215     String newline = GrouperInstallerUtils.newlineFromFile(fileContents);
15216     
15217     //if it starts with it, add a newline to start so the regexes work
15218     if (!fileContents.startsWith(newline)) {
15219       fileContents = newline + fileContents;
15220     }
15221     
15222     //lets look for property in file
15223     //this is a newline or form feed then some optional whitespace, and the property name
15224     //then some optional whitespace then an equals, then optional whitespace, then some text
15225     String propertyNameRegex = GrouperInstallerUtils.replace(propertyName, ".", "\\.");
15226     String regex = "[\\n\\r][ \\t]*" + propertyNameRegex + "[ \\t]*=[ \\t]*([^\\n\\r]*)";
15227     Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
15228     Matcher matcher = pattern.matcher(fileContents);
15229 
15230     if (matcher.find()) {
15231       String previousValue = matcher.group(1);
15232       
15233       if (GrouperInstallerUtils.trimToEmpty(previousValue).equals(GrouperInstallerUtils.trim(propertyValue))) {
15234         System.out.println(" - property " + propertyName + " already was set to: " + propertyValue + ", not changing file");
15235         return;
15236       }
15237       
15238       int startIndex = matcher.start(1);
15239       
15240       int endIndex = matcher.end(1);
15241       
15242       String newContents = fileContents.substring(0, startIndex) + propertyValue;
15243       
15244       //if not the last char
15245       if (endIndex < fileContents.length()-1) {
15246         newContents += fileContents.substring(endIndex, fileContents.length());
15247       }
15248 
15249       //if there is another match, there is a problem
15250       if (matcher.find()) {
15251         throw new RuntimeException("Why are there multiple matches for " + propertyName + " in propertyFile: " + file.getAbsolutePath() + "??????");
15252       }
15253 
15254       System.out.println(" - set property: " 
15255           + propertyName + " from: " + previousValue + " to: " + propertyValue);
15256       
15257       newContents = removeElConfigFromPropertiesFile(fileContents, newContents);
15258       
15259       GrouperInstallerUtils.writeStringToFile(file, newContents);
15260       return;
15261     }
15262     
15263     //lets see if it is in a comment
15264     //this is a newline or form feed then some optional whitespace, hash, optional whitespace, and the property name
15265     //then some optional whitespace then an equals, then optional whitespace, then some text
15266     regex = ".*[\\n\\r]([ \\t]*#[ \\t]*)" + propertyNameRegex + "[ \\t]*=[ \\t]*([^\\n\\r]*).*";
15267     pattern = Pattern.compile(regex, Pattern.DOTALL);
15268     matcher = pattern.matcher(fileContents);
15269 
15270     if (matcher.matches()) {
15271       String previousValue = matcher.group(2);
15272       
15273       int startIndexHash = matcher.start(1);
15274       
15275       int endIndexHash = matcher.end(1);
15276 
15277       int startIndex = matcher.start(2);
15278       
15279       int endIndex = matcher.end(2);
15280       
15281       String newContents = fileContents.substring(0, startIndexHash) + fileContents.substring(endIndexHash, startIndex)
15282         + propertyValue;
15283       
15284       //if not the last char
15285       if (endIndex < fileContents.length()-1) {
15286         newContents += fileContents.substring(endIndex, fileContents.length());
15287       }
15288       System.out.println(" - uncommented property: " 
15289           + propertyName + " from: " + previousValue + " to: " + propertyValue);
15290       
15291       newContents = removeElConfigFromPropertiesFile(fileContents, newContents);
15292 
15293       GrouperInstallerUtils.writeStringToFile(file, newContents);
15294       
15295       return;
15296     }
15297     
15298     //it must have not existed
15299     //add a newline..
15300     //add to end in case it was already there, now it will be overwritten
15301     String newContents = fileContents + newline + "# added by grouper-installer" + newline + propertyName + " = " + propertyValue + newline;
15302     
15303     newContents = removeElConfigFromPropertiesFile(newContents, propertyName);
15304 
15305     GrouperInstallerUtils.writeStringToFile(file, newContents);
15306 
15307     System.out.println(" - added to end of property file: " + propertyName + " = " + propertyValue);
15308     
15309   }
15310 
15311   /**
15312    * untar a file to a dir
15313    * @param fileName
15314    * @param autorunPropertiesKeyIfFileExistsUseLocal key in properties file to automatically fill in a value
15315    * @param dirToUntarTo or null to keep in same dir as tarfile
15316    * @return the directory where the files are (assuming has a single dir the same name as the archive)
15317    */
15318   private File untar(final String fileName, final String autorunPropertiesKeyIfFileExistsUseLocal,
15319       File dirToUntarTo) {
15320 
15321     if (!fileName.endsWith(".tar")) {
15322       throw new RuntimeException("File doesnt end in .tar: " + fileName);
15323     }
15324     String untarredFileName = fileName.substring(0, fileName.length() - ".tar".length());
15325     
15326     //ant has -bin which is annoying
15327     if (untarredFileName.endsWith("-bin")) {
15328       untarredFileName = untarredFileName.substring(0, untarredFileName.length() - "-bin".length());
15329     }
15330 
15331     if (dirToUntarTo == null) {
15332       dirToUntarTo = new File(untarredFileName).getParentFile();
15333     }
15334     
15335     File untarredFile = new File(dirToUntarTo.getAbsoluteFile() + File.separator + new File(untarredFileName).getName());
15336     untarredFileName = untarredFile.getAbsolutePath();
15337     
15338     if (untarredFile.exists()) {
15339       
15340       if (this.useAllUntarredDirectories != null && this.useAllUntarredDirectories == true) {
15341         return untarredFile;
15342       }
15343       
15344       System.out.print("Untarred dir exists: " + untarredFileName + ", use untarred dir (t|f)? [t]: ");
15345       boolean useUnzippedFile = readFromStdInBoolean(true, autorunPropertiesKeyIfFileExistsUseLocal);
15346       if (useUnzippedFile) {
15347         
15348         if (this.useAllUntarredDirectories == null) {
15349           System.out.print("Would you like to use all existing untarred directories (t|f)? [t]: ");
15350           this.useAllUntarredDirectories = readFromStdInBoolean(true, "grouperInstaller.autorun.useAllUntarredDirectories");
15351         }
15352         
15353         return untarredFile;
15354       }
15355       
15356       System.out.println("Deleting: " + untarredFileName);
15357       GrouperInstallerUtils.deleteRecursiveDirectory(untarredFileName);
15358     }
15359     
15360     System.out.println("Expanding: " + fileName + " to " + untarredFile.getAbsolutePath());
15361     
15362     final File[] result = new File[1];
15363     
15364     final File DIR_TO_UNTAR_TO = dirToUntarTo;
15365     
15366     Runnable runnable = new Runnable() {
15367 
15368       public void run() {
15369         result[0] = untarHelper(fileName, autorunPropertiesKeyIfFileExistsUseLocal, DIR_TO_UNTAR_TO);
15370       }
15371     };
15372 
15373     GrouperInstallerUtils.threadRunWithStatusDots(runnable, true);
15374 
15375     return result[0];
15376 
15377   }
15378 
15379   /**
15380    * untar a file to a dir
15381    * @param fileName
15382    * @param autorunPropertiesKeyIfFileExistsUseLocal key in properties file to automatically fill in a value
15383    * @param dirToUntarTo or null to keep in same dir as tarfile
15384    * @return the directory where the files are (assuming has a single dir the same name as the archive)
15385    */
15386   @SuppressWarnings("resource")
15387   private static File untarHelper(String fileName, String autorunPropertiesKeyIfFileExistsUseLocal, File dirToUntarTo) {
15388     TarArchiveInputStream tarArchiveInputStream = null;
15389     
15390     String untarredFileName = fileName.substring(0, fileName.length() - ".tar".length());
15391     
15392     //ant has -bin which is annoying
15393     if (untarredFileName.endsWith("-bin")) {
15394       untarredFileName = untarredFileName.substring(0, untarredFileName.length() - "-bin".length());
15395     }
15396     
15397     if (dirToUntarTo == null) {
15398       dirToUntarTo = new File(untarredFileName).getParentFile();
15399     }
15400 
15401     File untarredFile = new File(dirToUntarTo.getAbsoluteFile() + File.separator + new File(untarredFileName).getName());
15402 
15403     try {
15404 
15405       tarArchiveInputStream = new TarArchiveInputStream(new FileInputStream(fileName));
15406       
15407       while (true) {
15408   
15409         TarArchiveEntry tarArchiveEntry = tarArchiveInputStream.getNextTarEntry();
15410         if (tarArchiveEntry == null) {
15411           break;
15412         }
15413         
15414         //System.out.println("Entry: " + tarArchiveEntry.getName()
15415         //    + ", isDirectory: " + tarArchiveEntry.isDirectory()
15416         //    + ", isFile: " + tarArchiveEntry.isFile());
15417         String fileEntryName = dirToUntarTo.getAbsolutePath() + File.separator + tarArchiveEntry.getName();
15418         File tarEntryFile = new File(fileEntryName);
15419         
15420         if (tarArchiveEntry.isDirectory()) {
15421           if (!tarEntryFile.exists() && !tarEntryFile.mkdirs()) {
15422             throw new RuntimeException("Cant create dirs: " + tarEntryFile.getAbsolutePath());
15423           }
15424           continue;
15425         }
15426         
15427         byte[] content = new byte[(int)tarArchiveEntry.getSize()];
15428 
15429         int size = tarArchiveInputStream.read(content, 0, content.length);
15430         
15431         //for some reason we get an error when 0 bytes...
15432         if (size != content.length && (!(size == -1 && content.length == 0))) {
15433           throw new RuntimeException("Didnt read the right amount of bytes: " + size 
15434               + ", should have been: " + content.length + " on entry: " + tarArchiveEntry.getName());
15435         }
15436         
15437         ByteArrayInputStream byteArrayInputStream = null;
15438         FileOutputStream fileOutputStream = null;
15439         
15440         try {
15441           
15442           //create parent directories
15443           if (!tarEntryFile.getParentFile().exists() && !tarEntryFile.getParentFile().mkdirs()) {
15444             throw new RuntimeException("Cant create dirs: " + tarEntryFile.getParentFile().getAbsolutePath());
15445           }
15446 
15447           fileOutputStream = new FileOutputStream(tarEntryFile);
15448           byteArrayInputStream = new ByteArrayInputStream(content);
15449           GrouperInstallerUtils.copy(byteArrayInputStream, fileOutputStream);
15450           
15451         } catch (Exception e) {
15452           throw new RuntimeException("Problem with entry: " + tarArchiveEntry.getName(), e);
15453         } finally {
15454           GrouperInstallerUtils.closeQuietly(byteArrayInputStream);
15455           GrouperInstallerUtils.closeQuietly(fileOutputStream);
15456         }
15457         
15458       }
15459     } catch (Exception e) {
15460       throw new RuntimeException("Error untarring: " + fileName, e);
15461     } finally {
15462       GrouperInstallerUtils.closeQuietly(tarArchiveInputStream);
15463     }
15464     return untarredFile;
15465   }
15466 
15467   /**
15468    * unzip a file, this is for .zip files
15469    * @param fileName
15470    * @param autorunPropertiesKeyIfFileExistsUseLocal key in properties file to automatically fill in a value
15471    * @return the unzipped file
15472    */
15473   private static File unzipFromZip(final String fileName, final String autorunPropertiesKeyIfFileExistsUseLocal) {
15474 
15475     if (!fileName.endsWith(".zip")) {
15476       throw new RuntimeException("File doesnt end in .zip: " + fileName);
15477     }
15478     String unzippedFileName = fileName.substring(0, fileName.length() - ".zip".length());
15479 
15480     File unzippedDir = new File(unzippedFileName);
15481 
15482     if (unzippedDir.exists()) {
15483       System.out.print("Unzipped dir exists: " + unzippedFileName + ", use unzipped dir (t|f)? [t]: ");
15484       boolean useUnzippedFile = readFromStdInBoolean(true, autorunPropertiesKeyIfFileExistsUseLocal);
15485       if (useUnzippedFile) {
15486         return unzippedDir;
15487       }
15488       System.out.println("Deleting: " + unzippedFileName);
15489       GrouperInstallerUtils.deleteRecursiveDirectory(unzippedFileName);
15490     } else {
15491       if (!unzippedDir.mkdir()) {
15492         throw new RuntimeException("Cant make dir: " + unzippedDir.getAbsolutePath());
15493       }
15494     }
15495 
15496     System.out.println("Unzipping: " + fileName);
15497 
15498     final File[] result = new File[1];
15499     
15500     Runnable runnable = new Runnable() {
15501 
15502       public void run() {
15503         result[0] = unzipFromZipHelper(fileName, autorunPropertiesKeyIfFileExistsUseLocal);
15504       }
15505     };
15506 
15507     GrouperInstallerUtils.threadRunWithStatusDots(runnable, true);
15508 
15509     return result[0];
15510 
15511   }
15512 
15513   /**
15514    * unzip a file, this is for .zip files
15515    * @param fileName
15516    * @param autorunPropertiesKeyIfFileExistsUseLocal key in properties file to automatically fill in a value
15517    * @return the unzipped file
15518    */
15519   private static File unzipFromZipHelper(String fileName, String autorunPropertiesKeyIfFileExistsUseLocal) {
15520 
15521     String unzippedFileName = fileName.substring(0, fileName.length() - ".zip".length());
15522 
15523     File unzippedDir = new File(unzippedFileName);
15524 
15525     ZipFile zipFile = null;
15526     try {
15527       zipFile = new ZipFile(fileName);
15528       Enumeration<? extends ZipEntry> entries = zipFile.entries();
15529       while (entries.hasMoreElements()) {
15530         ZipEntry entry = entries.nextElement();
15531         File entryDestination = new File(unzippedDir, entry.getName());
15532         if (entry.isDirectory()) {
15533           entryDestination.mkdirs();
15534         } else {
15535           entryDestination.getParentFile().mkdirs();
15536           InputStream in = zipFile.getInputStream(entry);
15537           OutputStream out = new FileOutputStream(entryDestination);
15538           try {
15539             IOUtils.copy(in, out);
15540           } finally {
15541             GrouperInstallerUtils.closeQuietly(in);
15542             GrouperInstallerUtils.closeQuietly(out);
15543           }
15544         }
15545       }
15546     } catch (IOException ioe) {
15547       throw new RuntimeException(ioe);
15548     } finally {
15549       GrouperInstallerUtils.closeQuietly(zipFile);
15550     }
15551 
15552     return unzippedDir;
15553 
15554   }
15555 
15556   /**
15557    * unzip a file to another file, this is for .gz files
15558    * @param fileName
15559    * @param autorunPropertiesKeyIfFileExistsUseLocal key in properties file to automatically fill in a value
15560    * @return the unzipped file
15561    */
15562   private File unzip(final String fileName, final String autorunPropertiesKeyIfFileExistsUseLocal) {
15563 
15564     if (!fileName.endsWith(".gz")) {
15565       throw new RuntimeException("File doesnt end in .gz: " + fileName);
15566     }
15567     String unzippedFileName = fileName.substring(0, fileName.length() - ".gz".length());
15568     
15569     File unzippedFile = new File(unzippedFileName);
15570     if (unzippedFile.exists()) {
15571       
15572       if (this.useAllUnzippedFiles != null && this.useAllUnzippedFiles == true) {
15573         return unzippedFile;
15574       }
15575       
15576       System.out.print("Unzipped file exists: " + unzippedFileName + ", use unzipped file (t|f)? [t]: ");
15577       boolean useUnzippedFile = readFromStdInBoolean(true, autorunPropertiesKeyIfFileExistsUseLocal);
15578       if (useUnzippedFile) {
15579         if (this.useAllUnzippedFiles == null) {
15580           System.out.print("Would you like to use all existing unzipped files (t|f)? [t]: ");
15581           this.useAllUnzippedFiles = readFromStdInBoolean(true, "grouperInstaller.autorun.useAllUnzippedFiles");
15582         }
15583         
15584         return unzippedFile;
15585       }
15586       System.out.println("Deleting: " + unzippedFileName);
15587       if (!unzippedFile.delete()) {
15588         throw new RuntimeException("Cant delete file: " + unzippedFileName);
15589       }
15590     }
15591 
15592     System.out.println("Unzipping: " + fileName);
15593     
15594     final File[] result = new File[1];
15595     
15596     Runnable runnable = new Runnable() {
15597 
15598       public void run() {
15599         result[0] = unzipHelper(fileName, autorunPropertiesKeyIfFileExistsUseLocal);
15600       }
15601     };
15602 
15603     GrouperInstallerUtils.threadRunWithStatusDots(runnable, true);
15604 
15605     return result[0];
15606 
15607   }
15608 
15609   /**
15610    * unzip a file to another file, this is for .gz files
15611    * @param fileName
15612    * @param autorunPropertiesKeyIfFileExistsUseLocal key in properties file to automatically fill in a value
15613    * @return the unzipped file
15614    */
15615   private static File unzipHelper(String fileName, String autorunPropertiesKeyIfFileExistsUseLocal) {
15616 
15617     String unzippedFileName = fileName.substring(0, fileName.length() - ".gz".length());
15618     File unzippedFile = new File(unzippedFileName);
15619 
15620     GzipCompressorInputStream gzipCompressorInputStream = null;
15621     FileOutputStream fileOutputStream = null;
15622     try {
15623       gzipCompressorInputStream = new GzipCompressorInputStream(new BufferedInputStream(new FileInputStream(new File(fileName))));
15624       fileOutputStream = new FileOutputStream(unzippedFile);
15625       GrouperInstallerUtils.copy(gzipCompressorInputStream, fileOutputStream);
15626     } catch (Exception e) {
15627       throw new RuntimeException("Cant unzip file: " + fileName, e);
15628     } finally {
15629       GrouperInstallerUtils.closeQuietly(gzipCompressorInputStream);
15630       GrouperInstallerUtils.closeQuietly(fileOutputStream);
15631     }
15632     return unzippedFile;
15633   }
15634 
15635   /**
15636    * 
15637    * @return the file of the directory of the psp
15638    */
15639   private File downloadPsp() {
15640     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
15641     
15642     if (!urlToDownload.endsWith("/")) {
15643       urlToDownload += "/";
15644     }
15645     urlToDownload += "release/";
15646 
15647     String pspFileName = "grouper.psp-" + this.version + ".tar.gz";
15648     urlToDownload += this.version + "/" + pspFileName;
15649 
15650     File pspFile = new File(this.grouperTarballDirectoryString + pspFileName);
15651     
15652     downloadFile(urlToDownload, pspFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalPspDownloadTarEtc");
15653 
15654     return pspFile;
15655   }
15656   
15657   /**
15658    * 
15659    * @return the file of the directory of the psp
15660    */
15661   private File downloadPspng() {
15662     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
15663     
15664     if (!urlToDownload.endsWith("/")) {
15665       urlToDownload += "/";
15666     }
15667     urlToDownload += "release/";
15668 
15669     String pspFileName = "grouper.pspng-" + this.version + ".tar.gz";
15670     urlToDownload += this.version + "/" + pspFileName;
15671 
15672     File pspFile = new File(this.grouperTarballDirectoryString + pspFileName);
15673     
15674     downloadFile(urlToDownload, pspFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalPspngDownloadTarEtc");
15675 
15676     return pspFile;
15677   }
15678   
15679   /**
15680    * upgrade the ws
15681    */
15682   private void upgradeWs() {
15683   
15684     this.upgradeApiPreRevertPatch();
15685 
15686     System.out.println("You need to revert all patches to upgrade");
15687     this.patchRevertWs();
15688     
15689     System.out.println("\n##################################");
15690     System.out.println("Upgrading WS\n");
15691     
15692     //copy the jars there
15693     System.out.println("\n##################################");
15694     System.out.println("Upgrading WS jars\n");
15695   
15696     this.upgradeJars(new File(this.untarredWsDir + File.separator + 
15697         "grouper-ws" + File.separator + "build" + File.separator + "dist" + File.separator + "grouper-ws"
15698         + File.separator + "WEB-INF" + File.separator + "lib" + File.separator));
15699   
15700     System.out.println("\n##################################");
15701     System.out.println("Upgrading WS files\n");
15702   
15703     //copy files there
15704     this.copyFiles(this.untarredWsDir + File.separator + 
15705         "grouper-ws" + File.separator + "build" + File.separator + "dist" + File.separator + "grouper-ws"
15706         + File.separator,
15707         this.upgradeExistingApplicationDirectoryString,
15708         GrouperInstallerUtils.toSet("WEB-INF/lib", "WEB-INF/web.xml", "WEB-INF/web.wsTomcatAuthn.xml", "WEB-INF/server.wsTomcatAuthn.xml", "WEB-INF/classes",
15709             "WEB-INF/bin/gsh", "WEB-INF/bin/gsh.bat", "WEB-INF/bin/gsh.sh"));
15710 
15711     {
15712       boolean hadChange = false;
15713       for (String gshName : new String[]{"gsh", "gsh.bat", "gsh.sh"}) {
15714         File newGshFile = new File(this.untarredWsDir + File.separator + "grouper-ws" + File.separator 
15715             + "build" + File.separator + "dist" + File.separator + "grouper-ws"
15716             + File.separator + "WEB-INF" + File.separator + "bin" 
15717             + File.separator + gshName);
15718   
15719         File existingGshFile = new File(this.upgradeExistingApplicationDirectoryString 
15720             + File.separator + "WEB-INF" + File.separator + "bin" + File.separator + gshName);
15721   
15722         if (!GrouperInstallerUtils.contentEquals(newGshFile, existingGshFile)) {
15723           this.backupAndCopyFile(newGshFile, existingGshFile, true);
15724           if (!GrouperInstallerUtils.equals("gsh.bat", gshName)) {
15725             hadChange = true;
15726           }
15727         }
15728         
15729       }
15730       if (hadChange) {
15731         //set executable and dos2unix
15732         gshExcutableAndDos2Unix(this.upgradeExistingApplicationDirectoryString + "WEB-INF" 
15733             + File.separator + "bin" 
15734             + File.separator);
15735       }
15736     }
15737     
15738     upgradeWebXml(new File(this.untarredWsDir + File.separator + "grouper-ws" 
15739         + File.separator + "build" + File.separator + "dist" + File.separator + "grouper-ws"
15740         + File.separator + "WEB-INF" + File.separator + "web.xml"),
15741             new File(this.upgradeExistingApplicationDirectoryString 
15742                 + File.separator + "WEB-INF" + File.separator + "web.xml"));
15743     
15744     System.out.println("\n##################################");
15745     System.out.println("Upgrading WS config files\n");
15746 
15747     this.compareUpgradePropertiesFile(this.grouperWsBasePropertiesFile, 
15748         new File(this.untarredWsDir + File.separator + 
15749             "grouper-ws" + File.separator + "build" + File.separator + "dist" + File.separator + "grouper-ws"
15750             + File.separator + "WEB-INF" + File.separator + "classes" + File.separator + "grouper-ws.base.properties"),
15751         this.grouperWsPropertiesFile,
15752         this.grouperWsExamplePropertiesFile, null, "grouperInstaller.autorun.removeRedundantPropetiesFromGrouperWsProperties"
15753       );
15754 
15755     this.upgradeApiPostRevertPatch();
15756     
15757     //patch it
15758     this.patchWs();
15759 
15760   }
15761   /**
15762    * get the patches available to apply that are not already applied
15763    * @param thisAppToUpgrade app to upgrade to check
15764    * @return true if up to date, false if not
15765    */
15766   private boolean patchStatus(AppToUpgrade thisAppToUpgrade) {
15767   
15768     if (thisAppToUpgrade == AppToUpgrade.CLIENT) {
15769       throw new RuntimeException("Cant get status on " + thisAppToUpgrade);
15770     }
15771     
15772     Properties patchesExistingProperties = patchExistingProperties();
15773     
15774     String grouperVersion = this.grouperVersionOfJar().toString();
15775     
15776     grouperVersion = GrouperInstallerUtils.replace(grouperVersion, ".", "_");
15777     
15778     boolean foundNewPatch = false;
15779     
15780     OUTER: for (int i=0;i<1000;i++) {
15781       
15782       //grouper_v2_2_1_api_patch_0.state
15783       String keyBase = "grouper_v" + grouperVersion + "_" + thisAppToUpgrade.name().toLowerCase() + "_patch_" + i;
15784       System.out.println("\n################ Checking patch " + keyBase);
15785       String key = keyBase + ".state";
15786   
15787       String value = patchesExistingProperties.getProperty(key);
15788 
15789       if (!GrouperInstallerUtils.isBlank(value)) {
15790 
15791         GrouperInstallerPatchStatus grouperInstallerPatchStatus = GrouperInstallerPatchStatus.valueOfIgnoreCase(value, true, true);
15792 
15793         switch (grouperInstallerPatchStatus) {
15794           case applied:
15795             
15796             System.out.println("Patch: " + keyBase + ": was applied on: " + patchesExistingProperties.getProperty(keyBase + ".date"));
15797             break;
15798             
15799           case skippedPermanently:
15800             
15801             foundNewPatch = true;
15802             System.out.println("Patch: " + keyBase + ": was skipped permanently on: " + patchesExistingProperties.getProperty(keyBase + ".date"));
15803             break;
15804             
15805           case skippedTemporarily:
15806   
15807             foundNewPatch = true;
15808             System.out.println("Patch: " + keyBase + ": was skipped termporarily on: " + patchesExistingProperties.getProperty(keyBase + ".date"));
15809             break;
15810   
15811           case reverted:
15812   
15813             foundNewPatch = true;
15814             System.out.println("Patch: " + keyBase + ": was reverted on: " + patchesExistingProperties.getProperty(keyBase + ".date"));
15815             break;
15816   
15817           case error:
15818   
15819             foundNewPatch = true;
15820             System.out.println("Patch: " + keyBase + ": had an error installing on: " + patchesExistingProperties.getProperty(keyBase + ".date"));
15821             break;
15822   
15823           default:
15824             throw new RuntimeException("Not expecting: " + grouperInstallerPatchStatus);
15825         }
15826         
15827       }
15828   
15829       //lets see if it exists on the server
15830       File patchUntarredDir = downloadAndUnzipPatch(keyBase);
15831       
15832       //if no more patches
15833       if (patchUntarredDir == null) {
15834         System.out.println("");
15835         break OUTER;
15836       }
15837       
15838       //lets get the description:
15839       //  # will show up on screen so user knows what it is
15840       //  description = This patch fixes GRP-1080: browse folders refresh button only works in chrome, not other browsers
15841       //
15842       //  # patches that this patch is dependant on (comma separated)
15843       //  dependencies = 
15844       //
15845       //  # low, medium, or high risk to applying the patch
15846       //  risk = low
15847       //
15848       //  # is this is a security patch (true or false)
15849       //  security = false
15850       //
15851       //  # if this patch requires restart of processes (true or false)
15852       //  requiresRestart = false
15853       Properties patchProperties = GrouperInstallerUtils.propertiesFromFile(new File(patchUntarredDir.getAbsoluteFile() + File.separator + keyBase + ".properties"));
15854   
15855       foundNewPatch = true;
15856   
15857       // check dependencies
15858       {
15859         String[] dependencies = GrouperInstallerUtils.splitTrim(patchProperties.getProperty("dependencies"), ",");
15860   
15861         for (String dependency : GrouperInstallerUtils.nonNull(dependencies, String.class)) {
15862           if (!this.patchesInstalled.contains(dependency)) {
15863             System.out.println("Cannot install patch " + keyBase + " since it is dependent on a patch which is not installed: " + dependency);
15864           }
15865         }
15866       }
15867       
15868       boolean securityRelated = GrouperInstallerUtils.booleanValue(patchProperties.getProperty("security"), false);
15869       boolean requiresRestart = GrouperInstallerUtils.booleanValue(patchProperties.getProperty("requiresRestart"), true);
15870       
15871       //print description
15872       System.out.println("Patch " + keyBase + " is " + patchProperties.getProperty("risk") + " risk, "
15873           + (securityRelated ? "is a security patch" : "is not a security patch"));
15874       System.out.println("Patch " + keyBase + (requiresRestart ? " requires" : " does not require") + " a restart");
15875       System.out.println(patchProperties.getProperty("description") + "\n");      
15876     }
15877   
15878     if (!foundNewPatch) {
15879       System.out.println("There are no new " + thisAppToUpgrade + " patches to install");
15880       return true;
15881     }
15882     
15883     return false;
15884   }
15885   /**
15886    * build PSPNG
15887    * @param pspngDir
15888    */
15889   private void buildPspng(File pspngDir) {
15890 
15891     if (!pspngDir.exists() || pspngDir.isFile()) {
15892       throw new RuntimeException("Cant find psp: " + pspngDir.getAbsolutePath());
15893     }
15894     
15895     File pspngBuildToDir = new File(pspngDir.getAbsolutePath() 
15896         + File.separator + "target" + File.separator + "classes");
15897     
15898     boolean rebuildPspng = true;
15899     
15900     if (pspngBuildToDir.exists()) {
15901       System.out.print("The PSPNG has been built in the past, do you want it rebuilt? (t|f) [t]: ");
15902       rebuildPspng = readFromStdInBoolean(true, "grouperInstaller.autorun.rebuildPspngAfterHavingBeenBuilt");
15903     }
15904     
15905     if (!rebuildPspng) {
15906       return;
15907     }
15908     
15909     List<String> commands = new ArrayList<String>();
15910     
15911 //    \bin\mvn compile -DskipTests
15912     addMavenCommands(commands);
15913 
15914     //put 'compile -DskipTests' in there so it wont run tests which we dont want to do
15915     // dependency:copy-dependencies package -DskipTests
15916     //not compile
15917     commands.add("dependency:copy-dependencies");
15918     commands.add("package");
15919     commands.add("-DskipTests");
15920     commands.add("-Drat.ignoreErrors=true");
15921     commands.add("-Dlicense.skip=true");
15922         
15923     System.out.println("\n##################################");
15924     System.out.println("Building PSPNG with command:\n" + pspngDir.getAbsolutePath() + "> " 
15925         + convertCommandsIntoCommand(commands) + "\n");
15926 
15927     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
15928         true, true, null, new File(pspngDir.getAbsolutePath()), null, true);
15929     
15930     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
15931       System.out.println("stderr: " + commandResult.getErrorText());
15932     }
15933     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
15934       System.out.println("stdout: " + commandResult.getOutputText());
15935     }
15936 
15937     System.out.println("\nEnd building PSPNG");
15938     System.out.println("##################################\n");
15939     
15940   }
15941 
15942   /**
15943    * 
15944    */
15945   private void upgradeSourcesXmlToProperties() {
15946   
15947     //dont do this if less than 2.3.1
15948     if (new GiGrouperVersionersion(this.version).lessThanArg(new GiGrouperVersion("2.3.1"))) {
15949       return;
15950     }
15951     
15952     //this file is done
15953     File sourcesXmlFile = new File(this.grouperPropertiesFile.getParentFile().getAbsolutePath() + File.separator + "sources.xml");
15954 
15955     if (!sourcesXmlFile.exists()) {
15956       return;
15957     }
15958       
15959     
15960     System.out.print("Do you want to convert from sources.xml to subject.properties, note you need to do this to upgrade (t|f)? [t]: ");
15961     boolean convert = readFromStdInBoolean(true, "grouperInstaller.autorun.convertSourcesXmlToProperties");
15962   
15963     if (!convert) {
15964       System.out.println("Note: grouper will not run, but whatever you want to do!!!!");
15965     }
15966     File bakFile = null;
15967     if (this.subjectPropertiesFile.exists()) {
15968       //see if there is anything in it
15969       Properties grouperCacheProperties = GrouperInstallerUtils.propertiesFromFile(this.subjectPropertiesFile);
15970       if (grouperCacheProperties.size() > 0) {
15971         bakFile = this.backupAndDeleteFile(this.grouperCachePropertiesFile, true);
15972       }
15973     }
15974     
15975     URL sourcesXmlUrl = null;
15976     
15977     try {
15978       sourcesXmlUrl = sourcesXmlFile.toURI().toURL();
15979     } catch (Exception e) {
15980       throw new RuntimeException("Problem with sources.xml: " + (sourcesXmlFile == null ? null : sourcesXmlFile.getAbsoluteFile()), e);
15981     }
15982     
15983     //convert
15984     convertSourcesXmlToProperties(this.subjectPropertiesFile, sourcesXmlUrl);
15985     
15986     File subjectBakFile = bakFile(this.subjectPropertiesFile);
15987     GrouperInstallerUtils.copyFile(this.subjectPropertiesFile, subjectBakFile, true);
15988     this.backupAndDeleteFile(sourcesXmlFile, true);
15989     
15990     {
15991       File sourcesExampleXmlFile = new File(this.grouperPropertiesFile.getParentFile().getAbsolutePath() + File.separator + "sources.example.xml");
15992       if (sourcesExampleXmlFile.exists()) {
15993         this.backupAndDeleteFile(sourcesExampleXmlFile, true);
15994       }
15995     }
15996     
15997     if (bakFile != null) {
15998       System.out.println("Note, you had settings in your subject.properties (not common), this file has been moved to: " + bakFile.getAbsolutePath());
15999       System.out.println("Merge your settings from that file to " + this.subjectPropertiesFile.getAbsolutePath());
16000       System.out.print("Press <enter> to continue: ");
16001       readFromStdIn("grouperInstaller.autorun.convertSourcesXmlToPropertiesHadPropertiesInFile");
16002     }
16003   }
16004 
16005   /**
16006    * build client API
16007    * @param messagingActiveMqDir
16008    */
16009   private void buildMessagingActivemq(File messagingActiveMqDir) {
16010     if (!messagingActiveMqDir.exists() || messagingActiveMqDir.isFile()) {
16011       throw new RuntimeException("Cant find messaging activemq: " + messagingActiveMqDir.getAbsolutePath());
16012     }
16013     
16014     File messagingActivemqBuildToDir = new File(messagingActiveMqDir.getAbsoluteFile() + File.separator + "dist" + File.separator + "bin");
16015     
16016     boolean rebuildMessagingActivemq = true;
16017     
16018     if (messagingActivemqBuildToDir.exists()) {
16019       System.out.print("Grouper messaging activemq has been built in the past, do you want it rebuilt? (t|f) [t]: ");
16020       rebuildMessagingActivemq = readFromStdInBoolean(true, "grouperInstaller.autorun.rebuildMessagingActivemqAfterHavingBeenBuilt");
16021     }
16022     
16023     if (!rebuildMessagingActivemq) {
16024       return;
16025     }
16026   
16027     List<String> commands = new ArrayList<String>();
16028     
16029     addAntCommands(commands);
16030     
16031     System.out.println("\n##################################");
16032     System.out.println("Building messaging activemq with command:\n" + messagingActiveMqDir.getAbsolutePath() + "> " 
16033         + convertCommandsIntoCommand(commands) + "\n");
16034     
16035     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
16036         true, true, null, messagingActiveMqDir, null, true);
16037     
16038     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
16039       System.out.println("stderr: " + commandResult.getErrorText());
16040     }
16041     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
16042       System.out.println("stdout: " + commandResult.getOutputText());
16043     }
16044   
16045     System.out.println("\nEnd building messaging activemq");
16046     System.out.println("##################################\n");
16047     
16048   }
16049 
16050   /**
16051    * build client API
16052    * @param messagingAwsDir
16053    */
16054   private void buildMessagingAws(File messagingAwsDir) {
16055     if (!messagingAwsDir.exists() || messagingAwsDir.isFile()) {
16056       throw new RuntimeException("Cant find messaging aws: " + messagingAwsDir.getAbsolutePath());
16057     }
16058     
16059     File messagingAwsBuildToDir = new File(messagingAwsDir.getAbsoluteFile() + File.separator + "dist" + File.separator + "bin");
16060     
16061     boolean rebuildMessagingAws = true;
16062     
16063     if (messagingAwsBuildToDir.exists()) {
16064       System.out.print("Grouper messaging aws has been built in the past, do you want it rebuilt? (t|f) [t]: ");
16065       rebuildMessagingAws = readFromStdInBoolean(true, "grouperInstaller.autorun.rebuildMessagingAwsAfterHavingBeenBuilt");
16066     }
16067     
16068     if (!rebuildMessagingAws) {
16069       return;
16070     }
16071   
16072     List<String> commands = new ArrayList<String>();
16073     
16074     addAntCommands(commands);
16075     
16076     System.out.println("\n##################################");
16077     System.out.println("Building messaging aws with command:\n" + messagingAwsDir.getAbsolutePath() + "> " 
16078         + convertCommandsIntoCommand(commands) + "\n");
16079     
16080     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
16081         true, true, null, messagingAwsDir, null, true);
16082     
16083     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
16084       System.out.println("stderr: " + commandResult.getErrorText());
16085     }
16086     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
16087       System.out.println("stdout: " + commandResult.getOutputText());
16088     }
16089   
16090     System.out.println("\nEnd building aws rabbitmq");
16091     System.out.println("##################################\n");
16092     
16093   }
16094 
16095   /**
16096    * 
16097    * @param grouperCacheBasePropertiesFile 
16098    * @param grouperCachePropertiesFile
16099    * @param ehcacheXmlUrl
16100    */
16101   public static void convertEhcacheXmlToProperties(File grouperCacheBasePropertiesFile, File grouperCachePropertiesFile, URL ehcacheXmlUrl) {
16102   
16103     //look at base properties
16104     Properties grouperCacheProperties = grouperCachePropertiesFile.exists() ? 
16105         GrouperInstallerUtils.propertiesFromFile(grouperCachePropertiesFile) : new Properties();
16106   
16107     if (!grouperCacheBasePropertiesFile.exists()) {
16108       throw new RuntimeException(grouperCacheBasePropertiesFile.getAbsolutePath() + " must exist and does not!");
16109     }
16110     
16111     if (grouperCacheProperties.size() > 0) {
16112       throw new RuntimeException(grouperCachePropertiesFile.getAbsolutePath() + " exists and must not.  Delete the file and run this again!");
16113     }
16114   
16115     if (!grouperCachePropertiesFile.getParentFile().exists() || !grouperCachePropertiesFile.getParentFile().isDirectory()) {
16116       throw new RuntimeException(grouperCachePropertiesFile.getParentFile().getAbsolutePath() + " must exist and must be a directory");
16117     }
16118     
16119     //look at base properties
16120     Properties grouperCacheBaseProperties = GrouperInstallerUtils.propertiesFromFile(grouperCacheBasePropertiesFile);
16121     
16122     StringBuilder grouperEhcachePropertiesContents = new StringBuilder();
16123     
16124     grouperEhcachePropertiesContents.append(
16125               "# Copyright 2016 Internet2\n"
16126             + "#\n"
16127             + "# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
16128             + "# you may not use this file except in compliance with the License.\n"
16129             + "# You may obtain a copy of the License at\n"
16130             + "#\n"
16131             + "#   http://www.apache.org/licenses/LICENSE-2.0\n"
16132             + "#\n"
16133             + "# Unless required by applicable law or agreed to in writing, software\n"
16134             + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n"
16135             + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
16136             + "# See the License for the specific language governing permissions and\n"
16137             + "# limitations under the License.\n"
16138             + "\n"
16139             + "#\n"
16140             + "# Grouper Cache Configuration\n"
16141             + "#\n"
16142             + "\n"
16143             + "# The grouper cache config uses Grouper Configuration Overlays (documented on wiki)\n"
16144             + "# By default the configuration is read from grouper.cache.base.properties\n"
16145             + "# (which should not be edited), and the grouper.cache.properties overlays\n"
16146             + "# the base settings.  See the grouper.cache.base.properties for the possible\n"
16147             + "# settings that can be applied to the grouper.cache.properties\n\n"
16148         );
16149   
16150     {
16151       // <diskStore path="java.io.tmpdir"/>
16152       NodeList diskStoreNodeList = GrouperInstallerUtils.xpathEvaluate(ehcacheXmlUrl, "/ehcache/diskStore");
16153       if (diskStoreNodeList.getLength() != 1) {
16154         throw new RuntimeException("Expecting one diskStore element");
16155       }
16156   
16157       Element element = (Element)diskStoreNodeList.item(0);
16158   
16159       NamedNodeMap configuredNamedNodeMap = element.getAttributes();
16160       if (configuredNamedNodeMap.getLength() != 1 || !"path".equals(configuredNamedNodeMap.item(0).getNodeName())) {
16161         throw new RuntimeException("Expecting one diskStore attribute: path");
16162       }
16163       
16164       String path = element.getAttribute("path");
16165       
16166       if (!"java.io.tmpdir".equals(path)) {
16167         grouperEhcachePropertiesContents.append("grouper.cache.diskStorePath = " + path + "\n\n");
16168       }
16169       
16170     }    
16171   
16172     {
16173       //  <defaultCache
16174       //    maxElementsInMemory="1000"
16175       //    eternal="false"
16176       //    timeToIdleSeconds="10"
16177       //    timeToLiveSeconds="10"
16178       //    overflowToDisk="false"
16179       //    statistics="false"
16180       //  />
16181       
16182       NodeList diskStoreNodeList = GrouperInstallerUtils.xpathEvaluate(ehcacheXmlUrl, "/ehcache/defaultCache");
16183       if (diskStoreNodeList.getLength() != 1) {
16184         throw new RuntimeException("Expecting one defaultCache element");
16185       }
16186   
16187       Element element = (Element)diskStoreNodeList.item(0);
16188   
16189       NamedNodeMap configuredNamedNodeMap = element.getAttributes();
16190       
16191       if (configuredNamedNodeMap.getLength() != 6) {
16192         throw new RuntimeException("Expecting defaultCache with these attributes: maxElementsInMemory, "
16193             + "eternal, timeToIdleSeconds, timeToLiveSeconds, overflowToDisk, statistics");
16194       }
16195   
16196       boolean madeChanges = false;
16197       
16198       for (int i=0;i<configuredNamedNodeMap.getLength(); i++) {
16199         
16200         String attributeName = configuredNamedNodeMap.item(i).getNodeName();
16201         String value = element.getAttribute(attributeName);
16202   
16203         if ("maxElementsInMemory".equals(attributeName)) {
16204           if (!"1000".equals(value)) {
16205             grouperEhcachePropertiesContents.append("cache.defaultCache.maxElementsInMemory = " + value + "\n");
16206             madeChanges = true;
16207           }
16208         } else if ("eternal".equals(attributeName)) {
16209           if (!"false".equals(value)) {
16210             grouperEhcachePropertiesContents.append("cache.defaultCache.eternal = " + value + "\n");
16211             madeChanges = true;
16212           }
16213         } else if ("timeToIdleSeconds".equals(attributeName)) {
16214           if (!"10".equals(value)) {
16215             grouperEhcachePropertiesContents.append("cache.defaultCache.timeToIdleSeconds = " + value + "\n");
16216             madeChanges = true;
16217           }
16218           
16219         } else if ("timeToLiveSeconds".equals(attributeName)) {
16220           if (!"10".equals(value)) {
16221             grouperEhcachePropertiesContents.append("cache.defaultCache.timeToLiveSeconds = " + value + "\n");
16222             madeChanges = true;
16223           }
16224           
16225         } else if ("overflowToDisk".equals(attributeName)) {
16226           if (!"false".equals(value)) {
16227             grouperEhcachePropertiesContents.append("cache.defaultCache.overflowToDisk = " + value + "\n");
16228             madeChanges = true;
16229           }
16230           
16231         } else if ("statistics".equals(attributeName)) {
16232           if (!"false".equals(value)) {
16233             grouperEhcachePropertiesContents.append("cache.defaultCache.statistics = " + value + "\n");
16234             madeChanges = true;
16235           }
16236           
16237         } else {
16238           throw new RuntimeException("Not expecting attribuet defaultCache " + attributeName);
16239         }
16240       }
16241   
16242       if (madeChanges) {
16243         grouperEhcachePropertiesContents.append("\n");
16244       }
16245       
16246     }
16247     
16248     NodeList nodeList = GrouperInstallerUtils.xpathEvaluate(ehcacheXmlUrl, "/ehcache/cache");
16249     
16250     Set<String> usedKeys = new HashSet<String>();
16251     
16252     for (int i=0;i<nodeList.getLength();i++) {
16253       
16254       Element element = (Element)nodeList.item(i);
16255   
16256       //  <cache  name="edu.internet2.middleware.grouper.internal.dao.hib3.Hib3MemberDAO.FindBySubject"
16257       //      maxElementsInMemory="5000"
16258       //      eternal="false"
16259       //      timeToIdleSeconds="5"
16260       //      timeToLiveSeconds="10"
16261       //      overflowToDisk="false"  
16262       //      statistics="false"
16263       //  />
16264       
16265       String name = element.getAttribute("name");
16266       Integer maxElementsInMemory = GrouperInstallerUtils.intObjectValue(element.getAttribute("maxElementsInMemory"), true);
16267       Boolean eternal = GrouperInstallerUtils.booleanObjectValue(element.getAttribute("eternal"));
16268       Integer timeToIdleSeconds = GrouperInstallerUtils.intObjectValue(element.getAttribute("timeToIdleSeconds"), true);
16269       Integer timeToLiveSeconds = GrouperInstallerUtils.intObjectValue(element.getAttribute("timeToLiveSeconds"), true);
16270       Boolean overflowToDisk = GrouperInstallerUtils.booleanObjectValue(element.getAttribute("overflowToDisk"));
16271       Boolean statistics = GrouperInstallerUtils.booleanObjectValue(element.getAttribute("statistics"));
16272   
16273       //any attributes we dont expect?
16274       NamedNodeMap configuredNamedNodeMap = element.getAttributes();
16275       //see which attributes are new or changed
16276       for (int j=0;j<configuredNamedNodeMap.getLength();j++) {
16277         Node configuredAttribute = configuredNamedNodeMap.item(j);
16278         if (!configuredAttribute.getNodeName().equals("name")
16279             && !configuredAttribute.getNodeName().equals("maxElementsInMemory")
16280             && !configuredAttribute.getNodeName().equals("eternal")
16281             && !configuredAttribute.getNodeName().equals("timeToIdleSeconds")
16282             && !configuredAttribute.getNodeName().equals("timeToLiveSeconds")
16283             && !configuredAttribute.getNodeName().equals("overflowToDisk")
16284             && !configuredAttribute.getNodeName().equals("statistics")) {
16285           throw new RuntimeException("Cant process attribute: '" + configuredAttribute.getNodeName() + "'");
16286         }
16287       }
16288       
16289       String key = convertEhcacheNameToPropertiesKey(name, usedKeys);
16290       
16291       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.name = edu.internet2.middleware.grouper.internal.dao.hib3.Hib3GroupDAO
16292       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.maxElementsInMemory = 500
16293       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.eternal = false
16294       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.timeToIdleSeconds = 1
16295       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.timeToLiveSeconds = 1
16296       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.overflowToDisk = false
16297       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.statistics = false
16298   
16299       boolean madeChanges = false;
16300       
16301       if (maxElementsInMemory != null && !GrouperInstallerUtils.defaultString((String)grouperCacheBaseProperties.get("cache.name." + key + ".maxElementsInMemory")).equals(maxElementsInMemory.toString())) {
16302         grouperEhcachePropertiesContents.append("cache.name." + key + ".maxElementsInMemory = " + maxElementsInMemory + "\n");
16303         madeChanges = true;
16304       }
16305       if (eternal != null && !GrouperInstallerUtils.defaultString((String)grouperCacheBaseProperties.get("cache.name." + key + ".eternal")).equals(eternal.toString())) {
16306         grouperEhcachePropertiesContents.append("cache.name." + key + ".eternal = " + eternal + "\n");
16307         madeChanges = true;
16308       }
16309       if (timeToIdleSeconds != null && !GrouperInstallerUtils.defaultString((String)grouperCacheBaseProperties.get("cache.name." + key + ".timeToIdleSeconds")).equals(timeToIdleSeconds.toString())) {
16310         grouperEhcachePropertiesContents.append("cache.name." + key + ".timeToIdleSeconds = " + timeToIdleSeconds + "\n");
16311         madeChanges = true;
16312       }
16313       if (timeToLiveSeconds != null && !GrouperInstallerUtils.defaultString((String)grouperCacheBaseProperties.get("cache.name." + key + ".timeToLiveSeconds")).equals(timeToLiveSeconds.toString())) {
16314         grouperEhcachePropertiesContents.append("cache.name." + key + ".timeToLiveSeconds = " + timeToLiveSeconds + "\n");
16315         madeChanges = true;
16316       }
16317       if (overflowToDisk != null && !GrouperInstallerUtils.defaultString((String)grouperCacheBaseProperties.get("cache.name." + key + ".overflowToDisk")).equals(overflowToDisk.toString())) {
16318         grouperEhcachePropertiesContents.append("cache.name." + key + ".overflowToDisk = " + overflowToDisk + "\n");
16319         madeChanges = true;
16320       }
16321       if (statistics != null && !GrouperInstallerUtils.defaultString((String)grouperCacheBaseProperties.get("cache.name." + key + ".statistics")).equals(statistics.toString())) {
16322         grouperEhcachePropertiesContents.append("cache.name." + key + ".statistics = " + statistics + "\n");
16323         madeChanges = true;
16324       }
16325       if (madeChanges) {
16326         grouperEhcachePropertiesContents.append("\n");
16327       }
16328     }
16329   
16330     GrouperInstallerUtils.saveStringIntoFile(grouperCachePropertiesFile, grouperEhcachePropertiesContents.toString());
16331   }
16332 
16333   /**
16334    * get a subelement value.
16335    * e.g. if the node is &lt;source&gt;
16336    * and the sub element is &lt;id&gt;someId&lt;/id&gt;
16337    * It will return "someId" for subElementName "id"
16338    * @param parent
16339    * @param subElementName
16340    * @param required
16341    * @param descriptionForError 
16342    * @return the string or null if not there
16343    */
16344   public static String xmlElementValue(Element parent, String subElementName, boolean required, String descriptionForError) {
16345     
16346     NodeList nodeList = parent.getElementsByTagName(subElementName);
16347     
16348     if (nodeList.getLength() < 1) {
16349       if (required) {
16350         throw new RuntimeException("Cant find subElement <" + subElementName 
16351             + "> in parent element " + parent.getNodeName() + ", " + descriptionForError);
16352       }
16353       return null;
16354     }    
16355     
16356     if (nodeList.getLength() > 1) {
16357       throw new RuntimeException("Too many subElements <" + subElementName 
16358           + "> in parent element " + parent.getNodeName() + ", " 
16359           + nodeList.getLength() + ", " + descriptionForError);
16360     }
16361     return GrouperInstallerUtils.trimToEmpty(nodeList.item(0).getTextContent());
16362   }
16363   
16364   /**
16365    * put in a good comment about this param name
16366    * @param paramName
16367    * @param paramValue
16368    * @param subjectPropertiesContents
16369    */
16370   private static void convertSourcesXmlParamComment(String paramName, StringBuilder subjectPropertiesContents, String paramValue) {
16371     
16372     if (paramName == null) {
16373       throw new NullPointerException("param-name is null");
16374     }
16375     
16376     if (paramName.startsWith("subjectVirtualAttributeVariable_")) {
16377       subjectPropertiesContents.append("\n# when evaluating the virtual attribute EL expression, this variable can be used from this java class.\n"
16378           + "# " + paramName + " variable is the " + paramValue + " class.  Call static methods\n");
16379     } else if (paramName.startsWith("subjectVirtualAttribute_")) {
16380       
16381       Pattern pattern = Pattern.compile("^subjectVirtualAttribute_([\\d]+)_(.*)$");
16382       Matcher matcher = pattern.matcher(paramName);
16383       if (!matcher.matches()) {
16384         throw new RuntimeException(paramName + " is invalid, should be of form: subjectVirtualAttribute_<intIndex>_paramName");
16385       }
16386       
16387       String index = matcher.group(1);
16388       String attributeName = matcher.group(2);
16389       
16390       subjectPropertiesContents.append("\n# This virtual attribute index " + index + " is accessible via: subject.getAttributeValue(\"" + attributeName + "\");\n");
16391       
16392     } else if (GrouperInstallerUtils.equals(paramName, "findSubjectByIdOnCheckConfig")) {
16393       
16394       subjectPropertiesContents.append("\n# if a system check should try to resolve a subject by id on this source\n");
16395 
16396     } else if (GrouperInstallerUtils.equals(paramName, "subjectIdToFindOnCheckConfig")) {
16397       
16398       subjectPropertiesContents.append("\n# by default it will try to find a random string.  If you want a speicific ID to be found enter that here\n");
16399 
16400     } else if (GrouperInstallerUtils.equals(paramName, "findSubjectByIdentifiedOnCheckConfig")) {
16401       
16402       subjectPropertiesContents.append("\n# by default it will do a search by subject identifier\n");
16403 
16404     } else if (GrouperInstallerUtils.equals(paramName, "subjectIdentifierToFindOnCheckConfig")) {
16405       
16406       subjectPropertiesContents.append("\n# by default it will use a random value for subject identifier to lookup, you can specify a value here\n");
16407 
16408     } else if (GrouperInstallerUtils.equals(paramName, "findSubjectByStringOnCheckConfig")) {
16409       
16410       subjectPropertiesContents.append("\n# by default it will search for a subject by string\n");
16411 
16412     } else if (GrouperInstallerUtils.equals(paramName, "stringToFindOnCheckConfig")) {
16413       
16414       subjectPropertiesContents.append("\n# you can specify the search string here or it will be a random value\n");
16415 
16416     } else if (GrouperInstallerUtils.equals(paramName, "sortAttribute0")) {
16417       
16418       subjectPropertiesContents.append("\n# the 1st sort attribute for lists on screen that are derived from member table (e.g. search for member in group)\n"
16419           + "# you can have up to 5 sort attributes \n");
16420 
16421     } else if (GrouperInstallerUtils.equals(paramName, "sortAttribute1")) {
16422       
16423       subjectPropertiesContents.append("\n# the 2nd sort attribute for lists on screen that are derived from member table (e.g. search for member in group)\n"
16424           + "# you can have up to 5 sort attributes \n");
16425 
16426     } else if (GrouperInstallerUtils.equals(paramName, "sortAttribute2")) {
16427       
16428       subjectPropertiesContents.append("\n# the 3rd sort attribute for lists on screen that are derived from member table (e.g. search for member in group)\n"
16429           + "# you can have up to 5 sort attributes \n");
16430 
16431     } else if (GrouperInstallerUtils.equals(paramName, "sortAttribute3")) {
16432       
16433       subjectPropertiesContents.append("\n# the 4th sort attribute for lists on screen that are derived from member table (e.g. search for member in group)\n"
16434           + "# you can have up to 5 sort attributes \n");
16435 
16436     } else if (GrouperInstallerUtils.equals(paramName, "sortAttribute4")) {
16437       
16438       subjectPropertiesContents.append("\n# the 5th sort attribute for lists on screen that are derived from member table (e.g. search for member in group)\n"
16439           + "# you can have up to 5 sort attributes \n");
16440 
16441     } else if (GrouperInstallerUtils.equals(paramName, "searchAttribute0")) {
16442       
16443       subjectPropertiesContents.append("\n# the 1st search attribute for lists on screen that are derived from member table (e.g. search for member in group)\n"
16444           + "# you can have up to 5 search attributes \n");
16445 
16446     } else if (GrouperInstallerUtils.equals(paramName, "searchAttribute1")) {
16447       
16448       subjectPropertiesContents.append("\n# the 2nd search attribute for lists on screen that are derived from member table (e.g. search for member in group)\n"
16449           + "# you can have up to 5 search attributes \n");
16450 
16451     } else if (GrouperInstallerUtils.equals(paramName, "searchAttribute2")) {
16452       
16453       subjectPropertiesContents.append("\n# the 3rd search attribute for lists on screen that are derived from member table (e.g. search for member in group)\n"
16454           + "# you can have up to 5 search attributes \n");
16455 
16456     } else if (GrouperInstallerUtils.equals(paramName, "searchAttribute3")) {
16457       
16458       subjectPropertiesContents.append("\n# the 4th search attribute for lists on screen that are derived from member table (e.g. search for member in group)\n"
16459           + "# you can have up to 5 search attributes \n");
16460 
16461     } else if (GrouperInstallerUtils.equals(paramName, "searchAttribute4")) {
16462       
16463       subjectPropertiesContents.append("\n# the 5th search attribute for lists on screen that are derived from member table (e.g. search for member in group)\n"
16464           + "# you can have up to 5 search attributes\n");
16465 
16466     } else if (GrouperInstallerUtils.equals(paramName, "subjectIdentifierAttribute0")) {
16467       
16468       subjectPropertiesContents.append("\n# subject identifier to store in grouper's member table.  this is used to increase speed of loader and perhaps for provisioning\n"
16469           + "# you can have up to max 1 subject identifier\n");
16470 
16471     } else if (GrouperInstallerUtils.equals(paramName, "maxConnectionAge")) {
16472       
16473       subjectPropertiesContents.append("\n# seconds of max connection age\n");
16474 
16475     } else if (GrouperInstallerUtils.equals(paramName, "testConnectionOnCheckout")) {
16476       
16477       subjectPropertiesContents.append("\n# if connections from pool should be tested when checked out from pool\n");
16478 
16479     } else if (GrouperInstallerUtils.equals(paramName, "preferredTestQuery")) {
16480       
16481       subjectPropertiesContents.append("\n# query to use to test the connection when checking out from pool\n");
16482 
16483     } else if (GrouperInstallerUtils.equals(paramName, "idleConnectionTestPeriod")) {
16484       
16485       subjectPropertiesContents.append("\n# seconds between tests of idle connections in pool\n");
16486 
16487     } else if (GrouperInstallerUtils.equals(paramName, "dbDriver")) {
16488       
16489       subjectPropertiesContents.append("\n#       e.g. mysql:           com.mysql.jdbc.Driver\n"
16490           + "#       e.g. p6spy (log sql): com.p6spy.engine.spy.P6SpyDriver\n"
16491           + "#         for p6spy, put the underlying driver in spy.properties\n"
16492           + "#       e.g. oracle:          oracle.jdbc.driver.OracleDriver\n"
16493           + "#       e.g. postgres:        org.postgresql.Driver\n");
16494 
16495     } else if (GrouperInstallerUtils.equals(paramName, "dbUrl")) {
16496       
16497       subjectPropertiesContents.append("\n#       e.g. mysql:           jdbc:mysql://localhost:3306/grouper\n"
16498           + "#       e.g. p6spy (log sql): [use the URL that your DB requires]\n"
16499           + "#       e.g. oracle:          jdbc:oracle:thin:@server.school.edu:1521:sid\n"
16500           + "#       e.g. postgres:        jdbc:postgresql:grouper\n");
16501 
16502     } else if (GrouperInstallerUtils.equals(paramName, "dbUser")) {
16503       
16504       subjectPropertiesContents.append("\n# username when connecting to the database\n");
16505 
16506     } else if (GrouperInstallerUtils.equals(paramName, "dbPwd")) {
16507       
16508       subjectPropertiesContents.append("\n# password when connecting to the database (or file with encrypted password inside)\n");
16509 
16510     } else if (GrouperInstallerUtils.equals(paramName, "maxResults")) {
16511       
16512       subjectPropertiesContents.append("\n# maximum number of results from a search, generally no need to get more than 1000\n");
16513 
16514     } else if (GrouperInstallerUtils.equals(paramName, "dbTableOrView")) {
16515       
16516       subjectPropertiesContents.append("\n# the table or view to query results from.  Note, could prefix with a schema name\n");
16517 
16518     } else if (GrouperInstallerUtils.equals(paramName, "subjectIdCol")) {
16519       
16520       subjectPropertiesContents.append("\n# the column name to get the subjectId from\n");
16521 
16522     } else if (GrouperInstallerUtils.equals(paramName, "nameCol")) {
16523       
16524       subjectPropertiesContents.append("\n# the column name to get the name from\n");
16525 
16526     } else if (GrouperInstallerUtils.equals(paramName, "lowerSearchCol")) {
16527       
16528       subjectPropertiesContents.append("\n# search col where general searches take place, lower case\n");
16529 
16530     } else if (GrouperInstallerUtils.equals(paramName, "defaultSortCol")) {
16531       
16532       subjectPropertiesContents.append("\n# optional col if you want the search results sorted in the API (note, UI might override)\n");
16533 
16534     } else if (paramName.startsWith("subjectIdentifierCol")) {
16535       
16536       subjectPropertiesContents.append("\n# you can count up from 0 to N of columns to search by identifier (which might also include by id)\n");
16537 
16538     } else if (paramName.startsWith("subjectAttributeName")) {
16539       
16540       subjectPropertiesContents.append("\n# you can count up from 0 to N of attributes for various cols.  The name is how to reference in subject.getAttribute()\n");
16541 
16542     } else if (paramName.startsWith("subjectAttributeCol")) {
16543       
16544       subjectPropertiesContents.append("\n# now you can count up from 0 to N of attributes for various cols.  The name is how to reference in subject.getAttribute()\n");
16545 
16546     } else if (GrouperInstallerUtils.equals(paramName, "statusDatastoreFieldName")) {
16547       
16548       subjectPropertiesContents.append("\n# STATUS SECTION for searches to filter out inactives and allow\n"
16549           + "# the user to filter by status with e.g. status=all\n"
16550           + "# this is optional, and advanced\n"
16551           + "#\n"
16552           + "# field in database or ldap or endpoint that is the status field\n");
16553 
16554     } else if (GrouperInstallerUtils.equals(paramName, "statusLabel")) {
16555       
16556       subjectPropertiesContents.append("\n# search string from user which represents the status.  e.g. status=active\n");
16557 
16558     } else if (GrouperInstallerUtils.equals(paramName, "statusesFromUser")) {
16559       
16560       subjectPropertiesContents.append("\n# available statuses from screen (if not specified, any will be allowed). comma separated list.\n"
16561           + "# Note, this is optional and you probably dont want to configure it, it is mostly necessary\n"
16562           + "# when you have multiple sources with statuses...  if someone types an invalid status\n"
16563           + "# and you have this configured, it will not filter by it\n");
16564 
16565     } else if (GrouperInstallerUtils.equals(paramName, "statusAllFromUser")) {
16566       
16567       subjectPropertiesContents.append("\n# all label from the user\n");
16568 
16569     } else if (GrouperInstallerUtils.equals(paramName, "statusSearchDefault")) {
16570       
16571       subjectPropertiesContents.append("\n# if no status is specified, this will be used (e.g. for active only).  Note, the value should be of the\n"
16572           + "# form the user would type in\n");
16573 
16574     } else if (paramName.startsWith("statusTranslateUser")) {
16575       
16576       subjectPropertiesContents.append("\n# translate between screen values of status, and the data store value.  Increment the 0 to 1, 2, etc for more translations.\n"
16577           + "# so the user could enter: status=active, and that could translate to status_col=A.  The 'user' is what the user types in,\n"
16578           + "# the 'datastore' is what is in the datastore.  The user part is not case-sensitive.  Note, this could be a many to one\n");
16579 
16580     } else if (paramName.startsWith("statusTranslateDatastore")) {
16581       
16582       //hmmm, nothing to do here
16583     } else if (GrouperInstallerUtils.equals(paramName, "INITIAL_CONTEXT_FACTORY")) {
16584       
16585       subjectPropertiesContents.append("\n# e.g. com.sun.jndi.ldap.LdapCtxFactory\n");
16586 
16587     } else if (GrouperInstallerUtils.equals(paramName, "PROVIDER_URL")) {
16588       
16589       subjectPropertiesContents.append("\n# e.g. ldap://localhost:389\n");
16590 
16591     } else if (GrouperInstallerUtils.equals(paramName, "SECURITY_AUTHENTICATION")) {
16592       
16593       subjectPropertiesContents.append("\n# e.g. simple, none, sasl_mech\n");
16594 
16595     } else if (GrouperInstallerUtils.equals(paramName, "SECURITY_PRINCIPAL")) {
16596       
16597       subjectPropertiesContents.append("\n# e.g. cn=Manager,dc=example,dc=edu\n");
16598 
16599     } else if (GrouperInstallerUtils.equals(paramName, "SECURITY_CREDENTIALS")) {
16600       
16601       subjectPropertiesContents.append("\n# can be a password or a filename of the encrypted password\n");
16602 
16603     } else if (GrouperInstallerUtils.equals(paramName, "SubjectID_AttributeType")) {
16604       
16605       subjectPropertiesContents.append("\n# ldap attribute which is the subject id.  e.g. exampleEduRegID   Each subject has one and only one subject id.  Generally it is opaque and permanent.\n");
16606 
16607     } else if (GrouperInstallerUtils.equals(paramName, "SubjectID_formatToLowerCase")) {
16608       
16609       subjectPropertiesContents.append("\n# if the subject id should be changed to lower case after reading from datastore.  true or false\n");
16610 
16611     } else if (GrouperInstallerUtils.equals(paramName, "Name_AttributeType")) {
16612       
16613       subjectPropertiesContents.append("\n# attribute which is the subject name\n");
16614 
16615     } else if (GrouperInstallerUtils.equals(paramName, "Description_AttributeType")) {
16616       
16617       subjectPropertiesContents.append("\n# attribute which is the subject description\n");
16618 
16619     } else if (GrouperInstallerUtils.equals(paramName, "VTLDAP_VALIDATOR")) {
16620       
16621       subjectPropertiesContents.append("\n# LdapValidator provides an interface for validating ldap objects when they are in the pool.\n"
16622           + "# ConnectLdapValidator validates an ldap connection is healthy by testing it is connected.\n"
16623           + "# CompareLdapValidator validates an ldap connection is healthy by performing a compare operation.\n");
16624 
16625     } else if (GrouperInstallerUtils.equals(paramName, "VTLDAP_VALIDATOR_COMPARE_DN")) {
16626       
16627       subjectPropertiesContents.append("\n# if VTLDAP_VALIDATOR is CompareLdapValidator, this is the DN of the ldap object to get, e.g. ou=People,dc=vt,dc=edu\n");
16628 
16629     } else if (GrouperInstallerUtils.equals(paramName, "VTLDAP_VALIDATOR_COMPARE_SEARCH_FILTER_STRING")) {
16630       
16631       subjectPropertiesContents.append("\n# if VTLDAP_VALIDATOR is CompareLdapValidator, this is the filter string, e.g. ou=People\n");
16632 
16633     } else {
16634       
16635       //hmmm, not sure what to do here, no comment
16636       subjectPropertiesContents.append("\n");
16637 
16638     }
16639 
16640   }
16641   
16642   /**
16643    * valid source param pattern
16644    */
16645   private static Pattern sourcesValidParamPattern = Pattern.compile("^[A-Za-z0-9_]+$");
16646   
16647   /**
16648    * 
16649    * @param subjectPropertiesFile 
16650    * @param sourcesXmlUrl
16651    */
16652   public static void convertSourcesXmlToProperties(File subjectPropertiesFile, URL sourcesXmlUrl) {
16653 
16654     //look at base properties
16655     Properties subjectProperties = subjectPropertiesFile.exists() ? 
16656         GrouperInstallerUtils.propertiesFromFile(subjectPropertiesFile) : new Properties();
16657 
16658     if (subjectPropertiesFile.exists()) {
16659       
16660       //lets see if it just has the default.  the default has no properties
16661       if (subjectProperties.size() > 0) {
16662         throw new RuntimeException(subjectPropertiesFile.getAbsolutePath() + " exists and must not!  Backup your subject.properties and run this again and merge your subject.properties into the result");
16663       }
16664     }
16665     
16666     if (!subjectPropertiesFile.getParentFile().exists() || !subjectPropertiesFile.getParentFile().isDirectory()) {
16667       throw new RuntimeException(subjectPropertiesFile.getParentFile().getAbsolutePath() + " must exist and must be a directory");
16668     }
16669     
16670     StringBuilder subjectPropertiesContents = new StringBuilder();
16671     
16672     subjectPropertiesContents.append(
16673               "# Copyright 2016 Internet2\n"
16674             + "#\n"
16675             + "# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
16676             + "# you may not use this file except in compliance with the License.\n"
16677             + "# You may obtain a copy of the License at\n"
16678             + "#\n"
16679             + "#   http://www.apache.org/licenses/LICENSE-2.0\n"
16680             + "#\n"
16681             + "# Unless required by applicable law or agreed to in writing, software\n"
16682             + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n"
16683             + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
16684             + "# See the License for the specific language governing permissions and\n"
16685             + "# limitations under the License.\n"
16686             + "\n"
16687             + "#\n"
16688             + "# Subject configuration\n"
16689             + "#\n"
16690             + "\n"
16691             + "# The subject properties uses Grouper Configuration Overlays (documented on wiki)\n"
16692             + "# By default the configuration is read from subject.base.properties\n"
16693             + "# (which should not be edited), and the subject.properties overlays\n"
16694             + "# the base settings.  See the subject.base.properties for the possible\n"
16695             + "# settings that can be applied to the subject.properties\n\n"
16696         );
16697 
16698     subjectPropertiesContents.append(
16699         "# enter the location of the sources.xml.  Must start with classpath: or file:\n"
16700         + "# blank means dont use sources.xml, use subject.properties\n"
16701         + "# default is: classpath:sources.xml\n"
16702         + "# e.g. file:/dir1/dir2/sources.xml\n"
16703         + "subject.sources.xml.location = \n\n");
16704       
16705     //   <source adapterClass="edu.internet2.middleware.grouper.GrouperSourceAdapter">
16706     NodeList sourcesNodeList = GrouperInstallerUtils.xpathEvaluate(sourcesXmlUrl, "/sources/source");
16707     
16708     Set<String> usedConfigNames = new HashSet<String>();
16709     
16710     for (int i=0;i<sourcesNodeList.getLength();i++) {
16711 
16712       Element sourceElement = (Element)sourcesNodeList.item(i);
16713       
16714       String configName = null;
16715       String id = null;
16716       {
16717         //  #########################################
16718         //  ## Configuration for source: whateverId
16719         //  #########################################
16720         //  # generally the <configName> is the same as or similar to the source id.  This cannot have special characters
16721         //  # this links together all the configs for this source
16722         //  # subjectApi.source.<configName>.id = sourceId
16723         id = xmlElementValue(sourceElement, "id", true, "source index " + i);
16724         
16725         //these are configured in subject.base.properties
16726         if (GrouperInstallerUtils.equals(id, "g:gsa")
16727             || GrouperInstallerUtils.equals(id, "grouperEntities")) {
16728           continue;
16729         }
16730         configName = convertEhcacheNameToPropertiesKey(id, usedConfigNames);
16731         usedConfigNames.add(configName);
16732         
16733         subjectPropertiesContents.append(
16734             "\n#########################################\n"
16735             + "## Configuration for source id: " + id + "\n"
16736             + "## Source configName: " + configName + "\n"
16737             + "#########################################\n"
16738             + "subjectApi.source." + configName + ".id = " + id + "\n"
16739             );
16740       }
16741 
16742       {
16743         // <name>Grouper: Group Source Adapter</name>
16744         String name = xmlElementValue(sourceElement, "name", true, "source: " + id);
16745         subjectPropertiesContents.append("\n# this is a friendly name for the source\n"
16746             + "subjectApi.source." + configName + ".name = " + name + "\n");
16747       }
16748       
16749       {
16750         // <type>group</type>
16751         NodeList typeNodeList = sourceElement.getElementsByTagName("type");
16752         Set<String> typeSet = new LinkedHashSet<String>();
16753         
16754         for (int typeIndex=0; typeIndex<typeNodeList.getLength(); typeIndex++) {
16755           
16756           typeSet.add(GrouperInstallerUtils.trimToEmpty(typeNodeList.item(typeIndex).getTextContent()));
16757           
16758         }
16759         if (typeNodeList.getLength() > 0) {
16760           
16761           subjectPropertiesContents.append("\n# type is not used all that much.  Can have multiple types, comma separate.  Can be person, group, application\n"
16762               + "subjectApi.source." + configName + ".types = " + GrouperInstallerUtils.join(typeSet.iterator(), ", ") + "\n"
16763               );
16764         }
16765 
16766       }
16767 
16768       {
16769         NamedNodeMap configuredNamedNodeMap = sourceElement.getAttributes();
16770         if (configuredNamedNodeMap.getLength() != 1 || !"adapterClass".equals(configuredNamedNodeMap.item(0).getNodeName())) {
16771           throw new RuntimeException("Expecting one source attribute: adapterClass for source: " + id);
16772         }
16773         
16774         String adapterClass = sourceElement.getAttribute("adapterClass");
16775 
16776         subjectPropertiesContents.append("\n# the adapter class implements the interface: edu.internet2.middleware.subject.Source\n");
16777         subjectPropertiesContents.append("# adapter class must extend: edu.internet2.middleware.subject.provider.BaseSourceAdapter\n");
16778         subjectPropertiesContents.append("# edu.internet2.middleware.grouper.subj.GrouperJdbcSourceAdapter2  :  if doing JDBC this should be used if possible.  All subject data in one table/view.\n");
16779         subjectPropertiesContents.append("# edu.internet2.middleware.grouper.subj.GrouperJdbcSourceAdapter   :  oldest JDBC source.  Put freeform queries in here\n");
16780         subjectPropertiesContents.append("# edu.internet2.middleware.grouper.subj.GrouperJndiSourceAdapter   :  used for LDAP\n");
16781         subjectPropertiesContents.append("subjectApi.source." + configName + ".adapterClass = " + adapterClass + "\n");
16782       }      
16783 
16784       //  # You can flag a source as not throwing exception on a findAll (general search) i.e. if it is
16785       //  # ok if it is down.  Generally you probably won't want to do this.  It defaults to true if omitted.
16786       //  # subjectApi.source.<configName>.param.throwErrorOnFindAllFailure.value = true
16787 
16788       {
16789         //  <init-param>
16790         //    <param-name>subjectVirtualAttribute_0_searchAttribute0</param-name>
16791         //    <param-value>${subject.getAttributeValue('name')},${subject.getAttributeValue('displayName')},${subject.getAttributeValue('alternateName')}</param-value>
16792         //  </init-param>
16793         NodeList initParamNodeList = sourceElement.getElementsByTagName("init-param");
16794         
16795         Set<String> usedParamNames = new HashSet<String>();
16796 
16797         for (int initParamIndex=0; initParamIndex<initParamNodeList.getLength(); initParamIndex++) {
16798           
16799           Element initParamElement = (Element)initParamNodeList.item(initParamIndex);
16800           String paramName = xmlElementValue(initParamElement, "param-name", true, "param-name index " + initParamIndex + " in source " + id);
16801           String paramValue = xmlElementValue(initParamElement, "param-value", true, "param-value " + paramName + " in source " + id);
16802           
16803           String paramConfigKey = convertEhcacheNameToPropertiesKey(paramName, usedParamNames);
16804           convertSourcesXmlParamComment(paramName, subjectPropertiesContents, paramValue);
16805 
16806           //if the param name is invalid, then have a name config
16807           if (!GrouperInstallerUtils.equals(paramName, paramConfigKey)) {
16808             subjectPropertiesContents.append("subjectApi.source." + configName + ".param." + paramConfigKey + ".name = " + paramName + "\n");
16809           }
16810           
16811           //cant have newlines in there, convert to spaces
16812           paramValue = GrouperInstallerUtils.replaceNewlinesWithSpace(paramValue);
16813           
16814           //  # subjectApi.source.<configName>.param.throwErrorOnFindAllFailure.value = true
16815           subjectPropertiesContents.append("subjectApi.source." + configName + ".param." + paramConfigKey + ".value = " + paramValue + "\n");
16816 
16817         }
16818 
16819       }
16820 
16821       {
16822         //  <search>
16823         //    <searchType>searchSubject</searchType>
16824         //    <param>
16825         //      <param-name>sql</param-name>
16826         //                <param-value>
16827         //                  select
16828         //                    s.subjectid as id, s.name as name,
16829         //      </param-value>
16830         //    </param>
16831         //  </search>
16832         NodeList searchNodeList = sourceElement.getElementsByTagName("search");
16833         
16834         for (int searchIndex=0; searchIndex<searchNodeList.getLength(); searchIndex++) {
16835           
16836           Element searchElement = (Element)searchNodeList.item(searchIndex);
16837           
16838           String searchType = xmlElementValue(searchElement, "searchType", true, "search element in the source: " + id);
16839 
16840           NodeList searchParamNodeList = searchElement.getElementsByTagName("param");
16841 
16842           if (GrouperInstallerUtils.equals(searchType, "searchSubject")) {
16843             subjectPropertiesContents.append("\n#searchSubject: find a subject by ID.  ID is generally an opaque and permanent identifier, e.g. 12345678.\n"
16844                 + "#  Each subject has one and only on ID.  Returns one result when searching for one ID.\n");
16845           } else if (GrouperInstallerUtils.equals(searchType, "searchSubjectByIdentifier")) {
16846             subjectPropertiesContents.append("\n#searchSubjectByIdentifier: find a subject by identifier.  Identifier is anything that uniquely\n"
16847                 + "#  identifies the user, e.g. jsmith or jsmith@institution.edu.\n"
16848                 + "#  Subjects can have multiple identifiers.  Note: it is nice to have if identifiers are unique\n"
16849                 + "#  even across sources.  Returns one result when searching for one identifier.\n");
16850           } else if (GrouperInstallerUtils.equals(searchType, "search")) {
16851             subjectPropertiesContents.append("\n#   search: find subjects by free form search.  Returns multiple results.\n");
16852           } else {
16853             System.out.println("Not expecting searchType: '" + searchType + "'");
16854           }
16855 
16856           for (int searchParamIndex=0; searchParamIndex<searchParamNodeList.getLength(); searchParamIndex++) {
16857             
16858             Element searchParamElement = (Element)searchParamNodeList.item(searchParamIndex);
16859             
16860             String paramName = xmlElementValue(searchParamElement, "param-name", true, 
16861                 "search param name element index " + searchParamIndex + " in the source: " + id);
16862           
16863             String paramValue = xmlElementValue(searchParamElement, "param-value", true, 
16864                 "search param value element index " + searchParamIndex + " in the source: " + id);
16865 
16866             // cant have newlines in a properties file
16867             paramValue = GrouperInstallerUtils.replaceNewlinesWithSpace(paramValue);
16868 
16869             //  #
16870             //  # This is how search params are specified.  Note, each source can have different params for each search type
16871             //  # subjectApi.source.<configName>.search.<searchType>.param.<paramName>.value = something
16872             //  #
16873             //  ##############################################
16874             //  #
16875             //  # Searches for edu.internet2.middleware.grouper.subj.GrouperJdbcConnectionProvider
16876             //  #
16877             //  # searchSubject:
16878             //  #
16879             //  # subjectApi.source.<configName>.search.searchSubject.param.sql.value = select s.subjectid as id, s.name as name, (select sa2.value from subjectattribute sa2 where name='name' and sa2.SUBJECTID = s.subjectid) as lfname, (select sa3.value from subjectattribute sa3 where name='loginid' and sa3.SUBJECTID = s.subjectid) as loginid, (select sa4.value from subjectattribute sa4 where name='description' and sa4.SUBJECTID = s.subjectid) as description, (select sa5.value from subjectattribute sa5 where name='email' and sa5.SUBJECTID = s.subjectid) as email from subject s where {inclause}
16880             //  #    inclause allows searching by subject id for multiple ids in one query
16881             //  # subjectApi.source.<configName>.search.searchSubject.param.inclause.value = s.subjectid = ?
16882 
16883             if (!sourcesValidParamPattern.matcher(paramName).matches()) {
16884               throw new RuntimeException("Source " + id + " search " + searchType + " param name is not valid: '" + paramName + "'");
16885             }
16886             if (GrouperInstallerUtils.equals(searchType, "searchSubject")) {
16887               if (GrouperInstallerUtils.equals("sql", paramName)) {
16888                 subjectPropertiesContents.append("\n# sql is the sql to search for the subject by id should use an {inclause}\n");
16889               } else if (GrouperInstallerUtils.equals("inclause", paramName)) {
16890                 subjectPropertiesContents.append("\n# inclause allows searching by subject for multiple ids or identifiers in one query, must have {inclause} in the sql query,\n"
16891                     + "#    this will be subsituted to in clause with the following.  Should use a question mark ? for bind variable\n");
16892               } else if (GrouperInstallerUtils.equals("filter", paramName)) {
16893                   subjectPropertiesContents.append("\n# sql is the sql to search for the subject by id.  %TERM% will be subsituted by the id searched for\n");
16894               }
16895             } else if (GrouperInstallerUtils.equals(searchType, "searchSubjectByIdentifier")) {
16896               if (GrouperInstallerUtils.equals("sql", paramName)) {
16897                 subjectPropertiesContents.append("\n# sql is the sql to search for the subject by identifier should use an {inclause}\n");
16898               } else if (GrouperInstallerUtils.equals("inclause", paramName)) {
16899                 subjectPropertiesContents.append("\n# inclause allows searching by subject for multiple ids or identifiers in one query, must have {inclause} in the sql query,\n"
16900                     + "#    this will be subsituted to in clause with the following.  Should use a question mark ? for bind variable\n");
16901               } else if (GrouperInstallerUtils.equals("filter", paramName)) {
16902                 subjectPropertiesContents.append("\n# sql is the sql to search for the subject by identifier.  %TERM% will be subsituted by the identifier searched for\n");
16903               }
16904             } else if (GrouperInstallerUtils.equals(searchType, "search")) {
16905               if (GrouperInstallerUtils.equals("sql", paramName)) {
16906                 subjectPropertiesContents.append("\n# sql is the sql to search for the subject free-form search.  user question marks for bind variables\n");
16907               } else if (GrouperInstallerUtils.equals("inclause", paramName)) {
16908                 throw new RuntimeException("Should not have incluse for search of type search in source: " + id);
16909               } else if (GrouperInstallerUtils.equals("filter", paramName)) {
16910                 subjectPropertiesContents.append("\n# sql is the sql to search for the subject by free form search.  %TERM% will be subsituted by the text searched for\n");
16911               }
16912             }
16913             if (GrouperInstallerUtils.equals("scope", paramName)) {
16914               subjectPropertiesContents.append("\n# Scope Values can be: OBJECT_SCOPE, ONELEVEL_SCOPE, SUBTREE_SCOPE\n");
16915             } else if (GrouperInstallerUtils.equals("base", paramName)) {
16916               subjectPropertiesContents.append("\n# base dn to search in\n");
16917             }
16918             
16919             //  # subjectApi.source.<configName>.search.<searchType>.param.<paramName>.value = something
16920             subjectPropertiesContents.append("subjectApi.source." + configName + ".search." + searchType + ".param." + paramName + ".value = " + paramValue + "\n");
16921             
16922           }
16923         }
16924       }
16925       
16926       {
16927         // # attributes from ldap object to become subject attributes.  comma separated
16928         // <attribute>cn</attribute>
16929         // <attribute>sn</attribute>
16930         NodeList attributeNodeList = sourceElement.getElementsByTagName("attribute");
16931         Set<String> attributeSet = new LinkedHashSet<String>();
16932 
16933         for (int attributeIndex=0; attributeIndex<attributeNodeList.getLength(); attributeIndex++) {
16934 
16935           attributeSet.add(GrouperInstallerUtils.trimToEmpty(attributeNodeList.item(attributeIndex).getTextContent()));
16936         }
16937         if (attributeNodeList.getLength() > 0) {
16938 
16939           subjectPropertiesContents.append("\n# attributes from ldap object to become subject attributes.  comma separated\n"
16940               + "subjectApi.source." + configName + ".attributes = " + GrouperInstallerUtils.join(attributeSet.iterator(), ", ") + "\n");
16941 
16942         }
16943 
16944       }
16945       
16946       {
16947         // # internal attributes are used by grouper only not exposed to code that uses subjects.  comma separated
16948         // <internal-attributes>cn</internal-attributes>
16949         // <internal-attributes>sn</internal-attributes>
16950         NodeList internalAttributeNodeList = sourceElement.getElementsByTagName("internal-attribute");
16951         Set<String> internalAttributeSet = new LinkedHashSet<String>();
16952 
16953         for (int internalAttributeIndex=0; internalAttributeIndex<internalAttributeNodeList.getLength(); internalAttributeIndex++) {
16954 
16955           internalAttributeSet.add(GrouperInstallerUtils.trimToEmpty(internalAttributeNodeList.item(internalAttributeIndex).getTextContent()));
16956 
16957         }
16958         if (internalAttributeNodeList.getLength() > 0) {
16959 
16960           subjectPropertiesContents.append("\n# internal attributes are used by grouper only not exposed to code that uses subjects.  comma separated\n"
16961               + "subjectApi.source." + configName + ".internalAttributes = " + GrouperInstallerUtils.join(internalAttributeSet.iterator(), ", ") + "\n");
16962 
16963         }
16964 
16965       }
16966       //space between sources
16967       subjectPropertiesContents.append("\n");
16968     }
16969 
16970     GrouperInstallerUtils.saveStringIntoFile(subjectPropertiesFile, subjectPropertiesContents.toString());
16971 
16972   }
16973 
16974   /**
16975    * edit an xml file attribute in a xml file
16976    * @param file
16977    * @param elementName
16978    * @param elementMustHaveAttributeAndValue
16979    * @param newValue
16980    * @param description of change for sys out print
16981    * @param newAttributeName if adding new attribute, this is the name
16982    * @return true if edited file, or false if not but didnt need to, null if not found
16983    */
16984   public static Boolean editXmlFileAttribute(File file, String elementName, Map<String, String> elementMustHaveAttributeAndValue, 
16985       String newAttributeName, String newValue, String description) {
16986 
16987     if (!file.exists() || file.length() == 0) {
16988       throw new RuntimeException("Why does " + file.getName() + " not exist and have contents? " 
16989           + file.getAbsolutePath());
16990     }
16991     
16992     String fileContents = GrouperInstallerUtils.readFileIntoString(file);
16993     
16994     boolean inComment = false;
16995     
16996     //lets parse the file and get to the element
16997     OUTER: for (int i=0;i<fileContents.length();i++) {
16998       
16999       //look for start element
17000       char curChar = fileContents.charAt(i);
17001 
17002       Character nextChar = (i+1) < fileContents.length() ? fileContents.charAt(i+1) : null;
17003       Character nextNextChar = (i+2) < fileContents.length() ? fileContents.charAt(i+2) : null;
17004       Character nextNextNextChar = (i+3) < fileContents.length() ? fileContents.charAt(i+3) : null;
17005       
17006       //if we are in comment, see when we are out of comment
17007       if (inComment) {
17008         if (curChar == '-' && nextChar != null && nextChar == '-' && nextNextChar != null && nextNextChar == '>') {
17009           inComment = false;
17010         }
17011         continue;
17012         
17013       }
17014 
17015       //look for a tag or comment
17016       if (curChar != '<') {
17017         continue;
17018       }
17019       
17020       //see if this is a comment
17021       if (nextChar != null && nextChar == '!' && nextNextChar != null && nextNextChar == '-' && nextNextNextChar != null && nextNextNextChar == '-') {
17022         inComment = true;
17023         continue;
17024       }
17025 
17026       //get tagName
17027       String currentElementName = _internalXmlTagName(fileContents, i+1);
17028       
17029       //not the right tag
17030       if (!GrouperInstallerUtils.equals(currentElementName, elementName)) {
17031         continue;
17032       }
17033       
17034       int tagNameStart = fileContents.indexOf(currentElementName, i+1);
17035       
17036       //get the attributes
17037       int tagAttributesStart = tagNameStart + currentElementName.length();
17038       XmlParseAttributesResult xmlParseAttributesResult = _internalXmlParseAttributes(fileContents, tagAttributesStart);
17039       Map<String, String> currentAttributes = xmlParseAttributesResult.getAttributes();
17040       
17041       if (GrouperInstallerUtils.length(elementMustHaveAttributeAndValue) > 0) {
17042         for (String attributeName : elementMustHaveAttributeAndValue.keySet()) {
17043           String expectedValue = elementMustHaveAttributeAndValue.get(attributeName);
17044           String hasValue = currentAttributes.get(attributeName);
17045 
17046           //if we dont have that value, then keep going
17047           if (!GrouperInstallerUtils.equals(expectedValue, hasValue)) {
17048             continue OUTER;
17049           }
17050         }
17051       }
17052       
17053       //we have the tag and it has the expected attributes
17054 
17055       //see if the attribute is even there...
17056       if (!currentAttributes.containsKey(newAttributeName)) {
17057         System.out.println(" - adding " + description + " with value: '" + newValue + "'");
17058         String newFileContents = fileContents.substring(0, tagAttributesStart) + " " + newAttributeName + "=\"" + newValue + 
17059             "\" " + fileContents.substring(tagAttributesStart, fileContents.length());
17060         GrouperInstallerUtils.writeStringToFile(file, newFileContents);
17061         return true;
17062       }
17063 
17064       //does it already have the value?
17065       String currentValue = currentAttributes.get(newAttributeName);
17066       
17067       //value is already there
17068       if (GrouperInstallerUtils.equals(currentValue, newValue)) {
17069         return false;
17070       }
17071 
17072       //it has the wrong value
17073       int startQuote = xmlParseAttributesResult.getAttributeStartIndex().get(newAttributeName);
17074       int endQuote = xmlParseAttributesResult.getAttributeEndIndex().get(newAttributeName);
17075 
17076       System.out.println(" - changing " + description + " from old value: '" + currentValue 
17077           + "' to new value: '" + newValue + "'");
17078 
17079       String newFileContents = fileContents.substring(0, startQuote+1)  + newValue + 
17080           fileContents.substring(endQuote, fileContents.length());
17081       GrouperInstallerUtils.writeStringToFile(file, newFileContents);
17082       return true;
17083 
17084     }
17085 
17086     return null;
17087 
17088   }
17089 
17090   /**
17091    * 
17092    * @param fileContents
17093    * @param tagIndexStart
17094    * @return the tag name
17095    */
17096   private static String _internalXmlTagName(String fileContents, int tagIndexStart) {
17097     StringBuilder tagName = new StringBuilder();
17098     for (int i=tagIndexStart; i<fileContents.length(); i++) {
17099       char curChar = fileContents.charAt(i);
17100       if (tagName.length() == 0 && Character.isWhitespace(curChar)) {
17101         continue;
17102       }
17103       if (Character.isWhitespace(curChar) || '/' == curChar || '>' == curChar) {
17104         return tagName.toString();
17105       }
17106       tagName.append(curChar);
17107     }
17108     throw new RuntimeException("How did I get here???? '" + tagName.toString() + "'");
17109   }
17110   
17111   /**
17112    * xml parse attribute result
17113    */
17114   private static class XmlParseAttributesResult {
17115 
17116     /**
17117      * attributes name to value
17118      */
17119     private Map<String, String> attributes;
17120     
17121     /**
17122      * attribute name to startIndex (of quote)
17123      */
17124     private Map<String, Integer> attributeStartIndex;
17125 
17126     /**
17127      * attribute name to endIndex (of quote)
17128      */
17129     private Map<String, Integer> attributeEndIndex;
17130 
17131     
17132     /**
17133      * attributes name to value
17134      * @return the attributes
17135      */
17136     public Map<String, String> getAttributes() {
17137       return this.attributes;
17138     }
17139 
17140     
17141     /**
17142      * attributes name to value
17143      * @param attributes1 the attributes to set
17144      */
17145     public void setAttributes(Map<String, String> attributes1) {
17146       this.attributes = attributes1;
17147     }
17148 
17149     
17150     /**
17151      * attribute name to startIndex (of quote)
17152      * @return the attributeStartIndex
17153      */
17154     public Map<String, Integer> getAttributeStartIndex() {
17155       return this.attributeStartIndex;
17156     }
17157     
17158     /**
17159      * attribute name to startIndex (of quote)
17160      * @param attributeStartIndex1 the attributeStartIndex to set
17161      */
17162     public void setAttributeStartIndex(Map<String, Integer> attributeStartIndex1) {
17163       this.attributeStartIndex = attributeStartIndex1;
17164     }
17165     
17166     /**
17167      * attribute name to endIndex (of quote)
17168      * @return the attributeEndIndex
17169      */
17170     public Map<String, Integer> getAttributeEndIndex() {
17171       return this.attributeEndIndex;
17172     }
17173     
17174     /**
17175      * attribute name to endIndex (of quote)
17176      * @param attributeEndIndex1 the attributeEndIndex to set
17177      */
17178     public void setAttributeEndIndex(Map<String, Integer> attributeEndIndex1) {
17179       this.attributeEndIndex = attributeEndIndex1;
17180     }
17181     
17182   }
17183 
17184   /**
17185    * 
17186    */
17187   public static enum GrouperInstallerAdminManageServiceAction {
17188   
17189     /** start */
17190     start,
17191     
17192     /** stop */
17193     stop,
17194     
17195     /** restart */
17196     restart,
17197     
17198     /** status */
17199     status;
17200     
17201     /**
17202      * 
17203      * @param string
17204      * @param exceptionIfInvalid
17205      * @param exceptionIfBlank
17206      * @return the action
17207      */
17208     public static GrouperInstallerAdminManageServiceAction valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
17209       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerAdminManageServiceAction.class, string, exceptionIfBlank, exceptionIfInvalid);
17210     }
17211     
17212   }
17213 
17214   /**
17215    * parse attributes
17216    * @param fileContents
17217    * @param tagAttributesStart is the index where the attributes start
17218    * @return the map of attribute names and values
17219    */
17220   private static XmlParseAttributesResult _internalXmlParseAttributes(String fileContents, int tagAttributesStart) {
17221 
17222     XmlParseAttributesResult xmlParseAttributesResult = new XmlParseAttributesResult();
17223 
17224     Map<String, String> attributes = new LinkedHashMap<String, String>();
17225     Map<String, Integer> attributeStartIndex = new LinkedHashMap<String, Integer>();
17226     Map<String, Integer> attributeEndIndex = new LinkedHashMap<String, Integer>();
17227 
17228     xmlParseAttributesResult.setAttributes(attributes);
17229     xmlParseAttributesResult.setAttributeStartIndex(attributeStartIndex);
17230     xmlParseAttributesResult.setAttributeEndIndex(attributeEndIndex);
17231     
17232     boolean inAttributeStartValue = false;
17233     boolean inAttributeStartName = true;
17234     boolean inAttributeName = false;
17235     boolean inAttributeValue = false;
17236     
17237     StringBuilder attributeName = null;
17238     StringBuilder attributeValue = null;
17239     
17240     for (int i=tagAttributesStart; i<fileContents.length(); i++) {
17241       char curChar = fileContents.charAt(i);
17242       boolean isWhitespace = Character.isWhitespace(curChar);
17243 
17244       //waiting for the attribute
17245       if ((inAttributeStartValue || inAttributeStartName) && isWhitespace) {
17246         continue;
17247       }
17248 
17249       //if waiting for value and equals, keep looking
17250       if (inAttributeStartValue && curChar == '=') {
17251         continue;
17252       }
17253 
17254       //waiting to start an attribute name, its not whitespace so do it
17255       if (inAttributeStartName) {
17256         
17257         //we done if we got to this character
17258         if (curChar == '/' || curChar == '>') {
17259           return xmlParseAttributesResult;
17260         }
17261         
17262         inAttributeStartName = false;
17263         inAttributeName = true;
17264         attributeName = new StringBuilder();
17265       }
17266 
17267       //if in an attribute name and whitespace or equals, then we are done
17268       if (inAttributeName && (isWhitespace || curChar == '=' )) {
17269         inAttributeName = false;
17270         inAttributeStartValue = true;
17271         continue;
17272       }
17273 
17274       //getting the attribute name
17275       if (inAttributeName) {
17276         attributeName.append(curChar);
17277         continue;
17278       }
17279 
17280       //if waiting for start value and found quote
17281       if (inAttributeStartValue && curChar == '"') {
17282         inAttributeStartValue = false;
17283         inAttributeValue = true;
17284         attributeValue = new StringBuilder();
17285         attributeStartIndex.put(attributeName.toString(), i);
17286         continue;
17287       }
17288 
17289       //if in attribute value and not quote, append the char
17290       if (inAttributeValue && curChar != '"') {
17291         attributeValue.append(curChar);
17292         continue;
17293       }
17294 
17295       //done with attribute value
17296       if (inAttributeValue && curChar == '"') {
17297         inAttributeValue = false;
17298         inAttributeStartName = true;
17299         if (attributes.containsKey(attributeName.toString())) {
17300           throw new RuntimeException("Duplicate attribute: " + attributeName.toString());
17301         }
17302         attributes.put(attributeName.toString(), attributeValue.toString());
17303         attributeEndIndex.put(attributeName.toString(), i);
17304         continue;
17305       }
17306 
17307       throw new RuntimeException("Why are we here? " + i + ", " + fileContents);
17308     }
17309     return xmlParseAttributesResult;
17310   }
17311   
17312   /** revert patch excludes */
17313   private static Set<String> revertPatchExcludes = new HashSet<String>();
17314   
17315   static {
17316     revertPatchExcludes.add("grouper.cache.properties");
17317     revertPatchExcludes.add("ehcache.xml");
17318     revertPatchExcludes.add("ehcache.example.xml");
17319   }
17320 }