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 //    File tommeDir = new File("/Users/vsachdeva/git/i2-grouper-new/grouper/grouper-misc/grouper-installer/container/tomee");
585 //    File serverXmlFile = new File(tommeDir.getAbsolutePath()
586 //        + File.separator + "conf" + File.separator + "server.xml");
587 //    
588 //    //<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
589 //    String newConnectorString = "<Connector port=\"8009\" protocol=\"AJP/1.3\" redirectPort=\"8443\" "
590 //        + "tomcatAuthentication=\"false\" URIEncoding=\"UTF-8\" scheme=\"https\" secure=\"true\" />";
591 //    
592 //    System.out.println("Editing tomee config file: " + serverXmlFile.getAbsolutePath());
593     
594 //    editFile(serverXmlFile, "port=\"8009\"", new String[]{"<Connector"}, 
595 //        null, newConnectorString, "connector modifications in server.xml");
596     
597   //<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
598     //editFile(serverXmlFile, "port=\"([\\d]+)\"", new String[]{"<Connector", "protocol=\"AJP/1.3\""}, null, "", "tomcat JK port");
599     
600     
601     
602 //    String str1 = new Crypto("Urg4lyLGawLdwEvByGmf").encrypt("admin123");
603 //    System.out.println(str1);
604 //    String str = new Crypto("Urg4lyLGawLdwEvByGmf").decrypt("Y8iDZWF8RdowIiCgVKNg9Q==");
605 //    System.out.println(str);
606 
607      grouperInstaller.mainLogic();
608 
609 //    grouperInstaller.upgradeExistingApplicationDirectoryString = "D:\\temp\\temp\\grouperJarCopyDest\\";
610 //    grouperInstaller.grouperBaseBakDir = "D:\\temp\\temp\\grouperJarBak\\";
611     
612 //    grouperInstaller.upgradeJars(new File("D:\\temp\\temp\\grouperJarCopySource"), new File("D:\\temp\\temp\\grouperJarCopyDest"));
613     
614 //    grouperInstaller.version = "2.5.0";
615 //    
616 //    grouperInstaller.grouperTarballDirectoryString = "D:\\temp\\temp\\grouperInstaller\\";
617 //    
618 //    grouperInstaller.grouperInstallDirectoryString = "D:\\temp\\temp\\grouperInstaller\\install\\";
619 //    
620 //    grouperInstaller.installMessagingAwsSqs();
621     
622     //new GrouperInstaller().convertEhcacheBaseToProperties(new File("/Users/mchyzer/git/grouper_v2_3/grouper/conf/ehcache.example.xml"));
623 
624 //    GrouperInstaller.downloadFile("https://github.com/Internet2/grouper/archive/GROUPER_2_2_BRANCH.zip",
625 //        "C:\\app\\grouperInstallerTarballDir\\GROUPER_2_2_BRANCH.zip", false, null, 
626 //        "grouperInstaller.autorun.createPatchDownloadSourceUseLocalIfExist", true
627 //        );
628 
629 //    GrouperInstaller grouperInstaller = new GrouperInstaller();
630 //
631 //    grouperInstaller.upgradeExistingApplicationDirectoryString = "C:\\app\\grouper_2_1_installer\\grouper.ui-2.1.5\\dist\\grouper\\";
632 //  
633 //    grouperInstaller.grouperBaseBakDir = "C:\\app\\grouperInstallerTarballDir\\bak_UI_2014_10_26_15_06_19_957\\";
634 //      
635 //    grouperInstaller.copyFiles("C:\\app\\grouperInstallerTarballDir\\grouper.ui-2.2.1\\dist\\grouper\\",
636 //        "C:\\app\\grouper_2_1_installer\\grouper.ui-2.1.5\\dist\\grouper\\",
637 //        GrouperInstallerUtils.toSet("WEB-INF/lib", "WEB-INF/web.xml", "WEB-INF/classes"));
638 
639     
640    // copyFiles();
641     
642 //    
643 //    
644 //    grouperInstaller.dbUrl = "jdbc:hsqldb:hsql://localhost:9001/grouper";
645 //    grouperInstaller.dbUser = "sa";
646 //    grouperInstaller.dbPass = "";
647 //    grouperInstaller.giDbUtils = new GiDbUtils(grouperInstaller.dbUrl, grouperInstaller.dbUser, grouperInstaller.dbPass);
648 //    grouperInstaller.untarredApiDir = new File("c:/mchyzer/grouper/trunk/grouper-installer/grouper.apiBinary-2.0.2");
649 //    grouperInstaller.grouperInstallDirectoryString = "C:/mchyzer/grouper/trunk/grouper-installer/";
650 //    
651 //    grouperInstaller.version = "2.0.2";
652 //    grouperInstaller.grouperSystemPassword = "myNewPass";
653 //    
654 //    grouperInstaller.addDriverJarToClasspath();
655 ////      
656 //    grouperInstaller.startHsqlDb();
657 //    grouperInstaller.checkDatabaseConnection();
658 ////      grouperInstaller.initDb();
659 ////      
660 ////      grouperInstaller.addQuickstartSubjects();
661 ////      grouperInstaller.addQuickstartData();
662 //    
663 ////    File uiDir = grouperInstaller.downloadUi();
664 ////    File unzippedUiFile = unzip(uiDir.getAbsolutePath());
665 ////    grouperInstaller.untarredUiDir = untar(unzippedUiFile.getAbsolutePath());
666 //
667 ////      grouperInstaller.configureUi();
668 ////
669 //      File antDir = grouperInstaller.downloadAnt();
670 //      File unzippedAntFile = unzip(antDir.getAbsolutePath());
671 //      grouperInstaller.untarredAntDir = untar(unzippedAntFile.getAbsolutePath());
672 ////
673 ////      grouperInstaller.buildUi();
674 //
675 //    File tomcatDir = grouperInstaller.downloadTomcat();
676 //    File unzippedTomcatFile = unzip(tomcatDir.getAbsolutePath());
677 //    grouperInstaller.untarredTomcatDir = untar(unzippedTomcatFile.getAbsolutePath());
678 //
679 //    grouperInstaller.configureTomcat();
680 //    
681 ////    grouperInstaller.configureTomcatUiWebapp();
682 ////    
683 //    grouperInstaller.tomcatConfigureGrouperSystem();
684 //    
685 ////    grouperInstaller.tomcatBounce("restart");
686 //
687 //    File wsDir = grouperInstaller.downloadWs();
688 //
689 //    File unzippedWsFile = unzip(wsDir.getAbsolutePath());
690 //    grouperInstaller.untarredWsDir = untar(unzippedWsFile.getAbsolutePath());
691 //    grouperInstaller.configureWs();
692 //    grouperInstaller.buildWs();
693 //    
694 //    grouperInstaller.configureTomcatWsWebapp();
695 //    grouperInstaller.tomcatBounce("restart");
696 //
697 //    File clientDir = grouperInstaller.downloadClient();
698 //    
699 //    File unzippedClientFile = unzip(clientDir.getAbsolutePath());
700 //    grouperInstaller.untarredClientDir = untar(unzippedClientFile.getAbsolutePath());
701 //    grouperInstaller.configureClient();
702 //
703 //    grouperInstaller.addGrouperSystemWsGroup();
704 //    
705 //    grouperInstaller.runClientCommand();
706 //    
707 //    //CommandResult commandResult = GrouperInstallerUtils.execCommand("cmd /c cd");
708 //    //String result = commandResult.getOutputText();
709 //    //
710 //    //System.out.println(result);
711 //
712 //
713 //    //editPropertiesFile(new File("C:\\mchyzer\\grouper\\trunk\\grouper-installer\\grouper.apiBinary-2.1.0\\conf\\grouper.hibernate.properties"), 
714 //    //    "hibernate.connection.password", "sdf");
715       
716     //if started hsql, then we need to exit since that thread will not stop
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       if (this.dbUrl.contains(":hsqldb:")) {
3999 
4000         this.untarredApiDir = grouperHibernatePropertiesFileLocal;
4001         //find the untarred API dir
4002         int MAX_TRIES = 6;
4003         for (int i=0;i<MAX_TRIES;i++) {
4004           File tryFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "grouper.apiBinary-" + this.version);
4005           if (tryFile.exists()) {
4006             this.untarredApiDir = tryFile;
4007             break;
4008           }
4009           this.untarredApiDir = this.untarredApiDir.getParentFile();
4010           if (i==MAX_TRIES-1) {
4011             System.out.print("Normally the database is started by the installer from the unzipped API directory.  \n"
4012                 + "Based on your inputted install directory, the API directory cannot be found.\n"
4013                 + "HSQL cannot be accessed, maybe try again with a different install directory");
4014             System.exit(1);
4015           }
4016         }
4017         
4018         if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.stop 
4019             || grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
4020           this.shutdownHsql();
4021         }
4022 
4023         if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
4024           GrouperInstallerUtils.sleep(3000);
4025         }
4026 
4027         if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.start 
4028             || grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
4029           this.startHsqlDb(false);
4030 
4031           GrouperInstallerUtils.sleep(3000);
4032 
4033           //check connection to database
4034           if (null == this.giDbUtils.checkConnection()) {
4035             System.out.println("Success: database is up, connection successful.");
4036           } else {
4037             System.out.println("ERROR: database is down... could not start");
4038           }
4039         } else {
4040           //check connection to database
4041           if (null == this.giDbUtils.checkConnection()) {
4042             System.out.println("ERROR: database is up... could not stop.");
4043           } else {
4044             System.out.println("Success: database is down.");
4045           }
4046           
4047         }
4048  
4049       } else {
4050         
4051         System.out.println("Error: you are using an external database, (URL above), you need to " + grouperInstallerAdminManageServiceAction + " that database yourself");
4052         
4053       }
4054 
4055     }
4056   }
4057 
4058   /**
4059    * 
4060    * @param grouperInstallerAdminManageServiceAction
4061    */
4062   private void adminManageGrouperDaemon(
4063       GrouperInstallerAdminManageServiceAction grouperInstallerAdminManageServiceAction) {
4064     boolean done = false;
4065     if (!GrouperInstallerUtils.isWindows()) {
4066       
4067       System.out.println("In unix you should have a /etc/init.d or launchctl script which manages the grouper daemon (see details on wiki).");
4068       System.out.print("If you have a service configured please enter name or <enter> to continue without a service: ");
4069       String daemonName = readFromStdIn("grouperInstaller.autorun.grouperDaemonNameOrContinue");
4070       if (!GrouperInstallerUtils.isBlank(daemonName)) {
4071         done = true;
4072         boolean isService = true;
4073         String command = "/sbin/service";
4074         if (!new File(command).exists()) {
4075           command = "/usr/sbin/service";
4076         }
4077         if (!new File(command).exists()) {
4078           command = "/bin/launchctl";
4079           isService = false;
4080         }
4081         if (!new File(command).exists()) {
4082           System.out.println("Cannot find servie command, looked for /sbin/service, /usr/sbin/service, and /bin/launchctl.  "
4083               + "Your version of unix services is not supported.  Contact the Grouper support team.");
4084           System.exit(1);
4085         }
4086         if (isService) {
4087           List<String> commands = new ArrayList<String>();
4088           commands.add(command);
4089           commands.add(daemonName);
4090           commands.add(grouperInstallerAdminManageServiceAction.name());
4091           
4092           System.out.println(grouperInstallerAdminManageServiceAction + " " + daemonName
4093               + " with command: " + convertCommandsIntoCommand(commands) + "\n");
4094 
4095           GrouperInstallerUtils.execCommand(
4096               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
4097               new File(this.grouperInstallDirectoryString), null, false, false, true);
4098         } else {
4099           // <pid> <status> mytask
4100           if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.status) {
4101             // launchctl list | grep mytask
4102             List<String> commandsToRun = new ArrayList<String>();
4103             commandsToRun.add(shCommand());
4104             commandsToRun.add("-c");
4105             commandsToRun.add(command + " list | " + grepCommand() + " " + daemonName);
4106             
4107             System.out.println(grouperInstallerAdminManageServiceAction + " " + daemonName
4108                 + " with command: " + convertCommandsIntoCommand(commandsToRun) + "\n");
4109 
4110             GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commandsToRun, String.class), true, true, null, 
4111                 new File(this.grouperInstallDirectoryString), null, false, false, true);
4112             
4113           } else {
4114             // launchctl start|stop mytask
4115             if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.stop 
4116                 || grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
4117               //stop daemon
4118               List<String> commands = new ArrayList<String>();
4119               commands.add(command);
4120               commands.add("stop");
4121               commands.add(daemonName);
4122               
4123               System.out.println("stopping " + daemonName
4124                   + " with command: " + convertCommandsIntoCommand(commands) + "\n");
4125 
4126               GrouperInstallerUtils.execCommand(
4127                   GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
4128                   new File(this.grouperInstallDirectoryString), null, false, false, true);
4129               
4130             }
4131  
4132             if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
4133               GrouperInstallerUtils.sleep(3000);
4134             }
4135  
4136             if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.start 
4137                 || grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
4138               //start daemon
4139               List<String> commands = new ArrayList<String>();
4140               commands.add(command);
4141               commands.add("start");
4142               commands.add(daemonName);
4143               
4144               System.out.println("starting " + daemonName
4145                   + " with command: " + convertCommandsIntoCommand(commands) + "\n");
4146 
4147               GrouperInstallerUtils.execCommand(
4148                   GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
4149                   new File(this.grouperInstallDirectoryString), null, false, false, true);
4150               
4151               GrouperInstallerUtils.sleep(5000);
4152             }
4153           }              
4154         }
4155       }
4156     }
4157 
4158     if (!done) {
4159       
4160       if (new File(this.grouperInstallDirectoryString + "grouper.apiBinary-" + this.version).exists()) {
4161         this.untarredApiDir = new File(this.grouperInstallDirectoryString + "grouper.apiBinary-" + this.version);
4162       }
4163       
4164       //this is for loader dir
4165       if (new File(this.grouperInstallDirectoryString + "WEB-INF").exists()) {
4166         this.upgradeExistingApplicationDirectoryString = this.grouperInstallDirectoryString;
4167       } else if (new File(this.grouperInstallDirectoryString + "bin").exists()) {
4168         this.upgradeExistingApplicationDirectoryString = this.grouperInstallDirectoryString;
4169       }
4170       
4171       String gshCommandLocal = gshCommand();
4172       if (gshCommandLocal.endsWith(".sh")) {
4173         gshCommandLocal = gshCommandLocal.substring(0, gshCommandLocal.length()-".sh".length());
4174       }
4175       if (gshCommandLocal.endsWith(".bat")) {
4176         gshCommandLocal = gshCommandLocal.substring(0, gshCommandLocal.length()-".bat".length());
4177       }
4178       
4179       if (!GrouperInstallerUtils.isWindows()) {
4180         if (gshCommandLocal.contains(" ")) {
4181           System.out.println("On unix the gsh command cannot contain whitespace!");
4182           System.exit(1);
4183         }
4184       }
4185       
4186       // ps -ef | grep -- -loader | grep -v grep
4187       List<String> psCommands = new ArrayList<String>();
4188       psCommands.add(shCommand());
4189       psCommands.add("-c");
4190       psCommands.add( psCommand() + " -ef | " + grepCommand() + " " + gshCommandLocal + " | " 
4191           + grepCommand() + " -- -loader | " + grepCommand() + " -v grep");
4192 
4193       //unix
4194       //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
4195       //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
4196       
4197       //mac
4198       //0     1     0   0 Sun06PM ??         1:15.38 /sbin/launchd
4199       //0    45     1   0 Sun06PM ??         0:06.80 /usr/sbin/syslogd
4200       
4201       Pattern pidPattern = Pattern.compile("^[^\\s]+\\s+([^\\s]+)\\s+.*$");
4202       
4203       if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.stop 
4204           || grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
4205 
4206         if (GrouperInstallerUtils.isWindows()) {
4207           System.out.print("In windows you need to find the java process in task manager and kill it, press <enter> to continue... ");
4208           readFromStdIn("grouperInstaller.autorun.enterToContinueWindowsCantKillProcess");
4209         } else {
4210 
4211           System.out.println("Stopping the grouper daemon is not an exact science, be careful!");
4212           System.out.println("This script will find the process id of the daemon and kill it.  Make it is correct!");
4213           System.out.println("Finding the grouper daemon process with: " + convertCommandsIntoCommand(psCommands));
4214 
4215           //stop daemon
4216           CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(psCommands, String.class), false, true, null, 
4217              new File(this.grouperInstallDirectoryString), null, false, false, true);
4218           if (commandResult.getExitCode() != 0 && !GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
4219             throw new RuntimeException("Could not execute: " + convertCommandsIntoCommand(psCommands) 
4220                + "\n" + commandResult.getErrorText()
4221                + "\n" + commandResult.getOutputText());
4222           }
4223           String outputText = GrouperInstallerUtils.defaultString(commandResult.getOutputText());
4224           if (GrouperInstallerUtils.isBlank(outputText)) {
4225             System.out.println("Cannot find the grouper daemon process, it is not running");
4226           } else {
4227             outputText = outputText.replace('\r', '\n');
4228             outputText = GrouperInstallerUtils.replace(outputText, "\r", "\n");
4229             List<String> lines = GrouperInstallerUtils.splitTrimToList(outputText, "\n");
4230             int MAX_LINES = 2;
4231             if (lines.size() > MAX_LINES) {
4232               System.out.println("Found more output than expected, please examine the services on your system and kill them manually");
4233               for (String line : lines) {
4234                 System.out.println(line);
4235               }
4236             } else {
4237               Set<String> pidsDone = new HashSet<String>();
4238               for (int i=0; i<MAX_LINES; i++) {
4239                 // ^[^\s]+\s+([^\s]+)\s+.*$
4240                 // start, then first thing, then spaces, then second thing is the pic, then spaces, then whatever and end string
4241                 Matcher matcher = pidPattern.matcher(lines.get(0));
4242                 if (matcher.matches()) {
4243                   String pid = matcher.group(1);
4244                   if (pidsDone.contains(pid)) {
4245                     System.out.println("Could not kill pid " + pid);
4246                     System.exit(1);
4247                   }
4248                   List<String> killCommandList = GrouperInstallerUtils.splitTrimToList(killCommand() + " -KILL " + pid, " ");
4249                   System.out.println("The command to kill the daemon is: " + convertCommandsIntoCommand(killCommandList));
4250                   System.out.print("Found pid " + pid + ", do you want this script to kill it? (t|f) [t]: ");
4251                   boolean killDaemon = readFromStdInBoolean(true, "grouperInstaller.autorun.killPidOfDaemon");
4252                   
4253                   if (killDaemon) {
4254 
4255                     //keep track that we tried this one
4256                     pidsDone.add(pid);
4257                     
4258                     commandResult = GrouperInstallerUtils.execCommand(
4259                         GrouperInstallerUtils.toArray(killCommandList, String.class), true, true, null, 
4260                        null, null, true, false, true);
4261                     
4262                     GrouperInstallerUtils.sleep(5000);
4263 
4264                     //get next line, hopefully first one isnt there anymore, maybe not second either...
4265                     commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(psCommands, String.class), false, true, null, 
4266                        null, null, false, false, true);
4267 
4268                     if (commandResult.getExitCode() != 0 && !GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
4269                       throw new RuntimeException("Could not execute: " + convertCommandsIntoCommand(psCommands) 
4270                          + "\n" + commandResult.getErrorText()
4271                          + "\n" + commandResult.getOutputText());
4272                     }
4273                     
4274                     outputText = GrouperInstallerUtils.defaultString(commandResult.getOutputText());
4275                     if (GrouperInstallerUtils.isBlank(outputText)) {
4276                       break;
4277                     }
4278                     
4279                     outputText = outputText.replace('\r', '\n');
4280                     outputText = GrouperInstallerUtils.replace(outputText, "\r", "\n");
4281                     lines = GrouperInstallerUtils.splitTrimToList(outputText, "\n");
4282                     
4283                   } else {
4284                     break;
4285                   }
4286                 }
4287               }
4288             }
4289           }
4290         }
4291       }
4292 
4293       if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
4294         GrouperInstallerUtils.sleep(3000);
4295       }
4296 
4297       if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.start 
4298           || grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
4299         //start
4300         startLoader(false);
4301         GrouperInstallerUtils.sleep(5000);
4302       }
4303 
4304       if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.status && GrouperInstallerUtils.isWindows()) {
4305         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).");
4306       } else {
4307                    
4308         //no matter what, status, or other, do a status
4309         if (!GrouperInstallerUtils.isWindows()) {
4310           //stop daemon
4311           System.out.println("Finding the grouper daemon process with: " + convertCommandsIntoCommand(psCommands));
4312           CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(psCommands, String.class), false, true, null, 
4313              null, null, false, false, true);
4314           if (commandResult.getExitCode() != 0 && !GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
4315             throw new RuntimeException("Could not execute: " + convertCommandsIntoCommand(psCommands) 
4316                + "\n" + commandResult.getErrorText()
4317                + "\n" + commandResult.getOutputText());
4318           }
4319           String outputText = GrouperInstallerUtils.defaultString(commandResult.getOutputText());
4320           if (GrouperInstallerUtils.isBlank(outputText)) {
4321             System.out.println("Cannot find the grouper daemon process, it is not running");
4322           } else {
4323             outputText = outputText.replace('\r', '\n');
4324             outputText = GrouperInstallerUtils.replace(outputText, "\r", "\n");
4325             List<String> lines = GrouperInstallerUtils.splitTrimToList(outputText, "\n");
4326             System.out.println("Grouper loader is running, here is the process output:");
4327             for (String line : lines) {
4328               System.out.println(line);
4329             }
4330           }
4331         }
4332       }
4333     }
4334   }
4335 
4336   /**
4337    * admin manage logs
4338    */
4339   private void adminManageLogs() {
4340     //see what we are upgrading: api, ui, ws, client
4341     this.appToUpgrade = grouperAppToUpgradeOrPatch("look at logs for");
4342 
4343     System.out.println("Find where the application is running, then find the log4j.properties in the classpath.");
4344     
4345     switch (this.appToUpgrade) {
4346       case PSP:
4347       case PSPNG:
4348         System.out.println("This runs in the API, so logging for the API will be examined.");
4349         //pass through to API
4350       case API:
4351         System.out.println("The API (generally invoked via GSH) logs to where the log4.properties specifies.");
4352         File log4jPropertiesFile = new File(this.grouperInstallDirectoryString + "grouper.apiBinary-" + this.version + File.separator 
4353             + "conf" + File.separator + "log4j.properties");
4354         
4355         if (!log4jPropertiesFile.exists()) {
4356           
4357           List<File> allFiles = GrouperInstallerUtils.fileListRecursive(new File(this.grouperInstallDirectoryString));
4358           log4jPropertiesFile = null;
4359           boolean multipleFound = false;
4360           for (File file : allFiles) {
4361             if ("log4j.properties".equals(file.getName())) {
4362               if (log4jPropertiesFile != null) {
4363                 multipleFound = true;
4364                 log4jPropertiesFile = null;
4365                 break;
4366               }
4367               log4jPropertiesFile = file;
4368             }
4369           }
4370           if (multipleFound || log4jPropertiesFile == null) {
4371             System.out.print("What is the absolute path of the log4j.properties? : ");
4372             String log4jPropertiesLocation = readFromStdIn("grouperInstaller.autorun.log4jPropertiesLocation");
4373             log4jPropertiesFile = new File(log4jPropertiesLocation);
4374             if (!log4jPropertiesFile.exists()) {
4375               System.out.println("Bad location: " + log4jPropertiesFile.getAbsolutePath());
4376               System.exit(1);
4377             }
4378           }
4379         }
4380         
4381         File logFile = new File(this.grouperInstallDirectoryString  
4382             + "logs" + File.separator + "grouper_error.log");
4383         String grouperHomeWithSlash = this.grouperInstallDirectoryString;
4384 
4385         if (!logFile.exists()) {
4386           logFile = new File(this.grouperInstallDirectoryString + "grouper.apiBinary-" + this.version + File.separator 
4387               + "logs" + File.separator + "grouper_error.log");
4388           grouperHomeWithSlash = this.grouperInstallDirectoryString + "grouper.apiBinary-" + this.version + File.separator;
4389         }              
4390         System.out.println("By default the installer configures the log file to be: " + logFile.getAbsolutePath());
4391         
4392         
4393         analyzeLogFile(log4jPropertiesFile, grouperHomeWithSlash, null, null);
4394         break;
4395       case CLIENT:
4396         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.");
4397         break;
4398       case WS:
4399       case UI:
4400         File catalinaLogFile = new File(this.grouperInstallDirectoryString + "logs");
4401         if (!catalinaLogFile.exists()) {
4402           //if used the webapps dir
4403           catalinaLogFile = new File(this.grouperInstallDirectoryString + ".." + File.separator + ".." + File.separator + "logs");
4404         }
4405         if (!catalinaLogFile.exists()) {
4406           catalinaLogFile = new File(this.grouperInstallDirectoryString + File.separator 
4407               + "apache-tomcat-" + this.tomcatVersion() + "" + File.separator + "logs");
4408         }
4409         
4410         System.out.println("Tomcat logs STDOUT and STDERR to the catalinaErr.log "
4411             + "and catalinaOut.log logfiles, which should be here: " + catalinaLogFile.getAbsolutePath());
4412         if (!catalinaLogFile.exists()) {
4413           System.out.println("Warning: that directory does not exist, so you will need to locate the logs directory for tomcat.");
4414         }
4415         System.out.println("Locate the " + this.appToUpgrade + " application files.");
4416         System.out.println("By default the installer has the " + this.appToUpgrade + " running based on the tomcat server.xml, "
4417             + "but could also run in the webapps dir.");
4418         
4419         File serverXmlFile = new File(catalinaLogFile.getParentFile().getAbsolutePath() + File.separator + "conf" + File.separator + "server.xml");
4420         
4421         if (!serverXmlFile.exists()) {
4422           System.out.println("server.xml not found: " + serverXmlFile.getAbsolutePath());
4423         } else {
4424           //<Context docBase="/Users/mchyzer/tmp/grouperInstaller/grouper.ws-2.3.0/grouper-ws/build/dist/grouper-ws" path="/grouper-ws" reloadable="false"/>
4425           //<Context docBase="/Users/mchyzer/tmp/grouperInstaller/grouper.ui-2.3.0/dist/grouper" path="/grouper" reloadable="false"/>
4426 
4427           System.out.println("The server.xml is located: " + serverXmlFile.getAbsolutePath());
4428 
4429           String tomcatPath = this.appToUpgrade == AppToUpgrade.UI ? "grouper" : "grouper-ws";
4430 
4431           System.out.print("What is the URL starting path? [" + tomcatPath + "]: ");
4432           String newTomcatPath = readFromStdIn(this.appToUpgrade == AppToUpgrade.UI ? "grouperInstaller.autorun.urlPathForUi" : "grouperInstaller.autorun.urlPathForWs");
4433           
4434           if (!GrouperInstallerUtils.isBlank(newTomcatPath)) {
4435             tomcatPath = newTomcatPath;
4436           }
4437 
4438           if (tomcatPath.endsWith("/") || tomcatPath.endsWith("\\")) {
4439             tomcatPath = tomcatPath.substring(0, tomcatPath.length()-1);
4440           }
4441           if (tomcatPath.startsWith("/") || tomcatPath.startsWith("\\")) {
4442             tomcatPath = tomcatPath.substring(1, tomcatPath.length());
4443           }                  
4444           String currentDocBase = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
4445               "Server/Service/Engine/Host/Context[@path='/" + tomcatPath + "']", "docBase");
4446 
4447           if (this.appToUpgrade == AppToUpgrade.UI) {
4448             //<Context docBase="/Users/mchyzer/tmp/grouperInstaller/grouper.ui-2.3.0/dist/grouper" path="/grouper" reloadable="false"/>
4449             System.out.println("Looking for an entry in the server.xml that looks like this:");
4450             System.out.println("  <Context docBase=\""
4451                 + GrouperInstallerUtils.defaultIfBlank(currentDocBase, this.grouperInstallDirectoryString 
4452                     + "grouper.ui-" + this.version + File.separator + "dist" + File.separator 
4453                     + "grouper")
4454                 + "\" path=\"/" + tomcatPath + "\" reloadable=\"false\"/>");
4455 
4456           } else if (this.appToUpgrade == AppToUpgrade.WS) {
4457             //<Context docBase="/Users/mchyzer/tmp/grouperInstaller/grouper.ws-2.3.0/grouper-ws/build/dist/grouper-ws" path="/grouper-ws" reloadable="false"/>
4458             System.out.println("Looking for an entry in the server.xml that looks like this:");
4459             System.out.println("  <Context docBase=\""
4460                 + GrouperInstallerUtils.defaultIfBlank(currentDocBase, this.grouperInstallDirectoryString 
4461                     + "grouper.ws-" + this.version + File.separator + "grouper-ws" 
4462                     + File.separator + "build" + File.separator + "dist" + File.separator 
4463                     + "grouper-ws")
4464                 + "\" path=\"/" + tomcatPath + "\" reloadable=\"false\"/>");
4465           }
4466           
4467           if (!GrouperInstallerUtils.isBlank(currentDocBase)) {
4468             System.out.println("The docBase for the " + tomcatPath + " entry in the server.xml is: " + currentDocBase);
4469           } else {
4470             //check webapps
4471             System.out.println("The docBase could not be found in the server.xml, check in the tomcat" 
4472                 + File.separator + "webapps directory");
4473             currentDocBase = catalinaLogFile.getParentFile().getAbsolutePath() + File.separator + "webapps" + File.separator + tomcatPath;
4474             if (!new File(currentDocBase).exists()) {
4475               System.out.println("Cant find where grouper is linked from tomcat, looked in server.xml and the webapps directory");
4476               currentDocBase = null;
4477             }
4478           }
4479           if (currentDocBase != null) {
4480             log4jPropertiesFile = new File(currentDocBase + File.separator + "WEB-INF" + File.separator + "classes" + File.separator + "log4j.properties");
4481             
4482             analyzeLogFile(log4jPropertiesFile, "${grouper.home}" + File.separator, new File(catalinaLogFile + File.separator + "catalinaOut.log"),
4483                 new File(catalinaLogFile + File.separator + "catalinaErr.log"));
4484           }
4485         }
4486         
4487         break;
4488         
4489       default: 
4490         throw new RuntimeException("Not expecting appToUpgrade: " + this.appToUpgrade + "!");
4491     }
4492     
4493   }
4494   
4495   /**
4496    * 
4497    * @param log4jPropertiesFile
4498    * @param grouperHomeWithSlash
4499    * @param stdoutLocation
4500    * @param stderrLocation
4501    */
4502   private void analyzeLogFile(File log4jPropertiesFile, String grouperHomeWithSlash, File stdoutLocation, File stderrLocation) {
4503     System.out.println("The log4j.properties is located in: " 
4504         + log4jPropertiesFile.getAbsolutePath());
4505 
4506     if (!log4jPropertiesFile.exists()) {
4507     
4508       System.out.println("Error, the log4j.properties file could not be found.");
4509 
4510     } else {
4511       
4512       System.out.println("Examine the log4j.properties to see where it is logging");
4513       
4514       Properties log4jProperties = GrouperInstallerUtils.propertiesFromFile(log4jPropertiesFile);
4515       
4516       //= ERROR, grouper_error")
4517       String rootLoggerValue = log4jProperties.getProperty("log4j.rootLogger");              
4518       
4519       System.out.println("Generally the log4j.rootLogger property shows where logs go, it is set to: " + rootLoggerValue);
4520       
4521       Pattern pattern = Pattern.compile("\\s*[A-Z]+\\s*,\\s*(\\w+)\\s*");
4522       Matcher matcher = pattern.matcher(rootLoggerValue);
4523       if (!matcher.matches()) {
4524         System.out.println("Examine the log4j.properties for more information");
4525       } else {
4526         String logger = matcher.group(1);
4527         System.out.println("The log4j.rootLogger property in log4j.properties is set to: " + logger);
4528         //log4j.appender.grouper_error = org.apache.log4j.DailyRollingFileAppender
4529         //log4j.appender.grouper_error.File = ${grouper.home}logs/grouper_error.log
4530         String logFileName = log4jProperties.getProperty("log4j.appender." + logger + ".File");
4531         if (!GrouperInstallerUtils.isBlank(logFileName)) {
4532           System.out.println("There is a property in log4j.properties: log4j.appender." + logger + ".File = " + logFileName);
4533           if (logFileName.contains("${grouper.home}")) {
4534             logFileName = GrouperInstallerUtils.replace(logFileName, "${grouper.home}", grouperHomeWithSlash);
4535           }
4536           System.out.println("Grouper log should be: " + logFileName);
4537         } else {
4538           //log4j.appender.grouper_stdout = org.apache.log4j.ConsoleAppender
4539           String appender = log4jProperties.getProperty("log4j.appender." + logger);
4540           //log4j.appender.grouper_stderr.Target                    = System.err
4541           String target = log4jProperties.getProperty("log4j.appender." + logger + ".Target");
4542           String targetFriendly = null;
4543           if (GrouperInstallerUtils.equals(target, "System.err")) {
4544             targetFriendly = "STDERR";
4545           } else if (GrouperInstallerUtils.equals(target, "System.out")) {
4546             targetFriendly = "STDOUT";
4547           }
4548           if (GrouperInstallerUtils.equals(appender, "org.apache.log4j.ConsoleAppender") && targetFriendly != null) {
4549             System.out.println("Since log4j.properties log4j.appender." + logger + " = org.apache.log4j.ConsoleAppender you are logging to " + targetFriendly);
4550             if (GrouperInstallerUtils.equals(target, "System.err") && stderrLocation != null) {
4551               System.out.println("Grouper logs should be in " + stderrLocation.getAbsolutePath());
4552             } else if (GrouperInstallerUtils.equals(target, "System.out") && stdoutLocation != null) {
4553               System.out.println("Grouper logs should be in " + stdoutLocation.getAbsolutePath());
4554             }
4555           } else {
4556             System.out.println("Examine the log4j.properties for more information");
4557           }
4558         }
4559       }
4560     }
4561   }
4562   
4563   /**
4564    * admin
4565    */
4566   private void mainUpgradeTaskLogic() {
4567     
4568     GrouperInstallerUpgradeTaskAction grouperInstallerConvertAction = 
4569         (GrouperInstallerUpgradeTaskAction)promptForEnum(
4570             "What upgrade task do you want to do (convertEhcacheXmlToProperties, convertSourcesXmlToProperties, analyzeAndFixJars)? : ",
4571             "grouperInstaller.autorun.upgradeTaskAction", GrouperInstallerUpgradeTaskAction.class);
4572 
4573     switch(grouperInstallerConvertAction) {
4574       case convertEhcacheXmlToProperties:
4575 
4576         System.out.println("Note, you need to convert the ehcache.xml file for each Grouper runtime, e.g. loader, WS, UI.");
4577         System.out.println("Note, you need to be running Grouper 2.3.0 with API patch 35 installed.");
4578         System.out.print("Enter the location of the ehcache.xml file: ");
4579         String convertEhcacheXmlLocation = readFromStdIn("grouperInstaller.autorun.convertEhcacheXmlLocation");
4580 
4581         File ehcacheXmlFile = new File(convertEhcacheXmlLocation);
4582         if (!ehcacheXmlFile.exists()) {
4583           System.out.println("Cant find ehcache.xml: " + ehcacheXmlFile.getAbsolutePath());
4584           System.exit(1);
4585         }
4586 
4587         File grouperCacheBaseProperties = new File(ehcacheXmlFile.getParentFile().getAbsolutePath() + File.separator + "grouper.cache.base.properties");
4588 
4589         {
4590           System.out.print("Enter the location of the grouper.cache.base.properties file [" + grouperCacheBaseProperties.getAbsolutePath() + "]: ");
4591           String grouperCacheBasePropertiesLocation = readFromStdIn("grouperInstaller.autorun.convertEhcacheBasePropertiesLocation");
4592   
4593           if (!GrouperInstallerUtils.isBlank(grouperCacheBasePropertiesLocation)) {
4594             grouperCacheBaseProperties = new File(grouperCacheBasePropertiesLocation);
4595           }
4596         }
4597         
4598         File grouperCacheProperties = new File(ehcacheXmlFile.getParentFile().getAbsolutePath() + File.separator + "grouper.cache.properties");
4599 
4600         {
4601           System.out.print("Enter the location of the grouper.cache.properties file (to be created)  [" + grouperCacheProperties.getAbsolutePath() + "]: ");
4602           String grouperCachePropertiesLocation = readFromStdIn("grouperInstaller.autorun.convertEhcachePropertiesLocation");
4603   
4604           if (!GrouperInstallerUtils.isBlank(grouperCachePropertiesLocation)) {
4605             grouperCacheProperties = new File(grouperCachePropertiesLocation);
4606           }
4607         }
4608         
4609         try {
4610           convertEhcacheXmlToProperties(grouperCacheBaseProperties, grouperCacheProperties,
4611               ehcacheXmlFile.toURI().toURL());
4612         } catch (MalformedURLException mue) {
4613           throw new RuntimeException("Malformed url on " + convertEhcacheXmlLocation);
4614         }
4615 
4616         System.out.println("File was written: " + grouperCacheProperties.getAbsolutePath());
4617 
4618         break;
4619         
4620       case analyzeAndFixJars:
4621         
4622         //Find out what directory to install to.  This ends in a file separator
4623         this.grouperInstallDirectoryString = grouperInstallDirectory();
4624 
4625         reportOnConflictingJars(this.grouperInstallDirectoryString);
4626         
4627         break;
4628         
4629       case convertSourcesXmlToProperties:
4630 
4631         System.out.println("Note, you need to convert the sources.xml file for each Grouper runtime, e.g. loader, WS, UI.");
4632         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.");
4633         System.out.print("Enter the location of the sources.xml file: ");
4634         String convertSourcesXmlLocation = readFromStdIn("grouperInstaller.autorun.convertSourceXmlLocation");
4635 
4636         File sourcesXmlFile = new File(convertSourcesXmlLocation);
4637         if (!sourcesXmlFile.exists()) {
4638           System.out.println("Cant find sources.xml: " + sourcesXmlFile.getAbsolutePath());
4639           System.exit(1);
4640         }
4641 
4642         File subjectProperties = new File(sourcesXmlFile.getParentFile().getAbsolutePath() + File.separator + "subject.properties");
4643 
4644         {
4645           System.out.print("Enter the location of the subject.properties file [" + subjectProperties.getAbsolutePath() + "]: ");
4646           String grouperCacheBasePropertiesLocation = readFromStdIn("grouperInstaller.autorun.convertSubjectPropertiesLocation");
4647   
4648           if (!GrouperInstallerUtils.isBlank(grouperCacheBasePropertiesLocation)) {
4649             subjectProperties = new File(grouperCacheBasePropertiesLocation);
4650           }
4651         }
4652         
4653         try {
4654           convertSourcesXmlToProperties(subjectProperties, sourcesXmlFile.toURI().toURL());
4655         } catch (MalformedURLException mue) {
4656           throw new RuntimeException("Malformed url on " + convertSourcesXmlLocation);
4657         }
4658 
4659         System.out.println("File was written: " + subjectProperties.getAbsolutePath());
4660         System.out.println("You should archive your sources.xml and remove it from your project since it is now unused:\n  " 
4661             + sourcesXmlFile.getAbsolutePath());
4662 
4663         break;
4664     }
4665 
4666   }
4667   
4668   /**
4669    * patch grouper
4670    */
4671   private void mainPatchLogic() {
4672     
4673     //####################################
4674     //Find out what directory to upgrade to.  This ends in a file separator
4675     this.grouperTarballDirectoryString = grouperUpgradeTempDirectory();
4676     
4677     //see what we are upgrading: api, ui, ws, client
4678     this.appToUpgrade = grouperAppToUpgradeOrPatch("patch");
4679 
4680     //get the directory where the existing installation is
4681     this.upgradeExistingApplicationDirectoryString = upgradeExistingDirectory();
4682 
4683     GrouperInstallerPatchAction grouperInstallerPatchAction = 
4684         (GrouperInstallerPatchAction)promptForEnum(
4685             "What do you want to do with patches (install, revert, status, fixIndexFile)? ",
4686             "grouperInstaller.autorun.patchAction", GrouperInstallerPatchAction.class, GrouperInstallerPatchAction.install, null);
4687     
4688     switch(grouperInstallerPatchAction) {
4689       case install:
4690         
4691         fixIndexFileIfOk();
4692 
4693         //loop through applications, check patches
4694         this.appToUpgrade.patch(this);
4695 
4696         break;
4697         
4698       case revert:
4699         
4700         fixIndexFileIfOk();
4701 
4702         //look through applications, check for reverts
4703         this.appToUpgrade.revertPatch(this);
4704         break;
4705         
4706       case status:
4707         
4708         fixIndexFileIfOk();
4709 
4710         //print out status for applications
4711         this.appToUpgrade.patchStatus(this);
4712         break;
4713         
4714       case fixIndexFile:
4715         
4716         //print out status for applications
4717         this.appToUpgrade.fixIndexFile(this);
4718         break;
4719         
4720       default:
4721         throw new RuntimeException("Invalid patch action: " + grouperInstallerPatchAction);  
4722     }
4723     
4724   }
4725 
4726   /**
4727    * 
4728    */
4729   public static enum GrouperInstallerPatchAction {
4730 
4731     /** fix index file */
4732     fixIndexFile,
4733     
4734     /** install patches */
4735     install,
4736 
4737     /**
4738      * revert patches
4739      */
4740     revert,
4741     
4742     /**
4743      * get status on patches
4744      */
4745     status;
4746     
4747     /**
4748      * 
4749      * @param string
4750      * @param exceptionIfInvalid
4751      * @param exceptionIfBlank
4752      * @return the action
4753      */
4754     public static GrouperInstallerPatchAction valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
4755       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerPatchAction.class, string, exceptionIfBlank, exceptionIfInvalid);
4756     }
4757     
4758   }
4759   
4760   /**
4761    * 
4762    */
4763   public static enum GrouperInstallerAdminAction {
4764 
4765     /** manage */
4766     manage,
4767     
4768     /** develop */
4769     develop,
4770     
4771     /** convert */
4772     upgradeTask;
4773     
4774     /**
4775      * 
4776      * @param string
4777      * @param exceptionIfInvalid
4778      * @param exceptionIfBlank
4779      * @return the action
4780      */
4781     public static GrouperInstallerAdminAction valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
4782       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerAdminAction.class, string, exceptionIfBlank, exceptionIfInvalid);
4783     }
4784     
4785   }
4786   
4787   /**
4788    * 
4789    */
4790   public static enum GrouperInstallerUpgradeTaskAction {
4791 
4792     /** analyze and fix jars */
4793     analyzeAndFixJars,
4794     
4795     /** convert */
4796     convertEhcacheXmlToProperties,
4797     
4798     /** convert sources xml */
4799     convertSourcesXmlToProperties;
4800     
4801     /**
4802      * 
4803      * @param string
4804      * @param exceptionIfInvalid
4805      * @param exceptionIfBlank
4806      * @return the action
4807      */
4808     public static GrouperInstallerUpgradeTaskAction valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
4809       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerUpgradeTaskAction.class, string, exceptionIfBlank, exceptionIfInvalid);
4810     }
4811     
4812   }
4813   
4814   /**
4815    * 
4816    */
4817   public static enum GrouperInstallerAdminManageService {
4818 
4819     /** tomcat */
4820     tomcat,
4821     
4822     /** database (hsqldb) */
4823     database,
4824     
4825     /** daemon (loader) */
4826     grouperDaemon;
4827     
4828     /**
4829      * 
4830      * @param string
4831      * @param exceptionIfInvalid
4832      * @param exceptionIfBlank
4833      * @return the action
4834      */
4835     public static GrouperInstallerAdminManageService valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
4836       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerAdminManageService.class, string, exceptionIfBlank, exceptionIfInvalid);
4837     }
4838     
4839   }
4840   
4841   /**
4842    * 
4843    */
4844   public static enum GrouperInstallerManageAction {
4845 
4846     /** logs */
4847     logs,
4848 
4849     /** back to admin */
4850     back,
4851 
4852     /** exit */
4853     exit,
4854 
4855     /** services */
4856     services;
4857     
4858     /**
4859      * 
4860      * @param string
4861      * @param exceptionIfInvalid
4862      * @param exceptionIfBlank
4863      * @return the action
4864      */
4865     public static GrouperInstallerManageAction valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
4866       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerManageAction.class, string, exceptionIfBlank, exceptionIfInvalid);
4867     }
4868     
4869   }
4870   
4871   /**
4872    * 
4873    */
4874   public static enum GrouperInstallerDevelopAction {
4875 
4876     /** logs */
4877     translate,
4878 
4879     /** back to admin */
4880     back,
4881 
4882     /** exit */
4883     exit;
4884 
4885     /**
4886      * 
4887      * @param string
4888      * @param exceptionIfInvalid
4889      * @param exceptionIfBlank
4890      * @return the action
4891      */
4892     public static GrouperInstallerDevelopAction valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
4893       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerDevelopAction.class, string, exceptionIfBlank, exceptionIfInvalid);
4894     }
4895     
4896   }
4897   
4898   /**
4899    * get the existing properties file of patches
4900    * @return the file for patches
4901    */
4902   private Properties patchExistingProperties() {
4903     File patchExistingPropertiesFile = this.patchExistingPropertiesFile();
4904     if (patchExistingPropertiesFile == null || !patchExistingPropertiesFile.exists()) {
4905       return new Properties();
4906     }
4907     return GrouperInstallerUtils.propertiesFromFile(patchExistingPropertiesFile);
4908    }
4909 
4910   /**
4911    * get the existing properties file of patches
4912    * @return the file for patches
4913    */
4914   private File patchExistingPropertiesFile() {
4915     
4916     //dont cache this in a variable since the upgrade existing application variable
4917     File patchExistingPropertiesFile = null;
4918     //if theres a web-inf, put it there, if not, put it in regular...
4919     if (new File(this.upgradeExistingApplicationDirectoryString + "WEB-INF").exists()) {
4920       patchExistingPropertiesFile = new File(this.upgradeExistingApplicationDirectoryString 
4921           + "WEB-INF" + File.separator + "grouperPatchStatus.properties");
4922     } else {
4923       patchExistingPropertiesFile = new File(this.upgradeExistingApplicationDirectoryString 
4924           + "grouperPatchStatus.properties");
4925     }
4926     return patchExistingPropertiesFile;
4927   }
4928   
4929   /**
4930    * 
4931    */
4932   private void mainUpgradeLogic() {
4933 
4934     System.out.print("You should backup your files and database before you start.  Press <enter> to continue. ");
4935     readFromStdIn("grouperInstaller.autorun.backupFiles");
4936     
4937     System.out.println("\n##################################");
4938     System.out.println("Gather upgrade information\n");
4939 
4940     //####################################
4941     //Find out what directory to upgrade to.  This ends in a file separator
4942     this.grouperTarballDirectoryString = grouperUpgradeTempDirectory();
4943     
4944     //GRP-1429: grouperInstaller tarballs dir fails on upgrade
4945     //set this here since on upgrade some things download to this dir...
4946     this.grouperInstallDirectoryString = this.grouperTarballDirectoryString;
4947     
4948     //see what we are upgrading: api, ui, ws, client
4949     this.appToUpgrade = grouperAppToUpgradeOrPatch("upgrade");
4950 
4951     for (int i=0;i<10;i++) {
4952       System.out.println("Are there any running processes using this installation?  tomcats?  loader?  psp?  etc?  (t|f)? [f]:");
4953       boolean runningProcesses = readFromStdInBoolean(true, "grouperInstaller.autorun.runningProcesses");
4954       if (runningProcesses) {
4955         break;
4956       }
4957       System.out.println("Please stop any processes using this installation...");
4958       //lets sleep for a bit to let it start
4959       GrouperInstallerUtils.sleep(2000);
4960     }
4961     
4962     //get the directory where the existing installation is
4963     this.upgradeExistingApplicationDirectoryString = upgradeExistingDirectory();
4964 
4965     this.version = GrouperInstallerUtils.propertiesValue("grouper.version", true);
4966     System.out.println("Upgrading to grouper " + this.appToUpgrade.name() + " version: " + this.version);
4967  
4968     fixIndexFileIfOk();
4969     
4970     System.out.println("\n##################################");
4971     System.out.println("Download and build grouper packages\n");
4972 
4973     //download new files
4974     this.appToUpgrade.downloadAndBuildGrouperProjects(this);
4975 
4976     System.out.println("End download and build grouper packages\n");
4977     System.out.println("\n##################################");
4978 
4979     this.grouperBaseBakDir = this.grouperTarballDirectoryString + "bak_" + this.appToUpgrade + "_" 
4980         + new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS").format(new Date()) + File.separator; 
4981 
4982     GrouperInstallerUtils.tempFilePathForJars = this.grouperBaseBakDir 
4983         + "jarToDelete" + File.separator;
4984 
4985     //when we revert patches, default should be true
4986     this.revertAllPatchesDefault = true;
4987     try {
4988       this.appToUpgrade.upgradeApp(this);
4989     } finally {
4990       this.revertAllPatchesDefault = false;
4991     }
4992 
4993     this.reportOnConflictingJars(this.upgradeExistingApplicationDirectoryString);
4994     
4995     System.out.println("\nGrouper is upgraded from " + (this.originalGrouperJarVersion == null ? null : this.originalGrouperJarVersion) 
4996         + " to " + GrouperInstallerUtils.propertiesValue("grouper.version", true) +  "\n");
4997 
4998     //this file keeps track of partial upgrades
4999     GrouperInstallerUtils.fileDelete(this.grouperUpgradeOriginalVersionFile);
5000 
5001     //reset this so that patches go against new version
5002     this.grouperVersionOfJar = null;
5003     
5004   }
5005 
5006   /**
5007    * 
5008    */
5009   private void fixIndexFileIfOk() {
5010     Properties patchesExistingProperties = patchExistingProperties();
5011     
5012     //see what is already there
5013     String existingDate = patchesExistingProperties.getProperty("grouperInstallerLastFixedIndexFile.date");
5014 
5015     boolean defaultToFixIndex = true;
5016     
5017     if (!GrouperInstallerUtils.isBlank(existingDate)) {
5018       try {
5019         Date theDate = GrouperInstallerUtils.dateMinutesSecondsFormat.parse(existingDate);
5020         //this is when the installer was fixed to do the index file correctly
5021         if (theDate.getTime() > GrouperInstallerUtils.dateValue("20150929").getTime()) {
5022           defaultToFixIndex = false;
5023         }
5024       } catch (ParseException pe) {
5025         System.out.println("Cant parse date: " + existingDate);
5026       }
5027     }
5028 
5029     //if we are affecting 2.2.2+ then dont recommend this
5030     if (defaultToFixIndex) {
5031       
5032       //see the version
5033       String grouperVersion = this.grouperVersionOfJar().toString();
5034 
5035       GiGrouperVersion giGrouperVersion = GiGrouperVersion.valueOfIgnoreCase(grouperVersion);
5036       
5037       if (giGrouperVersion.greaterOrEqualToArg(GiGrouperVersion.valueOfIgnoreCase("2.2.2"))) {
5038         defaultToFixIndex = false;
5039       }
5040       
5041     }
5042     
5043     
5044     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") + "]: ");
5045     boolean fixIndexFile = readFromStdInBoolean(defaultToFixIndex, "grouperInstaller.autorun.fixIndexFile");
5046     if (fixIndexFile) {
5047       this.appToUpgrade.fixIndexFile(this);
5048     }
5049   }
5050 
5051   /**
5052    * upgrade the client
5053    */
5054   private void upgradeClient() {
5055 
5056     System.out.println("\n##################################");
5057     System.out.println("Upgrading grouper client\n");
5058 
5059     this.compareAndReplaceJar(this.grouperClientJar, new File(this.untarredClientDir + File.separator + "grouperClient.jar"), true, null);
5060 
5061     this.compareUpgradePropertiesFile(this.grouperClientBasePropertiesFile, 
5062       new File(this.untarredClientDir + File.separator + "grouper.client.base.properties"),
5063       this.grouperClientPropertiesFile,
5064       this.grouperClientExamplePropertiesFile,
5065       GrouperInstallerUtils.toSet("grouperClient.webService.client.version"),
5066       "grouperInstaller.autorun.removeRedundantPropetiesFromGrouperClient"
5067     );
5068 
5069     
5070   }
5071 
5072 
5073   /**
5074    * upgrade the ui
5075    */
5076   private void upgradeUi() {
5077 
5078     this.upgradeApiPreRevertPatch();
5079 
5080     System.out.println("You need to revert all patches to upgrade");
5081     this.patchRevertUi();
5082         
5083     System.out.println("\n##################################");
5084     System.out.println("Upgrading UI\n");
5085     
5086     //copy the jars there
5087     System.out.println("\n##################################");
5088     System.out.println("Upgrading UI jars\n");
5089 
5090     this.upgradeJars(new File(this.untarredUiDir + File.separator + "dist" + File.separator + "grouper" + File.separator 
5091         + "WEB-INF" + File.separator + "lib" + File.separator));
5092 
5093     System.out.println("\n##################################");
5094     System.out.println("Upgrading UI files\n");
5095 
5096     //copy files there
5097     this.copyFiles(this.untarredUiDir + File.separator + "dist" + File.separator + "grouper" + File.separator,
5098         this.upgradeExistingApplicationDirectoryString,
5099         GrouperInstallerUtils.toSet("WEB-INF/lib", "WEB-INF/web.xml", "WEB-INF/classes",
5100             "WEB-INF/bin/gsh", "WEB-INF/bin/gsh.bat", "WEB-INF/bin/gsh.sh"));
5101 
5102     {
5103       boolean hadChange = false;
5104       for (String gshName : new String[]{"gsh", "gsh.bat", "gsh.sh"}) {
5105         File newGshFile = new File(this.untarredUiDir + File.separator + "dist" 
5106             + File.separator + "grouper" + File.separator + "WEB-INF" + File.separator + "bin" 
5107             + File.separator + gshName);
5108 
5109         File existingGshFile = new File(this.upgradeExistingApplicationDirectoryString 
5110             + "WEB-INF" + File.separator + "bin" + File.separator + gshName);
5111 
5112         if (!GrouperInstallerUtils.contentEquals(newGshFile, existingGshFile)) {
5113           this.backupAndCopyFile(newGshFile, existingGshFile, true);
5114           if (!GrouperInstallerUtils.equals("gsh.bat", gshName)) {
5115             hadChange = true;
5116           }
5117         }
5118         
5119       }
5120       if (hadChange) {
5121         //set executable and dos2unix
5122         gshExcutableAndDos2Unix(this.untarredUiDir + File.separator + "dist" 
5123             + File.separator + "grouper" + File.separator + "WEB-INF" + File.separator + "bin" 
5124             + File.separator);
5125       }
5126     }
5127     
5128     upgradeWebXml(new File(this.untarredUiDir + File.separator + "dist" 
5129             + File.separator + "grouper" + File.separator + "WEB-INF" + File.separator + "web.xml"),
5130             new File(this.upgradeExistingApplicationDirectoryString 
5131                 + File.separator + "WEB-INF" + File.separator + "web.xml"));
5132     
5133     System.out.println("\n##################################");
5134     System.out.println("Upgrading UI config files\n");
5135 
5136     this.changeConfig("WEB-INF/classes/resources/grouper/nav.properties", 
5137         "WEB-INF/classes/grouperText/grouper.text.en.us.base.properties",
5138         "WEB-INF/classes/grouperText/grouper.text.en.us.properties", null, GiGrouperVersion.valueOfIgnoreCase("2.2.0"), true, 
5139         "grouperInstaller.autorun.continueAfterNavProperties",
5140         "grouperInstaller.autorun.removeOldKeysFromNavProperties");
5141 
5142     this.changeConfig("WEB-INF/classes/resources/grouper/media.properties", 
5143         "WEB-INF/classes/grouper-ui.base.properties",
5144         "WEB-INF/classes/grouper-ui.properties", null, GiGrouperVersion.valueOfIgnoreCase("2.2.0"), false,
5145         "grouperInstaller.autorun.continueAfterMediaProperties",
5146         "grouperInstaller.autorun.removeOldKeysFromMediaProperties");
5147 
5148     {
5149       File newBaseOwaspFile = new File(this.untarredUiDir + File.separator + "dist" 
5150           + File.separator + "grouper" + File.separator + "WEB-INF" + File.separator + "classes" 
5151           + File.separator + "Owasp.CsrfGuard.properties");
5152 
5153       File newOwaspFile = new File(this.untarredUiDir + File.separator + "dist" 
5154           + File.separator + "grouper" + File.separator + "WEB-INF" + File.separator + "classes" 
5155           + File.separator + "Owasp.CsrfGuard.overlay.properties");
5156 
5157       if (this.owaspCsrfGuardBaseFile == null) {
5158         this.owaspCsrfGuardBaseFile = new File(this.upgradeExistingClassesDirectoryString + newBaseOwaspFile.getName());
5159       }
5160       
5161       if (this.owaspCsrfGuardFile == null) {
5162         this.owaspCsrfGuardFile = new File(this.upgradeExistingClassesDirectoryString + newOwaspFile.getName());
5163       }
5164       
5165       this.backupAndCopyFile(newBaseOwaspFile, this.owaspCsrfGuardBaseFile, true);
5166 
5167       boolean editedOwaspOverlay = this.owaspCsrfGuardFile != null && this.owaspCsrfGuardFile.exists();
5168 
5169       File bakFile = this.backupAndCopyFile(newOwaspFile, this.owaspCsrfGuardFile, true);
5170 
5171       if (bakFile != null && editedOwaspOverlay) {
5172         if (!GrouperInstallerUtils.contentEquals(this.owaspCsrfGuardFile, newOwaspFile)) {
5173           System.out.println("If you have edited the Owasp.CsrfGuard.overlay.properties please merge the changes to the new file");
5174           System.out.println("Press <enter> when done");
5175           readFromStdIn("grouperInstaller.autorun.continueAfterEditedOwaspCsrfGuard");
5176         }
5177       }
5178     }
5179     
5180     this.upgradeApiPostRevertPatch();
5181 
5182     //patch it
5183     this.patchUi();
5184     
5185   }
5186 
5187   /**
5188    * upgrade the psp
5189    */
5190   private void upgradePsp() {
5191 
5192     this.upgradeApiPreRevertPatch();
5193 
5194     System.out.println("You need to revert all patches to upgrade");
5195     this.patchRevertPsp();
5196     
5197     System.out.println("\n##################################");
5198     System.out.println("Upgrading PSP\n");
5199     
5200     //copy the jars there
5201     System.out.println("\n##################################");
5202     System.out.println("Upgrading PSP jars\n");
5203 
5204     this.upgradeJars(new File(this.untarredPspDir + File.separator + "lib" + File.separator + "custom" + File.separator),
5205         new File(new File(this.upgradeExistingLibDirectoryString).getParentFile().getAbsolutePath() + File.separator + "custom"));
5206 
5207     System.out.println("\n##################################");
5208     System.out.println("Upgrading PSP files\n");
5209 
5210     //copy files there (this is the conf examples)
5211     this.copyFiles(this.untarredPspDir + File.separator + "conf" + File.separator,
5212         this.upgradeExistingApplicationDirectoryString + "conf" + File.separator, null);
5213 
5214     this.upgradeApiPostRevertPatch();
5215     
5216     //patch it
5217     this.patchPsp();
5218   }
5219 
5220   /**
5221    * upgrade the pspng
5222    */
5223   private void upgradePspng() {
5224 
5225     this.upgradeApiPreRevertPatch();
5226 
5227     System.out.println("You need to revert all patches to upgrade");
5228     this.patchRevertPspng();
5229     
5230     System.out.println("\n##################################");
5231     System.out.println("Upgrading PSPNG\n");
5232     
5233     //copy the jars there
5234     System.out.println("\n##################################");
5235     System.out.println("Upgrading PSPNG jars\n");
5236 
5237     this.upgradeJars(new File(this.untarredPspngDir + File.separator + "lib" + File.separator + "custom" + File.separator),
5238         new File(new File(this.upgradeExistingLibDirectoryString).getParentFile().getAbsolutePath() + File.separator + "custom"));
5239 
5240     System.out.println("\n##################################");
5241     System.out.println("Upgrading PSPNG files\n");
5242 
5243     //copy files there (this is the conf examples)
5244     this.copyFiles(this.untarredPspngDir + File.separator + "conf" + File.separator,
5245         this.upgradeExistingApplicationDirectoryString + "conf" + File.separator, null);
5246 
5247     this.upgradeApiPostRevertPatch();
5248     
5249     //patch it
5250     this.patchPspng();
5251   }
5252 
5253   
5254   /**
5255    * upgrade a web.xml file
5256    * @param newWebXml
5257    * @param existingWebXml
5258    */
5259   public void upgradeWebXml(File newWebXml, File existingWebXml) {
5260     
5261     File bakFile = backupAndCopyFile(newWebXml, existingWebXml, true);
5262     
5263     if (bakFile != null) {
5264       //it existed
5265       NodeList nodeList = GrouperInstallerUtils.xpathEvaluate(bakFile, "/web-app/security-constraint");
5266       boolean tookOutAuthn = false;
5267       if (nodeList == null || nodeList.getLength() == 0) {
5268         //take out authn from web.xml
5269         String webXmlContents = GrouperInstallerUtils.readFileIntoString(existingWebXml);
5270         int startAuthnIndex = webXmlContents.indexOf("<security-constraint>");
5271         int endAuthnIndex = webXmlContents.indexOf("</security-role>");
5272         if (startAuthnIndex != -1 && endAuthnIndex != -1 && endAuthnIndex > startAuthnIndex) {
5273           endAuthnIndex = endAuthnIndex + "</security-role>".length();
5274           //authn is there
5275           webXmlContents = webXmlContents.substring(0, startAuthnIndex) + webXmlContents.substring(endAuthnIndex, webXmlContents.length());
5276           GrouperInstallerUtils.saveStringIntoFile(existingWebXml, webXmlContents);
5277           tookOutAuthn = true;
5278           System.out.println("Taking out basic authentication from " + existingWebXml + " since it wasnt there before");
5279         }
5280       }
5281       System.out.println("If you customized the web.xml please merge your changes back in "
5282           + (tookOutAuthn ? "\n  Note: basic authentication was removed from the new web.xml to be consistent with the old web.xml" : "")
5283           + "\n  New file: " + existingWebXml.getAbsolutePath() + ", bak file:" + bakFile.getAbsolutePath() );
5284       System.out.println("Press the <enter> key to continue");
5285       readFromStdIn("grouperInstaller.autorun.continueAfterMergeWebXml");
5286       
5287       if (tookOutAuthn) {
5288         GrouperInstallerUtils.xpathEvaluate(existingWebXml, "/web-app");        
5289       }
5290     }
5291   }
5292 
5293   
5294   /**
5295    * @param legacyPropertiesFileRelativePath legacy file we are converting from
5296    * @param propertiesFileRelativePath 
5297    * @param propertiesToIgnore
5298    * @param basePropertiesFileRelativePath
5299    * @param versionMigrationHappened
5300    * @param removeOldCopy
5301    * @param autorunPropertiesKey key in properties file to automatically fill in a value
5302    * @param autorunPropertiesKeyRemoveOldKeys key in properties file to automatically fill in a value to remove old keys
5303    */
5304   @SuppressWarnings("unchecked")
5305   private void changeConfig(String legacyPropertiesFileRelativePath,
5306       String basePropertiesFileRelativePath,
5307       String propertiesFileRelativePath,
5308       Set<String> propertiesToIgnore,
5309       GiGrouperVersion versionMigrationHappened, boolean removeOldCopy, 
5310       String autorunPropertiesKey, String autorunPropertiesKeyRemoveOldKeys) {
5311 
5312     File legacyPropertiesFile = new File(this.upgradeExistingApplicationDirectoryString + legacyPropertiesFileRelativePath);
5313     File newBasePropertiesFile = new File(this.untarredUiDir + File.separator + "dist" 
5314         + File.separator + "grouper" + File.separator + basePropertiesFileRelativePath);
5315     File existingBasePropertiesFile = new File(this.upgradeExistingApplicationDirectoryString + basePropertiesFileRelativePath);
5316     File existingPropertiesFile = new File(this.upgradeExistingApplicationDirectoryString + propertiesFileRelativePath);
5317 
5318     this.compareUpgradePropertiesFile(existingBasePropertiesFile, newBasePropertiesFile, 
5319         existingPropertiesFile, null, propertiesToIgnore, autorunPropertiesKeyRemoveOldKeys);
5320 
5321     //look for existing properties in legacy file, and if there are properties in the base, then remove them
5322     if (legacyPropertiesFile.exists()) {
5323       
5324       Properties existingBaseProperties = GrouperInstallerUtils.propertiesFromFile(existingBasePropertiesFile);
5325       Properties existingProperties = GrouperInstallerUtils.propertiesFromFile(existingPropertiesFile);
5326       Properties legacyProperties = GrouperInstallerUtils.propertiesFromFile(legacyPropertiesFile);
5327       Set<String> propertyNamesToRemove = new LinkedHashSet<String>();
5328       Set<String> propertyNamesWrongValue = new LinkedHashSet<String>();
5329 
5330       for (String propertyName : (Set<String>)(Object)existingBaseProperties.keySet()) {
5331         if (legacyProperties.containsKey(propertyName)) {
5332           
5333           Object existingValue = existingProperties.containsKey(propertyName) ?
5334              existingProperties.get(propertyName) : existingBaseProperties.get(propertyName);
5335           
5336           //it might be in the override, what about other overrides?  who knows
5337           if (!GrouperInstallerUtils.equals(existingValue, 
5338               legacyProperties.get(propertyName))) {
5339 
5340             propertyNamesWrongValue.add(propertyName);
5341           }
5342           propertyNamesToRemove.add(propertyName);
5343         }
5344       }
5345       
5346       //if we found some, see if we can remove them
5347       if (propertyNamesToRemove.size() > 0) {
5348         
5349         if (propertyNamesWrongValue.size() > 0) {
5350 
5351           //these are properties that we different in the previous legacy file
5352           System.out.println(legacyPropertiesFileRelativePath + " has properties that have a different value than\n  the new place they are managed: "
5353               + basePropertiesFileRelativePath + ",\n  and the everride(s) which could be: " + propertiesFileRelativePath);
5354           System.out.println("Review these properties and merge the values, this could have happened due to changes in Grouper:");
5355           for (String propertyName: propertyNamesWrongValue) {
5356             System.out.println(" - " + propertyName);
5357           }
5358           System.out.println("When you are done merging press <enter>");
5359           readFromStdIn(autorunPropertiesKey);
5360 
5361         }
5362 
5363         if (removeOldCopy) {
5364           
5365           System.out.println(legacyPropertiesFileRelativePath + " is not used anymore by grouper, can it be backed up and removed (t|f)? [t]: ");
5366           boolean removeLegacy = readFromStdInBoolean(true, autorunPropertiesKeyRemoveOldKeys);
5367           if (removeLegacy) {
5368             File backupLegacy = bakFile(legacyPropertiesFile);
5369             GrouperInstallerUtils.copyFile(legacyPropertiesFile, backupLegacy, true);
5370             GrouperInstallerUtils.fileDelete(legacyPropertiesFile);
5371             System.out.println("File as removed.  Backup path: " + backupLegacy.getAbsolutePath());
5372           }
5373           
5374         } else {
5375           System.out.println(legacyPropertiesFileRelativePath + " has properties that can be removed since they are now managed in "
5376               + basePropertiesFileRelativePath);
5377           System.out.print("Would you like to have the properties automatically removed from " 
5378               + legacyPropertiesFile.getName() + " (t|f)? [t]: ");
5379           boolean removeRedundantProperties = readFromStdInBoolean(true, autorunPropertiesKeyRemoveOldKeys);
5380           
5381           if (removeRedundantProperties) {
5382             removeRedundantProperties(legacyPropertiesFile, propertyNamesToRemove);
5383           }
5384         }
5385       }
5386     }
5387   }
5388 
5389   
5390   /**
5391    * copy files if they are different from one place to another, print out statuses
5392    * @param fromDirString where to copy files from
5393    * @param toDirString where to copy files to
5394    * @param relativePathsToIgnore
5395    */
5396   public void copyFiles(String fromDirString, String toDirString, 
5397       Set<String> relativePathsToIgnore) {
5398     
5399     fromDirString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(fromDirString);
5400     toDirString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(toDirString);
5401 
5402     {
5403       //lets massage all the paths so they dont start or end with slash and
5404       //so they have File.separator instead of the wrong slash
5405       Set<String> tempRelativePathsToIgnore = new HashSet<String>();
5406       for (String path : GrouperInstallerUtils.nonNull(relativePathsToIgnore)) {
5407         path = GrouperInstallerUtils.fileMassagePathsNoLeadingOrTrailing(path);
5408         tempRelativePathsToIgnore.add(path);
5409       }
5410       relativePathsToIgnore = tempRelativePathsToIgnore;
5411     }
5412 
5413     int insertCount = 0;
5414     int updateCount = 0;
5415     Map<String, Boolean> relativeFilePathsChangedAndIfInsert = new LinkedHashMap<String, Boolean>();
5416 
5417     List<File> allFiles = GrouperInstallerUtils.fileListRecursive(new File(fromDirString));
5418     for (File fileToCopyFrom : allFiles) {
5419       String relativePath = null;
5420       {
5421         //get the relative path with no leading or trailing slash
5422         String path = fileToCopyFrom.getAbsolutePath();
5423         if (!GrouperInstallerUtils.filePathStartsWith(path,fromDirString)) {
5424           throw new RuntimeException("Why does path not start with fromDirString: " + path + ", " + fromDirString);
5425         }
5426         relativePath = path.substring(fromDirString.length());
5427         relativePath = GrouperInstallerUtils.fileMassagePathsNoLeadingOrTrailing(relativePath);
5428       }
5429       boolean ignore = false;
5430       
5431       //ignore paths passed in
5432       for (String pathToIgnore : relativePathsToIgnore) {
5433         if (GrouperInstallerUtils.filePathStartsWith(relativePath,pathToIgnore)) {
5434           ignore = true;
5435           break;
5436         }
5437       }
5438       
5439       if (!ignore) {
5440         
5441         //File to copy to
5442         File fileToCopyTo = new File(toDirString + relativePath);
5443         if (fileToCopyTo.exists()) {
5444           //compare contents
5445           if (GrouperInstallerUtils.contentEquals(fileToCopyFrom, fileToCopyTo)) {
5446             continue;
5447           }
5448           //not equals, make backup
5449           updateCount++;
5450 
5451           relativeFilePathsChangedAndIfInsert.put(relativePath, false);
5452 
5453           this.backupAndCopyFile(fileToCopyFrom, fileToCopyTo, false);
5454 
5455           continue;
5456         }
5457         
5458         //insert
5459         insertCount++;
5460         relativeFilePathsChangedAndIfInsert.put(relativePath, true);
5461         GrouperInstallerUtils.copyFile(fileToCopyFrom, fileToCopyTo, false);
5462         
5463       }
5464     }
5465 
5466     System.out.println("Upgrading files from: " + fromDirString + "\n  to: " + toDirString 
5467         + (GrouperInstallerUtils.length(relativePathsToIgnore) == 0 ? "" : 
5468         ("\n  ignoring paths: " + GrouperInstallerUtils.join(relativePathsToIgnore.iterator(), ", "))));
5469     System.out.println("Compared " + allFiles.size() + " files and found " 
5470         + insertCount + " adds and " + updateCount + " updates");
5471 
5472     if (insertCount > 0 || updateCount > 0) {
5473       
5474       System.out.println((insertCount + updateCount) + " files were backed up to: " + this.grouperBaseBakDir);
5475 
5476       boolean listFiles = insertCount + updateCount <= 10;
5477       if (!listFiles) {
5478         System.out.println("Do you want to see the list of files changed (t|f)? [f]: ");
5479         listFiles = readFromStdInBoolean(false, "grouperInstaller.autorun.viewListOfFilesChangedInCopy");
5480       }
5481 
5482       if (listFiles) {
5483 
5484         for (String relativeFilePathChanged : relativeFilePathsChangedAndIfInsert.keySet()) {
5485           boolean isInsert = relativeFilePathsChangedAndIfInsert.get(relativeFilePathChanged);
5486           System.out.println(relativeFilePathChanged + " was " + (isInsert ? "added" : "updated"));
5487         }
5488       }
5489     }
5490     
5491   }
5492 
5493   /**
5494    *
5495    * @param sourceDir directory to copy files from
5496    * @param targetDir directory to copy files to
5497    * @param filesToCopyFromSource list of files to copy, if exist in source and differ in target
5498    */
5499   private void syncFilesInDirWithBackup(String sourceDir, String targetDir, String[] filesToCopyFromSource) {
5500     for (String filename : filesToCopyFromSource) {
5501       File srcFile = new File(sourceDir + filename);
5502       File targetFile = new File(targetDir + filename);
5503 
5504       if (srcFile.isFile() && !GrouperInstallerUtils.contentEquals(srcFile, targetFile)) {
5505         try {
5506           @SuppressWarnings("unused")
5507           File bakFile = backupAndCopyFile(srcFile, targetFile, true);
5508         } catch (Exception e) {
5509           System.out.println(" - failed to copy newer bin file " + filename + ": " + e.getMessage());
5510         }
5511       }
5512     }
5513   }
5514 
5515   /**
5516    * upgrade the api
5517    */
5518   private void upgradeApi() {
5519     this.upgradeApiPreRevertPatch();
5520 
5521     System.out.println("You need to revert all patches to upgrade");
5522     this.patchRevertApi();
5523     
5524     this.upgradeApiPostRevertPatch();
5525   }
5526 
5527 
5528   /**
5529    * upgrade the api
5530    */
5531   private void upgradeApiPreRevertPatch() {
5532 
5533     //make sure existing gsh is executable and dos2unix
5534     gshExcutableAndDos2Unix(new File(gshCommand()).getParentFile().getAbsolutePath(), "existing");
5535     
5536     this.runChangeLogTempToChangeLog();
5537   }
5538   
5539   
5540   /**
5541    * upgrade the api
5542    */
5543   private void upgradeApiPostRevertPatch() {
5544 
5545     //revert patches
5546     this.upgradeClient();
5547 
5548     System.out.println("\n##################################");
5549     System.out.println("Upgrading API\n");
5550 
5551     //lets get the version of the existing jar
5552     this.originalGrouperJarVersionOrUpgradeFileVersion();
5553 
5554     this.compareAndReplaceJar(this.grouperJar, 
5555         new File(this.untarredApiDir + File.separator + "dist" + File.separator 
5556             + "lib" + File.separator + "grouper.jar"), true, null);
5557     
5558     if (this.appToUpgrade == AppToUpgrade.API) {
5559       boolean hadChange = false;
5560       for (String gshName : new String[]{"gsh", "gsh.bat", "gsh.sh"}) {
5561         File newGshFile = new File(this.untarredApiDir + File.separator + "bin" 
5562             + File.separator + gshName);
5563 
5564         File existingGshFile = new File(this.upgradeExistingApplicationDirectoryString 
5565             + "bin" + File.separator + gshName);
5566 
5567         if (!GrouperInstallerUtils.contentEquals(newGshFile, existingGshFile)) {
5568           this.backupAndCopyFile(newGshFile, existingGshFile, true);
5569           if (!GrouperInstallerUtils.equals("gsh.bat", gshName)) {
5570             hadChange = true;
5571           }
5572         }
5573         
5574       }
5575       if (hadChange) {
5576         //set executable and dos2unix
5577         gshExcutableAndDos2Unix(this.untarredApiDir + File.separator + "bin" 
5578             + File.separator);
5579       }
5580     }
5581 
5582     System.out.println("\n##################################");
5583     System.out.println("Upgrading API config files\n");
5584 
5585     this.compareUpgradePropertiesFile(this.grouperBasePropertiesFile, 
5586       new File(this.untarredApiDir + File.separator + "conf" + File.separator + "grouper.base.properties"),
5587       this.grouperPropertiesFile,
5588       this.grouperExamplePropertiesFile, null, 
5589       "grouperInstaller.autorun.removeRedundantPropetiesFromGrouperProperties"
5590     );
5591       
5592     this.compareUpgradePropertiesFile(this.grouperHibernateBasePropertiesFile, 
5593         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "grouper.hibernate.base.properties"),
5594         this.grouperHibernatePropertiesFile,
5595         this.grouperHibernateExamplePropertiesFile, null, 
5596         "grouperInstaller.autorun.removeRedundantPropetiesFromGrouperHibernateProperties"
5597       );
5598         
5599     this.compareUpgradePropertiesFile(this.grouperLoaderBasePropertiesFile, 
5600         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "grouper-loader.base.properties"),
5601         this.grouperLoaderPropertiesFile,
5602         this.grouperLoaderExamplePropertiesFile, null,
5603         "grouperInstaller.autorun.removeRedundantPropetiesFromGrouperLoaderProperties"
5604       );
5605     
5606     this.compareUpgradePropertiesFile(this.subjectBasePropertiesFile, 
5607         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "subject.base.properties"),
5608         this.subjectPropertiesFile,
5609         null, null,
5610         "grouperInstaller.autorun.removeRedundantPropetiesFromSubjectProperties"
5611       );
5612     
5613     this.compareUpgradePropertiesFile(this.grouperCacheBasePropertiesFile, 
5614         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "grouper.cache.base.properties"),
5615         this.grouperCachePropertiesFile,
5616         null, null,
5617         "grouperInstaller.autorun.removeRedundantPropetiesFromGrouperCacheProperties"
5618       );
5619 
5620     this.upgradeEhcacheXml();
5621     this.upgradeEhcacheXmlToProperties();
5622     this.upgradeSourcesXmlToProperties();
5623     
5624     this.compareAndCopyFile(this.grouperUtf8File, 
5625         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "grouperUtf8.txt"),
5626         true,
5627         new File(this.upgradeExistingClassesDirectoryString)
5628         );
5629     
5630     this.compareAndCopyFile(this.gshFileLoadPropertiesFile, 
5631         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "GSHFileLoad.properties"),
5632         true,
5633         new File(this.upgradeExistingClassesDirectoryString)
5634         );
5635     
5636     this.compareAndCopyFile(this.groovyshProfileFile, 
5637         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "groovysh.profile"),
5638         true,
5639         new File(this.upgradeExistingClassesDirectoryString)
5640         );
5641     
5642     this.compareAndCopyFile(this.grouperClientUsageExampleFile, 
5643         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "grouper.client.usage.example.txt"),
5644         true,
5645         new File(this.upgradeExistingClassesDirectoryString)
5646         );
5647     
5648     //do this only if less than 2.3.1
5649     if (new GiGrouperVersionersion(this.version).lessThanArg(new GiGrouperVersion("2.3.1"))) {
5650       System.out.println("\nYou should compare " + this.grouperPropertiesFile.getParentFile().getAbsolutePath() + File.separator + "sources.xml"
5651           + "\n  with " + this.untarredApiDir + File.separator + "conf" + File.separator + "sources.xml");
5652       System.out.print("Press <enter> to continue after you have merged the sources.xml. ");
5653       readFromStdIn("grouperInstaller.autorun.continueAfterMergingSourcesXml");
5654     }
5655     
5656     System.out.println("\n##################################");
5657     System.out.println("Upgrading API jars\n");
5658 
5659     this.upgradeJars(new File(this.untarredApiDir + File.separator + "lib" 
5660       + File.separator + "grouper" + File.separator));
5661     
5662     if (this.appToUpgrade.isApiOrganized()) {
5663 
5664       //if we need to put the jars in the jdbcSamples dir...
5665       this.upgradeJars(new File(this.untarredApiDir + File.separator + "lib" 
5666           + File.separator + "jdbcSamples" + File.separator), 
5667           new File(new File(this.upgradeExistingLibDirectoryString).getParentFile().getAbsolutePath() + File.separator + "jdbcSamples"));
5668     
5669     } else {
5670     
5671       this.upgradeJars(new File(this.untarredApiDir + File.separator + "lib" 
5672           + File.separator + "jdbcSamples" + File.separator));
5673     
5674     }
5675 
5676     {
5677       File subjectJar = new File(this.upgradeExistingLibDirectoryString + "subject.jar");
5678       if (subjectJar.exists()) {
5679         this.backupAndDeleteFile(subjectJar, true);
5680       }
5681     }
5682     
5683     {
5684       File oroJar = new File(this.upgradeExistingLibDirectoryString + "jakarta-oro.jar");
5685       if (oroJar.exists()) {
5686         this.backupAndDeleteFile(oroJar, true);
5687       }
5688     }
5689     
5690     System.out.println("\n##################################");
5691     System.out.println("Patch API\n");
5692 
5693     //patch it
5694     this.patchApi();
5695 
5696     //make sure log4j is debugging sql statements
5697     log4jDebugSql(this.upgradeExistingClassesDirectoryString + "log4j.properties");
5698     
5699     //verify that grouper.hibernate.properties doesn't have legacy properties
5700     removeLegacyHibernateProperties(this.upgradeExistingClassesDirectoryString + "grouper.hibernate.properties");
5701     
5702     System.out.println("\n##################################");
5703     System.out.println("Upgrading DB (registry)\n");
5704 
5705     this.apiUpgradeDbVersion(true);
5706 
5707     this.apiUpgradeAdditionalGshScripts();
5708 
5709   }
5710 
5711   /**
5712    * run additional GSH scripts based on what we are upgrading from...
5713    */
5714   private void apiUpgradeAdditionalGshScripts() {
5715     GiGrouperVersion giGrouperVersion = this.originalGrouperJarVersionOrUpgradeFileVersion();
5716     if (giGrouperVersion == null) {
5717       System.out.println("Grouper jar file: " + (this.grouperJar == null ? null : this.grouperJar.getAbsolutePath()));
5718       System.out.println("ERROR, cannot find grouper version in grouper jar file, do you want to continue? (t|f)? [f]: ");
5719       boolean continueScript = readFromStdInBoolean(false, "grouperInstaller.autorun.shouldContinueAfterNoGrouperVersionFound");
5720       if (!continueScript) {
5721         System.exit(1);
5722       }
5723     }
5724 
5725     boolean lessThan2_0 = this.originalGrouperJarVersion.lessThanArg(new GiGrouperVersion("2.0.0"));
5726     {
5727       String runUsduAutorun = null;
5728       if (lessThan2_0) {
5729         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]: ");
5730         runUsduAutorun = "grouperInstaller.autorun.runUsduPre2.0.0";
5731       } else {
5732         System.out.println("You are upgrading from after API version 2.0.0, so you dont have to do this,\n  "
5733             + "but do you want to run Unresolvable Subject Deletion Utility (USDU) (not recommended) (t|f)? [f]: ");
5734         runUsduAutorun = "grouperInstaller.autorun.runUsduPost2.0.0";
5735       }
5736       boolean runScript = readFromStdInBoolean(lessThan2_0, runUsduAutorun);
5737       
5738       if (runScript) {
5739         
5740         //running with command on command line doenst work on linux since the args with whitespace translate to 
5741         //save the commands to a file, and runt he file
5742         StringBuilder gshCommands = new StringBuilder();
5743   
5744         //gsh 0% GrouperSession.startRootSession()
5745         //edu.internet2.middleware.grouper.GrouperSession: 6f94c99d5b0948a3be96f94f00ab4d87,'GrouperSystem','application'
5746         //gsh 1% // run USDU to resolve all the subjects with type=person
5747         //gsh 3% usdu()
5748   
5749         gshCommands.append("grouperSession = GrouperSession.startRootSession();\n");
5750         gshCommands.append("usdu();\n");
5751   
5752         File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "gshUsdu.gsh");
5753         GrouperInstallerUtils.saveStringIntoFile(gshFile, gshCommands.toString());
5754         
5755         List<String> commands = new ArrayList<String>();
5756   
5757         addGshCommands(commands);
5758         commands.add(gshFile.getAbsolutePath());
5759   
5760         System.out.println("\n##################################");
5761         System.out.println("Running USDU with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
5762   
5763         GrouperInstallerUtils.execCommand(
5764             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
5765            new File(this.gshCommand()).getParentFile(), null, true);
5766   
5767       }
5768     }
5769     
5770     {
5771 
5772       String autorunResolveGroupSubjects = null;
5773       if (lessThan2_0) {
5774         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]: ");
5775         autorunResolveGroupSubjects = "grouperInstaller.autorun.resolveGroupSubjectsPre2.0.0";
5776       } else {
5777         System.out.println("You are upgrading from after API version 2.0.0, so you dont have to do this,\n  "
5778             + "but do you want to resolve all group subjects (not recommended) (t|f)? [f]: ");
5779         autorunResolveGroupSubjects = "grouperInstaller.autorun.resolveGroupSubjectsPost2.0.0";
5780       }
5781       boolean runScript = readFromStdInBoolean(lessThan2_0, autorunResolveGroupSubjects);
5782       
5783       if (runScript) {
5784         
5785         //running with command on command line doenst work on linux since the args with whitespace translate to 
5786         //save the commands to a file, and runt he file
5787         StringBuilder gshCommands = new StringBuilder();
5788   
5789         //gsh 5% GrouperSession.startRootSession();
5790         //edu.internet2.middleware.grouper.GrouperSession: 4163fb08b3b24922b55a14010d48e121,'GrouperSystem','application'
5791         //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); }
5792   
5793         gshCommands.append("grouperSession = GrouperSession.startRootSession();\n");
5794         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");
5795   
5796         File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "gshGroupUsdu.gsh");
5797         GrouperInstallerUtils.saveStringIntoFile(gshFile, gshCommands.toString());
5798         
5799         List<String> commands = new ArrayList<String>();
5800   
5801         addGshCommands(commands);
5802         commands.add(gshFile.getAbsolutePath());
5803   
5804         System.out.println("\n##################################");
5805         System.out.println("Resolving group subjects with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
5806 
5807         GrouperInstallerUtils.execCommand(
5808             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
5809            new File(this.gshCommand()).getParentFile(), null, true);
5810       }
5811     }
5812 
5813     {
5814       boolean lessThan2_1 = giGrouperVersion.lessThanArg(new GiGrouperVersion("2.1.0"));
5815       String autorunSeeRuleCheckType = null;
5816       if (lessThan2_1) {
5817         System.out.println("You are upgrading from pre API version 2.1.0, do you want to "
5818             + "see if you have rules with ruleCheckType: flattenedPermission* (recommended) (t|f)? [t]: ");
5819         autorunSeeRuleCheckType = "grouperInstaller.autorun.seeRulesFlattenedPermissionsPre2.1.0";
5820       } else {
5821         System.out.println("You are upgrading from after API version 2.1.0, so you dont have to do this,\n  "
5822             + "but do you want to see if you have rules with ruleCheckType: flattenedPermission* (not recommended) (t|f)? [f]: ");
5823         autorunSeeRuleCheckType = "grouperInstaller.autorun.seeRulesFlattenedPermissionsPost2.1.0";
5824       }
5825       boolean runScript = readFromStdInBoolean(lessThan2_1, autorunSeeRuleCheckType);
5826       
5827       if (runScript) {
5828         
5829         //running with command on command line doenst work on linux since the args with whitespace translate to 
5830         //save the commands to a file, and runt he file
5831         StringBuilder gshCommands = new StringBuilder();
5832     
5833         gshCommands.append("\"Count: \" + HibernateSession.bySqlStatic().select(int.class, \"SELECT count(*) FROM grouper_rules_v WHERE rule_check_type LIKE 'flattenedPermission%'\");\n");
5834   
5835         File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "gshRuleFlattenedPermissionCount.gsh");
5836         GrouperInstallerUtils.saveStringIntoFile(gshFile, gshCommands.toString());
5837         
5838         List<String> commands = new ArrayList<String>();
5839   
5840         addGshCommands(commands);
5841         commands.add(gshFile.getAbsolutePath());
5842   
5843         System.out.println("\n##################################");
5844         System.out.println("Counting flattenedPermission rules with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
5845 
5846         CommandResult commandResult = GrouperInstallerUtils.execCommand(
5847           GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
5848           new File(this.gshCommand()).getParentFile(), null, true);
5849 
5850         if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
5851           System.out.println("stderr: " + commandResult.getErrorText());
5852         }
5853         if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
5854           System.out.println("stdout: " + commandResult.getOutputText());
5855         }
5856 
5857         String result = commandResult.getOutputText().trim();
5858         String[] lines = GrouperInstallerUtils.splitLines(result);
5859         {
5860           Pattern pattern = Pattern.compile("^Count: ([0-9]+)$");
5861           int count = -1;
5862           for (String line : lines) {
5863             Matcher matcher = pattern.matcher(line);
5864             if (matcher.matches()) {
5865               count = GrouperInstallerUtils.intValue(matcher.group(1));
5866               break;
5867             }
5868           }
5869           if (count == -1) {
5870             System.out.println("Error getting count of rules, would you like to continue (t|f)? [t]:");
5871             if (!readFromStdInBoolean(true, "grouperInstaller.autorun.shouldContinueAfterErrorCountFlattenedRules")) {
5872               System.exit(1);
5873             }
5874           } else {
5875             if (count > 0) {
5876               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]: ");
5877               
5878               if (!readFromStdInBoolean(true, "grouperInstaller.autorun.shouldContinueAfterFoundFlattenedRules")) {
5879                 System.exit(1);
5880               }
5881             }
5882           }
5883         }
5884       }
5885     }
5886 
5887     {
5888       boolean lessThan2_2_0 = giGrouperVersion.lessThanArg(new GiGrouperVersion("2.2.0"));
5889       String autorunRun2_2gshScript = null;
5890       if (lessThan2_2_0) {
5891         System.out.println("You are upgrading from pre API version 2.2.0, "
5892             + "do you want to run the 2.2 upgrade GSH script (recommended) (t|f)? [t]: ");
5893         autorunRun2_2gshScript = "grouperInstaller.autorun.run2.2gshUpgradeScriptPre2.2.0";
5894       } else {
5895         System.out.println("You are upgrading from after API version 2.2.0, so you dont have to do this,\n  "
5896             + "but do you want to run the 2.2 upgrade GSH script (not recommended) (t|f)? [f]: ");
5897         autorunRun2_2gshScript = "grouperInstaller.autorun.run2.2gshUpgradeScriptPost2.2.0";
5898       }
5899       boolean runScript = readFromStdInBoolean(lessThan2_2_0, autorunRun2_2gshScript);
5900       
5901       if (runScript) {
5902         
5903         File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "misc" + File.separator + "postGrouper2_2Upgrade.gsh");
5904         
5905         List<String> commands = new ArrayList<String>();
5906   
5907         addGshCommands(commands);
5908         commands.add(gshFile.getAbsolutePath());
5909   
5910         System.out.println("\n##################################");
5911         System.out.println("Running 2.2 upgrade GSH with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
5912   
5913         GrouperInstallerUtils.execCommand(
5914             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
5915            new File(this.gshCommand()).getParentFile(), null, true);
5916   
5917       }
5918       
5919     }
5920 
5921     {
5922       boolean lessThan2_2_1 = giGrouperVersion.lessThanArg(new GiGrouperVersion("2.2.1"));
5923       String autorunRun2_2_1gshUpgradeScript = null;
5924       if (lessThan2_2_1) {
5925         System.out.println("You are upgrading from pre API version 2.2.1, do you want to "
5926             + "run the 2.2.1 upgrade GSH script (recommended) (t|f)? [t]: ");
5927         autorunRun2_2_1gshUpgradeScript = "grouperInstaller.autorun.run2.2.1gshUpgradeScriptPre2.2.1";
5928       } else {
5929         System.out.println("You are upgrading from after API version 2.2.1, so you dont have to do this,\n  "
5930             + "but do you want to run the 2.2.1 upgrade GSH script (not recommended) (t|f)? [f]: ");
5931         autorunRun2_2_1gshUpgradeScript = "grouperInstaller.autorun.run2.2.1gshUpgradeScriptPost2.2.1";
5932       }
5933       boolean runScript = readFromStdInBoolean(lessThan2_2_1, autorunRun2_2_1gshUpgradeScript);
5934       
5935       if (runScript) {
5936         
5937         File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "misc" + File.separator + "postGrouper2_2_1Upgrade.gsh");
5938         
5939         List<String> commands = new ArrayList<String>();
5940 
5941         addGshCommands(commands);
5942         commands.add(gshFile.getAbsolutePath());
5943 
5944         System.out.println("\n##################################");
5945         System.out.println("Running 2.2.1 upgrade GSH with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
5946 
5947         GrouperInstallerUtils.execCommand(
5948             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
5949            new File(this.gshCommand()).getParentFile(), null, true);
5950 
5951       }
5952     }
5953 
5954     {
5955       boolean lessThan2_3_0 = giGrouperVersion.lessThanArg(new GiGrouperVersion("2.3.0"));
5956       String autorunRun2_3_0gshUpgradeScript = null;
5957       if (lessThan2_3_0) {
5958         System.out.println("You are upgrading from pre API version 2.3.0, do you want to "
5959             + "run the 2.3.0 upgrade GSH script (recommended) (t|f)? [t]: ");
5960         autorunRun2_3_0gshUpgradeScript = "grouperInstaller.autorun.run2.3.0gshUpgradeScriptPre2.3.0";
5961       } else {
5962         System.out.println("You are upgrading from after API version 2.3.0, so you dont have to do this,\n  "
5963             + "but do you want to run the 2.3.0 upgrade GSH script (not recommended) (t|f)? [f]: ");
5964         autorunRun2_3_0gshUpgradeScript = "grouperInstaller.autorun.run2.3.0gshUpgradeScriptPost2.3.0";
5965       }
5966       boolean runScript = readFromStdInBoolean(lessThan2_3_0, autorunRun2_3_0gshUpgradeScript);
5967       
5968       if (runScript) {
5969         
5970         File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "misc" + File.separator + "postGrouper2_3_0Upgrade.gsh");
5971         
5972         List<String> commands = new ArrayList<String>();
5973 
5974         addGshCommands(commands);
5975         commands.add(gshFile.getAbsolutePath());
5976 
5977         System.out.println("\n##################################");
5978         System.out.println("Running 2.3.0 upgrade GSH with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
5979 
5980         GrouperInstallerUtils.execCommand(
5981             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
5982            new File(this.gshCommand()).getParentFile(), null, true);
5983 
5984       }
5985     }
5986 
5987   }
5988 
5989   /**
5990    * grouper version of jar
5991    */
5992   private GiGrouperVersion grouperVersionOfJar = null;
5993   
5994   /**
5995    * 
5996    * @return the version
5997    */
5998   private GiGrouperVersion grouperVersionOfJar() {
5999     if (this.grouperVersionOfJar == null) {
6000       String grouperJarVersionString = null;
6001       if (this.grouperJar != null && this.grouperJar.exists()) {
6002         grouperJarVersionString = GrouperInstallerUtils.jarVersion(this.grouperJar);
6003         
6004       } else if (this.grouperClientJar != null && this.grouperClientJar.exists()) {
6005         grouperJarVersionString = GrouperInstallerUtils.jarVersion(this.grouperClientJar);
6006         
6007       }
6008       if (!GrouperInstallerUtils.isBlank(grouperJarVersionString)) {
6009         this.grouperVersionOfJar = new GiGrouperVersion(grouperJarVersionString);
6010       }
6011   
6012       if (this.grouperVersionOfJar == null) {
6013         throw new RuntimeException("Cant find version of grouper! " + this.grouperJar + ", " + this.grouperClientJar);
6014       }
6015     }
6016     
6017     return this.grouperVersionOfJar;
6018   }
6019   
6020   /**
6021    * grouper jar version e.g. 2.1.5
6022    */
6023   private GiGrouperVersion originalGrouperJarVersion = null;
6024   
6025   /**
6026    * keep trac if we have found it or not
6027    */
6028   private boolean originalGrouperJarVersionRetrieved = false;
6029   
6030   /**
6031    * get the version of the grouper jar
6032    * @return the version or null if couldnt be found
6033    */
6034   private GiGrouperVersion originalGrouperJarVersionOrUpgradeFileVersion() {
6035 
6036     if (!this.originalGrouperJarVersionRetrieved) {
6037 
6038       this.originalGrouperJarVersionRetrieved = true;
6039       
6040       //lets see if an upgrade went halfway through
6041       this.grouperUpgradeOriginalVersionFile = new File(this.upgradeExistingApplicationDirectoryString + "grouperUpgradeOriginalVersion.txt");
6042 
6043       this.originalGrouperJarVersion = this.grouperVersionOfJar();
6044       
6045       if (this.grouperUpgradeOriginalVersionFile.exists()) {
6046         String grouperJarVersionString = GrouperInstallerUtils.readFileIntoString(this.grouperUpgradeOriginalVersionFile);
6047         GiGrouperVersionrsion.html#GiGrouperVersion">GiGrouperVersion fileGrouperJarVersion = new GiGrouperVersion(grouperJarVersionString);
6048         
6049         if (fileGrouperJarVersion != this.originalGrouperJarVersion) {
6050           
6051           System.out.println("It is detected that an upgrade did not complete from version " + fileGrouperJarVersion);
6052           this.originalGrouperJarVersion = fileGrouperJarVersion;
6053         }
6054       } else {
6055         GrouperInstallerUtils.writeStringToFile(this.grouperUpgradeOriginalVersionFile, this.originalGrouperJarVersion.toString());
6056       }
6057     }
6058     
6059     return this.originalGrouperJarVersion;
6060   }
6061   
6062   /**
6063    * file where version is kept for partial upgrades
6064    */
6065   private File grouperUpgradeOriginalVersionFile;
6066   
6067   /**
6068    * @param firstTime if first time
6069    */
6070   private void apiUpgradeDbVersion(boolean firstTime) {
6071     
6072     if (!GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.api.checkDdlVersion", true, false)) {
6073       System.out.println("Not checking DDL version since grouper.installer.properties: grouperInstaller.default.api.checkDdlVersion = false");
6074       return;
6075     }
6076 
6077     List<String> commands = new ArrayList<String>();
6078 
6079     addGshCommands(commands);
6080     commands.add("-registry");
6081     commands.add("-check");
6082     commands.add("-noprompt");
6083 
6084     System.out.println("\n##################################");
6085     System.out.println("Checking API database version with command: " + convertCommandsIntoCommand(commands) + "\n");
6086 
6087     CommandResult commandResult = GrouperInstallerUtils.execCommand(
6088         GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
6089         new File(this.gshCommand()).getParentFile(), null, true);
6090     
6091     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
6092       System.out.println("stdout: " + commandResult.getOutputText());
6093     }
6094     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
6095       System.out.println("stderr: " + commandResult.getErrorText());
6096     }
6097 
6098     String result = commandResult.getErrorText().trim();
6099     
6100     // Grouper ddl object type 'Grouper' has dbVersion: 27 and java version: 28
6101     // NOTE: Grouper database schema DDL may require updates, but the temp change log must 
6102     // be empty to perform an upgrade.  To process the temp change log, start up your current 
6103     // version of GSH and run: loaderRunOneJob("CHANGE_LOG_changeLogTempToChangeLog")
6104     if (result != null && result.contains("CHANGE_LOG_changeLogTempToChangeLog")) {
6105       System.out.println("You must run the change log temp to change log before upgrading.  You can start the upgrader again and run it.");
6106       System.exit(1);
6107     }
6108     
6109     String[] lines = GrouperInstallerUtils.splitLines(result);
6110     {
6111       boolean okWithVersion = false;
6112       boolean notOkWithVersion = false;
6113       for (String line : lines) {
6114         line = line.toLowerCase();
6115         //expecting stderr: NOTE: database table/object structure (ddl) is up to date
6116         if (line.contains("ddl") && line.contains("up to date") && line.contains("note:")) {
6117           okWithVersion = true;
6118         }
6119         //cant have this line
6120         if (line.contains("requires updates")) {
6121           notOkWithVersion = true;
6122         }
6123       }
6124       if (okWithVersion && !notOkWithVersion) {
6125         return;
6126       }
6127     }
6128 
6129     if (!firstTime) {
6130       System.out.println("Error: we tried to upgrade the database but it didnt work, would you like to continue skipping DDL (t|f)? ");
6131       boolean continueOn = readFromStdInBoolean(null, "grouperInstaller.autorun.shouldContinueIfErrorUpgradingDatabase");
6132       if (continueOn) {
6133         return;
6134       }
6135     }
6136     
6137     //we need to upgrade the DDL
6138     //Grouper ddl object type 'Grouper' has dbVersion: 26 and java version: 28
6139     //Grouper database schema DDL requires updates
6140     //(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),
6141     //script file is:
6142     //C:\app\grouper_2_2_0_installer\grouper.apiBinary-2.2.0\ddlScripts\grouperDdl_20141014_10_17_12_577.sql
6143     //Note: this script was not executed due to option passed in
6144     //To run script via gsh, carefully review it, then run this:
6145     //gsh -registry -runsqlfile C:\\app\\grouper_2_2_0_installer\\grouper.apiBinary-2.2.0\\ddlScripts\\grouperDdl_20141014_10_17_12_577.sql
6146 
6147     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]: ");
6148     boolean runIt = readFromStdInBoolean(true, "grouperInstaller.autorun.shouldRunDdlScript");
6149     
6150     if (runIt) {
6151 
6152       boolean foundScript = false;
6153 
6154       for (String line : lines) {
6155         if (line.contains("-registry -runsqlfile")) {
6156           
6157           String regexPattern = "^[^\\s]+\\s+-registry -runsqlfile (.*)$";
6158           Pattern pattern = Pattern.compile(regexPattern);
6159           
6160           Matcher matcher = pattern.matcher(line);
6161           
6162           if (!matcher.matches()) {
6163             throw new RuntimeException("Expected " + regexPattern + " but received: " + line);
6164           }
6165 
6166           String fileName = matcher.group(1);
6167           
6168           commands = new ArrayList<String>();
6169           
6170           addGshCommands(commands);
6171           commands.add("-registry");
6172           commands.add("-noprompt");
6173           commands.add("-runsqlfile");
6174           commands.add(fileName);
6175           
6176           foundScript = true;
6177           
6178           System.out.println("\n##################################");
6179           System.out.println("Upgrading database with command: " + convertCommandsIntoCommand(commands) + "\n");
6180 
6181           commandResult = GrouperInstallerUtils.execCommand(
6182               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
6183              new File(this.gshCommand()).getParentFile(), null, true);
6184 
6185           //no out/err since printing as we go
6186           System.out.println("\nDone upgrading database");
6187           System.out.println("\n##################################\n");
6188         }
6189       }
6190       //cant find script, thats ok, just check and go
6191       if (!foundScript) {
6192         throw new RuntimeException("didnt find script to to run: " + result);
6193       }
6194 
6195       //check again to make sure ok
6196       apiUpgradeDbVersion(false);
6197     }
6198   }
6199 
6200   /**
6201    * upgrade jars from a directory
6202    * @param fromDir jars from this directory
6203    */
6204   private void upgradeJars(File fromDir) {
6205     this.upgradeJars(fromDir, new File(this.upgradeExistingLibDirectoryString));
6206   }
6207   
6208   /**
6209    * upgrade jars from a directory
6210    * @param fromDir jars from this directory
6211    * @param toDir is where jars go if not there
6212    */
6213   private void upgradeJars(File fromDir, File toDir) {
6214 
6215     //for each jar in the directory
6216     if (!fromDir.exists() || !fromDir.isDirectory()) {
6217       throw new RuntimeException("Why does jar directory not exist? " + fromDir);
6218     }
6219     
6220     int changes = 0;
6221     
6222     // sort the files to get them a little more reproducible
6223     File[] fromFiles = GrouperInstallerUtils.nonNull(fromDir.listFiles(), File.class);
6224     List<File> fromFilesList = GrouperInstallerUtils.toList(fromFiles);
6225     Collections.sort(fromFilesList);
6226     for (File jarFile : fromFilesList) {
6227       
6228       //only do jar files
6229       if (!jarFile.getName().endsWith(".jar")) {
6230         continue;
6231       }
6232       
6233       // File existingJar = this.findLibraryFile(jarFile.getName(), false);
6234 
6235       List<File> relatedJars = null;
6236       
6237       relatedJars = GrouperInstallerUtils.jarFindJar(toDir, jarFile.getName());
6238       
6239       boolean foundFile = false;
6240       if (GrouperInstallerUtils.length(relatedJars) > 0) {
6241         
6242         for (File relatedJar : relatedJars) {
6243           if (!relatedJar.exists()) {
6244             continue;
6245           }
6246           if (GrouperInstallerUtils.fileSha1(relatedJar).equals(GrouperInstallerUtils.fileSha1(jarFile))) {
6247             if (relatedJar.getName().equals(jarFile.getName())) {
6248               foundFile = true;
6249               continue;
6250             }
6251           }
6252           
6253           File bakFile = bakFile(relatedJar);
6254           
6255           System.out.println("Deleting " + relatedJar.getAbsolutePath() + ", backed up to: " + bakFile.getAbsolutePath());
6256           changes++;
6257           boolean moved = GrouperInstallerUtils.fileMove(relatedJar, bakFile, false);
6258           if (!moved) {
6259             System.out.println("Non-fatal error: could not delete file: " + relatedJar.getAbsolutePath() 
6260                 + ",\ndelete this file when all processed are terminated.  Press <enter> to acknowledge this.");
6261             readFromStdIn("grouperInstaller.autorun.continueAfterCantDeleteJar");
6262           }
6263         }
6264       }
6265       if (!foundFile) {
6266         changes += this.compareAndReplaceJar(null, jarFile, false, toDir) ? 1 : 0;
6267       }
6268           
6269       
6270 //      if (existingJar == null) {
6271 //        //see if one exists by another version
6272 //        if (GrouperInstallerUtils.length(relatedJars) == 1) {
6273 //          existingJar = relatedJars.get(0);
6274 //        }
6275 //      }
6276 
6277     }
6278 
6279     System.out.println("Upgraded " + changes + " jar files from: " + fromDir.getAbsolutePath()
6280         + "\n  to: " + toDir.getAbsolutePath());
6281     
6282   }
6283   
6284   /**
6285    * 
6286    */
6287   private void upgradeEhcacheXml() {
6288 
6289     //ehcache, prompt to see if do it (if difference than example, and if old example different than new example?
6290     File newEhcacheExample = new File(this.untarredApiDir + File.separator + "conf" + File.separator + "ehcache.xml");
6291 
6292     //this file is done
6293     if (!newEhcacheExample.exists() || this.ehcacheFile == null || !this.ehcacheFile.exists()) {
6294       return;
6295     }
6296     
6297     //lets see if different
6298     String existingEhcacheContents = GrouperInstallerUtils.readFileIntoString(this.ehcacheFile);
6299     String existingExampleEhcacheContents = GrouperInstallerUtils.readFileIntoString(this.ehcacheExampleFile);
6300     String newEhcacheContents = GrouperInstallerUtils.readFileIntoString(newEhcacheExample);
6301     
6302     //if existing is the same as new...
6303     if (GrouperInstallerUtils.equals(existingEhcacheContents, newEhcacheContents)) {
6304       //make sure example is up to date
6305       if (this.ehcacheExampleFile != null && !GrouperInstallerUtils.equals(existingExampleEhcacheContents, newEhcacheContents)) {
6306         this.backupAndCopyFile(newEhcacheExample, this.ehcacheExampleFile, true);
6307       }
6308 
6309       //we are all good
6310       return;
6311     }
6312 
6313     //lets backup the example and regular file
6314     File ehcacheBakFile = bakFile(this.ehcacheFile);
6315     GrouperInstallerUtils.copyFile(this.ehcacheFile, ehcacheBakFile, true);
6316 
6317     boolean mergeFiles = true;
6318     
6319     if (this.ehcacheExampleFile != null) {
6320       File ehcacheExampleBakFile = bakFile(this.ehcacheExampleFile);
6321   
6322       GrouperInstallerUtils.copyFile(this.ehcacheExampleFile, ehcacheExampleBakFile, true);
6323     } else {
6324       GrouperInstallerUtils.copyFile(newEhcacheExample, this.ehcacheFile, true);
6325       mergeFiles = false;
6326     }
6327 
6328     if (mergeFiles) {
6329       //if the ehcache is the same as the example, lets just copy
6330       if (GrouperInstallerUtils.equals(existingEhcacheContents, existingExampleEhcacheContents)) {
6331         this.backupAndCopyFile(newEhcacheExample, this.ehcacheFile, false);
6332         if (this.ehcacheExampleFile != null) {
6333           this.backupAndCopyFile(newEhcacheExample, this.ehcacheExampleFile, false);
6334         }
6335         return;
6336       }
6337 
6338       //the ehcache file is different from the example and different from the new one, so merge it in
6339       mergeEhcacheXmlFiles(newEhcacheExample, this.ehcacheExampleFile, this.ehcacheFile);
6340     }
6341 
6342     System.out.println("Compare you old ehcache.xml with the new ehcache.xml file: " 
6343         + "\n  Old file: "
6344         + ehcacheBakFile.getAbsolutePath()
6345         + "\n  New file: " + this.ehcacheFile.getAbsolutePath()
6346         + "\n  Press <enter> when done");
6347     readFromStdIn("grouperInstaller.autorun.continueAfterCompareEhcache");
6348 
6349   }
6350 
6351   /**
6352    * 
6353    */
6354   private void upgradeEhcacheXmlToProperties() {
6355 
6356     //dont do this is less than 2.3.1
6357     if (new GiGrouperVersionersion(this.version).lessThanArg(new GiGrouperVersion("2.3.1"))) {
6358       return;
6359     }
6360     
6361     // the file may have been added during the install
6362     if (this.grouperCacheBasePropertiesFile == null) {
6363       this.grouperCacheBasePropertiesFile = findClasspathFile("grouper.cache.base.properties", false);
6364     }
6365     
6366     //this file is done
6367     if ((this.ehcacheFile == null || !this.ehcacheFile.exists())
6368         && this.grouperCacheBasePropertiesFile.exists() && this.grouperCachePropertiesFile.exists()) {
6369       return;
6370     }
6371     
6372     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]: ");
6373     boolean convert = readFromStdInBoolean(true, "grouperInstaller.autorun.convertEhcacheXmlToProperties");
6374 
6375     if (!convert) {
6376       System.out.println("Note: grouper will not run, but whatever you want to do!!!!");
6377     }
6378     
6379     // the file may have been added during the install
6380     if (this.grouperCachePropertiesFile == null) {
6381       this.grouperCachePropertiesFile = findClasspathFile("grouper.cache.properties", false);
6382     }
6383     
6384     if (this.grouperCachePropertiesFile.exists()) {
6385       //see if there is anything in it
6386       Properties grouperCacheProperties = GrouperInstallerUtils.propertiesFromFile(this.grouperCachePropertiesFile);
6387       if (grouperCacheProperties.size() > 0) {
6388         this.backupAndDeleteFile(this.grouperCachePropertiesFile, true);
6389       } else {
6390         GrouperInstallerUtils.fileDelete(this.grouperCachePropertiesFile);
6391       }
6392     }
6393 
6394     URL ehcacheXmlUrl = null;
6395     
6396     try {
6397       ehcacheXmlUrl = this.ehcacheFile.toURI().toURL();
6398     } catch (Exception e) {
6399       throw new RuntimeException("Problem with ehcache.xml: " + (this.ehcacheFile == null ? null : this.ehcacheFile.getAbsoluteFile()), e);
6400     }
6401     
6402     //convert
6403     convertEhcacheXmlToProperties(this.grouperCacheBasePropertiesFile, this.grouperCachePropertiesFile, ehcacheXmlUrl);
6404     
6405     File bakFile = bakFile(this.grouperCachePropertiesFile);
6406     GrouperInstallerUtils.copyFile(this.grouperCachePropertiesFile, bakFile, true);
6407     this.backupAndDeleteFile(this.ehcacheFile, true);
6408     this.backupAndDeleteFile(this.ehcacheExampleFile, true);
6409     
6410   }
6411 
6412   /**
6413    * 
6414    * @param newFile
6415    * @param existingFile
6416    * @param printDetails
6417    * @return the bakFile
6418    */
6419   public File backupAndCopyFile(File newFile, File existingFile, boolean printDetails) {
6420     
6421     if (!GrouperInstallerUtils.contentEquals(newFile, existingFile)) {
6422       
6423       File bakFile = null;
6424           
6425       boolean fileExists = existingFile.exists();
6426       if (fileExists) {
6427         bakFile = bakFile(existingFile);
6428         GrouperInstallerUtils.copyFile(existingFile, bakFile, true);
6429         if (printDetails) {
6430           System.out.println("Backing up: " + existingFile.getAbsolutePath() + " to: " + bakFile.getAbsolutePath());
6431         }
6432       }
6433       if (printDetails) {
6434         System.out.println("Copying " + (fileExists ? "new file" : "upgraded file") + ": " + newFile.getAbsolutePath() + " to: " + existingFile.getAbsolutePath());
6435       }
6436       GrouperInstallerUtils.copyFile(newFile, existingFile, false);
6437       return bakFile;
6438       
6439     }
6440 
6441     if (printDetails) {
6442       System.out.println(existingFile.getAbsolutePath() + " has not been updated so it was not changed");
6443     }
6444     
6445     return null;
6446   }
6447 
6448   /**
6449    * @param file
6450    * @param printDetails
6451    * @return the bakFile
6452    */
6453   public File backupAndDeleteFile(File file, boolean printDetails) {
6454 
6455     if (file != null && file.exists()) {
6456 
6457       File bakFile = null;
6458 
6459       bakFile = bakFile(file);
6460       GrouperInstallerUtils.copyFile(file, bakFile, true);
6461       if (printDetails) {
6462         System.out.println("Backing up: " + file.getAbsolutePath() + " to: " + bakFile.getAbsolutePath());
6463       }
6464       if (printDetails) {
6465         System.out.println("Deleting file: " + file.getAbsolutePath());
6466       }
6467       GrouperInstallerUtils.fileDelete(file);
6468       return bakFile;
6469 
6470     }
6471 
6472     if (printDetails) {
6473       System.out.println(file + " did not exist so it was not deleted");
6474     }
6475 
6476     return null;
6477   }
6478 
6479   /**
6480    * 
6481    * @param existingFile
6482    * @return the bak file
6483    */
6484   public File bakFile(File existingFile) {
6485     String existingFilePath = existingFile.getAbsolutePath();
6486     if (!GrouperInstallerUtils.filePathStartsWith(existingFilePath, this.upgradeExistingApplicationDirectoryString)) {
6487       throw new RuntimeException("Why does existing path not start with upgrade path??? " 
6488           + existingFilePath + ", " + this.upgradeExistingApplicationDirectoryString);
6489     }
6490     
6491     String bakString = this.grouperBaseBakDir 
6492         + existingFilePath.substring(this.upgradeExistingApplicationDirectoryString.length());
6493 
6494     File bakFile = new File(bakString);
6495     return bakFile;
6496   }
6497   
6498   /**
6499    * @param existingBasePropertiesFile 
6500    * @param newBasePropertiesFile 
6501    * @param existingPropertiesFile 
6502    * @param existingExamplePropertiesFile 
6503    * @param propertiesToIgnore
6504    * @param autorunPropertiesKeyRemoveRedundantProperties key in properties file to automatically fill in a value
6505    */
6506   private void compareUpgradePropertiesFile(File existingBasePropertiesFile, 
6507       File newBasePropertiesFile,
6508       File existingPropertiesFile,
6509       File existingExamplePropertiesFile,
6510       Set<String> propertiesToIgnore, String autorunPropertiesKeyRemoveRedundantProperties) {
6511 
6512     boolean hadChange = false;
6513     
6514     if (!newBasePropertiesFile.exists() || !newBasePropertiesFile.isFile()) {
6515       throw new RuntimeException("Why does this file not exist? " + newBasePropertiesFile.getAbsolutePath());
6516     }
6517     
6518     //if there is an existing base properties file, compare and replace and done
6519     if (existingBasePropertiesFile != null && existingBasePropertiesFile.exists() && existingBasePropertiesFile.isFile()) {
6520       
6521       String existingBaseContents = GrouperInstallerUtils.readFileIntoString(existingBasePropertiesFile);
6522       String newBaseContents = GrouperInstallerUtils.readFileIntoString(newBasePropertiesFile);
6523       
6524       if (!GrouperInstallerUtils.equals(existingBaseContents, newBaseContents)) {
6525         
6526         String existingBasePropertiesFilePath = existingBasePropertiesFile.getAbsolutePath();
6527         if (!GrouperInstallerUtils.filePathStartsWith(existingBasePropertiesFilePath, this.upgradeExistingApplicationDirectoryString)) {
6528           throw new RuntimeException("Why does existing path not start with upgrade path??? " 
6529               + existingBasePropertiesFilePath + ", " + this.upgradeExistingApplicationDirectoryString);
6530         }
6531         
6532         String bakBasePropertiesString = this.grouperBaseBakDir + existingBasePropertiesFilePath.substring(this.upgradeExistingApplicationDirectoryString.length());
6533 
6534         File bakBasePropertiesFile = new File(bakBasePropertiesString);
6535         
6536         //make sure parents exist
6537         GrouperInstallerUtils.createParentDirectories(bakBasePropertiesFile);
6538 
6539         System.out.println(existingBasePropertiesFile.getName() + " has changes and was upgraded.\n  It is backed up to " 
6540             + bakBasePropertiesFile.getAbsolutePath());
6541         
6542         hadChange = true;
6543         
6544         GrouperInstallerUtils.fileMove(existingBasePropertiesFile, bakBasePropertiesFile);
6545         
6546         GrouperInstallerUtils.copyFile(newBasePropertiesFile, existingBasePropertiesFile, true);
6547 
6548       }
6549       
6550     } else {
6551       
6552       hadChange = true;
6553       
6554       System.out.println(newBasePropertiesFile.getName() + " didn't exist and was installed.");
6555       
6556       //its null, but we dont have the path...
6557       if (existingBasePropertiesFile == null) {
6558         existingBasePropertiesFile = new File(this.upgradeExistingClassesDirectoryString + newBasePropertiesFile.getName());
6559       }
6560       GrouperInstallerUtils.copyFile(newBasePropertiesFile, existingBasePropertiesFile, true);
6561     }
6562     
6563     // if there is an example there, it can be removed
6564     if (existingExamplePropertiesFile != null && existingExamplePropertiesFile.exists() && existingExamplePropertiesFile.isFile()) {
6565 
6566       String existingExamplePropertiesFilePath = existingExamplePropertiesFile.getAbsolutePath();
6567       if (!GrouperInstallerUtils.filePathStartsWith(existingExamplePropertiesFilePath, this.upgradeExistingApplicationDirectoryString)) {
6568         throw new RuntimeException("Why does existing path not start with upgrade path??? " 
6569             + existingExamplePropertiesFilePath + ", " + this.upgradeExistingApplicationDirectoryString);
6570       }
6571       
6572       String bakExamplePropertiesString = this.grouperBaseBakDir 
6573           + existingExamplePropertiesFilePath.substring(this.upgradeExistingApplicationDirectoryString.length());
6574 
6575       File bakExamplePropertiesFile = new File(bakExamplePropertiesString);
6576       
6577       //make sure parents exist
6578       GrouperInstallerUtils.createParentDirectories(bakExamplePropertiesFile);
6579 
6580       System.out.println(existingExamplePropertiesFile.getName() + " is not needed and was deleted.\n  It is backed up to " 
6581           + bakExamplePropertiesFile.getAbsolutePath());
6582 
6583       GrouperInstallerUtils.fileMove(existingExamplePropertiesFile, bakExamplePropertiesFile);
6584     
6585     }
6586 
6587     if (existingPropertiesFile != null && existingPropertiesFile.exists() && existingPropertiesFile.isFile()) {
6588       
6589       // now then, if there is a properties file, we can look for duplicate configs, and remove them...
6590       Set<String> duplicateConfigPropertyNames = configPropertyDuplicates(newBasePropertiesFile, existingPropertiesFile);
6591 
6592       if (GrouperInstallerUtils.length(propertiesToIgnore) > 0 && GrouperInstallerUtils.length(duplicateConfigPropertyNames) > 0) {
6593         duplicateConfigPropertyNames.addAll(propertiesToIgnore);
6594       }
6595       
6596       if (GrouperInstallerUtils.length(duplicateConfigPropertyNames) > 0) {
6597 
6598         hadChange = true;
6599         
6600         System.out.println(existingPropertiesFile.getName() + " has " + duplicateConfigPropertyNames.size() 
6601             + " properties that can be removed since the values are the same in "
6602             + newBasePropertiesFile.getName());
6603 
6604         System.out.println("Would you like to have the " + duplicateConfigPropertyNames.size() 
6605             + " redundant properties automatically removed from " 
6606             + existingPropertiesFile.getName() + " (t|f)? [t]: ");
6607         boolean removeRedundantProperties = readFromStdInBoolean(true, autorunPropertiesKeyRemoveRedundantProperties);
6608         
6609         if (removeRedundantProperties) {
6610 
6611           String existingPropertiesFilePath = existingPropertiesFile.getAbsolutePath();
6612           if (!GrouperInstallerUtils.filePathStartsWith(existingPropertiesFilePath, this.upgradeExistingApplicationDirectoryString)) {
6613             throw new RuntimeException("Why does existing path not start with upgrade path??? " 
6614                 + existingPropertiesFilePath + ", " + this.upgradeExistingApplicationDirectoryString);
6615           }
6616           
6617           String bakPropertiesString = this.grouperBaseBakDir 
6618               + existingPropertiesFilePath.substring(this.upgradeExistingApplicationDirectoryString.length());
6619 
6620           File bakPropertiesFile = new File(bakPropertiesString);
6621           
6622           //make sure parents exist
6623           GrouperInstallerUtils.createParentDirectories(bakPropertiesFile);
6624 
6625           System.out.println(existingPropertiesFile.getName() + " had redundant properties removed after being backed up to " 
6626               + bakPropertiesFile.getAbsolutePath());
6627 
6628           GrouperInstallerUtils.copyFile(existingPropertiesFile, bakPropertiesFile, true);
6629           
6630           removeRedundantProperties(existingPropertiesFile, duplicateConfigPropertyNames);
6631           
6632         }
6633       }
6634     } else {
6635       
6636       hadChange = true;
6637       
6638       //if we didnt have a properties file, create one
6639       //file is null...
6640       String contents = "\n# The " + newBasePropertiesFile.getName().replace(".base", "") 
6641           + " file uses Grouper Configuration Overlays (documented on wiki)\n"
6642           + "# By default the configuration is read from " + newBasePropertiesFile.getName() + "\n"
6643           + "# (which should not be edited), and the " +newBasePropertiesFile.getName().replace(".base", "") + " overlays\n"
6644           + "# the base settings.  See the " + newBasePropertiesFile.getName() + " for the possible\n"
6645           + "# settings that can be applied to the " + newBasePropertiesFile.getName().replace(".base", "") + "\n\n";
6646 
6647       File file = null;
6648       
6649       if (existingPropertiesFile != null) {
6650         file = existingPropertiesFile;
6651       } else if (existingBasePropertiesFile != null) {
6652         file = new File(existingBasePropertiesFile.getAbsolutePath().replace(".base", ""));
6653 //      } else {
6654 //        String fileName =  existingPropertiesFile != null ? existingPropertiesFile.getAbsolutePath() : this.upgradeExistingClassesDirectoryString + newBasePropertiesFile.getName().replace(".base", "");
6655 //        file = new File(fileName);
6656       }
6657       
6658       System.out.println("Created overlay config file: " + file.getAbsolutePath());
6659       
6660       GrouperInstallerUtils.saveStringIntoFile(file, contents);
6661     }
6662     
6663     if (!hadChange) {
6664       System.out.println("Found no changes in " + existingBasePropertiesFile.getAbsolutePath());
6665     }
6666     
6667   }
6668 
6669   /**
6670    * remove duplicate properties
6671    * @param propertiesFile
6672    * @param duplicatePropertyNames
6673    */
6674   private static void removeRedundantProperties(File propertiesFile, Set<String> duplicatePropertyNames) {
6675     
6676     String fileContents = GrouperInstallerUtils.readFileIntoString(propertiesFile);
6677     
6678     String newline = GrouperInstallerUtils.newlineFromFile(fileContents);
6679 
6680     StringBuilder newContents = new StringBuilder();
6681 
6682     String[] lines = GrouperInstallerUtils.splitLines(fileContents);
6683     
6684     boolean inStartComments = true;
6685     boolean inHeaderComments = false;
6686 
6687     StringBuilder captureHeader = new StringBuilder();
6688     StringBuilder propertyAndComments = new StringBuilder();
6689     
6690     for (String line: lines) {
6691       
6692       line = line.trim();
6693       
6694       boolean isBlank = GrouperInstallerUtils.isBlank(line);
6695       boolean isComment = line.startsWith("#");
6696       boolean isSingleComment = line.startsWith("#") && !line.startsWith("##");
6697       boolean isHeaderComment = line.contains("#####");
6698       boolean isProperty = !isBlank && !isComment;
6699       
6700       //if in header then we are done with the start comments
6701       if (isHeaderComment) {
6702         inStartComments = false;
6703       }
6704 
6705       //we want to keep the start comments
6706       if (inStartComments) {
6707         
6708         if (isBlank || isComment) {
6709           newContents.append(line).append(newline);
6710           continue;
6711         }
6712         inStartComments = false;
6713       }
6714 
6715       //we are done with headers
6716       if (isProperty || isBlank || isSingleComment) {
6717         inHeaderComments = false;
6718       }
6719 
6720       if (isHeaderComment) {
6721         //if header and in headers, then we arent in headers
6722         if (inHeaderComments) {
6723           inHeaderComments = false;
6724         } else {
6725           //if this is a header, and we arent in headers, then we are in headers
6726           inHeaderComments = true;          
6727           captureHeader.setLength(0);
6728         }
6729       }
6730 
6731       if (isHeaderComment || inHeaderComments) {
6732         propertyAndComments.setLength(0);
6733         captureHeader.append(line).append(newline);
6734         continue;
6735       }
6736       
6737       if (isProperty) {
6738         
6739         //get the property
6740         int equalsIndex = line.indexOf('=');
6741         if (equalsIndex == -1) {
6742           //uh... ignore this... 
6743           System.out.println("Invalid line removed from properties file: " + propertiesFile.getAbsolutePath() + ":\n  " + line);
6744           continue;
6745         }
6746         
6747         String propertyName = line.substring(0, equalsIndex).trim();
6748         //unescape colons...
6749         if (duplicatePropertyNames.contains(propertyName) || duplicatePropertyNames.contains(propertyName.replace("\\:", ":"))) {
6750           propertyAndComments.setLength(0);
6751           //remove it!
6752           continue;
6753         }
6754 
6755         //keep it
6756         propertyAndComments.append(line).append(newline);
6757 
6758         //we need a header if there is one
6759         if (captureHeader.length() > 0) {
6760           newContents.append(newline);
6761           newContents.append(captureHeader);
6762           captureHeader.setLength(0);
6763         }
6764 
6765         //append the property and contents
6766         newContents.append(propertyAndComments);
6767 
6768         propertyAndComments.setLength(0);
6769         continue;
6770       }
6771       
6772       //must be whitespace or comment...
6773       propertyAndComments.append(line).append(newline);
6774     }
6775     
6776     GrouperInstallerUtils.saveStringIntoFile(propertiesFile, newContents.toString());
6777     
6778   }
6779   
6780   /**
6781    * 
6782    * @param file1
6783    * @param file2
6784    * @return the property names which are the same
6785    */
6786   @SuppressWarnings("unchecked")
6787   public static Set<String> configPropertyDuplicates(File file1, File file2) {
6788     Properties file1properties = GrouperInstallerUtils.propertiesFromFile(file1);
6789     Properties file2properties = GrouperInstallerUtils.propertiesFromFile(file2);
6790     
6791     Set<String> duplicatePropertyNames = new LinkedHashSet<String>();
6792     
6793     for (String propertyName : (Set<String>)(Object)file2properties.keySet()) {
6794       
6795       String file1Value = GrouperInstallerUtils.trimToEmpty(file1properties.getProperty(propertyName));
6796       String file2Value = GrouperInstallerUtils.trimToEmpty(file2properties.getProperty(propertyName));
6797       
6798       if (GrouperInstallerUtils.equals(file1Value, file2Value)) {
6799         duplicatePropertyNames.add(propertyName);
6800       }
6801       
6802     }
6803     return duplicatePropertyNames;
6804   }
6805   
6806   
6807   /**
6808    * the location of the existing installation, must end in file separator
6809    */
6810   private String upgradeExistingApplicationDirectoryString;
6811   
6812   /**
6813    * 
6814    */
6815   private static enum AppToUpgrade {
6816     
6817     /**
6818      * upgrading the UI
6819      */
6820     UI {
6821 
6822       @Override
6823       public void patchStatus(GrouperInstaller grouperInstaller) {
6824         grouperInstaller.patchStatusUi();
6825       }
6826 
6827       @Override
6828       public void patch(GrouperInstaller grouperInstaller) {
6829         grouperInstaller.patchUi();
6830       }
6831 
6832       @Override
6833       public void revertPatch(GrouperInstaller grouperInstaller) {
6834         grouperInstaller.patchRevertUi();
6835       }
6836 
6837       @Override
6838       public boolean validateExistingDirectory(GrouperInstaller grouperInstaller) {
6839         //API and client are in the UI
6840         if (!API.validateExistingDirectory(grouperInstaller)) {
6841           return false;
6842         }
6843         
6844         //no need to check if it exists... its new in 2.2
6845 
6846         //grouperInstaller.mediaPropertiesFile = grouperInstaller.findClasspathFile("media.properties", false);
6847 
6848         //media should be there, but not forever
6849         
6850         return true;
6851       }
6852 
6853       @Override
6854       public void downloadAndBuildGrouperProjects(GrouperInstaller grouperInstaller) {
6855         API.downloadAndBuildGrouperProjects(grouperInstaller);
6856         
6857         //####################################
6858         //download and configure ui
6859         grouperInstaller.downloadAndConfigureUi();
6860 
6861         //####################################
6862         //get ant
6863         grouperInstaller.downloadAndUnzipAnt();
6864 
6865         //####################################
6866         //build UI
6867         grouperInstaller.buildUi(false);
6868 
6869         File serverXml = null;
6870         for (int i=0;i<10;i++) {
6871           String defaultServerXml = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.ui.server.xml", false);
6872           System.out.println("What is the location of your tomcat server.xml for the UI?  "
6873               + "Note, if you dont use tomcat just leave it blank or type 'blank': " 
6874               + (GrouperInstallerUtils.isBlank(defaultServerXml) ? "" : ("[" + defaultServerXml + "]: ")));
6875           String serverXmlLocation = readFromStdIn("grouperInstaller.autorun.locationOfTomcatServerXml");
6876           
6877           if (GrouperInstallerUtils.equals(defaultServerXml, "blank")) {
6878             defaultServerXml = null;
6879             break;
6880           }
6881           
6882           if (GrouperInstallerUtils.isBlank(serverXmlLocation)) {
6883             if (GrouperInstallerUtils.isNotBlank(defaultServerXml)) {
6884               serverXmlLocation = defaultServerXml;
6885             } else {
6886               break;
6887             }
6888           }
6889           serverXml = new File(serverXmlLocation);
6890           if (serverXml.exists() && serverXml.isFile()) {
6891             break;
6892           }
6893           if (i != 9) {
6894             System.out.println("Error: server.xml cant be found, try again.");
6895           }
6896         }
6897         if (serverXml != null && serverXml.exists() && serverXml.isFile()) {
6898           grouperInstaller.configureTomcatUriEncoding(serverXml);
6899         }
6900                 
6901       }
6902 
6903       @Override
6904       public void upgradeApp(GrouperInstaller grouperInstaller) {
6905         grouperInstaller.upgradeUi();
6906       }
6907 
6908       @Override
6909       public void fixIndexFile(GrouperInstaller grouperInstaller) {
6910         grouperInstaller.fixIndexFileUi();
6911       }
6912 
6913       @Override
6914       public boolean isApiOrganized() {
6915         return false;
6916       }
6917     },
6918     
6919     /**
6920      * upgrading the API
6921      */
6922     API {
6923 
6924       @Override
6925       public boolean validateExistingDirectory(GrouperInstaller grouperInstaller) {
6926 
6927         //client is in the API
6928         if (!CLIENT.validateExistingDirectory(grouperInstaller)) {
6929           return false;
6930         }
6931         
6932         grouperInstaller.subjectPropertiesFile = grouperInstaller.findClasspathFile("subject.properties", false);
6933         grouperInstaller.subjectBasePropertiesFile = grouperInstaller.findClasspathFile("subject.base.properties", false);
6934 
6935         grouperInstaller.grouperUtf8File = grouperInstaller.findClasspathFile("grouperUtf8.txt", false);
6936         grouperInstaller.gshFileLoadPropertiesFile = grouperInstaller.findClasspathFile("GSHFileLoad.properties", false);
6937         grouperInstaller.grouperClientUsageExampleFile = grouperInstaller.findClasspathFile("grouper.client.usage.example.txt", false);
6938         grouperInstaller.groovyshProfileFile = grouperInstaller.findClasspathFile("groovysh.profile", false);
6939 
6940         //no need to check if it exists... its new in 2.2
6941         
6942         grouperInstaller.grouperPropertiesFile = grouperInstaller.findClasspathFile("grouper.properties", false);
6943         grouperInstaller.grouperBasePropertiesFile = grouperInstaller.findClasspathFile("grouper.base.properties", false);
6944         grouperInstaller.grouperExamplePropertiesFile = grouperInstaller.findClasspathFile("grouper.example.properties", false);
6945 
6946         if (grouperInstaller.grouperBasePropertiesFile == null 
6947             && grouperInstaller.grouperPropertiesFile == null 
6948             && grouperInstaller.grouperExamplePropertiesFile == null) {
6949           return false;
6950         }
6951         
6952         grouperInstaller.grouperHibernatePropertiesFile = grouperInstaller.findClasspathFile("grouper.hibernate.properties", false);
6953         grouperInstaller.grouperHibernateBasePropertiesFile = grouperInstaller.findClasspathFile("grouper.hibernate.base.properties", false);
6954         grouperInstaller.grouperHibernateExamplePropertiesFile = grouperInstaller.findClasspathFile("grouper.hibernate.example.properties", false);
6955 
6956         if (grouperInstaller.grouperHibernateBasePropertiesFile == null 
6957             && grouperInstaller.grouperHibernatePropertiesFile == null 
6958             && grouperInstaller.grouperHibernateExamplePropertiesFile == null) {
6959           return false;
6960         }
6961         
6962         grouperInstaller.grouperLoaderPropertiesFile = grouperInstaller.findClasspathFile("grouper-loader.properties", false);
6963         grouperInstaller.grouperLoaderBasePropertiesFile = grouperInstaller.findClasspathFile("grouper-loader.base.properties", false);
6964         grouperInstaller.grouperLoaderExamplePropertiesFile = grouperInstaller.findClasspathFile("grouper-loader.example.properties", false);
6965 
6966         if (grouperInstaller.grouperLoaderBasePropertiesFile == null 
6967             && grouperInstaller.grouperLoaderPropertiesFile == null 
6968             && grouperInstaller.grouperLoaderExamplePropertiesFile == null) {
6969           return false;
6970         }
6971         
6972         grouperInstaller.grouperCachePropertiesFile = grouperInstaller.findClasspathFile("grouper.cache.properties", false);
6973         grouperInstaller.grouperCacheBasePropertiesFile = grouperInstaller.findClasspathFile("grouper.cache.base.properties", false);
6974 
6975 //        //these must exist after 2.3.1+
6976 //        if (grouperInstaller.grouperCacheBasePropertiesFile == null 
6977 //            && grouperInstaller.grouperCachePropertiesFile == null 
6978 //            && new GiGrouperVersion(grouperInstaller.version).greaterOrEqualToArg(new GiGrouperVersion("2.3.1"))
6979 //            ) {
6980 //          return false;
6981 //        }
6982 
6983         //this must exist
6984         grouperInstaller.grouperJar = grouperInstaller.findLibraryFile("grouper.jar", false);
6985         if (grouperInstaller.grouperJar == null) {
6986           return false;
6987         }
6988 
6989         grouperInstaller.ehcacheFile = grouperInstaller.findClasspathFile("ehcache.xml", false);
6990         grouperInstaller.ehcacheExampleFile = grouperInstaller.findClasspathFile("ehcache.example.xml", false);        
6991         
6992         //all good
6993         return true;
6994       }
6995 
6996       @Override
6997       public void downloadAndBuildGrouperProjects(GrouperInstaller grouperInstaller) {
6998         CLIENT.downloadAndBuildGrouperProjects(grouperInstaller);
6999         
7000         //download api and set executable and dos2unix etc
7001         grouperInstaller.downloadAndConfigureApi();
7002 
7003       }
7004 
7005       @Override
7006       public void upgradeApp(GrouperInstaller grouperInstaller) {
7007         grouperInstaller.upgradeApi();
7008       }
7009 
7010       @Override
7011       public void patch(GrouperInstaller grouperInstaller) {
7012         grouperInstaller.patchApi();
7013       }
7014 
7015       @Override
7016       public void revertPatch(GrouperInstaller grouperInstaller) {
7017         grouperInstaller.patchRevertApi();
7018       }
7019 
7020       @Override
7021       public void patchStatus(GrouperInstaller grouperInstaller) {
7022         grouperInstaller.patchStatusApi();
7023       }
7024 
7025       @Override
7026       public void fixIndexFile(GrouperInstaller grouperInstaller) {
7027         grouperInstaller.fixIndexFileApi();
7028       }
7029 
7030       @Override
7031       public boolean isApiOrganized() {
7032         return true;
7033       }
7034     },
7035 
7036     /**
7037      * upgrading the client
7038      */
7039     CLIENT {
7040 
7041       @Override
7042       public void patchStatus(GrouperInstaller grouperInstaller) {
7043         throw new RuntimeException("Cant patch status client.  Client patches will be in the API if applicable");
7044       }
7045 
7046       @Override
7047       public void patch(GrouperInstaller grouperInstaller) {
7048         throw new RuntimeException("Cant patch client.  Client patches will be in the API if applicable");
7049       }
7050 
7051       @Override
7052       public void revertPatch(GrouperInstaller grouperInstaller) {
7053         throw new RuntimeException("Cant revert client.  Client patches will be in the API if applicable");
7054       }
7055 
7056       @Override
7057       public boolean validateExistingDirectory(GrouperInstaller grouperInstaller) {
7058 
7059         grouperInstaller.grouperClientPropertiesFile = grouperInstaller.findClasspathFile("grouper.client.properties", false);
7060         grouperInstaller.grouperClientBasePropertiesFile = grouperInstaller.findClasspathFile("grouper.client.base.properties", false);
7061         grouperInstaller.grouperClientExamplePropertiesFile = grouperInstaller.findClasspathFile("grouper.client.example.properties", false);
7062 
7063         if (grouperInstaller.grouperClientBasePropertiesFile == null 
7064             && grouperInstaller.grouperClientPropertiesFile == null 
7065             && grouperInstaller.grouperClientExamplePropertiesFile == null) {
7066           if (grouperInstaller.appToUpgrade == CLIENT) {
7067             return false;
7068           }
7069         }
7070         
7071         //this must exist
7072         grouperInstaller.grouperClientJar = grouperInstaller.findLibraryFile("grouperClient.jar", false);
7073         if (grouperInstaller.grouperClientJar == null) {
7074           if (grouperInstaller.appToUpgrade == CLIENT) {
7075             return false;
7076           }
7077         }
7078         
7079         //all good
7080         return true;
7081       }
7082 
7083       @Override
7084       public void downloadAndBuildGrouperProjects(GrouperInstaller grouperInstaller) {
7085         grouperInstaller.downloadAndBuildClient();
7086       }
7087 
7088       @Override
7089       public void upgradeApp(GrouperInstaller grouperInstaller) {
7090         grouperInstaller.upgradeClient();
7091       }
7092       
7093       @Override
7094       public void fixIndexFile(GrouperInstaller grouperInstaller) {
7095         throw new RuntimeException("Not implemented");
7096       }
7097 
7098       @Override
7099       public boolean isApiOrganized() {
7100         return false;
7101       }
7102     },
7103 
7104     /**
7105      * upgrading the WS
7106      */
7107     WS {
7108 
7109       @Override
7110       public void patchStatus(GrouperInstaller grouperInstaller) {
7111         grouperInstaller.patchStatusWs();
7112       }
7113 
7114       @Override
7115       public void patch(GrouperInstaller grouperInstaller) {
7116         grouperInstaller.patchWs();
7117       }
7118 
7119       @Override
7120       public void revertPatch(GrouperInstaller grouperInstaller) {
7121         grouperInstaller.patchRevertWs();
7122       }
7123 
7124       @Override
7125       public boolean validateExistingDirectory(GrouperInstaller grouperInstaller) {
7126         //API and client are in the UI
7127         if (!API.validateExistingDirectory(grouperInstaller)) {
7128           return false;
7129         }
7130 
7131         grouperInstaller.grouperWsPropertiesFile = grouperInstaller.findClasspathFile("grouper-ws.properties", false);
7132         grouperInstaller.grouperWsBasePropertiesFile = grouperInstaller.findClasspathFile("grouper-ws.base.properties", false);
7133         grouperInstaller.grouperWsExamplePropertiesFile = grouperInstaller.findClasspathFile("grouper-ws.example.properties", false);
7134 
7135         if (grouperInstaller.grouperWsBasePropertiesFile == null 
7136             && grouperInstaller.grouperWsPropertiesFile == null 
7137             && grouperInstaller.grouperWsExamplePropertiesFile == null) {
7138           return false;
7139         }
7140 
7141         return true;
7142       }
7143 
7144       @Override
7145       public void downloadAndBuildGrouperProjects(GrouperInstaller grouperInstaller) {
7146         API.downloadAndBuildGrouperProjects(grouperInstaller);
7147         
7148         //####################################
7149         //download and configure ws
7150         grouperInstaller.downloadAndUntarWs();
7151         
7152         //####################################
7153         //configure where api is
7154         grouperInstaller.configureWs();
7155 
7156         //####################################
7157         //get ant
7158         grouperInstaller.downloadAndUnzipAnt();
7159 
7160         //####################################
7161         //build Ws
7162         grouperInstaller.buildWs(false);
7163 
7164       }
7165 
7166       @Override
7167       public void upgradeApp(GrouperInstaller grouperInstaller) {
7168         grouperInstaller.upgradeWs();
7169       }
7170 
7171       @Override
7172       public void fixIndexFile(GrouperInstaller grouperInstaller) {
7173         grouperInstaller.fixIndexFileWs();
7174       }
7175 
7176       @Override
7177       public boolean isApiOrganized() {
7178         return false;
7179       }
7180     }, 
7181     
7182     /**
7183      * upgrading the UI
7184      */
7185     PSP {
7186 
7187       @Override
7188       public void patchStatus(GrouperInstaller grouperInstaller) {
7189         grouperInstaller.patchStatusPsp();
7190       }
7191 
7192       @Override
7193       public void patch(GrouperInstaller grouperInstaller) {
7194         grouperInstaller.patchPsp();
7195       }
7196 
7197       @Override
7198       public void revertPatch(GrouperInstaller grouperInstaller) {
7199         grouperInstaller.patchRevertPsp();
7200       }
7201 
7202       @Override
7203       public boolean validateExistingDirectory(GrouperInstaller grouperInstaller) {
7204         //API and client are in the UI
7205         if (!API.validateExistingDirectory(grouperInstaller)) {
7206           return false;
7207         }
7208         
7209         File customLibDir = new File(grouperInstaller.upgradeExistingApplicationDirectoryString + "lib" + File.separator + "custom");
7210         if (!customLibDir.exists()) {
7211           return false;
7212         }
7213 
7214         //see if psp jar is there
7215         List<File> files = GrouperInstallerUtils.jarFindJar(customLibDir, "psp.jar");
7216                 
7217         if (GrouperInstallerUtils.length(files) == 0) {
7218           return false;
7219         }
7220 
7221         return true;
7222       }
7223     
7224       @Override
7225       public void downloadAndBuildGrouperProjects(GrouperInstaller grouperInstaller) {
7226         API.downloadAndBuildGrouperProjects(grouperInstaller);
7227         
7228         //####################################
7229         //download and configure psp
7230         grouperInstaller.downloadAndBuildPsp();
7231     
7232       }
7233     
7234       @Override
7235       public void upgradeApp(GrouperInstaller grouperInstaller) {
7236         grouperInstaller.upgradePsp();
7237       }
7238 
7239       @Override
7240       public void fixIndexFile(GrouperInstaller grouperInstaller) {
7241         grouperInstaller.fixIndexFilePsp();
7242       }
7243       @Override
7244       public boolean isApiOrganized() {
7245         return true;
7246       }
7247     }, 
7248     
7249     /**
7250      * upgrading the UI
7251      */
7252     PSPNG {
7253     
7254       @Override
7255       public void patchStatus(GrouperInstaller grouperInstaller) {
7256         grouperInstaller.patchStatusPspng();
7257       }
7258     
7259       @Override
7260       public void patch(GrouperInstaller grouperInstaller) {
7261         grouperInstaller.patchPspng();
7262       }
7263     
7264       @Override
7265       public void revertPatch(GrouperInstaller grouperInstaller) {
7266         grouperInstaller.patchRevertPspng();
7267       }
7268     
7269       @Override
7270       public boolean validateExistingDirectory(GrouperInstaller grouperInstaller) {
7271         //API and client are in the UI
7272         if (!API.validateExistingDirectory(grouperInstaller)) {
7273           return false;
7274         }
7275         
7276         File customLibDir = new File(grouperInstaller.upgradeExistingApplicationDirectoryString + "lib" + File.separator + "custom");
7277         if (!customLibDir.exists()) {
7278           return false;
7279         }
7280     
7281         //see if psp jar is there
7282         String grouperVersion = grouperInstaller.grouperVersionOfJar().toString();
7283 
7284         List<File> files = GrouperInstallerUtils.jarFindJar(customLibDir, "grouper-pspng-" + grouperVersion + ".jar");
7285 
7286         if (GrouperInstallerUtils.length(files) == 0) {
7287           return false;
7288         }
7289     
7290         return true;
7291       }
7292     
7293       @Override
7294       public void downloadAndBuildGrouperProjects(GrouperInstaller grouperInstaller) {
7295         API.downloadAndBuildGrouperProjects(grouperInstaller);
7296         
7297         //####################################
7298         //download and configure psp
7299         grouperInstaller.downloadAndBuildPspng();
7300     
7301       }
7302     
7303       @Override
7304       public void upgradeApp(GrouperInstaller grouperInstaller) {
7305         grouperInstaller.upgradePspng();
7306       }
7307     
7308       @Override
7309       public void fixIndexFile(GrouperInstaller grouperInstaller) {
7310         grouperInstaller.fixIndexFilePspng();
7311       }
7312       @Override
7313       public boolean isApiOrganized() {
7314         return true;
7315       }
7316     };
7317 
7318     /**
7319      * if the organization is API organzied (e.g. has lib/jdbcSamples dir)
7320      * @return true/false
7321      */
7322     public abstract boolean isApiOrganized();
7323     
7324     /**
7325      * validate that the existing directory is valid, and find all the file paths
7326      * @param grouperInstaller 
7327      * @return true if valid, false if not
7328      */
7329     public abstract boolean validateExistingDirectory(GrouperInstaller grouperInstaller);
7330     
7331     /**
7332      * based on what is being upgraded, download and build the grouper projects
7333      * @param grouperInstaller
7334      */
7335     public abstract void downloadAndBuildGrouperProjects(GrouperInstaller grouperInstaller);
7336     
7337     /**
7338      * upgrade this app
7339      * @param grouperInstaller
7340      */
7341     public abstract void upgradeApp(GrouperInstaller grouperInstaller);
7342     
7343     /**
7344      * patch this app
7345      * @param grouperInstaller
7346      */
7347     public abstract void patch(GrouperInstaller grouperInstaller);
7348     
7349     /**
7350      * revert patch this app
7351      * @param grouperInstaller
7352      */
7353     public abstract void revertPatch(GrouperInstaller grouperInstaller);
7354     
7355     /**
7356      * patch status for this app
7357      * @param grouperInstaller
7358      */
7359     public abstract void patchStatus(GrouperInstaller grouperInstaller);
7360     
7361     /**
7362      * fix index file for this app
7363      * @param grouperInstaller
7364      */
7365     public abstract void fixIndexFile(GrouperInstaller grouperInstaller);
7366     
7367     /**
7368      * 
7369      * @param string
7370      * @param exceptionIfInvalid
7371      * @param exceptionIfBlank
7372      * @return the action
7373      */
7374     @SuppressWarnings("unused")
7375     public static AppToUpgrade valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
7376       return GrouperInstallerUtils.enumValueOfIgnoreCase(AppToUpgrade.class, string, exceptionIfBlank, exceptionIfInvalid);
7377     }
7378 
7379   }
7380 
7381   /**
7382    * patch names installed (used for dependency checking), without the file ending e.g. grouper_v2_2_1_api_patch_0
7383    */
7384   private Set<String> patchesInstalled = new HashSet<String>();
7385   
7386   /**
7387    * if grouper is stopped
7388    */
7389   private boolean grouperStopped = false;
7390 
7391   /**
7392    * if should revert all
7393    */
7394   private Boolean revertAllPatches = null;
7395   
7396   /**
7397    * if we should use all local files
7398    */
7399   private Boolean useAllLocalFiles = null;
7400   
7401   /**
7402    * if we should use all unzipped files
7403    */
7404   private Boolean useAllUnzippedFiles = null;
7405   
7406   /**
7407    * if we should use all untarred directories
7408    */
7409   private Boolean useAllUntarredDirectories = null;
7410   
7411   /**
7412    * default for revert all patches
7413    */
7414   private boolean revertAllPatchesDefault = false;
7415   
7416   /**
7417    * if should revert all
7418    */
7419   private Boolean installAllPatches = null;
7420   
7421   /**
7422    * if should install some patches
7423    */
7424   private Boolean installPatchesUpToACertainPatchLevel = null;
7425   
7426   /**
7427    * if should install up to patch levels, comma separated
7428    * e.g. grouper_v2_3_0_api_patch_9, grouper_v2_3_0_ui_patch_10, grouper_v2_3_0_ws_patch_5
7429    */
7430   private String installPatchesUpToThesePatchLevels = null;
7431   
7432   /**
7433    * if should install certain specified
7434    */
7435   private Boolean installCertainSpecifiedPatches = null;
7436   
7437   /**
7438    * if should install up to patch levels, comma separated
7439    * e.g. grouper_v2_3_0_api_patch_0, grouper_v2_3_0_api_patch_1, grouper_v2_3_0_ui_patch_0
7440    */
7441   private String installCertainSpecifiedPatchesList = null;
7442   
7443   /**
7444    * if should revert certain specified
7445    */
7446   private Boolean revertCertainSpecifiedPatches = null;
7447   
7448   /**
7449    * if should revert up to patch levels, comma separated
7450    * e.g. grouper_v2_3_0_api_patch_0, grouper_v2_3_0_api_patch_1, grouper_v2_3_0_ui_patch_0
7451    */
7452   private String revertCertainSpecifiedPatchesList = null;
7453   
7454   /**
7455    * revert patches for an app
7456    * @param thisAppToRevert
7457    * @param originalAppToUpgrade 
7458    * @return if reverted
7459    */
7460   private boolean revertPatches(AppToUpgrade thisAppToRevert, AppToUpgrade originalAppToUpgrade) {
7461 
7462     if (thisAppToRevert == AppToUpgrade.CLIENT) {
7463       throw new RuntimeException("Cant revert " + thisAppToRevert);
7464     }
7465     
7466     Properties patchesExistingProperties = patchExistingProperties();
7467     
7468     String grouperVersion = this.grouperVersionOfJar().toString();
7469 
7470     grouperVersion = GrouperInstallerUtils.replace(grouperVersion, ".", "_");
7471 
7472     Map<Integer, String> patchNumberToNameBase = new LinkedHashMap<Integer, String>();
7473     
7474     boolean foundPatch = false;
7475 
7476     Map<String, Set<String>> installedPatchDependencies = new HashMap<String, Set<String>>();
7477     
7478     File patchExistingPropertiesFile = patchExistingPropertiesFile();
7479     
7480     for (int i=1000;i>=0;i--) {
7481       
7482       //grouper_v2_2_1_api_patch_0.state
7483       String keyBase = "grouper_v" + grouperVersion + "_" + thisAppToRevert.name().toLowerCase() + "_patch_" + i;
7484       String key = keyBase + ".state";
7485 
7486       patchNumberToNameBase.put(i, keyBase);
7487       
7488       String value = patchesExistingProperties.getProperty(key);
7489 
7490       if (!GrouperInstallerUtils.isBlank(value)) {
7491         
7492         System.out.println("\n################ Checking patch " + keyBase);
7493 
7494         GrouperInstallerPatchStatus grouperInstallerPatchStatus = GrouperInstallerPatchStatus.valueOfIgnoreCase(value, true, true);
7495         
7496         switch (grouperInstallerPatchStatus) {
7497           case skippedPermanently:
7498             
7499             System.out.println("Patch: " + keyBase + ": was skipped permanently on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7500             continue;
7501 
7502           case skippedTemporarily:
7503 
7504             System.out.println("Patch: " + keyBase + ": was skipped termporarily on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7505             continue;
7506 
7507           case reverted:
7508 
7509             System.out.println("Patch: " + keyBase + ": was removed on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7510             continue;
7511 
7512           case error:
7513 
7514             System.out.println("Patch: " + keyBase + ": had an error installing on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7515             continue;
7516 
7517           case applied:
7518             
7519             System.out.println("Patch: " + keyBase + ": was applied on: " + patchesExistingProperties.getProperty(keyBase + ".date")  + "\n");
7520             this.patchesInstalled.add(keyBase);
7521             break;
7522 
7523           default:
7524             throw new RuntimeException("Not expecting: " + grouperInstallerPatchStatus);
7525         }
7526 
7527       } else {
7528         continue;
7529       }
7530 
7531       if (!this.patchesInstalled.contains(keyBase)) {
7532         System.out.println("\n");
7533         continue;
7534       }
7535 
7536       //lets see if it exists on the server
7537       File patchUntarredDir = downloadAndUnzipPatch(keyBase);
7538       
7539       //if no more patches
7540       if (patchUntarredDir == null) {
7541         System.out.print("Error: cant find directory for patch: " + keyBase + ", press <enter> to continue. ");
7542         readFromStdIn("grouperInstaller.autorun.continueAfterCantFindPatchDir");
7543         continue;
7544       }
7545 
7546       //lets get the description:
7547       //  # will show up on screen so user knows what it is
7548       //  description = This patch fixes GRP-1080: browse folders refresh button only works in chrome, not other browsers
7549       //
7550       //  # patches that this patch is dependant on (comma separated)
7551       //  dependencies = 
7552       //
7553       //  # low, medium, or high risk to applying the patch
7554       //  risk = low
7555       //
7556       //  # is this is a security patch (true or false)
7557       //  security = false
7558       //
7559       //  # if this patch requires restart of processes (true or false)
7560       //  requiresRestart = false
7561       Properties patchProperties = GrouperInstallerUtils.propertiesFromFile(new File(patchUntarredDir.getAbsoluteFile() + File.separator + keyBase + ".properties"));
7562 
7563       foundPatch = true;
7564 
7565       // check dependencies
7566       {
7567         List<String> dependencies = GrouperInstallerUtils.splitTrimToList(patchProperties.getProperty("dependencies"), ",");
7568         Set<String> dependenciesSet = new HashSet<String>(GrouperInstallerUtils.nonNull(dependencies));
7569         installedPatchDependencies.put(keyBase, dependenciesSet);
7570       }
7571 
7572       boolean securityRelated = GrouperInstallerUtils.booleanValue(patchProperties.getProperty("security"), false);
7573       boolean requiresRestart = GrouperInstallerUtils.booleanValue(patchProperties.getProperty("requiresRestart"), true);
7574 
7575       if (this.revertAllPatches == null) {
7576         System.out.println("Would you like to revert all patches (t|f)? [" + (this.revertAllPatchesDefault ? "t" : "f") + "]: ");
7577         this.revertAllPatches = readFromStdInBoolean(this.revertAllPatchesDefault, "grouperInstaller.autorun.revertAllPatches");
7578       }
7579       
7580       if (!this.revertAllPatches && this.revertCertainSpecifiedPatches == null) {
7581         System.out.println("Would you like to revert certain specified patches? (t|f)? [f]: ");
7582         this.revertCertainSpecifiedPatches = readFromStdInBoolean(false, "grouperInstaller.autorun.revertCertainSpecifiedPatches");
7583 
7584         if (this.revertCertainSpecifiedPatches) {
7585 
7586           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)? : ");
7587           this.revertCertainSpecifiedPatchesList = readFromStdIn("grouperInstaller.autorun.revertCertainSpecifiedPatchesList");
7588         }
7589       }
7590       if (this.revertCertainSpecifiedPatches == null) {
7591         this.revertCertainSpecifiedPatches = false;
7592       }
7593 
7594       //print description
7595       System.out.println("Patch " + keyBase + " is " + patchProperties.getProperty("risk") + " risk, "
7596           + (securityRelated ? "is a security patch" : "is not a security patch"));
7597       System.out.println(patchProperties.getProperty("description"));
7598       
7599       Boolean revertPatch = null;
7600       
7601       if (this.revertAllPatches) {
7602         revertPatch = true;
7603       } else if (this.revertCertainSpecifiedPatches) {
7604         if (revertPatch == null) {
7605           revertPatch = shouldRevertCertainSpecifiedPatches(keyBase);
7606         }
7607       } else {
7608         System.out.print("Would you like to revert patch " + keyBase + " (t|f)? [f]: ");
7609         revertPatch = readFromStdInBoolean(false, "grouperInstaller.autorun.revertPatch");
7610       }
7611 
7612 
7613       if (!revertPatch) {
7614         System.out.println("");
7615         continue;
7616       }
7617 
7618       //check dependencies
7619       for (String patchName : installedPatchDependencies.keySet()) {
7620         
7621         Set<String> dependencies = GrouperInstallerUtils.nonNull(installedPatchDependencies.get(patchName));
7622         
7623         if (dependencies.contains(keyBase)) {
7624           System.out.println("Error: cant revert " + keyBase + " because an installed patch is dependent on it: " + patchName);
7625           System.exit(1);
7626         }
7627       }
7628 
7629       if (requiresRestart && !this.grouperStopped) {
7630         System.out.print("This patch requires all processes that user Grouper to be stopped.\n  "
7631             + "Please stop these processes if they are running and press <enter> to continue... ");
7632         this.grouperStopped = true;
7633         readFromStdIn("grouperInstaller.autorun.continueAfterStoppingGrouperProcesses");
7634       }
7635       
7636       Map<String, String> patchDirToApplicationPath = new LinkedHashMap<String, String>();
7637       patchDirToApplicationPath.put("files", this.upgradeExistingApplicationDirectoryString);
7638       patchDirToApplicationPath.put("classes", this.upgradeExistingClassesDirectoryString);
7639       patchDirToApplicationPath.put("lib", this.upgradeExistingLibDirectoryString);
7640       patchDirToApplicationPath.put("bin", this.upgradeExistingBinDirectoryString);
7641 
7642       boolean patchHasProblem = false;
7643       
7644       //we are reverting this patch, lets see if the files are there...
7645       //this.upgradeExistingApplicationDirectoryString
7646       //patchUntarredDir
7647       File newDir = new File(patchUntarredDir.getAbsolutePath() + File.separator + "new");
7648       File oldDir = new File(patchUntarredDir.getAbsolutePath() + File.separator + "old");
7649       {
7650 
7651         for (String patchDir : patchDirToApplicationPath.keySet()) {
7652 
7653           String applicationPath = patchDirToApplicationPath.get(patchDir);
7654 
7655           File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
7656           File oldDirFiles = new File(oldDir.getAbsoluteFile() + File.separator + patchDir);
7657           
7658           if (newDirFiles.exists() && newDirFiles.isDirectory()) {
7659 
7660             // relative, e.g. WEB-INF/jsp/someFile.jsp
7661             Set<String> newFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(newDirFiles);
7662 
7663             for (String newFilePath : GrouperInstallerUtils.nonNull(newFileRelativePaths)) {
7664               File newFileInPatch = new File(newDirFiles.getAbsolutePath() + File.separator + newFilePath);
7665               File oldFileInPatch = new File(oldDirFiles.getAbsolutePath() + File.separator + newFilePath);
7666               
7667               if (revertPatchExcludes.contains(newFileInPatch.getName())) {
7668                 System.out.println("Skipping revert for file: " + newFileInPatch.getName());
7669                 continue;
7670               }
7671 
7672               File newFileInGrouper = new File(applicationPath + newFilePath);
7673 
7674               newFileInGrouper = fixLibraryFileIfFoundAndDifferent(newFileInGrouper, originalAppToUpgrade);
7675               
7676               if (!newFileInGrouper.exists() || !newFileInGrouper.isFile() 
7677                   || (!GrouperInstallerUtils.contentEquals(newFileInPatch, newFileInGrouper)
7678                       //its ok if the patch is already reverted?
7679                       && !GrouperInstallerUtils.contentEquals(oldFileInPatch, newFileInGrouper))) {
7680                 
7681                 // if it's just an example file and it didn't previously exist, then it's fine??
7682                 if (!newFileInGrouper.exists() && newFileInGrouper.getName().contains(".example.")) {
7683                   System.out.println("Grouper file " + newFileInGrouper.getAbsolutePath() + " doesn't exist.  Reverting patch anyways since this is an example file.");
7684                 } else {
7685                 
7686                   System.out.print("Problem reverting patch since this patch file:\n  " + newFileInPatch.getAbsolutePath() 
7687                       + "\n  is not the same as what the patch expects:\n  " + newFileInGrouper.getAbsolutePath()
7688                       + "\n  Do you want to force revert this patch (t|f)? [f]: ");
7689                   
7690                   boolean forceRevertPatch = readFromStdInBoolean(false, "grouperInstaller.autorun.forceRevertPatch");
7691                   
7692                   if (!forceRevertPatch) {
7693                     System.out.println("\nCannot revert patch since this patch file:\n  " + newFileInPatch.getAbsolutePath() 
7694                         + "\n  is not the same as what the patch expects:\n  " + newFileInGrouper.getAbsolutePath());
7695                     patchHasProblem = true;
7696                   }
7697                 }
7698               }
7699             }
7700           }
7701         }
7702       }
7703 
7704       {
7705         //deletes
7706         for (String patchDir : patchDirToApplicationPath.keySet()) {
7707 
7708           String applicationPath = patchDirToApplicationPath.get(patchDir);
7709 
7710           File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
7711           File oldDirFiles = new File(oldDir.getAbsoluteFile() + File.separator + patchDir);
7712           
7713           if (oldDirFiles.exists() && oldDirFiles.isDirectory()) {
7714 
7715             // relative, e.g. WEB-INF/jsp/someFile.jsp
7716             Set<String> oldFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(oldDirFiles);
7717 
7718             for (String oldFilePath : GrouperInstallerUtils.nonNull(oldFileRelativePaths)) {
7719               File newFileInPatch = new File(newDirFiles.getAbsolutePath() + File.separator + oldFilePath);
7720               File oldFileInPatch = new File(oldDirFiles.getAbsolutePath() + File.separator + oldFilePath);
7721 
7722               //if there is a new file, then its not a delete
7723               if (newFileInPatch.exists()) {
7724                 continue;
7725               }
7726               
7727               if (revertPatchExcludes.contains(newFileInPatch.getName())) {
7728                 System.out.println("Skipping revert for file: " + newFileInPatch.getName());
7729                 continue;
7730               }
7731               
7732               File newFileInGrouper = new File(applicationPath + oldFilePath);
7733               
7734               newFileInGrouper = fixLibraryFileIfFoundAndDifferent(newFileInGrouper, originalAppToUpgrade);
7735 
7736               if (newFileInGrouper.exists() && newFileInGrouper.isFile() 
7737                   && !GrouperInstallerUtils.contentEquals(oldFileInPatch, newFileInGrouper)) {
7738                 
7739                 System.out.print("Problem reverting patch since this patch file:\n  " + oldFileInPatch.getAbsolutePath() 
7740                     + "\n  is not the same as what the patch expects (shouldnt exist):\n  " + newFileInGrouper.getAbsolutePath()
7741                     + "\n  Do you want to force revert this patch (t|f)? [f]: ");
7742                 
7743                 boolean forceRevertPatch = readFromStdInBoolean(true, "grouperInstaller.autorun.forceRevertPatch");
7744                 
7745                 if (!forceRevertPatch) {
7746                   System.out.println("\nCannot revert patch since this patch file:\n  " + newFileInPatch.getAbsolutePath() 
7747                       + "\n  is not the same as what the patch expects:\n  " + newFileInGrouper.getAbsolutePath());
7748                   patchHasProblem = true;
7749                 }
7750               }
7751             }
7752           }
7753         }
7754       }
7755 
7756       if (patchHasProblem) {
7757         System.out.println("Cannot continue since patch has problem");
7758         System.exit(1);
7759       }
7760       
7761       //so far so good, all the new files are all good, revert the patch
7762       for (String patchDir : patchDirToApplicationPath.keySet()) {
7763         
7764         String applicationPath = patchDirToApplicationPath.get(patchDir);
7765 
7766         File oldDirFiles = new File(oldDir.getAbsoluteFile() + File.separator + patchDir);
7767         File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
7768         
7769         if (newDirFiles.exists() && newDirFiles.isDirectory()) {
7770         
7771           // relative, e.g. WEB-INF/jsp/someFile.jsp
7772           Set<String> newFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(newDirFiles);
7773           
7774           for (String newFilePath : GrouperInstallerUtils.nonNull(newFileRelativePaths)) {
7775 
7776             File oldFileInPatch = new File(oldDirFiles.getAbsolutePath() + File.separator + newFilePath);
7777 
7778             File newFileInGrouper = new File(applicationPath + newFilePath);
7779             
7780             if (revertPatchExcludes.contains(oldFileInPatch.getName())) {
7781               continue;
7782             }
7783             
7784             newFileInGrouper = fixLibraryFileIfFoundAndDifferent(newFileInGrouper, originalAppToUpgrade);
7785 
7786             if (oldFileInPatch.exists() && oldFileInPatch.isFile()) {
7787               System.out.println("Reverting file: " + newFileInGrouper.getAbsolutePath());
7788               GrouperInstallerUtils.copyFile(oldFileInPatch, newFileInGrouper, false);
7789             } else {
7790               System.out.println("Reverting (deleting) file: " + newFileInGrouper.getAbsolutePath());
7791               GrouperInstallerUtils.fileDelete(newFileInGrouper);
7792             }
7793           }
7794         }
7795       }
7796       
7797       //so far so good, revert the deletes
7798       for (String patchDir : patchDirToApplicationPath.keySet()) {
7799         
7800         String applicationPath = patchDirToApplicationPath.get(patchDir);
7801 
7802         File oldDirFiles = new File(oldDir.getAbsoluteFile() + File.separator + patchDir);
7803         File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
7804         
7805         if (oldDirFiles.exists() && oldDirFiles.isDirectory()) {
7806         
7807           // relative, e.g. WEB-INF/jsp/someFile.jsp
7808           Set<String> oldFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(oldDirFiles);
7809           
7810           for (String oldFilePath : GrouperInstallerUtils.nonNull(oldFileRelativePaths)) {
7811 
7812             File oldFileInPatch = new File(oldDirFiles.getAbsolutePath() + File.separator + oldFilePath);
7813             File newFileInPatch = new File(newDirFiles.getAbsolutePath() + File.separator + oldFilePath);
7814 
7815             if (newFileInPatch.exists()) {
7816               continue;
7817             }
7818             
7819             if (revertPatchExcludes.contains(oldFileInPatch.getName())) {
7820               continue;
7821             }
7822             
7823             File newFileInGrouper = new File(applicationPath + oldFilePath);
7824             
7825             newFileInGrouper = fixLibraryFileIfFoundAndDifferent(newFileInGrouper, originalAppToUpgrade);
7826 
7827             if (oldFileInPatch.exists() && oldFileInPatch.isFile()) {
7828               System.out.println("Reverting deleted file: " + newFileInGrouper.getAbsolutePath());
7829               GrouperInstallerUtils.copyFile(oldFileInPatch, newFileInGrouper, false);
7830             }
7831           }
7832         }
7833       }
7834       
7835       //fixLibDir(patchDirToApplicationPath.get("lib"), originalAppToUpgrade);
7836       
7837       this.patchesInstalled.remove(keyBase);
7838       installedPatchDependencies.remove(keyBase);
7839       System.out.println("Patch successfully reverted: " + keyBase);
7840 
7841       editPropertiesFile(patchExistingPropertiesFile, keyBase + ".date", 
7842           GrouperInstallerUtils.dateMinutesSecondsFormat.format(new Date()), false);
7843       editPropertiesFile(patchExistingPropertiesFile, keyBase + ".state", 
7844           GrouperInstallerPatchStatus.reverted.name(), false);
7845 
7846       System.out.println("");
7847     }
7848 
7849     if (!foundPatch) {
7850       System.out.println("There are no new " + thisAppToRevert + " patches to revert\n");
7851       return false;
7852     }
7853     
7854     return true;
7855       
7856   }
7857   
7858   /**
7859    * this makes sure libs are in the right spot, though might be risky so dont do it
7860    * @param libDirWithSlash
7861    * @param originalAppToUpgrade
7862    */
7863   private void fixLibDir(String libDirWithSlash, AppToUpgrade originalAppToUpgrade) {
7864     if (originalAppToUpgrade.isApiOrganized()) {
7865       FilenameFilter apiFilenameFilter = new FilenameFilter() {
7866         
7867         public boolean accept(File dir, String name) {
7868           
7869           // any jars in "lib"
7870           if (GrouperInstallerUtils.equals("lib", dir.getName()) && name.endsWith(".jar")) {
7871             return true;
7872           }
7873           return false;
7874         }
7875       };
7876       //make sure all libs have something between lib and grouper
7877       for (File file : new File(libDirWithSlash).listFiles(apiFilenameFilter)) {
7878         // move this to grouper dir
7879         final File newFile = new File(file.getParentFile().getAbsolutePath() + File.separator + "grouper" + File.separator + file.getName());
7880         GrouperInstallerUtils.fileMove(file, newFile);
7881         System.out.println("Moving jar: " + file.getAbsolutePath() + " to " + newFile.getAbsolutePath());
7882       }
7883     } else {
7884       for (File file : GrouperInstallerUtils.fileListRecursive(new File(libDirWithSlash))) {
7885         // any jars not in "lib" but parent dir of dir is lib
7886         if (file.getName().endsWith(".jar") && !GrouperInstallerUtils.equals("lib", file.getParentFile().getName()) && GrouperInstallerUtils.equals("lib", file.getParentFile().getParentFile().getName())) {
7887           // move this to grouper dir
7888           final File newFile = new File(file.getParentFile().getParentFile().getAbsolutePath() + File.separator + file.getName());
7889           GrouperInstallerUtils.fileMove(file, newFile);
7890           System.out.println("Moving jar: " + file.getAbsolutePath() + " to " + newFile.getAbsolutePath());
7891           
7892         }
7893       }
7894     }
7895   }
7896   
7897   /**
7898    * get the patches available to apply that are not already applied
7899    * @param thisAppToUpgrade app to upgrade to check
7900    * @param originalAppToUpgrade 
7901    * @return if patches were installed
7902    */
7903   private boolean downloadAndInstallPatches(AppToUpgrade thisAppToUpgrade, AppToUpgrade originalAppToUpgrade) {
7904 
7905     if (thisAppToUpgrade == AppToUpgrade.CLIENT) {
7906       throw new RuntimeException("Cant install patches for " + thisAppToUpgrade);
7907     }
7908     
7909     Properties patchesExistingProperties = patchExistingProperties();
7910 
7911     String grouperVersion = GrouperInstallerUtils.propertiesValue("grouper.version", true);
7912 
7913     grouperVersion = GrouperInstallerUtils.replace(grouperVersion, ".", "_");
7914 
7915     Map<Integer, String> patchNumberToNameBase = new LinkedHashMap<Integer, String>();
7916     
7917     boolean foundNewPatch = false;
7918     
7919     File patchExistingPropertiesFile = patchExistingPropertiesFile();
7920     
7921     OUTER: for (int i=0;i<1000;i++) {
7922       
7923       //grouper_v2_2_1_api_patch_0.state
7924       String keyBase = "grouper_v" + grouperVersion + "_" + thisAppToUpgrade.name().toLowerCase() + "_patch_" + i;
7925       System.out.println("\n################ Checking patch " + keyBase);
7926       String key = keyBase + ".state";
7927 
7928       patchNumberToNameBase.put(i, keyBase);
7929       
7930       String value = patchesExistingProperties.getProperty(key);
7931 
7932       if (!GrouperInstallerUtils.isBlank(value)) {
7933         
7934         GrouperInstallerPatchStatus grouperInstallerPatchStatus = GrouperInstallerPatchStatus.valueOfIgnoreCase(value, true, true);
7935         
7936         switch (grouperInstallerPatchStatus) {
7937           case applied:
7938             
7939             System.out.println("Patch: " + keyBase + ": was applied on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7940             this.patchesInstalled.add(keyBase);
7941             
7942             continue;
7943 
7944           case skippedPermanently:
7945             
7946             System.out.println("Patch: " + keyBase + ": was skipped permanently on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7947             continue;
7948 
7949           case skippedTemporarily:
7950 
7951             System.out.println("Patch: " + keyBase + ": was skipped termporarily on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7952 
7953             break;
7954 
7955           case reverted:
7956 
7957             System.out.println("Patch: " + keyBase + ": was reverted on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7958 
7959             break;
7960 
7961           case error:
7962 
7963             System.out.println("Patch: " + keyBase + ": had an error installing on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7964 
7965             break;
7966 
7967           default:
7968             throw new RuntimeException("Not expecting: " + grouperInstallerPatchStatus);
7969         }
7970         
7971       }
7972 
7973       //lets see if it exists on the server
7974       File patchUntarredDir = downloadAndUnzipPatch(keyBase);
7975       
7976       //if no more patches
7977       if (patchUntarredDir == null) {
7978         System.out.println("");
7979         break OUTER;
7980       }
7981       
7982       //lets get the description:
7983       //  # will show up on screen so user knows what it is
7984       //  description = This patch fixes GRP-1080: browse folders refresh button only works in chrome, not other browsers
7985       //
7986       //  # patches that this patch is dependant on (comma separated)
7987       //  dependencies = 
7988       //
7989       //  # low, medium, or high risk to applying the patch
7990       //  risk = low
7991       //
7992       //  # is this is a security patch (true or false)
7993       //  security = false
7994       //
7995       //  # if this patch requires restart of processes (true or false)
7996       //  requiresRestart = false
7997       Properties patchProperties = GrouperInstallerUtils.propertiesFromFile(new File(patchUntarredDir.getAbsoluteFile() + File.separator + keyBase + ".properties"));
7998 
7999       foundNewPatch = true;
8000 
8001       Boolean installPatch = null;
8002       
8003       if (this.installPatchesUpToACertainPatchLevel != null && this.installPatchesUpToACertainPatchLevel) {
8004         if (!GrouperInstallerUtils.isBlank(this.installPatchesUpToThesePatchLevels)) {
8005           
8006           installPatch = shouldInstallPatchUpToLevel(keyBase);
8007           
8008           if (!installPatch) {
8009             break OUTER;
8010           }
8011         }
8012       }
8013       if (this.installCertainSpecifiedPatches != null && this.installCertainSpecifiedPatches) {
8014         if (!GrouperInstallerUtils.isBlank(this.installCertainSpecifiedPatchesList)) {
8015           
8016           installPatch = shouldInstallCertainSpecifiedPatches(keyBase);
8017           
8018         }
8019       }
8020      
8021       // check dependencies
8022       if (installPatch == null || installPatch == true){
8023         String[] dependencies = GrouperInstallerUtils.splitTrim(patchProperties.getProperty("dependencies"), ",");
8024   
8025         boolean invalidDependency = false;
8026         for (String dependency : GrouperInstallerUtils.nonNull(dependencies, String.class)) {
8027           if (!this.patchesInstalled.contains(dependency)) {
8028             System.out.println("Cannot install patch " + keyBase + " since it is dependent on a patch which is not installed: " + dependency);
8029             invalidDependency = true;
8030           }
8031         }
8032         if (invalidDependency) {
8033           System.out.println("Press <enter> to continue. ");
8034           readFromStdIn("grouperInstaller.autorun.continueAfterPatchDependencyFails");
8035           continue OUTER;
8036         }
8037       }
8038       
8039       boolean securityRelated = GrouperInstallerUtils.booleanValue(patchProperties.getProperty("security"), false);
8040       boolean requiresRestart = GrouperInstallerUtils.booleanValue(patchProperties.getProperty("requiresRestart"), true);
8041       
8042       if (this.installAllPatches == null) {
8043         System.out.println("Would you like to install all patches (t|f)? [t]: ");
8044         this.installAllPatches = readFromStdInBoolean(true, "grouperInstaller.autorun.installAllPatches");
8045 
8046         if (!this.installAllPatches && this.installPatchesUpToACertainPatchLevel == null ) {
8047           System.out.println("Would you like to install patches up to a certain patch level? (t|f)? [f]: ");
8048           this.installPatchesUpToACertainPatchLevel = readFromStdInBoolean(false, "grouperInstaller.autorun.installPatchesUpToACertainPatchLevel");
8049           
8050           if (this.installPatchesUpToACertainPatchLevel) {
8051 
8052             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)? : ");
8053             this.installPatchesUpToThesePatchLevels = readFromStdIn("grouperInstaller.autorun.installPatchesUpToThesePatchLevels");
8054 
8055           }
8056           
8057         }
8058         
8059         if (this.installPatchesUpToACertainPatchLevel == null) {
8060           this.installPatchesUpToACertainPatchLevel = false;
8061         }
8062         
8063         if (!this.installAllPatches && !this.installPatchesUpToACertainPatchLevel && this.installCertainSpecifiedPatches == null) {
8064           System.out.println("Would you like to install certain specified patches? (t|f)? [f]: ");
8065           this.installCertainSpecifiedPatches = readFromStdInBoolean(false, "grouperInstaller.autorun.installCertainSpecifiedPatches");
8066 
8067           if (this.installCertainSpecifiedPatches) {
8068 
8069             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)? : ");
8070             this.installCertainSpecifiedPatchesList = readFromStdIn("grouperInstaller.autorun.installCertainSpecifiedPatchesList");
8071           }
8072         }
8073         if (this.installCertainSpecifiedPatches == null) {
8074           this.installCertainSpecifiedPatches = false;
8075         }
8076       }
8077 
8078       //print description
8079       System.out.println("Patch " + keyBase + " is " + patchProperties.getProperty("risk") + " risk, "
8080           + (securityRelated ? "is a security patch" : "is not a security patch"));
8081       System.out.println(patchProperties.getProperty("description"));
8082 
8083       if (this.installAllPatches) {
8084         installPatch = true;
8085       } else if (this.installPatchesUpToACertainPatchLevel) {
8086         if (installPatch == null) {
8087           installPatch = shouldInstallPatchUpToLevel(keyBase);
8088         }
8089       } else if (this.installCertainSpecifiedPatches) {
8090         if (installPatch == null) {
8091           installPatch = shouldInstallCertainSpecifiedPatches(keyBase);
8092         }
8093       } else {
8094         System.out.println("Would you like to install patch " + keyBase + " (t|f)? [t]: ");
8095         installPatch = readFromStdInBoolean(true, "grouperInstaller.autorun.installPatch");
8096       }
8097 
8098       //keep track that we skipped this in the patch properties file
8099       editPropertiesFile(patchExistingPropertiesFile, keyBase + ".date", 
8100           GrouperInstallerUtils.dateMinutesSecondsFormat.format(new Date()), true);
8101 
8102       //if we arent installing the patch
8103       if (!installPatch) {
8104         
8105         boolean temporary = false;
8106         
8107         //if installing up to a patch level, and not specifying about next time, make it temporary
8108         if (this.installPatchesUpToACertainPatchLevel && GrouperInstallerUtils.isBlank(GrouperInstallerUtils.propertiesValue("grouperInstaller.autorun.promptAboutPatchNextTime", false))) {
8109           temporary = true;
8110         } else if (this.installCertainSpecifiedPatches && GrouperInstallerUtils.isBlank(GrouperInstallerUtils.propertiesValue("grouperInstaller.autorun.promptAboutPatchNextTime", false))) {
8111           temporary = true;
8112 
8113         } else {
8114           System.out.println("Would you like to be prompted about this patch next time? (t|f)? [t]: ");
8115 
8116           temporary = readFromStdInBoolean(true, "grouperInstaller.autorun.promptAboutPatchNextTime");
8117         }
8118 
8119         GrouperInstallerPatchStatus grouperInstallerPatchStatus = null;
8120 
8121         if (temporary) {
8122           grouperInstallerPatchStatus = GrouperInstallerPatchStatus.skippedTemporarily;
8123         } else {
8124           grouperInstallerPatchStatus = GrouperInstallerPatchStatus.skippedPermanently;
8125         }
8126 
8127         editPropertiesFile(patchExistingPropertiesFile, keyBase + ".state", 
8128             grouperInstallerPatchStatus.name(), true);
8129         System.out.println("");
8130         continue OUTER;
8131       }
8132 
8133       if (requiresRestart && !this.grouperStopped) {
8134         System.out.println("This patch requires all processes that user Grouper to be stopped.\n  "
8135             + "Please stop these processes if they are running and press <enter> to continue...");
8136         this.grouperStopped = true;
8137         readFromStdIn("grouperInstaller.autorun.continueAfterPatchStopProcesses");
8138       }
8139       
8140       Map<String, String> patchDirToApplicationPath = new LinkedHashMap<String, String>();
8141       patchDirToApplicationPath.put("files", this.upgradeExistingApplicationDirectoryString);
8142       patchDirToApplicationPath.put("classes", this.upgradeExistingClassesDirectoryString);
8143       patchDirToApplicationPath.put("lib", this.upgradeExistingLibDirectoryString);
8144       patchDirToApplicationPath.put("bin", this.upgradeExistingBinDirectoryString);
8145 
8146       boolean patchHasProblem = false;
8147       
8148       //we are installing this patch, lets see if the files are there...
8149       //this.upgradeExistingApplicationDirectoryString
8150       //patchUntarredDir
8151       File oldDir = new File(patchUntarredDir.getAbsolutePath() + File.separator + "old");
8152       File newDir = new File(patchUntarredDir.getAbsolutePath() + File.separator + "new");
8153       {
8154 
8155         for (String patchDir : patchDirToApplicationPath.keySet()) {
8156           
8157           String applicationPath = patchDirToApplicationPath.get(patchDir);
8158 
8159           File oldDirFiles = new File(oldDir.getAbsoluteFile() + File.separator + patchDir);
8160           File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
8161           
8162           if (oldDirFiles.exists() && oldDirFiles.isDirectory()) {
8163           
8164             // relative, e.g. WEB-INF/jsp/someFile.jsp
8165             Set<String> oldFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(oldDirFiles);
8166             
8167             for (String oldFilePath : GrouperInstallerUtils.nonNull(oldFileRelativePaths)) {
8168               File oldFileInPatch = new File(oldDirFiles.getAbsolutePath() + File.separator + oldFilePath);
8169               File newFileInPatch = new File(newDirFiles.getAbsolutePath() + File.separator + oldFilePath);
8170 
8171               oldFilePath = patchFixFilePath(applicationPath, patchDir, oldFilePath, originalAppToUpgrade);
8172 
8173               File oldFileInGrouper = new File(applicationPath + oldFilePath);
8174   
8175               oldFileInGrouper = fixLibraryFileIfFoundAndDifferent(oldFileInGrouper, originalAppToUpgrade);
8176 
8177               if (!oldFileInPatch.exists() || !oldFileInPatch.isFile()) {
8178                 throw new RuntimeException("Why does file not exist or not file??? " + oldFileInPatch.getAbsolutePath());
8179               }
8180               boolean deletedNewPatchFile = !newFileInPatch.exists();
8181               boolean deletedGrouperFile = !oldFileInGrouper.exists();
8182               //if both deleted thats ok
8183               if ((!deletedGrouperFile || !deletedNewPatchFile) &&
8184                  ( !oldFileInGrouper.exists() || !oldFileInGrouper.isFile() 
8185                   || (!GrouperInstallerUtils.contentEquals(oldFileInPatch, oldFileInGrouper)
8186                       //patch is already applied?  thats ok i guess
8187                       && !GrouperInstallerUtils.contentEquals(newFileInPatch, oldFileInGrouper)))) {
8188                 
8189                 System.out.println("Problem applying patch since this patch old file:\n  " + oldFileInPatch.getAbsolutePath() 
8190                     + "\n  is not the same as what the patch expects:\n  " + oldFileInGrouper.getAbsolutePath()
8191                     + "\n  Do you want to force install this patch (t|f)? [f]: ");
8192                 
8193                 boolean forceInstallPatch = readFromStdInBoolean(false, "grouperInstaller.autorun.forceInstallPatch");
8194                 
8195                 if (!forceInstallPatch) {
8196                   System.out.println("Cannot apply patch since this patch file:\n  " + oldFileInPatch.getAbsolutePath() 
8197                       + "\n  is not the same as what the patch expects:\n  " + oldFileInGrouper.getAbsolutePath());
8198                   patchHasProblem = true;
8199                 }
8200               }
8201             }
8202           }
8203         }
8204       }
8205 
8206       //lets make sure that files which are new which dont have an old version do not exist in the application
8207       for (String patchDir : patchDirToApplicationPath.keySet()) {
8208         
8209         String applicationPath = patchDirToApplicationPath.get(patchDir);
8210 
8211         File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
8212         File oldDirFiles = new File(oldDir.getAbsoluteFile() + File.separator + patchDir);
8213         
8214         if (newDirFiles.exists() && newDirFiles.isDirectory()) {
8215         
8216           // relative, e.g. WEB-INF/jsp/someFile.jsp
8217           Set<String> newFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(newDirFiles);
8218 
8219           Set<String> oldFileRelativePaths = (oldDirFiles.exists() && oldDirFiles.isDirectory()) ? 
8220               GrouperInstallerUtils.fileDescendantRelativePaths(oldDirFiles) : new HashSet<String>();
8221 
8222           for (String newFilePath : GrouperInstallerUtils.nonNull(newFileRelativePaths)) {
8223 
8224             File newFileInPatch = new File(newDirFiles.getAbsoluteFile() + File.separator + newFilePath);
8225             
8226             newFilePath = patchFixFilePath(applicationPath, patchDir, newFilePath, originalAppToUpgrade);
8227 
8228             File oldFileInGrouper = new File(applicationPath + newFilePath);
8229 
8230             oldFileInGrouper = fixLibraryFileIfFoundAndDifferent(oldFileInGrouper, originalAppToUpgrade);
8231 
8232             if (!newFileInPatch.isFile()) {
8233               continue;
8234             }
8235             
8236             //if there wasnt a corresponding old file path
8237             if (!oldFileRelativePaths.contains(newFilePath) && !GrouperInstallerUtils.contentEquals(oldFileInGrouper, newFileInPatch)) {
8238 
8239               //then the file shouldnt exist
8240               if (oldFileInGrouper.exists()) {
8241 
8242                 System.out.println("Problem applying patch since this file:\n  " + oldFileInGrouper.getAbsolutePath() 
8243                   + "\n  should not exist yet\n  Do you want to force install this patch (t|f)? [f]: ");
8244             
8245                 boolean forceInstallPatch = readFromStdInBoolean(false, "grouperInstaller.autorun.forceInstallPatch");
8246                 
8247                 if (!forceInstallPatch) {
8248                 
8249                 
8250                   System.out.println("Cannot apply patch since this patch file:\n  " + newFileInPatch.getAbsolutePath() 
8251                       + "\n  is supposed to be new, but it already exists:\n  " + oldFileInGrouper.getAbsolutePath());
8252                   patchHasProblem = true;
8253 
8254                 }
8255               }
8256             }
8257           }
8258         }
8259       }
8260 
8261       if (patchHasProblem) {
8262         editPropertiesFile(patchExistingPropertiesFile, keyBase + ".state", 
8263             GrouperInstallerPatchStatus.error.name(), true);
8264 
8265         continue OUTER;
8266       }
8267 
8268       //so far so good, all the old files are all good, install the patch
8269       for (String patchDir : patchDirToApplicationPath.keySet()) {
8270         
8271         String applicationPath = patchDirToApplicationPath.get(patchDir);
8272 
8273         File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
8274         
8275         if (newDirFiles.exists() && newDirFiles.isDirectory()) {
8276         
8277           // relative, e.g. WEB-INF/jsp/someFile.jsp
8278           Set<String> newFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(newDirFiles);
8279           
8280           for (String newFilePath : GrouperInstallerUtils.nonNull(newFileRelativePaths)) {
8281             // adjust for jars in web apps
8282             //patchDir (e.g. lib), newFilePath (e.g. something.jar or grouper/something.jar), originalAppToUpgrade (e.g. UI)
8283             File newFileInPatch = new File(newDirFiles.getAbsolutePath() + File.separator + newFilePath);
8284             if (!newFileInPatch.isFile()) {
8285               continue;
8286             }
8287             newFilePath = patchFixFilePath(applicationPath, patchDir, newFilePath, originalAppToUpgrade);
8288             File oldFileInGrouper = new File(applicationPath + newFilePath);
8289             oldFileInGrouper = fixLibraryFileIfFoundAndDifferent(oldFileInGrouper, originalAppToUpgrade);
8290 
8291             if (!oldFileInGrouper.exists() && !oldFileInGrouper.getParentFile().exists()) {
8292               GrouperInstallerUtils.mkdirs(oldFileInGrouper.getParentFile());
8293             }
8294             System.out.println("Applying file: " + oldFileInGrouper.getAbsolutePath());
8295             GrouperInstallerUtils.copyFile(newFileInPatch, oldFileInGrouper, false);
8296           }
8297         }
8298         
8299         File oldDirFiles = new File(oldDir.getAbsoluteFile() + File.separator + patchDir);
8300         
8301         if (oldDirFiles.exists() && oldDirFiles.isDirectory()) {
8302         
8303           // relative, e.g. WEB-INF/jsp/someFile.jsp
8304           Set<String> oldFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(oldDirFiles);
8305           
8306           for (String oldFilePath : GrouperInstallerUtils.nonNull(oldFileRelativePaths)) {
8307             File oldFileInPatch = new File(oldDirFiles.getAbsolutePath() + File.separator + oldFilePath);
8308             File newFileInPatch = new File(newDirFiles.getAbsolutePath() + File.separator + oldFilePath);
8309             File oldFileInGrouper = new File(applicationPath + oldFilePath);
8310             oldFileInGrouper = fixLibraryFileIfFoundAndDifferent(oldFileInGrouper, originalAppToUpgrade);
8311 
8312             if (oldFileInPatch.exists() && !newFileInPatch.exists() && oldFileInGrouper.exists() && oldFileInGrouper.isFile()) {
8313 
8314               System.out.println("Deleting file: " + oldFileInGrouper.getAbsolutePath());
8315               GrouperInstallerUtils.fileDelete(oldFileInGrouper);
8316               
8317             }
8318           }
8319         }
8320       }
8321       
8322       //fixLibDir(patchDirToApplicationPath.get("lib"), originalAppToUpgrade);
8323 
8324       this.patchesInstalled.add(keyBase);
8325       System.out.println("Patch successfully applied: " + keyBase);
8326       
8327       editPropertiesFile(patchExistingPropertiesFile, keyBase + ".state", 
8328           GrouperInstallerPatchStatus.applied.name(), true);
8329       System.out.println("");
8330     }
8331 
8332     if (!foundNewPatch) {
8333       System.out.println("There are no new " + thisAppToUpgrade + " patches to install\n");
8334       return false;
8335     } 
8336     return true;
8337   }
8338 
8339   /**
8340    * <pre>
8341    * patch file extra grouper prefix pattern
8342    * ^grouper[/\\][^/\\]+[/\\]([^/\\]+)$
8343    * starts with grouper, then a slash, then capture a dir and jar filename
8344    * </pre>
8345    */
8346   private static Pattern patchFileExtraGrouperPrefixPattern = Pattern.compile("^grouper[/\\\\]([^/\\\\]+[/\\\\][^/\\\\]+)$");
8347   
8348   /**
8349    * applicationPath (e.g. lib), newFilePath (e.g. something.jar or grouper/something.jar), originalAppToUpgrade (e.g. UI)
8350    * @param applicationPath
8351    * @param patchDir
8352    * @param newFilePath
8353    * @param originalAppToUpgrade
8354    * @return String of new newFilePath
8355    */
8356   public String patchFixFilePath(String applicationPath, String patchDir, String newFilePath, AppToUpgrade originalAppToUpgrade) {
8357 
8358     if ("lib".equals(patchDir)) {
8359       // if this is the api then there should be something between lib and the jar
8360       String jarName = newFilePath;
8361       {
8362 //        Matcher matcher = patchFileExtraGrouperPrefixPattern.matcher(newFilePath);
8363 //        if (matcher.matches()) {
8364 //          jarName = matcher.group(1);
8365 //        }
8366         jarName = GrouperInstallerUtils.suffixAfterChar(newFilePath.replace("\\", "/"), '/');
8367 
8368       }
8369       
8370 
8371       if (originalAppToUpgrade.isApiOrganized()) {
8372         
8373         String noSlashApplicationPath = GrouperInstallerUtils.stripLastSlashIfExists(applicationPath);
8374         // if the application path has "grouper" already, then just put jarname on it
8375         if (!noSlashApplicationPath.endsWith("lib")) {
8376           newFilePath = jarName;
8377         } else {
8378           //if application is just lib, then make sure there is something in front of the jarname
8379           if (GrouperInstallerUtils.equals(newFilePath, jarName)) {
8380             newFilePath = "grouper/" + jarName;
8381           }
8382         }
8383          
8384       } else {
8385         // if this is a UI or WS, then we only want the jar
8386         newFilePath = jarName;
8387       }
8388     }
8389     return newFilePath;
8390   }
8391 
8392   
8393   /**
8394    * @param keyBase
8395    * @return if should revert patch
8396    */
8397   private boolean shouldRevertCertainSpecifiedPatches(String keyBase) {
8398     List<String> revertUpToThesePatchLevelsList = GrouperInstallerUtils.splitTrimToList(this.revertCertainSpecifiedPatchesList, ",");
8399     return revertUpToThesePatchLevelsList.contains(keyBase);
8400   }
8401 
8402   /**
8403    * @param keyBase
8404    * @return if should install patch
8405    */
8406   private boolean shouldInstallCertainSpecifiedPatches(String keyBase) {
8407     
8408     List<String> installUpToThesePatchLevelsList = GrouperInstallerUtils.splitTrimToList(this.installCertainSpecifiedPatchesList, ",");
8409     return installUpToThesePatchLevelsList.contains(keyBase);
8410   }
8411   
8412   /**
8413    * @param keyBase
8414    * @return if should install patch
8415    */
8416   private boolean shouldInstallPatchUpToLevel(String keyBase) {
8417     boolean installPatch = false;
8418 
8419     //e.g. ^grouper_v(\\d+)_(\\d+)_(\\d+)_(api|ws|ui|psp|pspng)_patch_(\\d+)$
8420     Matcher patchNameMatcher = patchNamePattern.matcher(keyBase);
8421     if (!patchNameMatcher.matches()) {
8422       throw new RuntimeException("Invalid patch name: " + keyBase);
8423     }
8424     
8425     String grouperVersionInstallPatch = patchNameMatcher.group(1) + "." + patchNameMatcher.group(2) + "." + patchNameMatcher.group(3);
8426     String systemInstallPatch = patchNameMatcher.group(4);
8427     int numberInstallPatch = GrouperInstallerUtils.intValue(patchNameMatcher.group(5));
8428 
8429     
8430     String[] installUpToThesePatchLevels = GrouperInstallerUtils.splitTrim(this.installPatchesUpToThesePatchLevels, ",");
8431     for (String patchName : installUpToThesePatchLevels) {
8432 
8433       //e.g. ^grouper_v(\\d+)_(\\d+)_(\\d+)_(api|ws|ui|psp|pspng)_patch_(\\d+)$
8434       patchNameMatcher = patchNamePattern.matcher(patchName);
8435       if (!patchNameMatcher.matches()) {
8436         throw new RuntimeException("Invalid patch name: " + patchName);
8437       }
8438       
8439       String grouperVersionUpToPatch = patchNameMatcher.group(1) + "." + patchNameMatcher.group(2) + "." + patchNameMatcher.group(3);
8440       String systemUpToPatch = patchNameMatcher.group(4);
8441       int numberUpToPatch = GrouperInstallerUtils.intValue(patchNameMatcher.group(5));
8442 
8443       if (GrouperInstallerUtils.equals(systemInstallPatch, systemUpToPatch)
8444           && GrouperInstallerUtils.equals(grouperVersionInstallPatch, grouperVersionUpToPatch)
8445           && numberInstallPatch <= numberUpToPatch) {
8446         installPatch = true;
8447         break;
8448       }
8449       
8450     }
8451     return installPatch;
8452   }
8453   
8454   /**
8455    * fix the index file
8456    * @param thisAppToUpgrade app to upgrade to check
8457    */
8458   private void fixIndexFile(AppToUpgrade thisAppToUpgrade) {
8459 
8460     if (thisAppToUpgrade == AppToUpgrade.CLIENT) {
8461       throw new RuntimeException("Cant fix index file for " + thisAppToUpgrade);
8462     }
8463     
8464     Properties patchesExistingProperties = patchExistingProperties();
8465 
8466     String grouperVersion = this.grouperVersionOfJar().toString();
8467 
8468     grouperVersion = GrouperInstallerUtils.replace(grouperVersion, ".", "_");
8469 
8470     //lets download all patches
8471     int nextPatchIndex = downloadPatches(thisAppToUpgrade, grouperVersion);
8472     
8473     File patchExistingPropertiesFile = patchExistingPropertiesFile();
8474 
8475     Map<String, String> patchDirToApplicationPath = new LinkedHashMap<String, String>();
8476     patchDirToApplicationPath.put("files", this.upgradeExistingApplicationDirectoryString);
8477     patchDirToApplicationPath.put("classes", this.upgradeExistingClassesDirectoryString);
8478     patchDirToApplicationPath.put("lib", this.upgradeExistingLibDirectoryString);
8479     patchDirToApplicationPath.put("bin", this.upgradeExistingBinDirectoryString);
8480 
8481     //map of full patch file name to the patch number that is installed
8482     Map<String, Integer> fileInMoreRecentPatchMap = new HashMap<String, Integer>();
8483 
8484     boolean patchesOverallOk = true;
8485     
8486     //process patches from greatest to least
8487     for (int i=nextPatchIndex-1;i>=0;i--) {
8488       
8489       //grouper_v2_2_1_api_patch_0.state
8490       String patchName = "grouper_v" + grouperVersion + "_" + thisAppToUpgrade.name().toLowerCase() + "_patch_" + i;
8491 
8492       String key = patchName + ".state";
8493 
8494       //see what is already there
8495       String existingState = patchesExistingProperties.getProperty(key);
8496       
8497       GrouperInstallerPatchStatus grouperInstallerPatchStatus = GrouperInstallerPatchStatus.valueOfIgnoreCase(existingState, false, true);
8498 
8499       File patchUntarredDir = new File(this.grouperTarballDirectoryString + "patches" + File.separator + patchName);
8500 
8501       //keep track that we skipped this in the patch properties file
8502       //editPropertiesFile(patchExistingPropertiesFile, keyBase + ".date", 
8503       //    GrouperInstallerUtils.dateMinutesSecondsFormat.format(new Date()));
8504       //editPropertiesFile(patchExistingPropertiesFile, keyBase + ".state", 
8505       //    grouperInstallerPatchStatus.name());
8506 
8507       boolean patchHasProblem = false;
8508       boolean patchHasAtLeastOneFile = false;
8509       boolean patchHasAtLeastOneFileInAnotherPatch = false;
8510       Set<String> patchErrors = new LinkedHashSet<String>();
8511       
8512       //keep track of patch paths (full path in patch)
8513       Set<String> patchPaths = new HashSet<String>();
8514       
8515       //we are installing this patch, lets see if the files are there...
8516       //this.upgradeExistingApplicationDirectoryString
8517       //patchUntarredDir
8518       File newDir = new File(patchUntarredDir.getAbsolutePath() + File.separator + "new");
8519       //loop through lib, classes, files
8520       for (String patchDir : patchDirToApplicationPath.keySet()) {
8521 
8522         String applicationPath = patchDirToApplicationPath.get(patchDir);
8523 
8524         File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
8525 
8526         // relative, e.g. WEB-INF/jsp/someFile.jsp
8527         Set<String> newFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(newDirFiles);
8528         // go through all files of the patches in the new dir
8529         for (String newFilePath : GrouperInstallerUtils.nonNull(newFileRelativePaths)) {
8530 
8531           String patchPath = patchDir + File.separator + newFilePath;
8532           
8533           Integer existsInPatchVersion = fileInMoreRecentPatchMap.get(patchPath);
8534           
8535           //if this file was in a newer patch, then thats ok
8536           if (existsInPatchVersion != null) {
8537             //this file is ok, its in a more recent patch
8538             patchHasAtLeastOneFileInAnotherPatch = true;
8539             continue;
8540           }
8541 
8542           File newFileInGrouper = new File(applicationPath + newFilePath);
8543 
8544           File newFileInPatch = new File(newDirFiles.getAbsolutePath() + File.separator + newFilePath);
8545           
8546           //see if the contents of the patch match those in grouper
8547           if (!GrouperInstallerUtils.contentEquals(newFileInPatch, newFileInGrouper)) {
8548 
8549             patchErrors.add("Problem in patch:\n  " + newFileInPatch.getAbsolutePath() 
8550                 + "\n  is not the same as what the patch expects:\n  " + newFileInGrouper.getAbsolutePath());
8551             patchHasProblem = true;
8552           } else {
8553 
8554             patchPaths.add(patchPath);
8555 
8556             patchHasAtLeastOneFile = true;
8557           }
8558         }
8559       }
8560         
8561       //is any file installed?  or if there are only files in other patches... hmm
8562       if (patchHasAtLeastOneFile || (patchHasAtLeastOneFileInAnotherPatch && !patchHasProblem )) {
8563         
8564         //add files in this patch to the list
8565         for (String patchPath : patchPaths) {
8566           fileInMoreRecentPatchMap.put(patchPath, i);
8567         }
8568         
8569         //one or more of the files in the patch had a problem
8570         if (patchHasProblem) {
8571           for (String patchError: patchErrors) {
8572             System.out.println(patchError);
8573           }
8574           if (grouperInstallerPatchStatus == null || (grouperInstallerPatchStatus != GrouperInstallerPatchStatus.applied
8575               && grouperInstallerPatchStatus != GrouperInstallerPatchStatus.error)) {
8576             patchesOverallOk = false;
8577             editPropertiesFile(patchExistingPropertiesFile, patchName + ".date", 
8578                 GrouperInstallerUtils.dateMinutesSecondsFormat.format(new Date()), true);
8579             editPropertiesFile(patchExistingPropertiesFile, patchName + ".state", 
8580                 GrouperInstallerPatchStatus.applied.name(), true);
8581             System.out.println("Patch " + patchName + " was listed as " + grouperInstallerPatchStatus + " but was changed to applied (even though there are files missing)");
8582             
8583           }
8584           continue;          
8585         }
8586         
8587         if (grouperInstallerPatchStatus == null || grouperInstallerPatchStatus != GrouperInstallerPatchStatus.applied) {
8588           patchesOverallOk = false;
8589           editPropertiesFile(patchExistingPropertiesFile, patchName + ".date", 
8590               GrouperInstallerUtils.dateMinutesSecondsFormat.format(new Date()), true);
8591           editPropertiesFile(patchExistingPropertiesFile, patchName + ".state", 
8592               GrouperInstallerPatchStatus.applied.name(), true);
8593           System.out.println("Patch " + patchName + " was listed as " + grouperInstallerPatchStatus + " but was changed to applied");
8594           
8595         }
8596         
8597       } else {
8598         if (grouperInstallerPatchStatus == null || grouperInstallerPatchStatus != GrouperInstallerPatchStatus.applied) {
8599           continue;
8600         }
8601         
8602         patchesOverallOk = false;
8603         editPropertiesFile(patchExistingPropertiesFile, patchName + ".date", 
8604             GrouperInstallerUtils.dateMinutesSecondsFormat.format(new Date()), true);
8605         editPropertiesFile(patchExistingPropertiesFile, patchName + ".state", 
8606             GrouperInstallerPatchStatus.skippedTemporarily.name(), true);
8607         System.out.println("Patch " + patchName + " was listed as applied but was changed to skippedTemporarily");
8608         continue;
8609       }
8610 
8611     }
8612 
8613     //tell the properties file that we have fixed the index file now
8614     editPropertiesFile(patchExistingPropertiesFile, "grouperInstallerLastFixedIndexFile.date", 
8615         GrouperInstallerUtils.dateMinutesSecondsFormat.format(new Date()), true);
8616   
8617     if (patchesOverallOk) {
8618       System.out.println("Patches for " + thisAppToUpgrade + " for version " + grouperVersion + " were in the index file correctly");
8619     }
8620   }
8621   
8622   /**
8623    * get all patches
8624    * @param thisAppToUpgrade app to upgrade to check
8625    * @param grouperVersion
8626    * @return next patch index
8627    */
8628   private int downloadPatches(AppToUpgrade thisAppToUpgrade, String grouperVersion) {
8629 
8630     if (thisAppToUpgrade == AppToUpgrade.CLIENT) {
8631       throw new RuntimeException("Cant install patches for " + thisAppToUpgrade);
8632     }
8633     
8634     Map<Integer, String> patchNumberToNameBase = new LinkedHashMap<Integer, String>();
8635     
8636     int nextPatchIndex = 0;
8637 
8638     OUTER: for (int i=0;i<1000;i++) {
8639 
8640       //grouper_v2_2_1_api_patch_0.state
8641       String keyBase = "grouper_v" + grouperVersion + "_" + thisAppToUpgrade.name().toLowerCase() + "_patch_" + i;
8642 
8643       patchNumberToNameBase.put(i, keyBase);
8644 
8645       //lets see if it exists on the server
8646       File patchUntarredDir = downloadAndUnzipPatch(keyBase);
8647 
8648       //if no more patches
8649       if (patchUntarredDir == null) {
8650         System.out.println("");
8651         break OUTER;
8652       }
8653 
8654       nextPatchIndex = i+1;
8655     }
8656 
8657     return nextPatchIndex;
8658 
8659   }
8660   
8661   /**
8662    * 
8663    * @param patchName e.g. grouper_v2_2_1_api_patch_0.tar.gz
8664    * @return the directory of the unzipped patch
8665    */
8666   public File downloadAndUnzipPatch(String patchName) {
8667     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
8668     
8669     if (!urlToDownload.endsWith("/")) {
8670       urlToDownload += "/";
8671     }
8672     urlToDownload += "release/";
8673     
8674     //e.g. 2.2.2
8675     Matcher patchNameMatcher = patchNamePattern.matcher(patchName);
8676     if (!patchNameMatcher.matches()) {
8677       throw new RuntimeException("Invalid patch name: " + patchName);
8678     }
8679     
8680     //String grouperVersion = GrouperInstallerUtils.propertiesValue("grouper.version", true);
8681     String grouperVersion = patchNameMatcher.group(1) + "." + patchNameMatcher.group(2) + "." + patchNameMatcher.group(3);
8682     
8683     urlToDownload +=  grouperVersion + "/patches/" + patchName + ".tar.gz";
8684 
8685     File patchFile = new File(this.grouperTarballDirectoryString + "patches" + File.separator + patchName + ".tar.gz");
8686     
8687     if (GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.downloadPatches", true, false)) {
8688 
8689       boolean foundFile = downloadFile(urlToDownload, patchFile.getAbsolutePath(), true, "Patch doesnt exist yet (not an error): ", 
8690           "grouperInstaller.autorun.useLocalPatchIfExists");
8691       
8692       if (!foundFile) {
8693 
8694         //if we are doing test patches
8695         if (GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.useTestPatches", false, false)) {
8696           String testUrlToDownload = GrouperInstallerUtils.replace(urlToDownload, ".tar.gz", "_test.tar.gz");
8697           
8698           //its a test url, but download to the same file name
8699           foundFile = downloadFile(testUrlToDownload, patchFile.getAbsolutePath(), true, "Patch doesnt exist yet (not an error): ", 
8700               "grouperInstaller.autorun.useLocalPatchIfExists");
8701         }
8702 
8703         if (!foundFile) {
8704           return null;
8705         }
8706       }
8707     } else {
8708       if (!patchFile.exists()) {
8709         return null;
8710       }
8711     }
8712     
8713     //####################################
8714     //unzip/untar the patch file
8715     
8716     File unzippedFile = unzip(patchFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalPatchIfExists");
8717     File untarredDir = untar(unzippedFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalPatchIfExists", null);
8718     return untarredDir;
8719   }
8720 
8721   /**
8722    * 
8723    * @param branchName
8724    * @return the directory of the unzipped source repo
8725    */
8726   public File downloadAndUnzipGrouperSource(String branchName) {
8727     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.source.url", false);
8728     
8729     if (GrouperInstallerUtils.isBlank(urlToDownload)) {
8730       urlToDownload = "https://github.com/Internet2/grouper/archive/$BRANCH_NAME$.zip";
8731     }
8732     
8733     urlToDownload = GrouperInstallerUtils.replace(urlToDownload, "$BRANCH_NAME$", branchName);
8734 
8735     String fileToDownload = GrouperInstallerUtils.substringAfterLast(urlToDownload, "/");
8736     
8737     File sourceFile = new File(this.grouperTarballDirectoryString + fileToDownload);
8738     
8739     if (GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.downloadSource", true, false)) {
8740 
8741       downloadFile(urlToDownload, sourceFile.getAbsolutePath(), "grouperInstaller.autorun.createPatchDownloadSourceUseLocalIfExist");
8742       
8743     } else {
8744       if (!sourceFile.exists()) {
8745         throw new RuntimeException("Cant find grouper source");
8746       }
8747     }
8748     
8749     //####################################
8750     //unzip/untar the source file
8751     File unzippedDir = unzipFromZip(sourceFile.getAbsolutePath(), "grouperInstaller.autorun.createPatchDownloadSourceUseLocalIfExist");
8752     return unzippedDir;
8753   }
8754 
8755   /**
8756    * 
8757    * @param branchName
8758    * @return the directory of the unzipped source repo
8759    */
8760   public File downloadAndUnzipPspSource(String branchName) {
8761     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.pspSource.url", false);
8762     
8763     if (GrouperInstallerUtils.isBlank(urlToDownload)) {
8764       urlToDownload = "https://github.com/Internet2/grouper-psp/archive/$BRANCH_NAME$.zip";
8765     }
8766     
8767     urlToDownload = GrouperInstallerUtils.replace(urlToDownload, "$BRANCH_NAME$", branchName);
8768 
8769     String fileToDownload = GrouperInstallerUtils.substringAfterLast(urlToDownload, "/");
8770     
8771     File sourceFile = new File(this.grouperTarballDirectoryString + fileToDownload);
8772     
8773     if (GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.downloadSource", true, false)) {
8774 
8775       downloadFile(urlToDownload, sourceFile.getAbsolutePath(), "grouperInstaller.autorun.createPatchDownloadSourceUseLocalIfExist");
8776       
8777     } else {
8778       if (!sourceFile.exists()) {
8779         throw new RuntimeException("Cant find grouper psp source");
8780       }
8781     }
8782     
8783     //####################################
8784     //unzip/untar the source file
8785     File unzippedDir = unzipFromZip(sourceFile.getAbsolutePath(), "grouperInstaller.autorun.createPatchDownloadSourceUseLocalIfExist");
8786     return unzippedDir;
8787   }
8788 
8789   /**
8790    * 
8791    */
8792   public static enum GrouperInstallerPatchStatus {
8793 
8794     /**
8795      * patch was applied
8796      */
8797     applied, 
8798     
8799     /**
8800      * patch was removed
8801      */
8802     reverted, 
8803     
8804     /**
8805      * patch was skipped temporarily, prompt again
8806      */
8807     skippedTemporarily, 
8808 
8809     /**
8810      * patch had an error applying
8811      */
8812     error, 
8813 
8814     /**
8815      * patch was skipped permanently, dont prompt again
8816      */
8817     skippedPermanently;
8818 
8819     /**
8820      * 
8821      * @param string
8822      * @param exceptionIfNotFound
8823      * @param exceptionIfInvalid
8824      * @return the patch status
8825      */
8826     public static GrouperInstallerPatchStatus valueOfIgnoreCase(String string, boolean exceptionIfNotFound, boolean exceptionIfInvalid) {
8827       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerPatchStatus.class, string, exceptionIfNotFound, exceptionIfInvalid);
8828     }
8829     
8830   }
8831 
8832   /**
8833    * patch status api
8834    */
8835   private void patchStatusApi() {
8836     this.patchStatus(AppToUpgrade.API);
8837   }
8838 
8839 
8840   /**
8841    * patch the api
8842    */
8843   private void patchApi() {
8844     this.downloadAndInstallPatches(AppToUpgrade.API, AppToUpgrade.API);
8845   }
8846 
8847   /**
8848    * fix index file api
8849    */
8850   private void fixIndexFileApi() {
8851     this.fixIndexFile(AppToUpgrade.API);
8852   }
8853 
8854   /**
8855    * fix index file ui
8856    */
8857   private void fixIndexFileUi() {
8858     this.fixIndexFile(AppToUpgrade.UI);
8859     this.fixIndexFile(AppToUpgrade.API);
8860   }
8861 
8862   /**
8863    * fix index file ws
8864    */
8865   private void fixIndexFileWs() {
8866     this.fixIndexFile(AppToUpgrade.WS);
8867     this.fixIndexFile(AppToUpgrade.API);
8868   }
8869 
8870   /**
8871    * fix index file psp
8872    */
8873   private void fixIndexFilePsp() {
8874     this.fixIndexFile(AppToUpgrade.PSP);
8875     this.fixIndexFile(AppToUpgrade.API);
8876   }
8877 
8878   /**
8879    * fix index file psp
8880    */
8881   private void fixIndexFilePspng() {
8882     this.fixIndexFile(AppToUpgrade.PSPNG);
8883     this.fixIndexFile(AppToUpgrade.API);
8884   }
8885 
8886   /**
8887    * patch status ui
8888    */
8889   private void patchStatusUi() {
8890     this.patchStatus(AppToUpgrade.API);
8891     this.patchStatus(AppToUpgrade.UI);
8892   }
8893 
8894   /**
8895    * patch status ws
8896    */
8897   private void patchStatusWs() {
8898     this.patchStatus(AppToUpgrade.API);
8899     this.patchStatus(AppToUpgrade.WS);
8900   }
8901 
8902   /**
8903    * patch status psp
8904    */
8905   private void patchStatusPsp() {
8906     this.patchStatus(AppToUpgrade.API);
8907     this.patchStatus(AppToUpgrade.PSP);
8908   }
8909 
8910   /**
8911    * patch status pspng
8912    */
8913   private void patchStatusPspng() {
8914     this.patchStatus(AppToUpgrade.API);
8915     this.patchStatus(AppToUpgrade.PSPNG);
8916   }
8917 
8918 
8919   /**
8920    * patch the client
8921    */
8922   private void patchUi() {
8923     this.downloadAndInstallPatches(AppToUpgrade.API, AppToUpgrade.UI);
8924     boolean patchesApplied = this.downloadAndInstallPatches(AppToUpgrade.UI, AppToUpgrade.UI);
8925     if (patchesApplied && (this.grouperInstallerMainFunction == GrouperInstallerMainFunction.patch 
8926         || this.grouperInstallerMainFunction == GrouperInstallerMainFunction.upgrade)) {
8927       System.out.print("Since patches were applied, you should delete files in your app server work directory,"
8928           + "\n  in tomcat it is named 'work'.  Hit <enter> to continue: ");
8929       readFromStdIn("grouperInstaller.autorun.continueAfterDeleteUiWorkDirectory");
8930     }
8931   }
8932   
8933   /**
8934    * patch the client
8935    */
8936   private void patchWs() {
8937     this.downloadAndInstallPatches(AppToUpgrade.API, AppToUpgrade.WS);
8938     boolean patchesApplied = this.downloadAndInstallPatches(AppToUpgrade.WS, AppToUpgrade.WS);
8939     if (patchesApplied && (this.grouperInstallerMainFunction == GrouperInstallerMainFunction.patch 
8940         || this.grouperInstallerMainFunction == GrouperInstallerMainFunction.upgrade)) {
8941       System.out.print("Since patches were applied, you should delete files in your app server work directory,"
8942           + "\n  in tomcat it is named 'work'.  Hit <enter> to continue: ");
8943       readFromStdIn("grouperInstaller.autorun.continueAfterDeleteWsWorkDirectory");
8944     }
8945   }
8946   
8947   /**
8948    * patch the psp
8949    */
8950   private void patchPsp() {
8951     this.downloadAndInstallPatches(AppToUpgrade.API, AppToUpgrade.PSP);
8952     this.downloadAndInstallPatches(AppToUpgrade.PSP, AppToUpgrade.PSP);
8953   }
8954 
8955   /**
8956    * patch the pspng
8957    */
8958   private void patchPspng() {
8959     this.downloadAndInstallPatches(AppToUpgrade.API, AppToUpgrade.PSPNG);
8960     this.downloadAndInstallPatches(AppToUpgrade.PSPNG, AppToUpgrade.PSPNG);
8961   }
8962 
8963   /**
8964    * revert patch the client
8965    */
8966   private void patchRevertApi() {
8967     this.revertPatches(AppToUpgrade.API, AppToUpgrade.API);
8968   }
8969 
8970   /**
8971    * revert patch the client
8972    */
8973   private void patchRevertUi() {
8974     this.revertPatches(AppToUpgrade.UI, AppToUpgrade.UI);
8975     boolean patchesReverted = this.revertPatches(AppToUpgrade.API, AppToUpgrade.UI);
8976     if (patchesReverted && (this.grouperInstallerMainFunction == GrouperInstallerMainFunction.patch 
8977         || this.grouperInstallerMainFunction == GrouperInstallerMainFunction.upgrade)) {
8978       System.out.print("Since patches were reverted, you should delete files in your app server work directory,"
8979           + "\n  in tomcat it is named 'work'.  Hit <enter> to continue: ");
8980       readFromStdIn("grouperInstaller.autorun.continueAfterDeleteUiWorkDirectory");
8981     }
8982   }
8983   
8984   /**
8985    * revert patch the client
8986    */
8987   private void patchRevertWs() {
8988     this.revertPatches(AppToUpgrade.WS,AppToUpgrade.WS);
8989     boolean patchesReverted = this.revertPatches(AppToUpgrade.API, AppToUpgrade.WS);
8990     if (patchesReverted && (this.grouperInstallerMainFunction == GrouperInstallerMainFunction.patch 
8991         || this.grouperInstallerMainFunction == GrouperInstallerMainFunction.upgrade)) {
8992       System.out.print("Since patches were reverted, you should delete files in your app server work directory,"
8993           + "\n  in tomcat it is named 'work'.  Hit <enter> to continue: ");
8994       readFromStdIn("grouperInstaller.autorun.continueAfterDeleteWsWorkDirectory");
8995     }
8996   }
8997   
8998   /**
8999    * revert patch the psp
9000    */
9001   private void patchRevertPsp() {
9002     this.revertPatches(AppToUpgrade.PSP, AppToUpgrade.PSP);
9003     this.revertPatches(AppToUpgrade.API, AppToUpgrade.PSP);
9004   }
9005 
9006   /**
9007    * revert patch the pspng
9008    */
9009   private void patchRevertPspng() {
9010     this.revertPatches(AppToUpgrade.PSPNG, AppToUpgrade.PSPNG);
9011     this.revertPatches(AppToUpgrade.API, AppToUpgrade.PSPNG);
9012   }
9013 
9014   /**
9015    * owasp csrf guard file
9016    */
9017   private File owaspCsrfGuardFile;
9018   
9019   /**
9020    * owasp csrf guard base file
9021    */
9022   private File owaspCsrfGuardBaseFile;
9023   
9024   /**
9025    * on an upgrade, compare a new jar and an existing jar and see if needs to be updated, and if so, update it
9026    * @param existingJarFile
9027    * @param newJarFile
9028    * @param printResultIfNotUpgrade
9029    * @param toDir if file not there, copy here.  If null, then go to upgrade existing lib directory string
9030    * @return true if upgraded, false if not
9031    */
9032   private boolean compareAndReplaceJar(File existingJarFile, File newJarFile, boolean printResultIfNotUpgrade, File toDir) {
9033     
9034     if (toDir == null) {
9035       toDir = new File(this.upgradeExistingLibDirectoryString);
9036     }
9037     
9038     if (existingJarFile == null || !existingJarFile.exists()) {
9039       System.out.println(newJarFile.getName() + " is a new file and is being copied to the application lib dir");
9040       existingJarFile = new File(toDir.getAbsoluteFile() + File.separator + newJarFile.getName());
9041       GrouperInstallerUtils.copyFile(newJarFile, existingJarFile, true);
9042       return true;
9043     }
9044 
9045     String existingJarFilePath = existingJarFile.getAbsolutePath();
9046     if (!GrouperInstallerUtils.filePathStartsWith(existingJarFilePath,this.upgradeExistingApplicationDirectoryString)) {
9047       throw new RuntimeException("Why does existing path not start with upgrade path??? " + existingJarFilePath + ", " + this.upgradeExistingApplicationDirectoryString);
9048     }
9049     
9050     String bakJarFileString = this.grouperBaseBakDir + existingJarFilePath.substring(this.upgradeExistingApplicationDirectoryString.length());
9051     File bakJarFile = new File(bakJarFileString);
9052     
9053     String existingVersion = GrouperInstallerUtils.jarVersion(existingJarFile);
9054     String newVersion = GrouperInstallerUtils.jarVersion(newJarFile);
9055     
9056     long existingSize = existingJarFile.length();
9057     long newSize = newJarFile.length();
9058     
9059     if (!GrouperInstallerUtils.equals(existingVersion, newVersion) || existingSize != newSize) {
9060 
9061       //make sure parents exist
9062       GrouperInstallerUtils.createParentDirectories(bakJarFile);
9063       
9064       System.out.println(existingJarFile.getName() + " had version " + existingVersion + " and size " + existingSize + " bytes and is being upgraded to version "
9065           + newVersion + " and size " + newSize + " bytes.\n  It is backed up to " + bakJarFile);
9066 
9067       GrouperInstallerUtils.fileMove(existingJarFile, bakJarFile);
9068       
9069       GrouperInstallerUtils.copyFile(newJarFile, existingJarFile, true);
9070       
9071       return true;
9072     }
9073     
9074     if (printResultIfNotUpgrade) {
9075       System.out.println(existingJarFile.getName() + " is up to date");
9076     }
9077     return false;
9078   }
9079 
9080   /**
9081    * on an upgrade, compare a new file and an existing file and see if needs to be updated, and if so, update it
9082    * @param existingFile
9083    * @param newFile
9084    * @param printResultIfNotUpgrade
9085    * @param toDir if file not there, copy here.  If null, then go to upgrade existing lib directory string
9086    * @return true if upgraded, false if not
9087    */
9088   private boolean compareAndCopyFile(File existingFile, File newFile, boolean printResultIfNotUpgrade, File toDir) {
9089     
9090     if (toDir == null) {
9091       throw new RuntimeException("Which dir to copy to??? " + newFile + ", " + existingFile);
9092     }
9093     
9094     if (existingFile == null || !existingFile.exists()) {
9095       System.out.println(newFile.getName() + " is a new file and is being copied to the application dir: " + toDir.getAbsolutePath());
9096       existingFile = new File(toDir.getAbsoluteFile() + File.separator + newFile.getName());
9097       GrouperInstallerUtils.copyFile(newFile, existingFile, true);
9098       return true;
9099     }
9100 
9101     String existingFilePath = existingFile.getAbsolutePath();
9102     if (!GrouperInstallerUtils.filePathStartsWith(existingFilePath,this.upgradeExistingApplicationDirectoryString)) {
9103       throw new RuntimeException("Why does existing path not start with upgrade path??? " + existingFilePath + ", " + this.upgradeExistingApplicationDirectoryString);
9104     }
9105     
9106     String bakFileString = this.grouperBaseBakDir + existingFilePath.substring(this.upgradeExistingApplicationDirectoryString.length());
9107     File bakFile = new File(bakFileString);
9108     
9109     String existingChecksum = GrouperInstallerUtils.fileSha1(existingFile);
9110     String newChecksum = GrouperInstallerUtils.fileSha1(newFile);
9111     
9112     long existingSize = existingFile.length();
9113     long newSize = newFile.length();
9114     
9115     if (!GrouperInstallerUtils.equals(existingChecksum, newChecksum) || existingSize != newSize) {
9116 
9117       //make sure parents exist
9118       GrouperInstallerUtils.createParentDirectories(bakFile);
9119       
9120       System.out.println(existingFile.getName() + " had checksum " + existingChecksum + " and size " + existingSize + " bytes and is being upgraded to checksum "
9121           + newChecksum + " and size " + newSize + " bytes.\n  It is backed up to " + bakFile);
9122 
9123       GrouperInstallerUtils.fileMove(existingFile, bakFile);
9124       
9125       GrouperInstallerUtils.copyFile(newFile, existingFile, true);
9126       
9127       return true;
9128     }
9129     
9130     if (printResultIfNotUpgrade) {
9131       System.out.println(existingFile.getName() + " is up to date");
9132     }
9133     return false;
9134   }
9135 
9136   /**
9137    * grouper.client.properties
9138    */
9139   private File grouperClientPropertiesFile;
9140   
9141   /**
9142    * grouper.client.base.properties
9143    */
9144   private File grouperClientBasePropertiesFile;
9145   
9146   /**
9147    * grouper.client.example.properties
9148    */
9149   private File grouperClientExamplePropertiesFile;
9150 
9151   /**
9152    * grouperClient.jar
9153    */
9154   private File grouperClientJar;
9155 
9156   /**
9157    * grouper.properties
9158    */
9159   private File grouperPropertiesFile;
9160   
9161   /**
9162    * grouper.base.properties
9163    */
9164   private File grouperBasePropertiesFile;
9165   
9166   /**
9167    * subject.properties
9168    */
9169   private File subjectPropertiesFile;
9170   
9171   /**
9172    * subject.base.properties
9173    */
9174   private File subjectBasePropertiesFile;
9175   
9176   /**
9177    * grouperUtf8.txt
9178    */
9179   private File grouperUtf8File;
9180   
9181   /**
9182    * GSHFileLoad.properties
9183    */
9184   private File gshFileLoadPropertiesFile;
9185   
9186   /**
9187    * groovysh.profile
9188    */
9189   private File groovyshProfileFile;
9190   
9191   /**
9192    * grouper.client.usage.example.txt
9193    */
9194   private File grouperClientUsageExampleFile;
9195   
9196   /**
9197    * grouper.example.properties
9198    */
9199   private File grouperExamplePropertiesFile;
9200 
9201   /**
9202    * grouper.hibernate.properties
9203    */
9204   private File grouperHibernatePropertiesFile;
9205   
9206   /**
9207    * grouper.hibernate.base.properties
9208    */
9209   private File grouperHibernateBasePropertiesFile;
9210   
9211   /**
9212    * grouper.hibernate.example.properties
9213    */
9214   private File grouperHibernateExamplePropertiesFile;
9215 
9216   /**
9217    * grouper-ws.properties
9218    */
9219   private File grouperWsPropertiesFile;
9220   
9221   /**
9222    * grouper-ws.example.properties
9223    */
9224   private File grouperWsBasePropertiesFile;
9225 
9226   /**
9227    * grouper-ws.base.properties
9228    */
9229   private File grouperWsExamplePropertiesFile;
9230   
9231   /**
9232    * ehcache.xml
9233    */
9234   private File ehcacheFile;
9235 
9236   /**
9237    * ehcache.example.xml
9238    */
9239   private File ehcacheExampleFile;
9240   
9241   /**
9242    * grouper-loader.properties
9243    */
9244   private File grouperLoaderPropertiesFile;
9245   
9246   /**
9247    * grouper-loader.base.properties
9248    */
9249   private File grouperLoaderBasePropertiesFile;
9250   
9251   /**
9252    * grouper.cache.properties
9253    */
9254   private File grouperCachePropertiesFile;
9255   
9256   /**
9257    * grouper.cache.base.properties
9258    */
9259   private File grouperCacheBasePropertiesFile;
9260   
9261   /**
9262    * grouper-loader.example.properties
9263    */
9264   private File grouperLoaderExamplePropertiesFile;
9265 
9266   /**
9267    * grouper.jar
9268    */
9269   private File grouperJar;
9270 
9271   
9272   /**
9273    * find a classpath file on classpath by resourceName
9274    * @param resourceName resource name of file
9275    * @param exceptionIfNotFound 
9276    * @return the file or null if not exception if not found
9277    */
9278   private File findClasspathFile(String resourceName, boolean exceptionIfNotFound) {
9279     
9280     Set<String> fileNamesTried = new LinkedHashSet<String>();
9281     
9282     File file = new File(this.upgradeExistingApplicationDirectoryString + "classes" + File.separator + resourceName);
9283     if (file.exists()) {
9284       return file;
9285     }
9286     
9287     fileNamesTried.add(file.getAbsolutePath());
9288     
9289     file = new File(this.upgradeExistingApplicationDirectoryString + "conf" + File.separator + resourceName);
9290     if (file.exists()) {
9291       return file;
9292     }
9293 
9294     fileNamesTried.add(file.getAbsolutePath());
9295 
9296     //these could be in this location
9297     if (GrouperInstallerUtils.equals("nav.properties", resourceName) 
9298         || GrouperInstallerUtils.equals("media.properties", resourceName)) {
9299       file = new File(this.upgradeExistingApplicationDirectoryString + "WEB-INF" + File.separator 
9300           + "classes" + File.separator + "resources" + File.separator + "grouper" + File.separator + resourceName);
9301       if (file.exists()) {
9302         return file;
9303       }
9304       
9305       fileNamesTried.add(file.getAbsolutePath());
9306     }
9307     
9308     file = new File(this.upgradeExistingApplicationDirectoryString + "WEB-INF" + File.separator + "classes" 
9309         + File.separator + resourceName);
9310     if (file.exists()) {
9311       return file;
9312     }
9313 
9314     fileNamesTried.add(file.getAbsolutePath());
9315     
9316     file = new File(this.upgradeExistingApplicationDirectoryString + resourceName);
9317     if (file.exists()) {
9318       return file;
9319     }
9320 
9321     fileNamesTried.add(file.getAbsolutePath());
9322     
9323     if (exceptionIfNotFound) {
9324       throw new RuntimeException("Cant find file, looked in: " + GrouperInstallerUtils.join(fileNamesTried.iterator(), ", "));
9325     }
9326     
9327     return null;
9328   }
9329 
9330   /**
9331    * lib dirs where libs might be in this.upgradeExistingApplicationDirectoryString
9332    */
9333   private static List<String> libDirs = GrouperInstallerUtils.toList(
9334       "lib" + File.separator, 
9335       "WEB-INF" + File.separator + "lib" + File.separator,
9336       "lib" + File.separator + "grouper" + File.separator,
9337       "lib" + File.separator + "custom" + File.separator,
9338       "lib" + File.separator + "jdbcSamples" + File.separator,
9339       "dist" + File.separator + "lib" + File.separator,
9340       "");
9341 
9342   /**
9343    * get all library files
9344    * @param appDir 
9345    * @return the list of files
9346    */
9347   private List<File> findAllLibraryFiles(String appDir) {
9348     
9349     if (!appDir.endsWith("/") && !appDir.endsWith("\\")) {
9350       appDir = appDir + File.separator;
9351     }
9352     
9353     List<File> result = new ArrayList<File>();
9354     for (String libDir : libDirs) {
9355 
9356       File dir = new File(appDir + libDir);
9357       if (dir.exists() && dir.isDirectory()) {
9358         for (File file : dir.listFiles()) {
9359           if (file.getName().endsWith(".jar")) {
9360             result.add(file);
9361           }
9362         }
9363       }
9364       
9365     }
9366     return result;
9367   }
9368 
9369   /**
9370    * if file is there, return it.  if not a jar, return original
9371    * find a library file on lib dir by lib name, if there return it
9372    * otherwise just return the original file name.  fix the name if not correct
9373    * @param originalThoughtLocation 
9374    * @param originalAppToUpgrade
9375    * @return the file or null if not exception if not found
9376    */
9377   private File fixLibraryFileIfFoundAndDifferent(File originalThoughtLocation, AppToUpgrade originalAppToUpgrade) {
9378     
9379     if (originalThoughtLocation == null || (originalThoughtLocation.exists() && originalThoughtLocation.isFile())) {
9380       return originalThoughtLocation;
9381     }
9382     
9383     if (!originalThoughtLocation.getAbsolutePath().endsWith(".jar")) {
9384       return originalThoughtLocation;
9385     }
9386     
9387     File foundLibraryFile = findLibraryFile(originalThoughtLocation.getName(), false);
9388     if (foundLibraryFile != null && foundLibraryFile.exists() && foundLibraryFile.isFile()) {
9389       return foundLibraryFile;
9390     }
9391     
9392     if (!originalAppToUpgrade.isApiOrganized()) {
9393       
9394       // is in WEB-INF/lib/something.jar
9395       if (GrouperInstallerUtils.equals("lib", originalThoughtLocation.getParentFile().getName())) {
9396         return originalThoughtLocation;
9397       }
9398       
9399       if (GrouperInstallerUtils.equals("lib", originalThoughtLocation.getParentFile().getParentFile().getName())) {
9400         return new File(originalThoughtLocation.getParentFile().getParentFile().getAbsoluteFile() + File.separator + originalThoughtLocation.getName());
9401       }
9402       
9403       return originalThoughtLocation;
9404     }
9405     
9406     //is api
9407     if (!GrouperInstallerUtils.equals("lib", originalThoughtLocation.getParentFile().getName())
9408         && GrouperInstallerUtils.equals("lib", originalThoughtLocation.getParentFile().getParentFile().getName())) {
9409       return originalThoughtLocation;
9410     }
9411       
9412     if (GrouperInstallerUtils.equals("lib", originalThoughtLocation.getParentFile().getName())) {
9413       return new File(originalThoughtLocation.getParentFile().getAbsoluteFile() + File.separator + "grouper" + File.separator + originalThoughtLocation.getName());
9414     }
9415     
9416     // not sure what to do here
9417     return originalThoughtLocation;
9418   }
9419 
9420   /**
9421    * find a library file on lib dir by libName
9422    * @param libName lib name of file
9423    * @param exceptionIfNotFound 
9424    * @return the file or null if not exception if not found
9425    */
9426   private File findLibraryFile(String libName, boolean exceptionIfNotFound) {
9427     
9428     Set<String> fileNamesTried = new LinkedHashSet<String>();
9429 
9430     for (String libDir : libDirs) {
9431 
9432       File file = new File(this.upgradeExistingApplicationDirectoryString + libDir + libName);
9433       if (file.exists()) {
9434         return file;
9435       }
9436       
9437       fileNamesTried.add(file.getAbsolutePath());
9438       
9439     }
9440     
9441     if (exceptionIfNotFound) {
9442       throw new RuntimeException("Cant find file, looked in: " + GrouperInstallerUtils.join(fileNamesTried.iterator(), ", "));
9443     }
9444     
9445     return null;
9446   }
9447   
9448   /**
9449    * 
9450    */
9451   private void mainInstallContainerLogic() {
9452     
9453     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]: ");
9454     
9455     boolean dockerInstalledAndRunning = readFromStdInBoolean(true, "");
9456     
9457     if (!dockerInstalledAndRunning) {
9458       System.out.println("Please install and run docker before proceeding. Thanks! ");
9459       return;
9460     }
9461     
9462     boolean validBaseDirectoryFound = false;
9463     String path = null;
9464     do {
9465       File grouperContainerBaseDirectory = new File(new File("").getAbsolutePath());
9466       
9467       System.out.print("Where do you want your host grouper container base directory (e.g. /opt/grouperContainer)? ["+grouperContainerBaseDirectory.getAbsolutePath()+"]: ");
9468       String localGrouperContainerBaseDirectoryString = readFromStdIn("Placeholder");
9469       if (!GrouperInstallerUtils.isBlank(localGrouperContainerBaseDirectoryString)) {
9470         File grouperContainerBaseDirectoryFile = new File(localGrouperContainerBaseDirectoryString);
9471         if (!grouperContainerBaseDirectoryFile.exists() || !grouperContainerBaseDirectoryFile.isDirectory()) { 
9472           System.out.println("Error: cant find directory: '" + grouperContainerBaseDirectoryFile.getAbsolutePath() + "'");
9473         } else {
9474           path = grouperContainerBaseDirectoryFile.getAbsolutePath();
9475           validBaseDirectoryFound = true;
9476         }
9477       } else {
9478         path = grouperContainerBaseDirectory.getAbsolutePath();
9479         validBaseDirectoryFound = true;
9480       }
9481       
9482     } while (validBaseDirectoryFound == false);
9483     
9484     
9485     // create README.txt file at the path
9486     File readmeFile = new File(path + File.separator + "README.txt");
9487     if (readmeFile.exists()) {
9488       String newFileName = "README_" + new Date().toString().replace(" ", "_") + ".txt";
9489       System.out.println("README.txt already exists. Going to rename to "+newFileName);
9490       readmeFile.renameTo(new File(path + File.separator + newFileName));
9491       readmeFile = new File(path + File.separator + "README.txt");
9492     }
9493         
9494     GrouperInstallerUtils.fileCreate(readmeFile);
9495     
9496     // create logs directory
9497     StringBuilder contentToWrite = new StringBuilder();
9498     contentToWrite.append("Create logs directory in "+path);
9499     contentToWrite.append("\n\n");
9500     contentToWrite.append("\n\n");
9501     try {      
9502       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
9503     } catch (Exception e) {
9504       System.out.println("Could not write to README.txt file.");
9505     }
9506     
9507     File logsDirectory = new File(path+File.separator+"logs"+File.separator+"nothing");
9508     GrouperInstallerUtils.createParentDirectories(logsDirectory);
9509     
9510     File logsDirectoryOnly = new File(path+File.separator+"logs");
9511     
9512     // run chmod o+w for logs directory
9513     contentToWrite = new StringBuilder();
9514     contentToWrite.append("Run chmod o+w for "+logsDirectoryOnly.getAbsolutePath());
9515     contentToWrite.append("\n\n");
9516     contentToWrite.append("\n\n");
9517     try {      
9518       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
9519     } catch (Exception e) {
9520       System.out.println("Could not write to README.txt file.");
9521     }
9522     
9523     List<String> openWriteCommands = GrouperInstallerUtils.toList("chmod", "o+w", 
9524         logsDirectoryOnly.getAbsolutePath() + File.separator);
9525     
9526     System.out.println("Making logs directory o+w so that logs can be written from inside the container: " + convertCommandsIntoCommand(openWriteCommands) + "\n");
9527 
9528     String errorMessageOnChangingLogsDirectoryPermissions = "";
9529     boolean errorOnChangingLogsDirectoryPermissions = false;
9530     try {
9531       CommandResult openWriteCommandResult = GrouperInstallerUtils.execCommand(
9532           GrouperInstallerUtils.toArray(openWriteCommands, String.class), true, true, null, 
9533           new File("."), null, true);
9534       
9535       if (openWriteCommandResult.getExitCode() != 0) {
9536         errorMessageOnChangingLogsDirectoryPermissions = openWriteCommandResult.getErrorText();
9537         errorOnChangingLogsDirectoryPermissions = true;
9538       } 
9539       
9540     } catch (Throwable e) {
9541       errorOnChangingLogsDirectoryPermissions = true;
9542     }
9543     
9544     if (errorOnChangingLogsDirectoryPermissions) {
9545       System.out.println("Could not change permissions on logs directory at "+logsDirectoryOnly.getAbsolutePath());
9546       if (GrouperInstallerUtils.isNotBlank(errorMessageOnChangingLogsDirectoryPermissions)) {
9547         System.out.println("Received error message: "+errorMessageOnChangingLogsDirectoryPermissions+ " ");
9548       }
9549       return;
9550     }
9551     
9552     // create log4j.properties file in <path>/slashRoot/opt/grouper/grouperWebapp/WEB-INF/classes
9553     File classesDir = new File(path+File.separator+"slashRoot"+File.separator+"opt"+File.separator+"grouper"
9554         +File.separator+"grouperWebapp"+File.separator+"WEB-INF"+File.separator+"classes");
9555     
9556     File log4jPropertiesFile = new File(classesDir.getAbsolutePath()+File.separator+"log4j.properties");
9557     
9558     contentToWrite = new StringBuilder();
9559     contentToWrite.append("Create log4j.properties file in "+log4jPropertiesFile.getAbsolutePath());
9560     contentToWrite.append("\n\n");
9561     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");
9562     contentToWrite.append("\n\n");
9563     contentToWrite.append("\n\n");
9564     try {      
9565       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
9566     } catch (Exception e) {
9567       System.out.println("Could not write to README.txt file.");
9568     }
9569     
9570     GrouperInstallerUtils.createParentDirectories(log4jPropertiesFile);
9571     boolean reuseLog4jPropertiesFile = false;
9572     while(true) {
9573       if (log4jPropertiesFile.exists()) {
9574         System.out.print("log4j.properties already exists at '"+log4jPropertiesFile.getParent()+"' ");
9575         System.out.print("Do you want to reuse it (t|f) [t]: ");
9576         reuseLog4jPropertiesFile = readFromStdInBoolean(true, "Placeholder");
9577         if (reuseLog4jPropertiesFile) {
9578           System.out.println("Going to reuse existing log4j.properties file. ");
9579           break;
9580         } else {
9581           System.out.print("Delete log4j.properties and press <return> to continue ");
9582           readFromStdIn("nothing");
9583           log4jPropertiesFile = new File(path+File.separator+"conf"+File.separator+"log4j.properties");
9584           continue;
9585         }
9586       } else {
9587         GrouperInstallerUtils.fileCreate(log4jPropertiesFile);
9588         break;
9589       }
9590     }
9591     
9592     try {
9593       InputStream in = getClass().getResourceAsStream("/log4j.sample.properties"); 
9594       BufferedReader reader = new BufferedReader(new InputStreamReader(in));
9595       StringBuilder log4jContent = new StringBuilder();
9596       String line;
9597       while( (line = reader.readLine()) != null) {
9598         log4jContent.append(line);
9599         log4jContent.append("\n");
9600       }
9601       Files.write(Paths.get(log4jPropertiesFile.getAbsolutePath()), log4jContent.toString().getBytes(), StandardOpenOption.APPEND);
9602     } catch (Exception e) {
9603       System.out.println("Could not write content to "+log4jPropertiesFile.getAbsolutePath());
9604       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. ");
9605       System.out.print("press <return> to continue ");
9606       readFromStdIn("Placeholder");
9607     }
9608     
9609     
9610     String dockerImageVersion = GrouperInstallerUtils.propertiesValue("grouperInstaller.docker.image.version", false);
9611     
9612     if (GrouperInstallerUtils.isBlank(dockerImageVersion)) {
9613       dockerImageVersion = getClass().getPackage().getImplementationVersion();
9614     }
9615     
9616     if (GrouperInstallerUtils.isBlank(dockerImageVersion)) {
9617       dockerImageVersion = GrouperInstallerUtils.propertiesValue("grouper.version", true);
9618     }
9619     
9620     contentToWrite = new StringBuilder("Make sure docker is installed and running. Run the following command to check if docker is installed.");
9621     contentToWrite.append("\n\n");
9622     contentToWrite.append("which docker");
9623     contentToWrite.append("\n\n");
9624     contentToWrite.append("If docker is not installed, go to: https://docs.docker.com/install/ and select the correct platform and follow the instructions. ");
9625     contentToWrite.append("\n\n");
9626     contentToWrite.append("\n\n");
9627     
9628     contentToWrite.append("Run the following command to check if docker is running");
9629     contentToWrite.append("\n\n");
9630     contentToWrite.append("docker info");
9631     
9632     contentToWrite.append("\n\n");
9633     contentToWrite.append("\n\n");
9634     contentToWrite.append("Run the following command to start docker if it's not running already. Command might vary based on the platform.");
9635     contentToWrite.append("\n\n");
9636     contentToWrite.append("sudo service docker start");
9637     
9638     contentToWrite.append("\n\n");
9639     contentToWrite.append("\n\n");
9640     
9641     try {      
9642       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
9643     } catch (Exception e) {
9644       System.out.println("Could not write to README.txt file.");
9645     }
9646     
9647     List<String> commands = new ArrayList<String>();
9648     commands.add(shCommand());
9649     commands.add("-c");
9650     commands.add("which docker");
9651     
9652     String dockerLocation = null;
9653     boolean errorDetectingDocker = false;
9654     String errorMessageDetectingDocker = null;
9655     
9656     try {
9657       CommandResult commandResult = GrouperInstallerUtils.execCommand(
9658           GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
9659           new File("."), null, false, false, true);
9660       
9661       if (commandResult.getExitCode() != 0) {
9662         errorMessageDetectingDocker = commandResult.getErrorText();
9663         errorDetectingDocker = true;
9664       } else {
9665         dockerLocation = commandResult.getOutputText();
9666       }
9667       
9668     } catch (Throwable e) {
9669       errorDetectingDocker = true;
9670     }
9671     
9672     if (errorDetectingDocker) {
9673       System.out.println("Could not detect if docker is installed with command 'which docker' ");
9674       if (GrouperInstallerUtils.isNotBlank(errorMessageDetectingDocker)) {
9675         System.out.println("Received error message: "+errorMessageDetectingDocker+ " ");
9676       }
9677       
9678       System.out.println("Make sure you are running the installer as the user that runs docker ");
9679       
9680       System.out.print("If you have docker installed, enter 't' otherwise enter 'f': ");
9681       boolean isDockerInstalled = readFromStdInBoolean(null, "Placeholder");
9682       
9683       if (isDockerInstalled) {
9684         System.out.print("Please enter the full absolute path to docker. eg: /usr/local/bin/docker ");
9685         dockerLocation = readFromStdIn("Placeholder");
9686         
9687         while (true) {
9688           if (GrouperInstallerUtils.isBlank(dockerLocation) || !new File(dockerLocation).exists()) {
9689             System.out.print("Path is invalid. Please try again. ");
9690             dockerLocation = readFromStdIn("Placeholder");
9691           } else {
9692             break;
9693           }
9694         }
9695       } else {
9696         System.out.print("Please install docker first and try again. ");
9697         return;
9698       }
9699     } else {
9700       // docker is installed
9701       System.out.println("We detected docker is installed at: "+dockerLocation);
9702       System.out.print("Is the path above correct? (t|f) [t]: ");
9703       boolean correctDockerLocationDetected = readFromStdInBoolean(true, "Placeholder");
9704       
9705       if (correctDockerLocationDetected == false) {
9706         System.out.print("Please enter the full absolute path to docker. eg: /usr/local/bin/docker : ");
9707         dockerLocation = readFromStdIn("Placeholder");
9708         
9709         while (true) {
9710           if (GrouperInstallerUtils.isBlank(dockerLocation) || !new File(dockerLocation).exists()) {
9711             System.out.print("Path is invalid. Please try again. ");
9712             dockerLocation = readFromStdIn("Placeholder");
9713           } else {
9714             break;
9715           }
9716         }
9717       }
9718     }
9719     
9720     
9721     dockerLocation = dockerLocation.trim();
9722     
9723     // check if docker is running or not. 
9724     
9725     System.out.println("Going to check if docker is running. ");
9726     boolean dockerIsRunning = false;
9727     CommandResult commandResult = null;
9728     while (true) {
9729       try {
9730         commandResult = GrouperInstallerUtils.execCommand(
9731             new String[] {shCommand(), "-c", dockerLocation + " info"}, true, true, null, 
9732             new File("."), null, false, false, true);
9733         if (commandResult.getExitCode() == 0) {
9734           dockerIsRunning = true;
9735         }
9736       } catch (Exception e) {}
9737       
9738       if (dockerIsRunning == false) {
9739         
9740         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): ");
9741         boolean isDockerRunning = readFromStdInBoolean(null, "Placeholder");
9742         
9743         if (isDockerRunning) {
9744           break;
9745         } else {
9746           System.out.print("Start docker and press <return> to continue ");
9747           readFromStdIn("Placeholder");
9748           continue;
9749         }
9750         
9751       } else {
9752         System.out.println("docker is running. ");
9753         System.out.println(commandResult.getOutputText());
9754         break;
9755       }
9756     }
9757     
9758     
9759     // if there's a container with name gsh, ws, ui, etc is already running. ask user to stop them first
9760     
9761     contentToWrite = new StringBuilder();
9762     contentToWrite.append("Run the following command to view the containers names");
9763     contentToWrite.append("\n\n");
9764     contentToWrite.append("docker ps --all --format \"{{.Names}}\" ");
9765     contentToWrite.append("\n");
9766     contentToWrite.append("If you have gsh, ws, grouper or ui containers already there. Please stop them, remove them and then continue.");
9767     contentToWrite.append("\n");
9768     
9769     contentToWrite.append("To stop a running container, run the following command. ");
9770     contentToWrite.append("\n");
9771     contentToWrite.append("docker kill <container name>");
9772     contentToWrite.append("\n");
9773     contentToWrite.append("You might want to add -f flag to docker kill command if unable to stop.");
9774     contentToWrite.append("\n");
9775     contentToWrite.append("To remove the container, run the following command.");
9776     contentToWrite.append("\n");
9777     contentToWrite.append("docker rm <container name>");
9778     contentToWrite.append("\n");
9779     contentToWrite.append("You might want to add -f flag to docker rm command if unable to remove.");
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 check if gsh, ws, grouper-ui, or ui containers already exist.");
9790     List<String> conflictingNames = new ArrayList<String>();
9791     boolean conflictingNamesRanSuccessfully = false;
9792     try {
9793       commandResult = GrouperInstallerUtils.execCommand(
9794           new String[] {shCommand(), "-c", dockerLocation + " ps --all --format \"{{.Names}}\""}, true, true, null, 
9795           new File("."), null, false, false, true);
9796       if (commandResult.getExitCode() == 0) {
9797         conflictingNamesRanSuccessfully = true;
9798         String containerNamesString = commandResult.getOutputText();
9799         if (GrouperInstallerUtils.isNotBlank(containerNamesString)) {
9800           String[] containerNames = containerNamesString.split("\n");
9801           
9802           List<String> containersThatCanCauseConflict = Arrays.asList("gsh", "ui", "ws", "grouper", "grouper-ui");
9803           
9804           for (String containerName: containerNames) {
9805             if (containersThatCanCauseConflict.contains(containerName)) {
9806               conflictingNames.add(containerName);
9807             }
9808           }
9809         }
9810       }
9811     } catch (Exception e) {}
9812     
9813     if (conflictingNamesRanSuccessfully == false) {
9814       System.out.println("There was an error trying to figure out if gsh, ui, or ws containers already exist.");
9815       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. ");
9816       System.out.println("Run docker rm -f <container name> to force remove a docker container");
9817     }
9818     
9819     if (conflictingNames.size() > 0) {
9820       System.out.println("We found that containers with names "+String.join(", ", conflictingNames) + " already exist. Please delete them before proceeding. ");
9821       System.out.println("Command to delete a docker container is 'docker rm <container name>'. Use -f flag to force remove. ");
9822       System.out.print("press <return> once you have deleted the conflicting containers. ");
9823       readFromStdIn("Placeholder");
9824     }
9825     if (conflictingNamesRanSuccessfully && conflictingNames.size() == 0) {
9826       System.out.println("No conflicting containers found. ");
9827     }
9828     
9829     // pull grouper docker image
9830     contentToWrite = new StringBuilder();
9831     contentToWrite.append("Pull grouper docker image by running the following command. ");
9832     contentToWrite.append("\n\n");
9833     contentToWrite.append("docker pull i2incommon/grouper:"+dockerImageVersion);
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     System.out.println("Going to pull grouper docker image: i2incommon/grouper:"+dockerImageVersion);
9844     boolean pulledDockerImage = false;
9845     try {
9846       
9847       String dockerPullCommand = dockerLocation + " pull i2incommon/grouper:"+dockerImageVersion;
9848       
9849       commandResult = GrouperInstallerUtils.execCommand(
9850           new String[] {shCommand(), "-c", dockerPullCommand}, true, true, null, 
9851           new File("."), null, false, true, true);
9852       
9853       if (commandResult.getExitCode() == 0) {
9854         pulledDockerImage = true;
9855       }
9856       
9857       if (commandResult.getOutputText() != null) {
9858         System.out.println(commandResult.getOutputText());
9859       }
9860       
9861     } catch (Exception e) {
9862       // TODO: handle exception
9863     }
9864     
9865     if(pulledDockerImage == false) {
9866       System.out.println("Could not pull grouper docker image. Pull it manually by running: docker pull i2incommon/grouper:"+dockerImageVersion);
9867       System.out.print("press <return> when done ");
9868       readFromStdIn("Placeholder");
9869     }
9870     
9871     // create slashRoot directory
9872     contentToWrite = new StringBuilder();
9873     contentToWrite.append("Create slashRoot directory in "+path);
9874     contentToWrite.append("\n\n");
9875     try {      
9876       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
9877     } catch (Exception e) {
9878       System.out.println("Could not write to README.txt file.");
9879     }
9880     
9881     // create morphString.properties file in slashRoot/opt/grouper/grouperWebapp/WEB-INF/classes/
9882     contentToWrite = new StringBuilder();
9883     contentToWrite.append("Create morphString.properties file in "+path+"/slashRoot/opt/grouper/grouperWebapp/WEB-INF/classes/");
9884     contentToWrite.append("\n");
9885     contentToWrite.append("Add the following lines to morphString.properties file. Replace the placeholders below with actual values");
9886     contentToWrite.append("\n");
9887     contentToWrite.append("encrypt.key = <random alphanumeric key with minimum 8 characters>");
9888     contentToWrite.append("\n\n");
9889     contentToWrite.append("\n\n");
9890     
9891     try {      
9892       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
9893     } catch (Exception e) {
9894       System.out.println("Could not write to README.txt file.");
9895     }
9896     
9897     File morphStringPropertiesFile = new File(classesDir+File.separator+"morphString.properties");
9898     
9899     GrouperInstallerUtils.createParentDirectories(morphStringPropertiesFile);
9900     boolean reuseMorphStringPropertiesFile = false;
9901     while(true) {
9902       if (morphStringPropertiesFile.exists()) {
9903         System.out.println("morphString.properties already exists at "+morphStringPropertiesFile.getParent()+" ");
9904         System.out.print("Do you want to reuse it (t|f) [t]: ");
9905         reuseMorphStringPropertiesFile = readFromStdInBoolean(true, "Placeholder");
9906         if (reuseMorphStringPropertiesFile) {
9907           System.out.println("Going to reuse existing morphString.properties file. ");
9908           break;
9909         } else {
9910           System.out.print("Delete morphString.properties and press <return> to continue ");
9911           readFromStdIn("nothing");
9912           morphStringPropertiesFile = new File(path+File.separator+"conf"+File.separator+"morphString.properties");
9913           continue;
9914         }
9915       } else {
9916         GrouperInstallerUtils.fileCreate(morphStringPropertiesFile);
9917         break;
9918       }
9919     }
9920     
9921     if (reuseMorphStringPropertiesFile == false) {
9922       
9923       String validCharactersMorphString = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
9924       SecureRandom sr = new SecureRandom();
9925       
9926       StringBuilder morphStringBuilder = new StringBuilder(20);
9927       for( int i = 0; i < 20; i++ ) {
9928         morphStringBuilder.append( validCharactersMorphString.charAt(sr.nextInt(validCharactersMorphString.length())));
9929       }
9930       
9931       System.out.print("Do you want to use the randomly generated morphString key? (" +  morphStringBuilder.toString()+") (t|f) [t]: ");
9932       boolean useAutoGeneratedMorphKey = readFromStdInBoolean(true, "Placeholder");
9933       String morphStringPasswd = null;
9934       if (useAutoGeneratedMorphKey == false) {
9935         System.out.print("Enter morphString key. Minimum 8 characters required: ");
9936         while (true) {          
9937           String manualMorphKey = readFromStdIn("Placeholder");
9938           if (GrouperInstallerUtils.isNotBlank(manualMorphKey) && manualMorphKey.trim().length() >= 8) {
9939             morphStringPasswd = manualMorphKey.trim();
9940             break;
9941           } else {
9942             System.out.print("morphString key is invalid. Minimum 8 characters required. Please try again: ");
9943             continue;
9944           } 
9945         }
9946         
9947       } else {
9948         morphStringPasswd = morphStringBuilder.toString();
9949       }
9950       
9951       editPropertiesFile(morphStringPropertiesFile, "encrypt.key", morphStringPasswd, false);
9952     }
9953     
9954     Properties morphStringProperties = GrouperInstallerUtils.propertiesFromFile(morphStringPropertiesFile);
9955     
9956     // create grouper.hibernate.properties file
9957     contentToWrite = new StringBuilder();
9958     contentToWrite.append("Create grouper.hibernate.properties file in " +path+"/" + classesDir.getAbsolutePath());
9959     contentToWrite.append("\n");
9960     contentToWrite.append("Add the following lines to grouper.hibernate.properties file. Replace the placeholders below with actual values");
9961     contentToWrite.append("\n");
9962     contentToWrite.append("hibernate.connection.url = <db url> eg: jdbc:mysql://localhost:3306/grouper");
9963     contentToWrite.append("\n");
9964     contentToWrite.append("hibernate.connection.username = <user> eg: root");
9965     contentToWrite.append("\n");
9966     contentToWrite.append("hibernate.connection.password = <morph string encrypted password> eg: 86asd9f87a9sdf87a9s78df97");
9967     contentToWrite.append("\n");
9968     
9969     contentToWrite.append("grouper.is.ui.basicAuthn = true");
9970     contentToWrite.append("\n");
9971     
9972     contentToWrite.append("grouper.is.ws.basicAuthn = true");
9973     contentToWrite.append("\n");
9974     
9975     contentToWrite.append("grouper.is.scim.basicAuthn = true");
9976     contentToWrite.append("\n");
9977     contentToWrite.append("\n");
9978     contentToWrite.append("\n");
9979     
9980     try {      
9981       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
9982     } catch (Exception e) {
9983       System.out.println("Could not write to README.txt file.");
9984     }
9985     
9986     System.out.println("Going to create grouper.hibernate.properties file in " + path + File.separator+ classesDir.getAbsolutePath());
9987     
9988     File grouperHibernatePropertiesFile = new File(classesDir+File.separator+"grouper.hibernate.properties");
9989     boolean reuseHibernatePropertiesFile = false;
9990     while(true) {
9991       GrouperInstallerUtils.createParentDirectories(grouperHibernatePropertiesFile);
9992       if (grouperHibernatePropertiesFile.exists()) {
9993         System.out.println("grouper.hibernate.properties already exists at "+grouperHibernatePropertiesFile.getParent()+" ");
9994         System.out.print("Do you want to reuse it (t|f) [t]: ");
9995         reuseHibernatePropertiesFile = readFromStdInBoolean(true, "Placeholder");
9996         if (reuseHibernatePropertiesFile) {
9997           System.out.println("Going to reuse existing grouper.hibernate.properties file. ");
9998           break;
9999         } else {
10000           System.out.print("Delete grouper.hibernate.properties and press <return> to continue ");
10001           readFromStdIn("nothing");
10002           grouperHibernatePropertiesFile = new File(classesDir+File.separator+"grouper.hibernate.properties");
10003           continue;
10004         }
10005       } else {
10006         GrouperInstallerUtils.fileCreate(grouperHibernatePropertiesFile);
10007         break;
10008       }
10009     }
10010     
10011     // create a blank grouper.client.properties
10012     contentToWrite = new StringBuilder();
10013     contentToWrite.append("Create a blank grouper.client.properties file in " +path+File.separator+classesDir.getAbsolutePath());
10014     contentToWrite.append("\n");
10015     contentToWrite.append("\n");
10016     contentToWrite.append("\n");
10017     
10018     try {      
10019       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
10020     } catch (Exception e) {
10021       System.out.println("Could not write to README.txt file.");
10022     }
10023     File grouperClientPropertiesFile = new File(classesDir+File.separator+"grouper.client.properties");
10024     boolean reuseClientPropertiesFile = false;
10025     while(true) {
10026       GrouperInstallerUtils.createParentDirectories(grouperClientPropertiesFile);
10027       if (grouperClientPropertiesFile.exists()) {
10028         System.out.println("grouper.client.properties already exists at "+grouperClientPropertiesFile.getParent()+" ");
10029         System.out.print("Do you want to reuse it (t|f) [t]: ");
10030         reuseClientPropertiesFile = readFromStdInBoolean(true, "Placeholder");
10031         if (reuseClientPropertiesFile) {
10032           System.out.println("Going to reuse existing grouper.client.properties file. ");
10033           break;
10034         } else {
10035           System.out.print("Delete grouper.client.properties and press <return> to continue ");
10036           readFromStdIn("nothing");
10037           grouperClientPropertiesFile = new File(classesDir+File.separator+"grouper.client.properties");
10038           continue;
10039         }
10040       } else {
10041         GrouperInstallerUtils.fileCreate(grouperClientPropertiesFile);
10042         break;
10043       }
10044     }
10045     
10046     // create a blank subject.properties
10047     contentToWrite = new StringBuilder();
10048     contentToWrite.append("Create a blank subject.properties file in " +path+File.separator+classesDir.getAbsolutePath());
10049     contentToWrite.append("\n");
10050     contentToWrite.append("\n");
10051     contentToWrite.append("\n");
10052     
10053     try {      
10054       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
10055     } catch (Exception e) {
10056       System.out.println("Could not write to README.txt file.");
10057     }
10058     File grouperSubjectPropertiesFile = new File(classesDir+File.separator+"subject.properties");
10059     boolean reuseSubjectPropertiesFile = false;
10060     while(true) {
10061       GrouperInstallerUtils.createParentDirectories(grouperSubjectPropertiesFile);
10062       if (grouperSubjectPropertiesFile.exists()) {
10063         System.out.println("subject.properties already exists at "+grouperSubjectPropertiesFile.getParent()+" ");
10064         System.out.print("Do you want to reuse it (t|f) [t]: ");
10065         reuseSubjectPropertiesFile = readFromStdInBoolean(true, "Placeholder");
10066         if (reuseSubjectPropertiesFile) {
10067           System.out.println("Going to reuse existing subject.properties file. ");
10068           break;
10069         } else {
10070           System.out.print("Delete subject.properties and press <return> to continue ");
10071           readFromStdIn("nothing");
10072           grouperSubjectPropertiesFile = new File(classesDir+File.separator+"subject.properties");
10073           continue;
10074         }
10075       } else {
10076         GrouperInstallerUtils.fileCreate(grouperSubjectPropertiesFile);
10077         break;
10078       }
10079     }
10080     
10081     //####################################
10082     //ask about database
10083     if (reuseHibernatePropertiesFile == false) {
10084       System.out.print("Database setup");
10085 
10086       System.out.println("\n##################################\n");
10087       System.out.println("Example mysql URL: jdbc:mysql://1.2.3.4:3306/grouper?useSSL=false");
10088       System.out.println("Example oracle URL: jdbc:oracle:thin:@server.school.edu:1521:sid");
10089       System.out.println("Example postgres URL: jdbc:postgresql://1.2.3.4:5432/database");
10090       System.out.print("\nEnter the database URL: ");
10091       String newDbUrl = readFromStdIn("grouperInstaller.autorun.dbUrl");
10092       if (!GrouperInstallerUtils.isBlank(newDbUrl)) {
10093         this.dbUrl = newDbUrl;
10094         if (newDbUrl.contains("postgresql")) {
10095           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");
10096           for (int i=0;i<3;i++) {
10097             System.out.print("Ready to continue? (t|f)? [t] ");
10098             boolean shouldContinue = readFromStdInBoolean(true, "grouperInstaller.autorun.dbContinueAfterChangeSourcesXmlForPostgresSqlServer");
10099             if (shouldContinue) {
10100               break;
10101             }
10102           }
10103         }
10104         
10105         if (newDbUrl.contains("oracle")) {
10106           
10107           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] ");
10108           boolean oracleTermsAgreed = readFromStdInBoolean(true, "Placeholder");
10109           File oracleLibPath = new File(path+File.separator+"slashRoot"+File.separator+"opt"+
10110               File.separator+"grouper"+File.separator+"grouperWebapp"+File.separator+"WEB-INF"+File.separator+"lib");
10111           if (!oracleTermsAgreed) {
10112             System.out.print("Place the oracle jdbc jar in " + oracleLibPath.getAbsolutePath() + " and press <return> to continue ");
10113             readFromStdIn("Placeholder");
10114           } else {
10115             System.out.print("Do you want the installer to install the oracle jar (t|f)? [t] ");
10116             boolean shouldContinue = readFromStdInBoolean(true, "Placeholder");
10117             if (shouldContinue) {
10118               
10119               File oracleLibJarPath = new File(path+File.separator+"slashRoot"+File.separator+"opt"+
10120                   File.separator+"grouper"+File.separator+"grouperWebapp"+File.separator+"WEB-INF"+File.separator+"lib"+File.separator+"ojdbc8-19.3.0.0.jar");
10121               
10122               GrouperInstallerUtils.createParentDirectories(oracleLibJarPath);
10123               
10124               downloadFile("https://repo1.maven.org/maven2/com/oracle/ojdbc/ojdbc8/19.3.0.0/ojdbc8-19.3.0.0.jar", oracleLibJarPath.getAbsolutePath(), "");
10125             }
10126           }
10127           
10128           
10129         }
10130         
10131       }
10132       System.out.print("Database user: ");
10133       String newDbUser = readFromStdIn("grouperInstaller.autorun.dbUser");
10134       if (!GrouperInstallerUtils.isBlank(newDbUser)) {
10135         this.dbUser = newDbUser;
10136       }
10137       System.out.print("Database password (note, you aren't setting the pass here, you are using an existing pass, this will be echoed back) [" 
10138           + GrouperInstallerUtils.defaultIfEmpty(this.dbPass, "<blank>") + "]: ");
10139       String newDbPass = readFromStdIn("grouperInstaller.autorun.dbPass");
10140       
10141       String encryptedDbPassword =  "";
10142       
10143       if (GrouperInstallerUtils.isNotBlank(newDbPass)) {
10144         encryptedDbPassword = new Crypto(morphStringProperties.getProperty("encrypt.key") + "w").encrypt(newDbPass);
10145       }
10146 
10147       editPropertiesFile(grouperHibernatePropertiesFile, "hibernate.connection.url", this.dbUrl, false);
10148       editPropertiesFile(grouperHibernatePropertiesFile, "hibernate.connection.username", this.dbUser, false);
10149       editPropertiesFile(grouperHibernatePropertiesFile, "hibernate.connection.password", encryptedDbPassword, false);
10150       
10151       editPropertiesFile(grouperHibernatePropertiesFile, "grouper.is.ui.basicAuthn", "true", false);
10152       editPropertiesFile(grouperHibernatePropertiesFile, "grouper.is.ws.basicAuthn", "true", false);
10153       editPropertiesFile(grouperHibernatePropertiesFile, "grouper.is.scim.basicAuthn", "true", false);
10154     } 
10155     
10156     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] ");
10157     boolean autoInitDatabase = readFromStdInBoolean(true, "Placeholder");
10158     boolean initDbDocker = false;
10159     if (autoInitDatabase) {
10160       String versionWithAnyPatch = dockerImageVersion.substring(0, dockerImageVersion.lastIndexOf("."));
10161       versionWithAnyPatch = versionWithAnyPatch + ".*";
10162       editPropertiesFile(grouperHibernatePropertiesFile, "registry.auto.ddl.upToVersion", versionWithAnyPatch, false);
10163       initDbDocker = true;
10164     } else {
10165       System.out.print("Do you want to init the database one time now (t|f)? [t] ");
10166       boolean initDatabaseOneTime = readFromStdInBoolean(true, "Placeholder");
10167       if (initDatabaseOneTime == true) {
10168         initDbDocker = true;
10169       }
10170     }
10171     
10172     contentToWrite = new StringBuilder();
10173     contentToWrite.append("Run the following command to init the database. It is not a required step.");
10174     contentToWrite.append("\n");
10175     
10176     StringBuilder buildInitCommand = new StringBuilder();
10177     buildInitCommand.append("docker run --detach ");
10178     buildInitCommand.append("--mount type=bind,src=");
10179     buildInitCommand.append(path+File.separator);
10180     buildInitCommand.append("logs,dst=/opt/grouper/logs ");
10181     buildInitCommand.append("--mount type=bind,src=");
10182     buildInitCommand.append(path+File.separator);
10183     buildInitCommand.append("slashRoot,dst=/opt/grouper/slashRoot ");
10184     buildInitCommand.append("--name gsh ");
10185     buildInitCommand.append("i2incommon/grouper:"+dockerImageVersion );
10186     buildInitCommand.append(" gsh -registry -check -runscript -noprompt" );
10187     
10188     contentToWrite.append(buildInitCommand.toString());
10189     contentToWrite.append("\n\n");
10190     contentToWrite.append("\n\n");
10191     
10192     try {      
10193       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
10194     } catch (Exception e) {
10195       System.out.println("Could not write to README.txt file.");
10196     }
10197     
10198     boolean removeDockerGshContainer = false;
10199     if (initDbDocker) {
10200       boolean dbInitialized = false;
10201       try {
10202         commands = new ArrayList<String>();
10203         commands.add(shCommand());
10204         commands.add("-c");
10205         
10206         commands.add(buildInitCommand.toString());
10207         
10208         CommandResult dockerDbInitCommandResult = GrouperInstallerUtils.execCommand(
10209             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10210             new File("."), null, false, false, true);
10211         
10212         if (dockerDbInitCommandResult.getExitCode() == 0) {
10213           dbInitialized = true;
10214         }
10215         
10216       } catch (Exception e) {}
10217       
10218       if (dbInitialized == false) {
10219         System.out.println("Could not initialize db. Run the following command manually in another terminal window/tab.");
10220         System.out.println(buildInitCommand.toString());
10221         System.out.print("Press <return> to continue once the command has been run: ");
10222         readFromStdIn("Placeholder");
10223       } else {
10224         removeDockerGshContainer = true;
10225         StringBuilder dockerPsCommand = new StringBuilder();
10226         dockerPsCommand.append("docker ps -a --filter \"name=gsh\" --format \"{{.Status}}\" ");
10227         
10228         for (int i=0; i<100; i++) {
10229           System.out.println("Waiting for docker command to finish.");
10230           GrouperInstallerUtils.sleep(4000);
10231           
10232           commands = new ArrayList<String>();
10233           commands.add(shCommand());
10234           commands.add("-c");
10235           
10236           commands.add(dockerPsCommand.toString());
10237           
10238           CommandResult dockerPsCommandResult = GrouperInstallerUtils.execCommand(
10239               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10240               new File("."), null, false, false, true);
10241           
10242           if (dockerPsCommandResult.getExitCode() == 0) {
10243             
10244             String output = dockerPsCommandResult.getOutputText();
10245             if (output.contains("Exited")) {
10246               
10247               GrouperInstallerUtils.sleep(20000);
10248               
10249               commands = new ArrayList<String>();
10250               commands.add(shCommand());
10251               commands.add("-c");
10252               
10253               StringBuilder dockerLogsCommand = new StringBuilder();
10254               dockerLogsCommand.append("docker logs gsh ");
10255               
10256               commands.add(dockerLogsCommand.toString());
10257               
10258               CommandResult dockerLogsCommandResult = GrouperInstallerUtils.execCommand(
10259                   GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10260                   new File("."), null, false, false, true);
10261               
10262               if (dockerLogsCommandResult.getExitCode() == 0) {
10263                 String logs = dockerLogsCommandResult.getOutputText();
10264                 
10265                 File dbInitLogsFile = new File(path+File.separator+"docker_logs_init_db_"+new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS").format(new Date())+".log");
10266                 GrouperInstallerUtils.fileCreate(dbInitLogsFile);
10267                 
10268                 try {      
10269                   Files.write(Paths.get(dbInitLogsFile.getAbsolutePath()), logs.getBytes(), StandardOpenOption.APPEND);
10270                 } catch (Exception e) {
10271                   System.out.println("Could not write logs to "+dbInitLogsFile.getAbsolutePath()+" file. ");
10272                 }
10273                 
10274                 System.out.println("docker database initialization logs are at: "+dbInitLogsFile.getAbsolutePath());
10275                 
10276                 if (logs.contains("Script was executed successfully")
10277                     && logs.contains("SQL statements executed successfully")
10278                     && !logs.contains("Exception") && !logs.contains("exception")) {
10279                   System.out.println("From the logs: Script was executed successfully");
10280                   System.out.print("Press <return> to continue. ");
10281                   readFromStdIn("Placeholder");
10282                 } else {
10283                   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. ");
10284                   System.out.print("Press <return> to continue. ");
10285                   readFromStdIn("Placeholder");
10286                 }
10287                 
10288                 try (BufferedReader reader = new BufferedReader(new FileReader(dbInitLogsFile))) {
10289                   StringBuilder logContent = new StringBuilder();
10290                   String line;
10291                   int lineNumber =0;
10292                   while( (line = reader.readLine()) != null) {
10293                     logContent.append(line);
10294                     logContent.append("\n");
10295                     lineNumber++;
10296                     
10297                     if (lineNumber > 24) {
10298                       break;
10299                     }
10300                   }
10301                   System.out.println("First 25 lines of logs are below: ");
10302                   System.out.println(logContent);
10303                 } catch (Exception e) {}
10304                 
10305               } else {
10306                 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. ");
10307                 System.out.print("Press <return> to continue. ");
10308                 readFromStdIn("Placeholder");
10309               }
10310               
10311               break;
10312             } 
10313             
10314           } else {
10315             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. ");
10316             readFromStdIn("Placeholder");
10317             break;
10318           }
10319           
10320         }
10321         
10322       }
10323       
10324     }
10325     
10326     // remove gsh docker container
10327     contentToWrite = new StringBuilder();
10328     contentToWrite.append("Run 'docker rm -f gsh' to remove the gsh container.");
10329     contentToWrite.append("\n\n");
10330     contentToWrite.append("\n\n");
10331     try {
10332       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
10333     } catch (Exception e) {
10334       System.out.println("Could not write to README.txt file.");
10335     }
10336     
10337     if (removeDockerGshContainer) {
10338       commands = new ArrayList<String>();
10339       commands.add(shCommand());
10340       commands.add("-c");
10341       commands.add("docker rm -f gsh");
10342       
10343       boolean dockerRemovedGshContainer = false;
10344       String dockerRmCommandError = null;
10345       try {
10346         CommandResult dockerRmCommandResult = GrouperInstallerUtils.execCommand(
10347             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10348             new File("."), null, false, false, true);
10349         if (dockerRmCommandResult.getExitCode() == 0) {
10350           dockerRemovedGshContainer = true;
10351         } else if (GrouperInstallerUtils.isNotBlank(dockerRmCommandResult.getErrorText())) {
10352           dockerRmCommandError = dockerRmCommandResult.getErrorText();
10353         }
10354       } catch (Exception e) {
10355         dockerRemovedGshContainer = false;
10356       }
10357       
10358       if (dockerRemovedGshContainer) {
10359         System.out.println("Removed 'gsh' container successfully. ");
10360       } else if (dockerRmCommandError != null) {
10361         System.out.println("Could not remove docker gsh container. Please run 'docker rm -f gsh' in another terminal window before proceeding. ");
10362       }
10363       
10364       System.out.print("Press <return> to continue ");
10365       readFromStdIn("Placeholder");
10366     }
10367     
10368     // Add passwords for grouper ui
10369     contentToWrite = new StringBuilder();    
10370     contentToWrite.append("If you want to use grouper basic authentication for UI, follow the instructions below.");
10371     contentToWrite.append("\n");
10372     contentToWrite.append("Create createGrouperSystemPasswordUi.gsh file in "+path+File.separator+"slashRoot"+File.separator+"opt"+
10373         File.separator+"grouper"+File.separator+"grouperWebapp"+File.separator+"WEB-INF"+File.separator+"bin");
10374     contentToWrite.append("\n");
10375     contentToWrite.append("Add the following lines to createGrouperSystemPasswordUi.gsh. Replace placeholder with actual values below.");
10376     contentToWrite.append("\n");
10377     contentToWrite.append("GrouperSession grouperSession = GrouperSession.startRootSession();");
10378     contentToWrite.append("\n");
10379     contentToWrite.append("GrouperPasswordSave grouperPasswordSave = new GrouperPasswordSave();");
10380     contentToWrite.append("\n");
10381     contentToWrite.append("grouperPasswordSave.assignUsername(\"GrouperSystem\");");
10382     contentToWrite.append("\n");
10383     contentToWrite.append("grouperPasswordSave.assignEntityType(\"username\");");
10384     contentToWrite.append("\n");
10385     contentToWrite.append("grouperPasswordSave.assignPassword(\"<password>\");");
10386     contentToWrite.append("\n");
10387     contentToWrite.append("grouperPasswordSave.assignApplication(GrouperPassword.Application.UI);");
10388     contentToWrite.append("\n");
10389     contentToWrite.append("new Authentication().assignUserPassword(grouperPasswordSave);");
10390     contentToWrite.append("\n\n");
10391     contentToWrite.append("\n\n");
10392     
10393     try {
10394       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
10395     } catch (Exception e) {
10396       System.out.println("Could not write to README.txt file.");
10397     }
10398     
10399     //System.out.print("Do you want to use grouper authenitcation for UI? (t|f)? [t]: ");
10400     //boolean useGrouperAuthenticationUi = readFromStdInBoolean(true, "grouperInstaller.autorun.useBuiltInGrouperAuthenticationUI");
10401     boolean useGrouperAuthenticationUi = true;
10402     
10403     if (useGrouperAuthenticationUi) {
10404       System.out.print("Enter the password for user 'GrouperSystem' for grouper UI: ");
10405       String uiPassword = readFromStdIn("Placeholder");
10406       
10407       while (true) {
10408         if (uiPassword == null || uiPassword.trim().length() < 4 ) {
10409           System.out.print("Invalid password. Minimum 4 characters required. Please try again: ");
10410           uiPassword = readFromStdIn("Placeholder");
10411         } else {
10412           break;
10413         }
10414       }
10415       
10416       File grouperUiSystemPasswordSh = new File(path+File.separator+"slashRoot"+File.separator+"opt"+
10417           File.separator+"grouper"+File.separator+"grouperWebapp"+File.separator+"WEB-INF"+File.separator+"bin"+File.separator+"createGrouperSystemPasswordUi.gsh");
10418       GrouperInstallerUtils.createParentDirectories(grouperUiSystemPasswordSh);
10419       boolean reuseCreateGrouperSystemPasswordUiFile = false;
10420       while(true) {
10421         if (grouperUiSystemPasswordSh.exists()) {
10422           System.out.println("createGrouperSystemPasswordUi.gsh already exists at "+grouperUiSystemPasswordSh.getParent()+" ");
10423           System.out.print("Do you want to reuse it (t|f) [t]: ");
10424           reuseCreateGrouperSystemPasswordUiFile = readFromStdInBoolean(true, "Placeholder");
10425           if (reuseCreateGrouperSystemPasswordUiFile) {
10426             System.out.println("Going to reuse existing createGrouperSystemPasswordUi.gsh file. ");
10427             break;
10428           } else {
10429             System.out.print("Delete createGrouperSystemPasswordUi.sh and press <return> to continue ");
10430             readFromStdIn("nothing");
10431             grouperUiSystemPasswordSh = new File(path+File.separator+"slashRoot"+File.separator+"opt"+
10432                 File.separator+"grouper"+File.separator+"grouperWebapp"+File.separator+"WEB-INF"+File.separator+"bin"+File.separator+"createGrouperSystemPasswordUi.gsh");
10433             continue;
10434           }
10435         } else {
10436           GrouperInstallerUtils.fileCreate(grouperUiSystemPasswordSh);
10437           break;
10438         }
10439       }
10440       
10441       if (reuseCreateGrouperSystemPasswordUiFile == false) {
10442         StringBuilder createPasswordCommands = new StringBuilder();
10443         createPasswordCommands.append("GrouperSession grouperSession = GrouperSession.startRootSession(); \n");
10444         createPasswordCommands.append("GrouperPasswordSave grouperPasswordSave = new GrouperPasswordSave(); \n");
10445         createPasswordCommands.append("grouperPasswordSave.assignUsername(\"GrouperSystem\"); \n");
10446         createPasswordCommands.append("grouperPasswordSave.assignEntityType(\"username\"); \n");
10447         createPasswordCommands.append("grouperPasswordSave.assignPassword(\""+uiPassword+"\");");
10448         createPasswordCommands.append("\n");
10449         
10450         createPasswordCommands.append("grouperPasswordSave.assignApplication(GrouperPassword.Application.UI); \n");
10451         createPasswordCommands.append("new Authentication().assignUserPassword(grouperPasswordSave); \n");
10452         
10453         GrouperInstallerUtils.writeStringToFile(grouperUiSystemPasswordSh, createPasswordCommands.toString());
10454       }
10455       
10456       // run docker command to add UI password to grouper
10457       contentToWrite = new StringBuilder();
10458       contentToWrite.append("Run the following command to add UI password to grouper.");
10459       contentToWrite.append("\n");
10460       StringBuilder uiPasswordDockerCommand = new StringBuilder();
10461       uiPasswordDockerCommand.append("docker run --detach ");
10462       uiPasswordDockerCommand.append("--mount type=bind,src=");
10463       uiPasswordDockerCommand.append(path+File.separator);
10464       uiPasswordDockerCommand.append("logs,dst=/opt/grouper/logs ");
10465       uiPasswordDockerCommand.append("--mount type=bind,src=");
10466       uiPasswordDockerCommand.append(path+File.separator);
10467       uiPasswordDockerCommand.append("slashRoot,dst=/opt/grouper/slashRoot ");
10468       uiPasswordDockerCommand.append("--name gsh ");
10469       uiPasswordDockerCommand.append("i2incommon/grouper:"+dockerImageVersion );
10470       uiPasswordDockerCommand.append(" gsh /opt/grouper/grouperWebapp/WEB-INF/bin/createGrouperSystemPasswordUi.gsh");
10471       contentToWrite.append(uiPasswordDockerCommand.toString());
10472       contentToWrite.append("\n\n");
10473       contentToWrite.append("\n\n");
10474       try {
10475         Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
10476       } catch (Exception e) {
10477         System.out.println("Could not write to README.txt file.");
10478       }
10479       
10480       removeDockerGshContainer = false;
10481       boolean uiPasswordCreated = false;
10482       try {
10483         commands = new ArrayList<String>();
10484         commands.add(shCommand());
10485         commands.add("-c");
10486         commands.add(uiPasswordDockerCommand.toString());
10487         commandResult = GrouperInstallerUtils.execCommand(
10488             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10489             new File("."), null, false, false, true);
10490         
10491         if (commandResult.getExitCode() == 0) {
10492           uiPasswordCreated = true;
10493         }
10494         
10495       } catch (Exception e) {}
10496       
10497       if (uiPasswordCreated == false) {
10498         System.out.println("Could not create password for grouper UI. Run the following command manually. ");
10499         System.out.println(uiPasswordDockerCommand.toString());
10500         System.out.print("Press <return> to continue ");
10501         readFromStdIn("Placeholder");
10502       } else {
10503         
10504         removeDockerGshContainer = true;
10505         StringBuilder dockerPsCommand = new StringBuilder();
10506         dockerPsCommand.append("docker ps -a --filter \"name=gsh\" --format \"{{.Status}}\" ");
10507         
10508         for (int i=0; i<100; i++) {
10509           System.out.println("Waiting for docker command to finish.");
10510           GrouperInstallerUtils.sleep(4000);
10511           
10512           commands = new ArrayList<String>();
10513           commands.add(shCommand());
10514           commands.add("-c");
10515           
10516           commands.add(dockerPsCommand.toString());
10517           
10518           CommandResult dockerPsCommandResult = GrouperInstallerUtils.execCommand(
10519               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10520               new File("."), null, false, false, true);
10521           
10522           if (dockerPsCommandResult.getExitCode() == 0) {
10523             
10524             String output = dockerPsCommandResult.getOutputText();
10525             if (output.contains("Exited")) {
10526               
10527               GrouperInstallerUtils.sleep(20000);
10528               
10529               commands = new ArrayList<String>();
10530               commands.add(shCommand());
10531               commands.add("-c");
10532               
10533               StringBuilder dockerLogsCommand = new StringBuilder();
10534               dockerLogsCommand.append("docker logs gsh ");
10535               
10536               commands.add(dockerLogsCommand.toString());
10537               
10538               CommandResult dockerLogsCommandResult = GrouperInstallerUtils.execCommand(
10539                   GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10540                   new File("."), null, false, false, true);
10541               
10542               if (dockerLogsCommandResult.getExitCode() == 0) {
10543                 String logs = dockerLogsCommandResult.getOutputText();
10544                 
10545                 File uiPasswordLogsFile = new File(path+File.separator+"docker_logs_ui_password_"+new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS").format(new Date())+".log");
10546                 GrouperInstallerUtils.fileCreate(uiPasswordLogsFile);
10547                 
10548                 try {      
10549                   Files.write(Paths.get(uiPasswordLogsFile.getAbsolutePath()), logs.getBytes(), StandardOpenOption.APPEND);
10550                 } catch (Exception e) {
10551                   System.out.println("Could not write logs to "+uiPasswordLogsFile.getAbsolutePath()+" file. ");
10552                 }
10553                 
10554                 System.out.println("docker ui password setup logs are at: "+uiPasswordLogsFile.getAbsolutePath());
10555                 
10556                 if (logs.contains("===> null\n" + 
10557                     "groovy:000> :exit") && !logs.contains("Exception") && !logs.contains("exception")) {
10558                   System.out.println("Password was created successfully.");
10559                   System.out.print("Press <return> to continue. ");
10560                   readFromStdIn("Placeholder");
10561                 } else {
10562                   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. ");
10563                   System.out.print("Press <return> to continue. ");
10564                   readFromStdIn("Placeholder");
10565                 }
10566                 
10567                 try (BufferedReader reader = new BufferedReader(new FileReader(uiPasswordLogsFile))) {
10568                   StringBuilder logContent = new StringBuilder();
10569                   String line;
10570                   int lineNumber =0;
10571                   while( (line = reader.readLine()) != null) {
10572                     logContent.append(line);
10573                     logContent.append("\n");
10574                     lineNumber++;
10575                     
10576                     if (lineNumber > 24) {
10577                       break;
10578                     }
10579                   }
10580                   System.out.println("First 25 lines of logs are below: ");
10581                   System.out.println(logContent);
10582                 } catch (Exception e) {}
10583                 
10584               } else {
10585                 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. ");
10586                 System.out.print("Press <return> to continue. ");
10587                 readFromStdIn("Placeholder");
10588               }
10589               
10590               break;
10591             } 
10592             
10593           } else {
10594             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. ");
10595             readFromStdIn("Placeholder");
10596             break;
10597           }
10598           
10599         }
10600         
10601       }
10602       
10603       if (removeDockerGshContainer) {
10604         commands = new ArrayList<String>();
10605         commands.add(shCommand());
10606         commands.add("-c");
10607         commands.add("docker rm -f gsh");
10608         
10609         boolean dockerRemovedGshContainer = false;
10610         String dockerRmCommandError = null;
10611         try {
10612           CommandResult dockerRmCommandResult = GrouperInstallerUtils.execCommand(
10613               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10614               new File("."), null, false, false, true);
10615           if (dockerRmCommandResult.getExitCode() == 0) {
10616             dockerRemovedGshContainer = true;
10617           } else if (GrouperInstallerUtils.isNotBlank(dockerRmCommandResult.getErrorText())) {
10618             dockerRmCommandError = dockerRmCommandResult.getErrorText();
10619           }
10620         } catch (Exception e) {
10621           dockerRemovedGshContainer = false;
10622         }
10623         
10624         if (dockerRemovedGshContainer) {
10625           System.out.println("Removed gsh container successfully. ");
10626         } else if (dockerRmCommandError != null) {
10627           System.out.println("Could not remove docker gsh container. Please run 'docker rm -f gsh' in another terminal window before proceeding. ");
10628         }
10629         
10630         System.out.print("Press <return> to continue ");
10631         readFromStdIn("Placeholder");
10632       }
10633         
10634       // remove createGrouperSystemPasswordUi.sh file
10635       contentToWrite = new StringBuilder();    
10636       contentToWrite.append("Delete createGrouperSystemPasswordUi.gsh file from "+path+File.separator+"slashRoot"+File.separator+"opt"+
10637           File.separator+"grouper"+File.separator+"grouperWebapp"+File.separator+"WEB-INF"+File.separator+"bin");
10638       contentToWrite.append(" because it contains password in plain text.");
10639       contentToWrite.append("\n\n");
10640       try {
10641         Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
10642       } catch (Exception e) {
10643         System.out.println("Could not write to README.txt file.");
10644       }
10645       
10646       grouperUiSystemPasswordSh.delete();
10647       
10648     }
10649     
10650     // Add passwords for grouper ws
10651     contentToWrite = new StringBuilder();    
10652     contentToWrite.append("If you want to use grouper basic authentication for grouper web services, follow the instructions below.");
10653     contentToWrite.append("\n");
10654     contentToWrite.append("Create createGrouperSystemPasswordWs.gsh file in "+path+File.separator+"slashRoot"+File.separator+"opt"+
10655         File.separator+"grouper"+File.separator+"grouperWebapp"+File.separator+"WEB-INF"+File.separator+"bin");
10656     contentToWrite.append("\n");
10657     contentToWrite.append("Add the following lines to createGrouperSystemPasswordWs.gsh. Replace placeholder with actual values below.");
10658     contentToWrite.append("\n");
10659     contentToWrite.append("GrouperSession grouperSession = GrouperSession.startRootSession();");
10660     contentToWrite.append("\n");
10661     contentToWrite.append("GrouperPasswordSave grouperPasswordSave = new GrouperPasswordSave();");
10662     contentToWrite.append("\n");
10663     contentToWrite.append("grouperPasswordSave.assignUsername(\"GrouperSystem\");");
10664     contentToWrite.append("\n");
10665     contentToWrite.append("grouperPasswordSave.assignEntityType(\"username\");");
10666     contentToWrite.append("\n");
10667     contentToWrite.append("grouperPasswordSave.assignPassword(\"<password>\");");
10668     contentToWrite.append("\n");
10669     contentToWrite.append("grouperPasswordSave.assignApplication(GrouperPassword.Application.WS);");
10670     contentToWrite.append("\n");
10671     contentToWrite.append("new Authentication().assignUserPassword(grouperPasswordSave);");
10672     contentToWrite.append("\n\n");
10673     contentToWrite.append("\n\n");
10674     
10675     try {
10676       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
10677     } catch (Exception e) {
10678       System.out.println("Could not write to README.txt file.");
10679     }
10680 //    System.out.print("Do you want to use grouper authenitcation for WS? (t|f)? [t]: ");
10681 //    boolean useGrouperAuthenticationWs = readFromStdInBoolean(true, "grouperInstaller.autorun.useBuiltInGrouperAuthenticationWS");
10682     boolean useGrouperAuthenticationWs = true;
10683     
10684     if (useGrouperAuthenticationWs) {
10685       System.out.print("Please enter the password for user 'GrouperSystem' for grouper web services: ");
10686       String wsPassword = readFromStdIn("Placeholder");
10687       
10688       while (true) {
10689         if (wsPassword == null || wsPassword.trim().length() < 4 ) {
10690           System.out.print("Invalid password. Minimum 4 characters required. Please try again: ");
10691           wsPassword = readFromStdIn("Placeholder");
10692         } else {
10693           break;
10694         }
10695       }
10696       
10697       File grouperWsSystemPasswordSh = new File(path+File.separator+"slashRoot"+File.separator+"opt"+
10698           File.separator+"grouper"+File.separator+"grouperWebapp"+File.separator+"WEB-INF"+File.separator+"bin"+File.separator+"createGrouperSystemPasswordWs.gsh");
10699       GrouperInstallerUtils.createParentDirectories(grouperWsSystemPasswordSh);
10700       boolean reuseCreateGrouperSystemPasswordWsFile = false;
10701       while(true) {
10702         if (grouperWsSystemPasswordSh.exists()) {
10703           System.out.println("createGrouperSystemPasswordWs.gsh already exists at "+grouperWsSystemPasswordSh.getParent()+" ");
10704           System.out.print("Do you want to reuse it (t|f) [t]: ");
10705           reuseCreateGrouperSystemPasswordWsFile = readFromStdInBoolean(true, "Placeholder");
10706           if (reuseCreateGrouperSystemPasswordWsFile) {
10707             System.out.println("Going to reuse existing createGrouperSystemPasswordWs.gsh file. ");
10708             break;
10709           } else {
10710             System.out.print("Delete createGrouperSystemPasswordWs.gsh and press <return> to continue ");
10711             readFromStdIn("nothing");
10712             grouperWsSystemPasswordSh = new File(path+File.separator+"slashRoot"+File.separator+"opt"+
10713                 File.separator+"grouper"+File.separator+"grouperWebapp"+File.separator+"WEB-INF"+File.separator+"bin"+File.separator+"createGrouperSystemPasswordWs.gsh");
10714             continue;
10715           }
10716         } else {
10717           GrouperInstallerUtils.fileCreate(grouperWsSystemPasswordSh);
10718           break;
10719         }
10720       }
10721       
10722       if (reuseCreateGrouperSystemPasswordWsFile == false) {
10723         StringBuilder createPasswordCommands = new StringBuilder();
10724         createPasswordCommands.append("GrouperSession grouperSession = GrouperSession.startRootSession(); \n");
10725         createPasswordCommands.append("GrouperPasswordSave grouperPasswordSave = new GrouperPasswordSave(); \n");
10726         createPasswordCommands.append("grouperPasswordSave.assignUsername(\"GrouperSystem\"); \n");
10727         createPasswordCommands.append("grouperPasswordSave.assignEntityType(\"username\"); \n");
10728         createPasswordCommands.append("grouperPasswordSave.assignPassword(\""+wsPassword+"\"); \n");
10729         createPasswordCommands.append("\n");
10730         
10731         createPasswordCommands.append("grouperPasswordSave.assignApplication(GrouperPassword.Application.WS); \n");
10732         createPasswordCommands.append("new Authentication().assignUserPassword(grouperPasswordSave); \n");
10733         
10734         GrouperInstallerUtils.writeStringToFile(grouperWsSystemPasswordSh, createPasswordCommands.toString());
10735       }
10736       
10737    // run docker command to add WS password to grouper
10738       contentToWrite = new StringBuilder();
10739       contentToWrite.append("Run the following command to add WS password to grouper.");
10740       contentToWrite.append("\n");
10741       StringBuilder wsPasswordDockerCommand = new StringBuilder();
10742       wsPasswordDockerCommand.append("docker run --detach ");
10743       wsPasswordDockerCommand.append("--mount type=bind,src=");
10744       wsPasswordDockerCommand.append(path+File.separator);
10745       wsPasswordDockerCommand.append("logs,dst=/opt/grouper/logs ");
10746       wsPasswordDockerCommand.append("--mount type=bind,src=");
10747       wsPasswordDockerCommand.append(path+File.separator);
10748       wsPasswordDockerCommand.append("slashRoot,dst=/opt/grouper/slashRoot ");
10749       wsPasswordDockerCommand.append("--name gsh ");
10750       wsPasswordDockerCommand.append("i2incommon/grouper:"+dockerImageVersion );
10751       wsPasswordDockerCommand.append(" gsh /opt/grouper/grouperWebapp/WEB-INF/bin/createGrouperSystemPasswordWs.gsh");
10752       contentToWrite.append(wsPasswordDockerCommand.toString());
10753       contentToWrite.append("\n\n");
10754       contentToWrite.append("\n\n");
10755       try {
10756         Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
10757       } catch (Exception e) {
10758         System.out.println("Could not write to README.txt file.");
10759       }
10760       
10761       boolean wsPasswordCreated = false;
10762       removeDockerGshContainer = false;
10763       try {
10764         commands = new ArrayList<String>();
10765         commands.add(shCommand());
10766         commands.add("-c");
10767         
10768         commands.add(wsPasswordDockerCommand.toString());
10769         
10770         commandResult = GrouperInstallerUtils.execCommand(
10771             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10772             new File("."), null, false, false, true);
10773         
10774         if (commandResult.getExitCode() == 0) {
10775           wsPasswordCreated = true;
10776         }
10777         
10778       } catch (Exception e) {}
10779       
10780       if (wsPasswordCreated == false) {
10781         System.out.println("Could not create password for WS. Run the following command manually.");
10782         System.out.println(wsPasswordDockerCommand.toString());
10783         System.out.print("Press <return> to continue");
10784         readFromStdIn("Placeholder");
10785       } else {
10786 
10787         removeDockerGshContainer = true;
10788         StringBuilder dockerPsCommand = new StringBuilder();
10789         dockerPsCommand.append("docker ps -a --filter \"name=gsh\" --format \"{{.Status}}\" ");
10790         
10791         for (int i=0; i<100; i++) {
10792           System.out.println("Waiting for docker command to finish.");
10793           GrouperInstallerUtils.sleep(4000);
10794           
10795           commands = new ArrayList<String>();
10796           commands.add(shCommand());
10797           commands.add("-c");
10798           
10799           commands.add(dockerPsCommand.toString());
10800           
10801           CommandResult dockerPsCommandResult = GrouperInstallerUtils.execCommand(
10802               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10803               new File("."), null, false, false, true);
10804           
10805           if (dockerPsCommandResult.getExitCode() == 0) {
10806             
10807             String output = dockerPsCommandResult.getOutputText();
10808             if (output.contains("Exited")) {
10809               
10810               GrouperInstallerUtils.sleep(20000);
10811               
10812               commands = new ArrayList<String>();
10813               commands.add(shCommand());
10814               commands.add("-c");
10815               
10816               StringBuilder dockerLogsCommand = new StringBuilder();
10817               dockerLogsCommand.append("docker logs gsh ");
10818               
10819               commands.add(dockerLogsCommand.toString());
10820               
10821               CommandResult dockerLogsCommandResult = GrouperInstallerUtils.execCommand(
10822                   GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10823                   new File("."), null, false, false, true);
10824               
10825               if (dockerLogsCommandResult.getExitCode() == 0) {
10826                 String logs = dockerLogsCommandResult.getOutputText();
10827                 
10828                 File wsPasswordLogsFile = new File(path+File.separator+"docker_logs_ws_password_"+new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS").format(new Date())+".log");
10829                 GrouperInstallerUtils.fileCreate(wsPasswordLogsFile);
10830                 
10831                 try {      
10832                   Files.write(Paths.get(wsPasswordLogsFile.getAbsolutePath()), logs.getBytes(), StandardOpenOption.APPEND);
10833                 } catch (Exception e) {
10834                   System.out.println("Could not write logs to "+wsPasswordLogsFile.getAbsolutePath()+" file. ");
10835                 }
10836                 
10837                 System.out.println("docker ws password setup logs are at: "+wsPasswordLogsFile.getAbsolutePath());
10838                 
10839                 if (logs.contains("===> null\n" + 
10840                     "groovy:000> :exit") && !logs.contains("Exception") && !logs.contains("exception")) {
10841                   System.out.println("Password was created successfully.");
10842                   System.out.print("Press <return> to continue. ");
10843                   readFromStdIn("Placeholder");
10844                 } else {
10845                   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. ");
10846                   System.out.print("Press <return> to continue. ");
10847                   readFromStdIn("Placeholder");
10848                 }
10849                 
10850                 try (BufferedReader reader = new BufferedReader(new FileReader(wsPasswordLogsFile))) {
10851                   StringBuilder logContent = new StringBuilder();
10852                   String line;
10853                   int lineNumber = 0;
10854                   while( (line = reader.readLine()) != null) {
10855                     logContent.append(line);
10856                     logContent.append("\n");
10857                     lineNumber++;
10858                     
10859                     if (lineNumber > 24) {
10860                       break;
10861                     }
10862                   }
10863                   System.out.println("First 25 lines of logs are below: ");
10864                   System.out.println(logContent);
10865                 } catch (Exception e) {}
10866                 
10867               } else {
10868                 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. ");
10869                 System.out.print("Press <return> to continue. ");
10870                 readFromStdIn("Placeholder");
10871               }
10872               
10873               break;
10874             } 
10875             
10876           } else {
10877             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. ");
10878             readFromStdIn("Placeholder");
10879             break;
10880           }
10881           
10882         }
10883       
10884       }
10885       
10886       if (removeDockerGshContainer) {
10887         commands = new ArrayList<String>();
10888         commands.add(shCommand());
10889         commands.add("-c");
10890         commands.add("docker rm -f gsh");
10891         
10892         boolean dockerRemovedGshContainer = false;
10893         String dockerRmCommandError = null;
10894         try {
10895           CommandResult dockerRmCommandResult = GrouperInstallerUtils.execCommand(
10896               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10897               new File("."), null, false, false, true);
10898           if (dockerRmCommandResult.getExitCode() == 0) {
10899             dockerRemovedGshContainer = true;
10900           } else if (GrouperInstallerUtils.isNotBlank(dockerRmCommandResult.getErrorText())) {
10901             dockerRmCommandError = dockerRmCommandResult.getErrorText();
10902           }
10903         } catch (Exception e) {
10904           dockerRemovedGshContainer = false;
10905         }
10906         
10907         if (dockerRemovedGshContainer) {
10908           System.out.println("Removed 'gsh' container successfully. ");
10909         } else if (dockerRmCommandError != null) {
10910           System.out.println("Could not remove docker gsh container. Please run 'docker rm -f gsh' in another terminal window before proceeding. ");
10911         }
10912         
10913         System.out.print("Press <return> to continue ");
10914         readFromStdIn("Placeholder");
10915       }
10916       
10917       // remove createGrouperSystemPasswordWs.sh file
10918       contentToWrite = new StringBuilder();    
10919       contentToWrite.append("Delete createGrouperSystemPasswordWs.gsh file from "+path+File.separator+"slashRoot"+File.separator+"opt"+
10920           File.separator+"grouper"+File.separator+"grouperWebapp"+File.separator+"WEB-INF"+File.separator+"bin");
10921       contentToWrite.append(" because it contains password in plain text.");
10922       contentToWrite.append("\n\n");
10923       try {
10924         Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
10925       } catch (Exception e) {
10926         System.out.println("Could not write to README.txt file.");
10927       }
10928       
10929       grouperWsSystemPasswordSh.delete();
10930       
10931     }
10932     
10933     Integer portNumberInt = 8080;
10934     System.out.print("Please provide the host HTTP (not HTTPS) port number to run the UI container (default 8080): ");
10935     while (true) {
10936       String portNumber = readFromStdIn("Placeholder");
10937       if (GrouperInstallerUtils.isNotBlank(portNumber)) {
10938         try {
10939           portNumberInt = Integer.valueOf(portNumber);
10940           break;
10941         } catch (Exception e) {
10942           System.out.print("Invalid port number '"+portNumber+"'. Please try again. (default 8080): ");
10943         }
10944       } else {
10945         break;
10946       }
10947     }
10948     
10949     // start the container
10950     contentToWrite = new StringBuilder();
10951     contentToWrite.append("Run the following command to start the container.");
10952     contentToWrite.append("\n");
10953     StringBuilder grouperContainerStartDockerCommand = new StringBuilder();
10954     grouperContainerStartDockerCommand.append("docker run");
10955     //grouperContainerStartDockerCommand.append(" -e GROUPER_UI='true' -e GROUPER_WS='true' -e GROUPER_DAEMON='true' -e GROUPER_SCIM='true' ");
10956     //grouperContainerStartDockerCommand.append("-e RUN_APACHE='true' -e RUN_SHIB_SP='false' -e RUN_TOMEE='true' ");
10957     grouperContainerStartDockerCommand.append(" --detach --publish "+portNumberInt.toString()+":8080 ");
10958     grouperContainerStartDockerCommand.append("--mount type=bind,src=");
10959     grouperContainerStartDockerCommand.append(path+File.separator);
10960     grouperContainerStartDockerCommand.append("logs,dst=/opt/grouper/logs ");
10961     grouperContainerStartDockerCommand.append("--mount type=bind,src=");
10962     grouperContainerStartDockerCommand.append(path+File.separator);
10963     grouperContainerStartDockerCommand.append("slashRoot,dst=/opt/grouper/slashRoot ");
10964     grouperContainerStartDockerCommand.append("--restart always --name grouper-ui ");
10965     grouperContainerStartDockerCommand.append("i2incommon/grouper:"+dockerImageVersion );
10966     grouperContainerStartDockerCommand.append(" ui" );
10967     contentToWrite.append(grouperContainerStartDockerCommand.toString());
10968     contentToWrite.append("\n\n");
10969     contentToWrite.append("\n\n");
10970     try {
10971       Files.write(Paths.get(readmeFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
10972     } catch (Exception e) {
10973       System.out.println("Could not write to README.txt file.");
10974     }
10975     
10976     
10977     System.out.print("Press <return> to start the UI container: ");
10978     readFromStdIn("Placeholder");
10979     boolean dockerGrouperContainerStarted = false;
10980     try {
10981       commands = new ArrayList<String>();
10982       commands.add(shCommand());
10983       commands.add("-c");
10984       
10985       commands.add(grouperContainerStartDockerCommand.toString());
10986       
10987       commandResult = GrouperInstallerUtils.execCommand(
10988           GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10989           new File("."), null, false, false, true);
10990       
10991       if (commandResult.getExitCode() == 0) {
10992         dockerGrouperContainerStarted = true;
10993       }
10994       
10995     } catch (Exception e) {}
10996     
10997     if (dockerGrouperContainerStarted == false) {
10998       System.out.println("Could not start grouper container. Run the following command manually.");
10999       System.out.println(grouperContainerStartDockerCommand.toString());
11000     } else {
11001       System.out.println("Inside container grouper runs on port 8080");
11002     }
11003     
11004     System.out.println("Logs are at: "+new File(path+File.separator+"logs").getAbsolutePath());
11005     System.out.println("Command history and documentation are in "+ readmeFile.getAbsolutePath());
11006     System.out.println("Grouper UI is running at : http://localhost:"+portNumberInt+"/grouper/");
11007     System.out.print("Press <return> to exit ");
11008     readFromStdIn("Placeholder");
11009   }
11010     
11011   /**
11012    * 
11013    */
11014   private void mainBuildContainerLogic() {
11015     
11016     //Find out the working directory.  This ends in a file separator
11017     this.grouperTarballDirectoryString = grouperUpgradeTempDirectory();
11018     
11019     this.version = GrouperInstallerUtils.propertiesValue("grouper.version", false);
11020     
11021     if (GrouperInstallerUtils.isBlank(this.version)) {
11022       this.version = getClass().getPackage().getImplementationVersion();
11023     }
11024     
11025     System.out.println("Installing grouper version: " + this.version);
11026     
11027     downloadAndUnzipMaven();
11028     
11029     //####################################
11030     //download apache tomee
11031     File tomeeDir = downloadTomee();
11032     File unzippedTomeeFile = unzip(tomeeDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
11033     this.untarredTomeeDir = untar(unzippedTomeeFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc", null);
11034     
11035     // download grouper tag from github
11036     File grouperSourceCodeDir = downloadGrouperSourceTagFromGithub();
11037     File unzippedGrouperSourceCodeFile = unzip(grouperSourceCodeDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
11038     File untarredGrouperSourceCodeDir = untar(unzippedGrouperSourceCodeFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc", null);
11039     
11040     
11041     String grouperUntarredReleaseDir = untarredGrouperSourceCodeDir.getAbsolutePath().substring(0, untarredGrouperSourceCodeDir.getAbsolutePath().lastIndexOf(File.separator));
11042     grouperUntarredReleaseDir = grouperUntarredReleaseDir + File.separator + "grouper-" + untarredGrouperSourceCodeDir.getName() ;
11043     
11044     // now create an output directory (webapp) and tomee
11045     String containerDirString = grouperContainerDirectory();
11046     File containerTomeeDir = new File(containerDirString + "tomee");
11047     containerTomeeDir.mkdirs();
11048     
11049     File webAppDir = new File(containerDirString + "webapp");
11050     webAppDir.mkdirs();
11051         
11052     // let's start with grouper-ui/webapp directory. We will copy everything else later
11053     GrouperInstallerUtils.copyDirectory(new File(grouperUntarredReleaseDir + File.separator + "grouper-ui" + File.separator+"webapp"), webAppDir);
11054     
11055     File webInfDir = new File(webAppDir+File.separator+"WEB-INF");
11056     webInfDir.mkdirs(); // should already be there
11057     File webInfConfDir = new File(webInfDir+File.separator+"conf");
11058     webInfConfDir.mkdirs();
11059     
11060     File libDir = new File(webInfDir+File.separator+"lib");
11061     libDir.mkdirs();
11062     
11063     File libUiAndDaemonDir = new File(webInfDir+File.separator+"libUiAndDaemon");
11064     libUiAndDaemonDir.mkdirs();
11065     
11066     File libWsDir = new File(webInfDir+File.separator+"libWs");
11067     libWsDir.mkdirs();
11068     
11069     File libScimDir = new File(webInfDir+File.separator+"libScim");
11070     libScimDir.mkdirs();
11071     
11072     File modulesDir = new File(webInfDir+File.separator+"modules");
11073     modulesDir.mkdirs();
11074     File servicesDir = new File(webInfDir+File.separator+"services");
11075     servicesDir.mkdirs();
11076     File classesDir = new File(webInfDir+File.separator+"classes");
11077     classesDir.mkdirs();
11078     File binDir = new File(webInfDir+File.separator+"bin");
11079     binDir.mkdirs();
11080     
11081     // go in grouper-container and run mvn dependency:copy-dependencies
11082     Map<File, File> projectDirToOutputLibDir = new LinkedHashMap<File, File>();
11083     projectDirToOutputLibDir.put(new File(grouperUntarredReleaseDir + File.separator + "grouper-container" + File.separator + "grouper-api-container"), libDir);
11084     projectDirToOutputLibDir.put(new File(grouperUntarredReleaseDir + File.separator + "grouper-container" + File.separator + "grouper-uiDaemon-container"), libUiAndDaemonDir);
11085     projectDirToOutputLibDir.put(new File(grouperUntarredReleaseDir + File.separator + "grouper-container" + File.separator + "grouper-ws-container"), libWsDir);
11086     projectDirToOutputLibDir.put(new File(grouperUntarredReleaseDir + File.separator + "grouper-container" + File.separator + "grouper-scim-container"), libScimDir);
11087     
11088     List<String> commands = new ArrayList<String>();
11089     addMavenCommands(commands);
11090     
11091     commands.add("-DincludeScope=runtime");
11092     commands.add("-Dgrouper.version="+this.version);
11093  
11094     commands.add("dependency:copy-dependencies");
11095           
11096     for (File file: projectDirToOutputLibDir.keySet()) {
11097       System.out.println("\n##################################");
11098       System.out.println("Downloading third party jars for "+ file.getName()+" with command:\n" 
11099           + convertCommandsIntoCommand(commands) + "\n");
11100       
11101       CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
11102           true, true, null, new File(file.getAbsolutePath()), null, true);
11103       
11104       if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
11105         System.out.println("stderr: " + commandResult.getErrorText());
11106       }
11107       if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
11108         System.out.println("stdout: " + commandResult.getOutputText());
11109       }
11110     }
11111     
11112     // now copy all dependency jars into corresponding container/webapp/WEB-INF/lib{""|UiAndDaemon|Ws|Scim|}
11113     try {
11114       Set<String> allGrouperApiJars = new HashSet<String>();
11115       for (File file: projectDirToOutputLibDir.keySet()) {        
11116         File jarsDirectory = new File(file.getAbsolutePath()+File.separator+"target"+File.separator+"dependency");
11117         
11118         if (allGrouperApiJars.size() == 0) { // we are relying on the first item in projectDirToOutputLibDir being the grouper api 
11119           for (File jarFile: findAllLibraryFiles(jarsDirectory.getAbsolutePath())) {
11120             allGrouperApiJars.add(jarFile.getName());
11121           }
11122           // copy the grouper api jars into container/webapp/WEB-INF/lib
11123           GrouperInstallerUtils.copyDirectory(jarsDirectory, projectDirToOutputLibDir.get(file), null, true);
11124           continue;
11125         }
11126         
11127         // make sure the jar file is already not there
11128         for (File jarFile: findAllLibraryFiles(jarsDirectory.getAbsolutePath())) {
11129           if (allGrouperApiJars.contains(jarFile.getName()) == false) {
11130             File destFile = new File(projectDirToOutputLibDir.get(file).getAbsolutePath() + File.separator + jarFile.getName());
11131             GrouperInstallerUtils.copyFile(jarFile, destFile);
11132           }
11133         }
11134         
11135       }
11136     } catch (Exception e) {
11137       throw new RuntimeException("Could not copy jars from dependency directories ", e);
11138     }
11139     
11140     // delete all grouper snapshots jars from WEB-INF/lib
11141 //    List<File> allLibraryJars = findAllLibraryFiles(libDir.getAbsolutePath());
11142 //    
11143 //    for (File file: allLibraryJars) {
11144 //      if (file.getName().contains("grouper") && file.getName().contains("SNAPSHOT")) {
11145 //        GrouperInstallerUtils.fileDelete(file);
11146 //      }
11147 //    }
11148     
11149     // now copy grouper/conf, grouper-ws/conf, grouper-ui/conf and grouperClient/conf to classesDir
11150     
11151     List<File> projectsToGetConfFrom = new ArrayList<File>();
11152     projectsToGetConfFrom.add(new File(grouperUntarredReleaseDir + File.separator + "grouper"));
11153     projectsToGetConfFrom.add(new File(grouperUntarredReleaseDir + File.separator + "grouper-ws"+File.separator+"grouper-ws"));
11154     projectsToGetConfFrom.add(new File(grouperUntarredReleaseDir + File.separator + "grouper-ui"));
11155     projectsToGetConfFrom.add(new File(grouperUntarredReleaseDir + File.separator + "grouper-misc"+File.separator+"grouperClient"));
11156     
11157     try {
11158       for (File file: projectsToGetConfFrom) {
11159         File confDir = new File(file.getAbsolutePath()+File.separator+"conf");
11160         if (confDir.exists()) {
11161           GrouperInstallerUtils.copyDirectory(confDir, classesDir, null, true);
11162         }
11163       }
11164     } catch (Exception e) {
11165       throw new RuntimeException("Could not copy files from conf directory to classes directory", e);
11166     }
11167     
11168     // now copy all misc/*.example.properties into classes directory and rename them to not have example in the filename
11169     
11170     File miscDir = new File(grouperUntarredReleaseDir + File.separator + "grouper" + File.separator + "misc");
11171     File[] filesToBeCopied = miscDir.listFiles(new FilenameFilter() {
11172       
11173       @Override
11174       public boolean accept(File dir, String name) {
11175         return name.endsWith("example.properties") && !name.equals("grouper.text.en.us.example.properties");
11176       }
11177     });
11178     
11179     for (File miscFileToBeCopied: filesToBeCopied) {
11180       String newFileName = miscFileToBeCopied.getName().replace(".example", "");
11181       File destFile = new File(classesDir.getAbsolutePath() + File.separator + newFileName);
11182       GrouperInstallerUtils.copyFile(miscFileToBeCopied, destFile);
11183     }
11184     
11185     // now copy grouper.text.en.us.example.properties into classes/grouperText
11186     File textFileToBeCopied = new File(grouperUntarredReleaseDir + File.separator + "grouper" + 
11187         File.separator + "misc" + File.separator + "grouper.text.en.us.example.properties");
11188     
11189     File textDestFile = new File(classesDir.getAbsolutePath() + File.separator + "grouperText" + 
11190         File.separator + "grouper.text.en.us.properties");
11191     GrouperInstallerUtils.copyFile(textFileToBeCopied, textDestFile);
11192     
11193     
11194     // now copy all grouper-ws/webapp specific files into outputDir/webapp
11195     File grouperWsWebinfDir = new File(grouperUntarredReleaseDir+File.separator+"grouper-ws"+File.separator+
11196         "grouper-ws"+File.separator+"webapp"+File.separator+"WEB-INF");
11197     
11198     GrouperInstallerUtils.copyDirectory(new File(grouperWsWebinfDir.getAbsolutePath()+File.separator+"modules"), modulesDir);
11199     GrouperInstallerUtils.copyDirectory(new File(grouperWsWebinfDir.getAbsolutePath()+File.separator+"services"), servicesDir);
11200     GrouperInstallerUtils.copyDirectory(new File(grouperWsWebinfDir.getAbsolutePath()+File.separator+"conf"), webInfConfDir);
11201     
11202     // now copy grouper/bin contents into outputDir/webapp/WEB-INF/bin
11203     GrouperInstallerUtils.copyDirectory(new File(grouperUntarredReleaseDir + File.separator + "grouper" + File.separator + "bin"), binDir);
11204     
11205     // make gsh.sh executable
11206     commands = GrouperInstallerUtils.toList("chmod", "+x", binDir.getAbsolutePath() + File.separator + "gsh.sh");
11207 
11208     System.out.println("Making sure gsh.sh is executable with command: " + convertCommandsIntoCommand(commands) + "\n");
11209 
11210     CommandResult commandResult = GrouperInstallerUtils.execCommand(
11211         GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
11212         binDir, null, true);
11213     
11214     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
11215       System.out.println("stderr: " + commandResult.getErrorText());
11216     }
11217     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
11218       System.out.println("stdout: " + commandResult.getOutputText());
11219     }
11220     
11221     // now download all the grouper project jars
11222     downloadGrouperJarsIntoLibDirectory(webInfDir);
11223     
11224     // delete slf4j related jars from all the lib dirs
11225     deleteJarsFromLibDirs(webInfDir);
11226     
11227     // take care of conflicting jars
11228     reportOnConflictingJars(libDir.getAbsolutePath());
11229     reportOnConflictingJars(libUiAndDaemonDir.getAbsolutePath());
11230     reportOnConflictingJars(libWsDir.getAbsolutePath());
11231     reportOnConflictingJars(libScimDir.getAbsolutePath());
11232     
11233     // copy apache-tomee-webprofile-x.y.z to tomee
11234     // why can't uncompressed directory has the same name??? :((
11235     File tomeeUntarredDir = new File(this.grouperTarballDirectoryString + File.separator + "apache-tomee-webprofile-" + TOMEE_VERSION);
11236     try {      
11237       GrouperInstallerUtils.copyDirectory(tomeeUntarredDir, containerTomeeDir, null, true);
11238     } catch (Exception e) {
11239       throw new RuntimeException("Could not copy untarred tomee into container/tomee", e);
11240     }
11241     
11242     // put logging related jars in tomee/bin directory
11243     File tomeeBinDir = new File(containerTomeeDir + File.separator + "bin");
11244     
11245     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", "");
11246     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", "");
11247     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", "");
11248     
11249     // put slf4j in lib dir
11250     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", "");
11251     
11252     // point tomee to downloaded webpp
11253     configureTomeeGrouperUberWebapp(containerTomeeDir, webAppDir);
11254     
11255     // copy slf4j from tomee/lib to web-inf/lib
11256     File tomeeLibDir = new File(containerTomeeDir + File.separator + "lib");
11257     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"));
11258     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"));
11259     
11260   }
11261   
11262   /**
11263    * 
11264    */
11265   //TODO delete everything related to install
11266   @Deprecated
11267   private void mainInstallLogic() {
11268     
11269     //####################################
11270     //Find out what directory to install to.  This ends in a file separator
11271     this.grouperInstallDirectoryString = grouperInstallDirectory();
11272 
11273     //Find out what directory to upgrade to.  This ends in a file separator
11274     this.grouperTarballDirectoryString = grouperUpgradeTempDirectory();
11275 
11276     //####################################
11277     //get default ip address
11278     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]: ");
11279     this.defaultIpAddress = readFromStdIn("grouperInstaller.autorun.defaultIpAddressForPorts");
11280     
11281     if (GrouperInstallerUtils.isBlank(this.defaultIpAddress)) {
11282       this.defaultIpAddress = "0.0.0.0";
11283     }
11284 
11285     if (!GrouperInstallerUtils.equals("0.0.0.0", this.defaultIpAddress)) {
11286       System.out.println("Note, you will probably need to change the hsql IP address, and tomcat server.xml IP addresses...");
11287     }
11288     
11289     //####################################
11290     //System.out.println("Grouper install directory is: " + grouperInstallDirectoryFile.getAbsolutePath());
11291 
11292     this.version = GrouperInstallerUtils.propertiesValue("grouper.version", true);
11293     System.out.println("Installing grouper version: " + this.version);
11294     //see if it is already downloaded
11295     
11296     //download api and set executable and dos2unix etc
11297     downloadAndConfigureApi();
11298 
11299     //####################################
11300     //ask about database
11301 
11302     File localGrouperHibernatePropertiesFile = new File(this.untarredApiDir.getAbsoluteFile() + File.separator + "conf" 
11303         + File.separator + "grouper.hibernate.properties");
11304 
11305     Properties grouperHibernateProperties = GrouperInstallerUtils.propertiesFromFile(localGrouperHibernatePropertiesFile);
11306 
11307     this.dbUrl = GrouperInstallerUtils.defaultString(grouperHibernateProperties.getProperty("hibernate.connection.url"), "jdbc:hsqldb:hsql://localhost:9001/grouper");
11308     this.dbUser = GrouperInstallerUtils.defaultString(grouperHibernateProperties.getProperty("hibernate.connection.username"));
11309     this.dbPass = GrouperInstallerUtils.defaultString(grouperHibernateProperties.getProperty("hibernate.connection.password"));
11310 
11311     boolean useHsqldb = false;
11312 
11313     if (this.dbUrl.contains(":hsqldb:")) {
11314       System.out.print("Do you want to use the default and included hsqldb database (t|f)? [t]: ");
11315       useHsqldb = readFromStdInBoolean(true, "grouperInstaller.autorun.useBuiltInHsql");
11316     }
11317 
11318     if (!useHsqldb) {
11319 
11320       System.out.println("\n##################################\n");
11321       System.out.println("Example mysql URL: jdbc:mysql://localhost:3306/grouper");
11322       System.out.println("Example oracle URL: jdbc:oracle:thin:@server.school.edu:1521:sid");
11323       System.out.println("Example hsqldb URL: jdbc:hsqldb:hsql://localhost:9001/grouper");
11324       System.out.println("Example postgres URL: jdbc:postgresql://localhost:5432/database");
11325       System.out.println("Example mssql URL: jdbc:sqlserver://localhost:3280;databaseName=grouper");
11326       System.out.print("\nEnter the database URL [" + this.dbUrl + "]: ");
11327       String newDbUrl = readFromStdIn("grouperInstaller.autorun.dbUrl");
11328       if (!GrouperInstallerUtils.isBlank(newDbUrl)) {
11329         this.dbUrl = newDbUrl;
11330         if (newDbUrl.contains("postgresql") || newDbUrl.contains("sqlserver")) {
11331           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");
11332           for (int i=0;i<3;i++) {
11333             System.out.print("Ready to continue? (t|f)? [t] ");
11334             boolean shouldContinue = readFromStdInBoolean(true, "grouperInstaller.autorun.dbContinueAfterChangeSourcesXmlForPostgresSqlServer");
11335             if (shouldContinue) {
11336               break;
11337             }
11338           }
11339         }
11340       }
11341       System.out.print("Database user [" + this.dbUser + "]: ");
11342       String newDbUser = readFromStdIn("grouperInstaller.autorun.dbUser");
11343       if (!GrouperInstallerUtils.isBlank(newDbUser)) {
11344         this.dbUser = newDbUser;
11345       }
11346       System.out.print("Database password (note, you aren't setting the pass here, you are using an existing pass, this will be echoed back) [" 
11347           + GrouperInstallerUtils.defaultIfEmpty(this.dbPass, "<blank>") + "]: ");
11348       String newDbPass = readFromStdIn("grouperInstaller.autorun.dbPass");
11349       if (!GrouperInstallerUtils.isBlank(newDbPass)) {
11350         this.dbPass = newDbPass;
11351       }
11352     }
11353 
11354     this.giDbUtils = new GiDbUtils(this.dbUrl, this.dbUser, this.dbPass);
11355     this.giDbUtils.registerDriverOnce(this.grouperInstallDirectoryString);
11356 
11357     //####################################
11358     //change the config file
11359     //get the config file
11360 
11361     //lets edit the three properties:
11362     System.out.println("Editing " + localGrouperHibernatePropertiesFile.getAbsolutePath() + ": ");
11363     editPropertiesFile(localGrouperHibernatePropertiesFile, "hibernate.connection.url", this.dbUrl, false);
11364     editPropertiesFile(localGrouperHibernatePropertiesFile, "hibernate.connection.username", this.dbUser, false);
11365     editPropertiesFile(localGrouperHibernatePropertiesFile, "hibernate.connection.password", this.dbPass, false);
11366 
11367     //####################################
11368     //check to see if listening on port?
11369 
11370     //#####################################
11371     //add driver to classpath
11372     //note, we are note really doing this now, we are using drivers already on classpath since this doesnt work
11373     //this.addDriverJarToClasspath();
11374 
11375     //####################################
11376     //start database if needed (check on port?  ask to change port?)
11377     if (this.dbUrl.contains("hsqldb")) {
11378       //C:\mchyzer\grouper\trunk\grouper-installer\grouper.apiBinary-2.1.0
11379       startHsqlDb(true);
11380     }
11381     
11382     //####################################
11383     //check connection to database
11384     checkDatabaseConnection();
11385     
11386     //####################################
11387     // patch the API
11388     this.upgradeExistingApplicationDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath());
11389     this.upgradeExistingClassesDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath())
11390         + "conf" + File.separator;
11391     this.upgradeExistingLibDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath())
11392         + "lib" + File.separator;
11393     this.upgradeExistingBinDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath())
11394             + "bin" + File.separator;
11395     patchApi();
11396 
11397     //make sure log4j is debugging sql statements
11398     log4jDebugSql(this.upgradeExistingClassesDirectoryString + "log4j.properties");
11399     
11400     //####################################
11401     //ask then init the DB
11402     initDb();
11403     addQuickstartSubjects();
11404     addQuickstartData();
11405     
11406     //####################################
11407     //download and configure ui
11408     System.out.print("Do you want to install the user interface (t|f)? [t]: ");
11409     boolean installUi = readFromStdInBoolean(true, "grouperInstaller.autorun.installUi");
11410     if (installUi) {
11411       downloadAndConfigureUi();
11412     }
11413     
11414     //####################################
11415     //get ant
11416     downloadAndUnzipAnt();
11417     
11418     //####################################
11419     //look for or ask or download tomcat
11420     File tomcatDir = downloadTomcat();
11421     File unzippedTomcatFile = unzip(tomcatDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
11422     this.untarredTomcatDir = untar(unzippedTomcatFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc", 
11423         new File(this.grouperInstallDirectoryString));
11424 
11425     //####################################
11426     //ask for tomcat port
11427     configureTomcat();
11428 
11429     File apiPatchStatusFile = new File(GrouperInstallerUtils.fileAddLastSlashIfNotExists(
11430         this.untarredApiDir.getAbsolutePath()) + "grouperPatchStatus.properties");
11431 
11432     //####################################
11433     //build UI
11434     if (installUi) {
11435 
11436       buildUi(true);
11437 
11438       //####################################
11439       //configureTomcatUiWebapp
11440       configureTomcatUiWebapp();
11441   
11442       //####################################
11443       //copy api patch level to ui
11444       File uiPatchStatusFile = new File(GrouperInstallerUtils.fileAddLastSlashIfNotExists(
11445           this.grouperUiBuildToDirName()) + "WEB-INF" + File.separator + "grouperPatchStatus.properties");
11446       System.out.println("Copying applied API patch status to UI:");
11447       System.out.println("  - from: "  + apiPatchStatusFile.getAbsolutePath());
11448       System.out.println("  - to: "  + uiPatchStatusFile.getAbsolutePath());
11449       GrouperInstallerMergePatchFiles.mergePatchFiles(
11450           apiPatchStatusFile, uiPatchStatusFile, true);
11451 
11452 
11453       //####################################
11454       // patch the ui
11455       this.upgradeExistingApplicationDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperUiBuildToDirName());
11456       this.upgradeExistingClassesDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperUiBuildToDirName())
11457           + "WEB-INF" + File.separator + "classes" + File.separator ;
11458       this.upgradeExistingLibDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperUiBuildToDirName())
11459           + "WEB-INF" + File.separator + "lib" + File.separator;
11460       this.upgradeExistingBinDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperUiBuildToDirName())
11461           + "WEB-INF" + File.separator + "bin" + File.separator ;
11462 
11463       //####################################
11464       // temp fix for 2.4 full install - the api and ui /bin dirs differ in 2.4; this step
11465       // will sync by renaming ui 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.grouperUiBuildToDirName()) + "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_UI_"
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 UI /bin directories...");
11475       syncFilesInDirWithBackup(apiBinSource, targetBinSouce, filesToCopyFromApiBin);
11476       this.grouperBaseBakDir = null;
11477 
11478       this.patchUi();
11479     }
11480     
11481     //####################################
11482     //set the GrouperSystem password
11483     tomcatConfigureGrouperSystem();
11484 
11485     if (installUi) {
11486       //####################################
11487       //bounce tomcat
11488       tomcatBounce("restart");
11489       
11490       //####################################
11491       //tell user to go to url
11492       System.out.println("##################################\n");
11493       System.out.println("Go here for the Grouper UI (change hostname if on different host): http://localhost:" + this.tomcatHttpPort + "/" + this.tomcatUiPath + "/");
11494       System.out.println("\n##################################\n");
11495     }
11496     
11497     System.out.print("Do you want to install web services (t|f)? [t]: ");
11498     boolean installWs = readFromStdInBoolean(true, "grouperInstaller.autorun.installWs");
11499     
11500     if (installWs) {
11501       this.downloadAndUntarWs();
11502       
11503       //#################################### 
11504       //configure Ws
11505       this.configureWs();
11506       
11507       //####################################
11508       //build WS
11509       buildWs(true);
11510       
11511       //####################################
11512       //copy to tomcat
11513       configureTomcatWsWebapp();
11514   
11515       //####################################
11516       //copy api patch level to ui
11517       File wsPatchStatusFile = new File(GrouperInstallerUtils.fileAddLastSlashIfNotExists(
11518           this.grouperWsBuildToDirName()) + "WEB-INF" + File.separator + "grouperPatchStatus.properties");
11519       System.out.println("Copying applied API patch status to WS:");
11520       System.out.println("  - from: "  + apiPatchStatusFile.getAbsolutePath());
11521       System.out.println("  - to: "  + wsPatchStatusFile.getAbsolutePath());
11522       GrouperInstallerMergePatchFiles.mergePatchFiles(
11523           apiPatchStatusFile, wsPatchStatusFile, true);
11524 
11525       //####################################
11526       // patch the ws
11527       this.upgradeExistingApplicationDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperWsBuildToDirName());
11528       this.upgradeExistingClassesDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperWsBuildToDirName())
11529           + "WEB-INF" + File.separator + "classes" + File.separator ;
11530       this.upgradeExistingLibDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperWsBuildToDirName())
11531           + "WEB-INF" + File.separator + "lib" + File.separator;
11532       this.upgradeExistingBinDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperWsBuildToDirName())
11533               + "WEB-INF" + File.separator + "bin" + File.separator ;
11534 
11535       //####################################
11536       // temp fix for 2.4 full install - the api and ws /bin dirs differ in 2.4; this step
11537       // will sync by renaming ws files that differ and then copy the api versions
11538       String apiBinSource = GrouperInstallerUtils.fileAddLastSlashIfNotExists(
11539               this.untarredApiDir.getAbsolutePath()) + "bin" + File.separator;
11540       String targetBinSouce = GrouperInstallerUtils.fileAddLastSlashIfNotExists(
11541               this.grouperWsBuildToDirName()) + "WEB-INF" + File.separator + "bin" + File.separator;
11542       String[] filesToCopyFromApiBin = new String[]{"gsh.sh", "gsh.bat", "gsh", "README.txt", "setenv.example.bat", "setenv.example.sh"};
11543       this.grouperBaseBakDir = this.grouperTarballDirectoryString + "bak_WS_"
11544               + new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS").format(new Date()) + File.separator;
11545 
11546       System.out.println("Reconciling differences between API and WS /bin directories...");
11547       syncFilesInDirWithBackup(apiBinSource, targetBinSouce, filesToCopyFromApiBin);
11548       this.grouperBaseBakDir = null;
11549 
11550       this.patchWs();
11551   
11552       //####################################
11553       //bounce tomcat
11554       tomcatBounce("restart");
11555   
11556       //####################################
11557       //tell user to go to url
11558       System.out.println("This is the Grouper WS URL (change hostname if on different host): http://localhost:" + this.tomcatHttpPort + "/" + this.tomcatWsPath + "/");
11559     }
11560     
11561     System.out.print("Do you want to install the web services client (t|f)? [t]: ");
11562     boolean installClient = readFromStdInBoolean(true, "grouperInstaller.autorun.installClient");
11563     
11564     if (installClient) {
11565       //download and build client
11566       this.downloadAndBuildClient();
11567   
11568       //####################################
11569       //configure where WS is
11570       this.configureClient();
11571       
11572       if (installWs) {
11573         //####################################
11574         //add grouper system to WS group
11575         this.addGrouperSystemWsGroup();
11576         
11577         //####################################
11578         //run a client command
11579         this.runClientCommand();
11580       }
11581     }
11582 
11583     //####################################
11584     //install pspng
11585     System.out.print("Do you want to install the provisioning service provider next generation (t|f)? [t]: ");
11586     boolean installPspng = readFromStdInBoolean(true, "grouperInstaller.autorun.installPspng");
11587     if (installPspng) {
11588       downloadAndBuildPspng();  
11589       
11590       //copy jars
11591       GrouperInstallerUtils.copyDirectory(new File(this.untarredPspngDir.getAbsolutePath() + File.separator + "lib" + File.separator + "custom"), 
11592           new File(this.untarredApiDir.getAbsolutePath() + File.separator + "lib" + File.separator + "custom"));
11593       GrouperInstallerUtils.copyDirectory(new File(this.untarredPspngDir.getAbsolutePath() + File.separator + "dist"), 
11594           new File(this.untarredApiDir.getAbsolutePath() + File.separator + "lib" + File.separator + "custom"));
11595 
11596       //####################################
11597       // patch the PSP
11598       this.upgradeExistingApplicationDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath());
11599       this.upgradeExistingClassesDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath())
11600           + "conf" + File.separator;
11601       this.upgradeExistingLibDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath())
11602           + "lib" + File.separator;
11603       patchPspng();
11604             
11605     }
11606 
11607 
11608     if (!installPspng) {
11609       //####################################
11610       //install psp
11611       System.out.print("Do you want to install the provisioning service provider (t|f)? [t]: ");
11612       if (readFromStdInBoolean(true, "grouperInstaller.autorun.installPsp")) {
11613         downloadAndBuildPsp();              
11614         GrouperInstallerUtils.copyDirectory(this.untarredPspDir, this.untarredApiDir);
11615   
11616         //####################################
11617         // patch the PSP
11618         this.upgradeExistingApplicationDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath());
11619         this.upgradeExistingClassesDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath())
11620             + "conf" + File.separator;
11621         this.upgradeExistingLibDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath())
11622             + "lib" + File.separator;
11623         patchPsp();
11624               
11625       }
11626     }
11627     
11628     reportOnConflictingJars(this.upgradeExistingApplicationDirectoryString);
11629 
11630     //#####################################
11631     //start the loader
11632     startLoader(true);
11633     
11634     //prompt and install ws scim
11635     installWsScim();
11636     
11637     //prompt and install rabbitmq messaging
11638     installMessagingRabbitMq();
11639     
11640     //prompt and install aws sqs messaging
11641     installMessagingAwsSqs();
11642     
11643     //prompt and install activemq messaging
11644     installMessagingActiveMq();
11645     
11646     //prompt and install activeMq messaging
11647     //installMessagingRabbitMq();
11648     
11649 
11650     //#####################################
11651     //success
11652     System.out.println("\n##################################\n");
11653 
11654     System.out.println("\nInstallation success!");
11655 
11656 
11657     System.out.println("\nRun the installer's 'admin' function to get information and manage about your installation (db, tomcat, logs, etc)");
11658     
11659     if (installUi) {
11660       System.out.println("\nGo here for the Grouper UI (change hostname if on different host): http://localhost:" + this.tomcatHttpPort + "/" + this.tomcatUiPath + "/");
11661       
11662     }
11663     if (installWs) {
11664       System.out.println("\nThis is the Grouper WS URL (change hostname if on different host): http://localhost:" + this.tomcatHttpPort + "/" + this.tomcatWsPath + "/");
11665     }
11666     System.out.println("\n##################################\n");
11667 
11668   }
11669 
11670   /**
11671    * 
11672    */
11673   private void installWsScim() {
11674     //#####################################
11675     // Install Grouper WS Scim Tier API
11676     //####################################
11677     System.out.print("Do you want to install the grouper ws scim (t|f)? [t]: ");
11678     boolean installWsScim = readFromStdInBoolean(true, "grouperInstaller.autorun.installGrouperWsScim");
11679     if (installWsScim) {
11680       downloadAndUntarWs();
11681       
11682       //####################################
11683       //get maven
11684       // NOTE: we dont need maven, ship the binary
11685       //downloadAndUnzipMaven();
11686       
11687       //####################################
11688       //look for or ask or download apache tomee
11689       File tomeeDir = downloadTomee();
11690       File unzippedTomeeFile = unzip(tomeeDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
11691       this.untarredTomeeDir = untar(unzippedTomeeFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc", 
11692           new File(this.grouperInstallDirectoryString));
11693 
11694       //####################################
11695       //ask for tomee port
11696       configureTomee();
11697 
11698       //####################################
11699       //build grouper ws scim
11700       // NOTE: we dont need to build it, ship the binary
11701       //buildWsScim();
11702 
11703       //####################################
11704       //configureTomeeGrouperWsScimWebapp
11705       configureTomeeGrouperWsScimWebapp();
11706       
11707       //####################################
11708       //set the GrouperSystem password
11709       tomeeConfigureGrouperSystem();
11710 
11711       //####################################
11712       //bounce tomcat
11713       tomeeBounce("restart");
11714       
11715       //####################################
11716       //tell user to go to url
11717       System.out.println("##################################\n");
11718       System.out.println("Go here for the Grouper WS Scim (change hostname if on different host): http://localhost:" + this.tomeeHttpPort + "/" + "grouper-ws-scim" + "/");
11719       System.out.println("\n##################################\n");
11720     }
11721   }
11722 
11723   /**
11724    * 
11725    */
11726   private void installMessagingRabbitMq() {
11727     //#####################################
11728     // Install Grouper Messaging RabbitMQ
11729     //####################################
11730     System.out.print("Do you want to install grouper rabbitMQ messaging (t|f)? [f]: ");
11731     boolean installRabbitMqMessaging = readFromStdInBoolean(false, "grouperInstaller.autorun.installGrouperRabbitMqMessaging");
11732     if (installRabbitMqMessaging) {
11733       
11734       String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
11735       
11736       if (!urlToDownload.endsWith("/")) {
11737         urlToDownload += "/";
11738       }
11739 
11740       urlToDownload += "release/";
11741       String rabbitMqFileName = "grouper.rabbitMq-" + this.version + ".tar.gz";
11742       urlToDownload += this.version + "/" + rabbitMqFileName;
11743 
11744       File rabbitMqFile = new File(this.grouperTarballDirectoryString + rabbitMqFileName);
11745       
11746       downloadFile(urlToDownload, rabbitMqFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalRabbitMqDownloadTarEtc");
11747 
11748       File unzippedRabbitMqFile = unzip(rabbitMqFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalRabbitMqDownloadTarEtc");
11749       File unzippedRabbitMqDir = untar(unzippedRabbitMqFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalRabbitMqDownloadTarEtc", 
11750           new File(this.grouperInstallDirectoryString));
11751 
11752       File rabbitMqInstallDirectoryFile = null;
11753       boolean success = false;
11754       for (int i=0;i<10;i++) {
11755 
11756         System.out.print("Where do you want the Grouper RabbitMQ messaging connector installed? ");
11757         String rabbitMqInstallDirectoryFileString = readFromStdIn("grouperInstaller.autorun.rabbitMqWhereInstalled");
11758         rabbitMqInstallDirectoryFile = new File(rabbitMqInstallDirectoryFileString);
11759         if (!rabbitMqInstallDirectoryFile.exists() || !rabbitMqInstallDirectoryFile.isDirectory()) {
11760           System.out.println("Error: cant find directory: '" + rabbitMqInstallDirectoryFile.getAbsolutePath() + "'");
11761           continue;
11762         }
11763 
11764         //make sure directory is where the app is
11765         
11766         List<File> grouperClientFiles = GrouperInstallerUtils.jarFindJar(rabbitMqInstallDirectoryFile, "grouperClient.jar");
11767         
11768         if (GrouperInstallerUtils.length(grouperClientFiles) == 0) {
11769           System.out.println("Cant find grouperClient.jar in a subdir of the install dir, please try again!");
11770           continue;
11771         }
11772         
11773         
11774         if (GrouperInstallerUtils.length(grouperClientFiles) > 1) {
11775           System.out.println("Found more than one grouperClient.jar in a subdir of the install dir, must only be one, please try again!");
11776           continue;
11777         }
11778 
11779         //ok, we know where the jars go
11780         File dirWhereFilesGo = grouperClientFiles.get(0).getParentFile();
11781         
11782         List<File> jarFiles = GrouperInstallerUtils.fileListRecursive(new File(unzippedRabbitMqDir.getAbsolutePath() + File.separatorChar 
11783             + "lib" + File.separatorChar));
11784       
11785         for (File jarFile : jarFiles) {
11786           
11787           String fileName = jarFile.getName();
11788           
11789           if (!fileName.endsWith(".jar")) {
11790             continue;
11791           }
11792           
11793           String sourceFileName = unzippedRabbitMqDir.getAbsolutePath() + File.separatorChar 
11794               + "lib" + File.separatorChar + fileName;
11795           
11796           File sourceFile = new File(sourceFileName);
11797           
11798           String destFileName = dirWhereFilesGo.getAbsolutePath() + File.separatorChar + fileName;
11799           
11800           File destFile = new File(destFileName);
11801           
11802           copyJarFileIfNotExists(sourceFile, destFile, false, false);
11803 
11804         }
11805 
11806         success = true;
11807         break;
11808       }        
11809       
11810       if (!success) {
11811         System.exit(1);
11812       }
11813       
11814       //####################################
11815       //tell user to configure
11816       System.out.println("##################################\n");
11817       
11818       System.out.println("Configure your grouper.client.properties based on this file " 
11819           + unzippedRabbitMqDir.getAbsoluteFile() + File.separator 
11820           + "grouper.client.rabbitMq.example.properties");
11821       System.out.println("\n##################################\n");
11822     }
11823   }
11824   
11825   /**
11826    * 
11827    */
11828   private void installMessagingAwsSqs() {
11829 
11830     //#####################################
11831     // Install Grouper Messaging AWS SQS
11832     //####################################
11833     System.out.print("Do you want to install grouper AWS SQS messaging (t|f)? [f]: ");
11834     boolean installAwsMessaging = readFromStdInBoolean(false, "grouperInstaller.autorun.installGrouperAwsSqsMessaging");
11835     if (installAwsMessaging) {
11836       
11837       String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
11838       
11839       if (!urlToDownload.endsWith("/")) {
11840         urlToDownload += "/";
11841       }
11842 
11843       urlToDownload += "release/";
11844       String awsFileName = "grouper.aws-" + this.version + ".tar.gz";
11845       urlToDownload += this.version + "/" + awsFileName;
11846 
11847       File awsFile = new File(this.grouperTarballDirectoryString + awsFileName);
11848       
11849       downloadFile(urlToDownload, awsFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalAwsSqsDownloadTarEtc");
11850 
11851       File unzippedAwsFile = unzip(awsFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalAwsSqsDownloadTarEtc");
11852       File unzippedAwsDir = untar(unzippedAwsFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalAwsSqsDownloadTarEtc", 
11853           new File(this.grouperInstallDirectoryString));
11854 
11855       File awsInstallDirectoryFile = null;
11856       boolean success = false;
11857       for (int i=0;i<10;i++) {
11858 
11859         System.out.print("Where do you want the Grouper AWS SQS messaging connector installed? ");
11860         String awsInstallDirectoryFileString = readFromStdIn("grouperInstaller.autorun.AwsSqsWhereInstalled");
11861         awsInstallDirectoryFile = new File(awsInstallDirectoryFileString);
11862         if (!awsInstallDirectoryFile.exists() || !awsInstallDirectoryFile.isDirectory()) {
11863           System.out.println("Error: cant find directory: '" + awsInstallDirectoryFile.getAbsolutePath() + "'");
11864           continue;
11865         }
11866 
11867         //make sure directory is where the app is
11868         
11869         List<File> grouperClientFiles = GrouperInstallerUtils.jarFindJar(awsInstallDirectoryFile, "grouperClient.jar");
11870         
11871         if (GrouperInstallerUtils.length(grouperClientFiles) == 0) {
11872           System.out.println("Cant find grouperClient.jar in a subdir of the install dir, please try again!");
11873           continue;
11874         }
11875         
11876         
11877         if (GrouperInstallerUtils.length(grouperClientFiles) > 1) {
11878           System.out.println("Found more than one grouperClient.jar in a subdir of the install dir, must only be one, please try again!");
11879           continue;
11880         }
11881 
11882         //ok, we know where the jars go
11883         File dirWhereFilesGo = grouperClientFiles.get(0).getParentFile();
11884         
11885         List<File> jarFiles = GrouperInstallerUtils.fileListRecursive(new File(unzippedAwsDir.getAbsolutePath() + File.separatorChar 
11886               + "lib" + File.separatorChar));
11887         
11888         for (File jarFile : jarFiles) {
11889           
11890           String fileName = jarFile.getName();
11891           
11892           if (!fileName.endsWith(".jar")) {
11893             continue;
11894           }
11895           
11896           String sourceFileName = unzippedAwsDir.getAbsolutePath() + File.separatorChar 
11897               + "lib" + File.separatorChar + fileName;
11898           
11899           File sourceFile = new File(sourceFileName);
11900           
11901           String destFileName = dirWhereFilesGo.getAbsolutePath() + File.separatorChar + fileName;
11902           
11903           File destFile = new File(destFileName);
11904           
11905           copyJarFileIfNotExists(sourceFile, destFile, false, false);
11906 
11907         }
11908 
11909         success = true;
11910         break;
11911       }        
11912       
11913       if (!success) {
11914         System.exit(1);
11915       }
11916       
11917       //####################################
11918       //tell user to configure
11919       System.out.println("##################################\n");
11920       
11921       System.out.println("Configure your grouper.client.properties based on this file " 
11922           + unzippedAwsDir.getAbsoluteFile() + File.separator 
11923           + "grouper.client.aws.example.properties");
11924       System.out.println("\n##################################\n");
11925     }
11926   
11927   }
11928   
11929   /**
11930    * 
11931    */
11932   private void installMessagingActiveMq() {
11933 
11934     //#####################################
11935     // Install Grouper Messaging ActiveMq
11936     //####################################
11937     System.out.print("Do you want to install grouper activeMq messaging (t|f)? [f]: ");
11938     boolean installActiveMqMessaging = readFromStdInBoolean(false, "grouperInstaller.autorun.installGrouperActiveMqMessaging");
11939     if (installActiveMqMessaging) {
11940       
11941       String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
11942       
11943       if (!urlToDownload.endsWith("/")) {
11944         urlToDownload += "/";
11945       }
11946 
11947       urlToDownload += "release/";
11948       String activeMqFileName = "grouper.activeMq-" + this.version + ".tar.gz";
11949       urlToDownload += this.version + "/" + activeMqFileName;
11950 
11951       File activeMqFile = new File(this.grouperTarballDirectoryString + activeMqFileName);
11952       
11953       downloadFile(urlToDownload, activeMqFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalActiveMqDownloadTarEtc");
11954 
11955       File unzippedActiveMqFile = unzip(activeMqFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalActiveMqDownloadTarEtc");
11956       File unzippedActiveMqDir = untar(unzippedActiveMqFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalActiveMqDownloadTarEtc", 
11957           new File(this.grouperInstallDirectoryString));
11958 
11959       File activeMqInstallDirectoryFile = null;
11960       boolean success = false;
11961       for (int i=0;i<10;i++) {
11962 
11963         System.out.print("Where do you want the Grouper ActiveMq messaging connector installed? ");
11964         String activeMqInstallDirectoryFileString = readFromStdIn("grouperInstaller.autorun.activeMqWhereInstalled");
11965         activeMqInstallDirectoryFile = new File(activeMqInstallDirectoryFileString);
11966         if (!activeMqInstallDirectoryFile.exists() || !activeMqInstallDirectoryFile.isDirectory()) {
11967           System.out.println("Error: cant find directory: '" + activeMqInstallDirectoryFile.getAbsolutePath() + "'");
11968           continue;
11969         }
11970 
11971         //make sure directory is where the app is
11972         
11973         List<File> grouperClientFiles = GrouperInstallerUtils.jarFindJar(activeMqInstallDirectoryFile, "grouperClient.jar");
11974         
11975         if (GrouperInstallerUtils.length(grouperClientFiles) == 0) {
11976           System.out.println("Cant find grouperClient.jar in a subdir of the install dir, please try again!");
11977           continue;
11978         }
11979         
11980         
11981         if (GrouperInstallerUtils.length(grouperClientFiles) > 1) {
11982           System.out.println("Found more than one grouperClient.jar in a subdir of the install dir, must only be one, please try again!");
11983           continue;
11984         }
11985 
11986         //ok, we know where the jars go
11987         File dirWhereFilesGo = grouperClientFiles.get(0).getParentFile();
11988         
11989         List<File> jarFiles = GrouperInstallerUtils.fileListRecursive(new File(unzippedActiveMqDir.getAbsolutePath() + File.separatorChar 
11990             + "lib" + File.separatorChar));
11991       
11992         for (File jarFile : jarFiles) {
11993           
11994           String fileName = jarFile.getName();
11995           
11996           if (!fileName.endsWith(".jar")) {
11997             continue;
11998           }
11999           
12000           String sourceFileName = unzippedActiveMqDir.getAbsolutePath() + File.separatorChar 
12001               + "lib" + File.separatorChar + fileName;
12002           
12003           File sourceFile = new File(sourceFileName);
12004           
12005           String destFileName = dirWhereFilesGo.getAbsolutePath() + File.separatorChar + fileName;
12006           
12007           File destFile = new File(destFileName);
12008           
12009           copyJarFileIfNotExists(sourceFile, destFile, false, false);
12010 
12011         }
12012 
12013         success = true;
12014         break;
12015       }        
12016       
12017       if (!success) {
12018         System.exit(1);
12019       }
12020       
12021       //####################################
12022       //tell user to configure
12023       System.out.println("##################################\n");
12024       
12025       System.out.println("Configure your grouper.client.properties based on this file " 
12026           + unzippedActiveMqDir.getAbsoluteFile() + File.separator 
12027           + "grouper.client.activeMq.example.properties");
12028       System.out.println("\n##################################\n");
12029     }
12030   
12031   }
12032 
12033   /**
12034    * 
12035    */
12036   private void downloadAndBuildPsp() {
12037     File pspDir = downloadPsp();
12038     File unzippedPspFile = unzip(pspDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalPspDownloadTarEtc");
12039     this.untarredPspDir = untar(unzippedPspFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalPspDownloadTarEtc", 
12040         null);
12041 
12042   }
12043   
12044   /**
12045    * 
12046    */
12047   private void downloadAndBuildPspng() {
12048     File pspngDir = downloadPspng();
12049     File unzippedPspngFile = unzip(pspngDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalPspngDownloadTarEtc");
12050     this.untarredPspngDir = untar(unzippedPspngFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalPspngDownloadTarEtc", 
12051         null);
12052 
12053   }
12054   
12055   /** untarred psp dir file */
12056   private File untarredPspDir;
12057   
12058   /** untarred pspng dir file */
12059   private File untarredPspngDir;
12060   
12061   /**
12062    * 
12063    */
12064   public void downloadAndUnzipAnt() {
12065     File antDir = downloadAnt();
12066     File unzippedAntFile = unzip(antDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
12067     this.untarredAntDir = untar(unzippedAntFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc", null);
12068   }
12069 
12070   /**
12071    * 
12072    */
12073   public void downloadAndUnzipMaven() {
12074     File mavenDir = downloadMaven();
12075     File unzippedMavenFile = unzip(mavenDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
12076     this.untarredMavenDir = untar(unzippedMavenFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc", null);
12077   }
12078 
12079   /**
12080    * 
12081    */
12082   public void downloadAndUntarWs() {
12083 
12084     //####################################
12085     //download the ws
12086     File wsDir = downloadWs();
12087 
12088     //####################################
12089     //unzip/untar the ws file
12090     File unzippedWsFile = unzip(wsDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalWsDownloadTarEtc");
12091     System.out.println("Unzipped Ws file is "+unzippedWsFile);
12092     this.untarredWsDir = untar(unzippedWsFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalWsDownloadTarEtc", 
12093         new File(this.grouperInstallDirectoryString));
12094 
12095   }
12096 
12097   /**
12098    * 
12099    */
12100   public void downloadAndConfigureUi() {
12101     //####################################
12102     //get UI
12103     File uiDir = downloadUi();
12104     
12105     //####################################
12106     //unzip/untar the ui file
12107     File unzippedUiFile = unzip(uiDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalUiDownloadTarEtc");
12108     this.untarredUiDir = untar(unzippedUiFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalUiDownloadTarEtc", 
12109         new File(this.grouperInstallDirectoryString));
12110 
12111     //####################################
12112     //configure UI
12113     configureUi();
12114   }
12115 
12116   /**
12117    * 
12118    */
12119   public void downloadAndConfigureApi() {
12120     File apiFile = downloadApi();
12121     
12122     //####################################
12123     //unzip/untar the api file
12124     
12125     File unzippedApiFile = unzip(apiFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalApiDownloadTarEtc");
12126     File theUntarredApiDir = untar(unzippedApiFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalApiDownloadTarEtc", 
12127         new File(this.grouperInstallDirectoryString));
12128     
12129     File theGrouperJar = new File(GrouperInstallerUtils.fileAddLastSlashIfNotExists(theUntarredApiDir.getAbsolutePath())
12130         + "dist" + File.separator + "lib" + File.separator + "grouper.jar");
12131     
12132     gshExcutableAndDos2Unix(theUntarredApiDir.getAbsolutePath() + File.separator + "bin" + File.separator);
12133 
12134     //these might be set from UI or WS
12135     if (this.untarredApiDir == null) {
12136       this.untarredApiDir = theUntarredApiDir;
12137     }
12138     
12139     if (this.grouperJar == null) {
12140       this.grouperJar = theGrouperJar;
12141     }
12142   }
12143 
12144   /**
12145    * @param binDirLocation which includes trailing slash
12146    */
12147   public void gshExcutableAndDos2Unix(String binDirLocation) {
12148     gshExcutableAndDos2Unix(binDirLocation, null);
12149   }
12150 
12151   /**
12152    * run dos2unix on a file
12153    * @param file
12154    * @param fileNameInPrompt 
12155    * @param configSuffixAutorun 
12156    */
12157   public static void dos2unix(File file, String fileNameInPrompt, String configSuffixAutorun) {
12158     dos2unix(GrouperInstallerUtils.toSet(file), fileNameInPrompt, configSuffixAutorun);
12159   }
12160 
12161   /**
12162    * run dos2unix on a file
12163    * @param files
12164    * @param fileNameInPrompt e.g. gsh.sh
12165    * @param configSuffixAutorun suffix after grouperInstaller.autorun.dos2unix in properties file
12166    */
12167   public static void dos2unix(Collection<File> files, String fileNameInPrompt, String configSuffixAutorun) {
12168 
12169     if (!GrouperInstallerUtils.isWindows()) {
12170 
12171       System.out.print("Do you want to run dos2unix on " + fileNameInPrompt + " (t|f)? [t]: ");
12172       boolean dos2unixRunOnFile = readFromStdInBoolean(true, "grouperInstaller.autorun.dos2unix" + configSuffixAutorun);
12173       
12174       if (dos2unixRunOnFile) {
12175 
12176         for (File file : files) {
12177           
12178           if (!file.exists()) {
12179             continue;
12180           }
12181           
12182           List<String> commands = GrouperInstallerUtils.toList("dos2unix", 
12183               file.getAbsolutePath());
12184     
12185           System.out.println("Making sure " + file.getName() + " is in unix format: " + convertCommandsIntoCommand(commands) + "\n");
12186           String error = null;
12187           CommandResult commandResult = null;
12188           boolean didntWork = false;
12189           Throwable throwable = null;
12190           try {
12191             commandResult = GrouperInstallerUtils.execCommand(
12192                 GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
12193                 file.getParentFile(), null, false, true, false);
12194 
12195             if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
12196               System.out.println("stderr: " + commandResult.getErrorText());
12197             }
12198             if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
12199               System.out.println("stdout: " + commandResult.getOutputText());
12200             }
12201             continue;
12202           } catch (Throwable t) {
12203             didntWork = true;
12204             error = t.getMessage();
12205             throwable = t;
12206           }
12207           
12208           if (didntWork) {
12209             try {
12210               //lets try the java way?
12211               String fileContents = GrouperInstallerUtils.readFileIntoString(file);
12212               if (fileContents.contains("\r\n")) {
12213                 System.out.println("Problem with command 'dos2unix'.   Is it installed?  Converting to unix via java replacing \\r\\n with \\n: " + file.getAbsolutePath());
12214                 fileContents = fileContents.replaceAll("\r\n", "\n");
12215                 GrouperInstallerUtils.saveStringIntoFile(file, fileContents);
12216               }
12217               continue;
12218             } catch (Throwable t) {
12219               t.printStackTrace();
12220             }
12221           }
12222           
12223           if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
12224             System.out.println("stderr: " + commandResult.getErrorText());
12225           }
12226           if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
12227             System.out.println("stdout: " + commandResult.getOutputText());
12228           }
12229           if (!GrouperInstallerUtils.isBlank(error)) {
12230             if (throwable != null) {
12231               throwable.printStackTrace();
12232             }
12233             System.out.println("Error: " + error);
12234             System.out.println("NOTE: you might need to run this to convert newline characters to mac/unix:\n\n" +
12235                 "cat " + file.getAbsolutePath()
12236                 + " | col -b > " + file.getAbsolutePath() + "\n");
12237           }
12238         }
12239       }
12240 
12241     }
12242   }
12243   
12244   /**
12245    * @param binDirLocation which includes trailing slash
12246    * @param specify if specifying location
12247    */
12248   public void gshExcutableAndDos2Unix(String binDirLocation, String specify) {
12249     //lts make sure gsh is executable and in unix format
12250 
12251     if (!GrouperInstallerUtils.isWindows()) {
12252 
12253       specify = GrouperInstallerUtils.trimToEmpty(specify);
12254       
12255       if (specify.length() > 0) {
12256         specify += " ";
12257       }
12258       
12259       System.out.print("Do you want to set " + specify + "gsh script to executable (t|f)? [t]: ");
12260       boolean setGshFile = readFromStdInBoolean(true, "grouperInstaller.autorun.setGshScriptsToExecutable");
12261       
12262       if (setGshFile) {
12263       
12264         binDirLocation = GrouperInstallerUtils.fileAddLastSlashIfNotExists(binDirLocation);
12265         
12266         List<String> commands = GrouperInstallerUtils.toList("chmod", "+x", 
12267             binDirLocation + "gsh.sh");
12268   
12269         System.out.println("Making sure gsh.sh is executable with command: " + convertCommandsIntoCommand(commands) + "\n");
12270   
12271         CommandResult commandResult = GrouperInstallerUtils.execCommand(
12272             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
12273             new File(binDirLocation), null, true);
12274         
12275         if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
12276           System.out.println("stderr: " + commandResult.getErrorText());
12277         }
12278         if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
12279           System.out.println("stdout: " + commandResult.getOutputText());
12280         }
12281 
12282         if (new File(binDirLocation + "gsh").exists()) {
12283           commands = GrouperInstallerUtils.toList("chmod", "+x", 
12284               binDirLocation + "gsh");
12285     
12286           System.out.println("Making sure gsh is executable with command: " + convertCommandsIntoCommand(commands) + "\n");
12287     
12288           commandResult = GrouperInstallerUtils.execCommand(
12289               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
12290               new File(binDirLocation), null, true);
12291           
12292           if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
12293             System.out.println("stderr: " + commandResult.getErrorText());
12294           }
12295           if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
12296             System.out.println("stdout: " + commandResult.getOutputText());
12297           }
12298         }
12299         
12300         dos2unix(GrouperInstallerUtils.toSet(new File(binDirLocation + "gsh.sh"), new File(binDirLocation + "gsh")), "gsh.sh", "OnGsh");
12301 
12302       }
12303       
12304     }
12305   }
12306   
12307   /**
12308    * if we are debugging sql in log4j
12309    */
12310   private Boolean log4jDebugSql = null;
12311   
12312   /**
12313    * if this file has been taken care of for a while
12314    */
12315   private Set<File> log4jDebugDone = new HashSet<File>();
12316   
12317   /**
12318    * if this file has been taken care of for a while
12319    */
12320   private Set<File> removeLegacyHibernatePropertiesDone = new HashSet<File>();
12321   
12322   /**
12323    * @param hibernateFileLocation
12324    */
12325   public void removeLegacyHibernateProperties(String hibernateFileLocation) {
12326 
12327     //if not a file dont worry about it
12328     File hibernateFile = new File(hibernateFileLocation);
12329 
12330     if (this.removeLegacyHibernatePropertiesDone.contains(hibernateFile)) {
12331       return;
12332     }
12333 
12334     this.removeLegacyHibernatePropertiesDone.add(hibernateFile);
12335 
12336     if (!hibernateFile.exists()) {
12337       System.out.println("Cant find grouper.hibernate.properties: " + hibernateFileLocation);
12338       return;
12339     }
12340     
12341     //see if its there
12342     Properties hibernateProperties = GrouperInstallerUtils.propertiesFromFile(hibernateFile);
12343     String current = GrouperInstallerUtils.propertiesValue(hibernateProperties, "hibernate.cache.region.factory_class");
12344     
12345     if (current == null) {
12346       //not there, we're good
12347       return;
12348     }
12349 
12350 
12351     removeRedundantProperties(hibernateFile, GrouperInstallerUtils.toSet("hibernate.cache.region.factory_class"));
12352     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.");
12353   }
12354   
12355   /**
12356    * @param log4jLocation
12357    */
12358   public void log4jDebugSql(String log4jLocation) {
12359 
12360     //if not a file dont worry about it
12361     File log4jFile = new File(log4jLocation);
12362 
12363     if (this.log4jDebugDone.contains(log4jFile)) {
12364       return;
12365     }
12366 
12367     this.log4jDebugDone.add(log4jFile);
12368 
12369     if (!log4jFile.exists()) {
12370       System.out.println("Cant find log4j.properties: " + log4jLocation);
12371       return;
12372     }
12373     
12374     //see if its already there
12375     Properties log4jProperties = GrouperInstallerUtils.propertiesFromFile(log4jFile);
12376     String currentAntEntry = GrouperInstallerUtils.propertiesValue(log4jProperties, "log4j.logger.org.apache.tools.ant");
12377     
12378     if (GrouperInstallerUtils.equalsIgnoreCase(GrouperInstallerUtils.trimToEmpty(currentAntEntry), "DEBUG")
12379         || GrouperInstallerUtils.equalsIgnoreCase(GrouperInstallerUtils.trimToEmpty(currentAntEntry), "INFO")
12380         || GrouperInstallerUtils.equalsIgnoreCase(GrouperInstallerUtils.trimToEmpty(currentAntEntry), "WARN")) {
12381       //we are already there
12382       return;
12383     }
12384 
12385     if (this.log4jDebugSql == null) {
12386       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]: ");
12387       this.log4jDebugSql = readFromStdInBoolean(true, "grouperInstaller.autorun.log4jDebugSql");
12388     }
12389 
12390     if (this.log4jDebugSql) {
12391 
12392       editPropertiesFile(log4jFile, "log4j.logger.org.apache.tools.ant", "WARN", false);
12393 
12394     }
12395   }
12396 
12397   /**
12398    * 
12399    */
12400   public void downloadAndBuildClient() {
12401     //####################################
12402     //download the client
12403     File clientDir = downloadClient();
12404 
12405     //####################################
12406     //unzip/untar the client file
12407     File unzippedClientFile = unzip(clientDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalClientDownloadTarEtc");
12408     this.untarredClientDir = untar(unzippedClientFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalClientDownloadTarEtc", 
12409         new File(this.grouperInstallDirectoryString));
12410     
12411   }
12412 
12413   /**
12414    * 
12415    */
12416   private int tomcatHttpPort = -1;
12417   
12418   /**
12419    * 
12420    */
12421   private int tomeeHttpPort = -1;
12422   
12423   
12424   /**
12425    * 
12426    */
12427   private void configureTomcat() {
12428     
12429     System.out.print("Do you want to set the tomcat memory limit (t|f)? [t]: ");
12430     boolean setTomcatMemory = readFromStdInBoolean(true, "grouperInstaller.autorun.setTomcatMemoryLimit");
12431     
12432     if (setTomcatMemory) {
12433       
12434       {
12435         File catalinaBatFile = new File(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin" + File.separator + "catalina.bat");
12436         
12437         System.out.println("Editing file: " + catalinaBatFile.getAbsolutePath());
12438         
12439         Boolean edited = editFile(catalinaBatFile, "^\\s*set\\s+\"JAVA_OPTS\\s*=.*-Xmx([0-9mMgG]+)", null, null, "512M", "max memory");
12440         if (edited == null) {
12441           addToFile(catalinaBatFile, "\nset \"JAVA_OPTS=-server -Xmx512M -XX:MaxPermSize=256M\"\n", 65, "max memory");
12442         }
12443         if (null == editFile(catalinaBatFile, "^\\s*set\\s+\"JAVA_OPTS\\s*=.*-XX:MaxPermSize=([0-9mMgG]+)", null, null, "256M", "permgen memory")) {
12444           throw new RuntimeException("Why not edit permgen in file " + catalinaBatFile);
12445         }
12446       }
12447       
12448       {
12449         File catalinaShFile = new File(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin" + File.separator + "catalina.sh");
12450         
12451         System.out.println("Editing file: " + catalinaShFile.getAbsolutePath());
12452 
12453         Boolean edited = editFile(catalinaShFile, "^\\s*JAVA_OPTS\\s*=\".*-Xmx([0-9mMgG]+)", null, null, "512M", "max memory");
12454         if (edited == null) {
12455           addToFile(catalinaShFile, "\nJAVA_OPTS=\"-server -Xmx512M -XX:MaxPermSize=256M\"\n", 65, "max memory");
12456         }
12457         if (null == editFile(catalinaShFile, "^\\s*JAVA_OPTS\\s*=\".*-XX:MaxPermSize=([0-9mMgG]+)", null, null, "256M", "permgen memory")) {
12458           throw new RuntimeException("Why not edit permgen in file " + catalinaShFile);
12459         }
12460       }
12461     }      
12462     
12463     
12464     if (!GrouperInstallerUtils.isWindows()) {
12465 
12466       System.out.print("Do you want to set tomcat scripts to executable (t|f)? [t]: ");
12467       boolean setTomcatFiles = readFromStdInBoolean(true, "grouperInstaller.autorun.setTomcatScriptsToExecutable");
12468       
12469       //GrouperInstallerUtils.toSet("catalina.sh", "startup.sh", "shutdown.sh");
12470       Set<String> shFileNames = new HashSet<String>();
12471 
12472       File binDir = new File(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin");
12473 
12474       //get all sh files, doing wildcards doesnt work
12475       for (File file : binDir.listFiles()) {
12476         String fileName = GrouperInstallerUtils.defaultString(file.getName());
12477         if (file.isFile() && fileName.endsWith(".sh")) {
12478           shFileNames.add(fileName);
12479         }
12480       }
12481 
12482       if (setTomcatFiles) {
12483       
12484         for (String command : shFileNames) {
12485           List<String> commands = new ArrayList<String>();
12486           
12487           commands.add("chmod");
12488           commands.add("+x");
12489           //have to do * since all the  sh files need chmod
12490           commands.add(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin" + File.separator + command);
12491     
12492           System.out.println("Making tomcat file executable with command: " + convertCommandsIntoCommand(commands) + "\n");
12493     
12494           CommandResult commandResult = GrouperInstallerUtils.execCommand(
12495               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
12496               new File(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin"), null, true);
12497           
12498           if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
12499             System.out.println("stderr: " + commandResult.getErrorText());
12500           }
12501           if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
12502             System.out.println("stdout: " + commandResult.getOutputText());
12503           }
12504         }
12505       }
12506       
12507       Set<File> shFiles = new LinkedHashSet<File>();
12508       for (String shFileName : shFileNames) {
12509         shFiles.add(new File(shFileName));
12510       }
12511       
12512       dos2unix(shFiles, "tomcat sh files", "OnTomcatFiles");
12513 
12514     }
12515       
12516     //see what the current ports are
12517     this.tomcatHttpPort = -1;
12518     
12519     File serverXmlFile = new File(this.untarredTomcatDir.getAbsolutePath() + File.separator + "conf" + File.separator + "server.xml");
12520     
12521     int shutdownPort = GrouperInstallerUtils.xpathEvaluateAttributeInt(serverXmlFile, "/Server", "port", -1);
12522     
12523     int originalShutdownPort = shutdownPort;
12524     
12525     //  /Server/Service/Connector <Connector port="8080" protocol="HTTP/1.1" 
12526     this.tomcatHttpPort = GrouperInstallerUtils.xpathEvaluateAttributeInt(serverXmlFile, "/Server/Service/Connector[@protocol='HTTP/1.1']", "port", -1);
12527 
12528     int originalTomcatHttpPort = this.tomcatHttpPort;
12529 
12530     // <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
12531     int jkPort = GrouperInstallerUtils.xpathEvaluateAttributeInt(serverXmlFile, "/Server/Service/Connector[@protocol='AJP/1.3']", "port", -1);
12532 
12533     int originalJkPort = jkPort;
12534     
12535     String portsCommaSeparated = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.tomcatPorts", false);
12536     if (!GrouperInstallerUtils.isBlank(portsCommaSeparated)) {
12537       
12538       String[] portsStrings = GrouperInstallerUtils.splitTrim(portsCommaSeparated, ",");
12539       
12540       if (portsStrings.length != 3) {
12541         throw new RuntimeException("Why is grouperInstaller.default.tomcatPorts from grouper.installer.properties not 3 ints comma separated? " + portsCommaSeparated);
12542       }
12543       
12544       this.tomcatHttpPort = GrouperInstallerUtils.intValue(portsStrings[0]);
12545       jkPort = GrouperInstallerUtils.intValue(portsStrings[1]);
12546       shutdownPort = GrouperInstallerUtils.intValue(portsStrings[2]);
12547       
12548     }
12549     
12550     while(true) {
12551       System.out.print("What ports do you want tomcat to run on (HTTP, JK, shutdown): [" + this.tomcatHttpPort + ", " + jkPort + ", " + shutdownPort + "]: ");
12552       
12553       String ports = readFromStdIn("grouperInstaller.autorun.tomcatPorts");
12554       
12555       if (GrouperInstallerUtils.isBlank(ports)) {
12556         if (this.tomcatHttpPort == originalTomcatHttpPort && jkPort == originalJkPort && shutdownPort == originalShutdownPort) {
12557           break;
12558         }
12559       } else {
12560         String[] portsArray = GrouperInstallerUtils.splitTrim(ports, ",");
12561         if (GrouperInstallerUtils.length(portsArray) == 3) {
12562           for (String portString : portsArray) {
12563             try {
12564               GrouperInstallerUtils.intValue(portString);
12565             } catch (Exception e) {
12566               continue;
12567             }
12568           }
12569         } else {
12570           continue;
12571         }
12572         //ok, we have three integer entries
12573         this.tomcatHttpPort = GrouperInstallerUtils.intValue(portsArray[0]);
12574         jkPort = GrouperInstallerUtils.intValue(portsArray[1]);
12575         shutdownPort = GrouperInstallerUtils.intValue(portsArray[2]);
12576       }
12577       
12578       if (!GrouperInstallerUtils.portAvailable(this.tomcatHttpPort, this.defaultIpAddress)) {
12579         System.out.print("The tomcat HTTP port is in use or unavailable: " + this.tomcatHttpPort + ", do you want to pick different ports? (t|f): ");
12580         boolean pickDifferentPorts = readFromStdInBoolean(null, "grouperInstaller.autorun.pickDifferentPortIfInUse");
12581         if (pickDifferentPorts) {
12582           continue;
12583         }
12584       }
12585       if (!GrouperInstallerUtils.portAvailable(jkPort, this.defaultIpAddress)) {
12586         System.out.print("The tomcat JK port is in use or unavailable: " + this.tomcatHttpPort + ", do you want to pick different ports? (t|f): ");
12587         boolean pickDifferentPorts = readFromStdInBoolean(null, "grouperInstaller.autorun.pickDifferentPortIfInUse");
12588         if (pickDifferentPorts) {
12589           continue;
12590         }
12591       }
12592       
12593       System.out.println("Editing tomcat config file: " + serverXmlFile.getAbsolutePath());
12594       //lets edit the file
12595       //<Connector port="8080" protocol="HTTP/1.1" 
12596       editFile(serverXmlFile, "port=\"([\\d]+)\"", new String[]{"<Connector", "protocol=\"HTTP/1.1\""}, 
12597           new String[]{"SSLEnabled=\"true\""}, Integer.toString(this.tomcatHttpPort), "tomcat HTTP port");
12598       //<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
12599       editFile(serverXmlFile, "port=\"([\\d]+)\"", new String[]{"<Connector", "protocol=\"AJP/1.3\""}, null, Integer.toString(jkPort), "tomcat JK port");
12600       //<Server port="8005" shutdown="SHUTDOWN">
12601       editFile(serverXmlFile, "port=\"([\\d]+)\"", new String[]{"<Server", "shutdown=\"SHUTDOWN\""}, null, Integer.toString(shutdownPort), "tomcat shutdown port");
12602       break;
12603     }
12604 
12605     configureTomcatUriEncoding(serverXmlFile);
12606     
12607   }
12608   
12609   /**
12610    * 
12611    */
12612   private void configureTomee() {
12613     
12614     System.out.print("Do you want to set the tomee memory limit (t|f)? [t]: ");
12615     boolean setTomeeMemory = readFromStdInBoolean(true, "grouperInstaller.autorun.setTomeeMemoryLimit");
12616     
12617     if (setTomeeMemory) {
12618       
12619       {
12620         File catalinaBatFile = new File(this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin" + File.separator + "catalina.bat");
12621         
12622         System.out.println("Editing file: " + catalinaBatFile.getAbsolutePath());
12623         
12624         Boolean edited = editFile(catalinaBatFile, "^\\s*set\\s+\"JAVA_OPTS\\s*=.*-Xmx([0-9mMgG]+)", null, null, "512M", "max memory");
12625         if (edited == null) {
12626           addToFile(catalinaBatFile, "\nset \"JAVA_OPTS=-server -Xmx512M -XX:MaxPermSize=256M\"\n", 65, "max memory");
12627         }
12628         if (null == editFile(catalinaBatFile, "^\\s*set\\s+\"JAVA_OPTS\\s*=.*-XX:MaxPermSize=([0-9mMgG]+)", null, null, "256M", "permgen memory")) {
12629           throw new RuntimeException("Why not edit permgen in file " + catalinaBatFile);
12630         }
12631       }
12632       
12633       {
12634         File catalinaShFile = new File(this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin" + File.separator + "catalina.sh");
12635         
12636         System.out.println("Editing file: " + catalinaShFile.getAbsolutePath());
12637 
12638         Boolean edited = editFile(catalinaShFile, "^\\s*JAVA_OPTS\\s*=\".*-Xmx([0-9mMgG]+)", null, null, "512M", "max memory");
12639         if (edited == null) {
12640           addToFile(catalinaShFile, "\nJAVA_OPTS=\"-server -Xmx512M -XX:MaxPermSize=256M\"\n", 65, "max memory");
12641         }
12642         if (null == editFile(catalinaShFile, "^\\s*JAVA_OPTS\\s*=\".*-XX:MaxPermSize=([0-9mMgG]+)", null, null, "256M", "permgen memory")) {
12643           throw new RuntimeException("Why not edit permgen in file " + catalinaShFile);
12644         }
12645       }
12646     }      
12647     
12648     
12649     if (!GrouperInstallerUtils.isWindows()) {
12650 
12651       System.out.print("Do you want to set tomee scripts to executable (t|f)? [t]: ");
12652       boolean setTomeeFiles = readFromStdInBoolean(true, "grouperInstaller.autorun.setTomeeScriptsToExecutable");
12653       
12654       //GrouperInstallerUtils.toSet("catalina.sh", "startup.sh", "shutdown.sh");
12655       Set<String> shFileNames = new HashSet<String>();
12656 
12657       File binDir = new File(this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin");
12658 
12659       //get all sh files, doing wildcards doesnt work
12660       for (File file : binDir.listFiles()) {
12661         String fileName = GrouperInstallerUtils.defaultString(file.getName());
12662         if (file.isFile() && fileName.endsWith(".sh")) {
12663           shFileNames.add(fileName);
12664         }
12665       }
12666 
12667       if (setTomeeFiles) {
12668       
12669         for (String command : shFileNames) {
12670           List<String> commands = new ArrayList<String>();
12671           
12672           commands.add("chmod");
12673           commands.add("+x");
12674           //have to do * since all the  sh files need chmod
12675           commands.add(this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin" + File.separator + command);
12676     
12677           System.out.println("Making tomee file executable with command: " + convertCommandsIntoCommand(commands) + "\n");
12678     
12679           CommandResult commandResult = GrouperInstallerUtils.execCommand(
12680               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
12681               new File(this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin"), null, true);
12682           
12683           if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
12684             System.out.println("stderr: " + commandResult.getErrorText());
12685           }
12686           if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
12687             System.out.println("stdout: " + commandResult.getOutputText());
12688           }
12689         }
12690       }
12691       
12692       Set<File> shFiles = new LinkedHashSet<File>();
12693       for (String shFileName : shFileNames) {
12694         shFiles.add(new File(shFileName));
12695       }
12696       
12697       dos2unix(shFiles, "tomee sh files", "OnTomeeFiles");
12698 
12699     }
12700       
12701     //see what the current ports are
12702     this.tomeeHttpPort = -1;
12703     
12704     File serverXmlFile = new File(this.untarredTomeeDir.getAbsolutePath() + File.separator + "conf" + File.separator + "server.xml");
12705     
12706     int shutdownPort = GrouperInstallerUtils.xpathEvaluateAttributeInt(serverXmlFile, "/Server", "port", -1);
12707     
12708     int originalShutdownPort = shutdownPort;
12709     
12710     //  /Server/Service/Connector <Connector port="8080" protocol="HTTP/1.1" 
12711     this.tomeeHttpPort = GrouperInstallerUtils.xpathEvaluateAttributeInt(serverXmlFile, "/Server/Service/Connector[@protocol='HTTP/1.1']", "port", -1);
12712 
12713     int originalTomeeHttpPort = this.tomeeHttpPort;
12714 
12715     // <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
12716     int jkPort = GrouperInstallerUtils.xpathEvaluateAttributeInt(serverXmlFile, "/Server/Service/Connector[@protocol='AJP/1.3']", "port", -1);
12717 
12718     int originalJkPort = jkPort;
12719     
12720     String portsCommaSeparated = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.tomeePorts", false);
12721     if (!GrouperInstallerUtils.isBlank(portsCommaSeparated)) {
12722       
12723       String[] portsStrings = GrouperInstallerUtils.splitTrim(portsCommaSeparated, ",");
12724       
12725       if (portsStrings.length != 3) {
12726         throw new RuntimeException("Why is grouperInstaller.default.tomeePorts from grouper.installer.properties not 3 ints comma separated? " + portsCommaSeparated);
12727       }
12728       
12729       this.tomeeHttpPort = GrouperInstallerUtils.intValue(portsStrings[0]);
12730       jkPort = GrouperInstallerUtils.intValue(portsStrings[1]);
12731       shutdownPort = GrouperInstallerUtils.intValue(portsStrings[2]);
12732       
12733     }
12734     
12735     while(true) {
12736       System.out.print("What ports do you want tomee to run on (HTTP, JK, shutdown): [" + this.tomeeHttpPort + ", " + jkPort + ", " + shutdownPort + "]: ");
12737       
12738       String ports = readFromStdIn("grouperInstaller.autorun.tomeePorts");
12739       
12740       if (GrouperInstallerUtils.isBlank(ports)) {
12741         if (this.tomeeHttpPort == originalTomeeHttpPort && jkPort == originalJkPort && shutdownPort == originalShutdownPort) {
12742           break;
12743         }
12744       } else {
12745         String[] portsArray = GrouperInstallerUtils.splitTrim(ports, ",");
12746         if (GrouperInstallerUtils.length(portsArray) == 3) {
12747           for (String portString : portsArray) {
12748             try {
12749               GrouperInstallerUtils.intValue(portString);
12750             } catch (Exception e) {
12751               continue;
12752             }
12753           }
12754         } else {
12755           continue;
12756         }
12757         //ok, we have three integer entries
12758         this.tomeeHttpPort = GrouperInstallerUtils.intValue(portsArray[0]);
12759         jkPort = GrouperInstallerUtils.intValue(portsArray[1]);
12760         shutdownPort = GrouperInstallerUtils.intValue(portsArray[2]);
12761       }
12762       
12763       if (!GrouperInstallerUtils.portAvailable(this.tomeeHttpPort, this.defaultIpAddress)) {
12764         System.out.print("The tomee HTTP port is in use or unavailable: " + this.tomeeHttpPort + ", do you want to pick different ports? (t|f): ");
12765         boolean pickDifferentPorts = readFromStdInBoolean(null, "grouperInstaller.autorun.pickDifferentPortIfInUse");
12766         if (pickDifferentPorts) {
12767           continue;
12768         }
12769       }
12770       if (!GrouperInstallerUtils.portAvailable(jkPort, this.defaultIpAddress)) {
12771         System.out.print("The tomee JK port is in use or unavailable: " + this.tomeeHttpPort + ", do you want to pick different ports? (t|f): ");
12772         boolean pickDifferentPorts = readFromStdInBoolean(null, "grouperInstaller.autorun.pickDifferentPortIfInUse");
12773         if (pickDifferentPorts) {
12774           continue;
12775         }
12776       }
12777       
12778       System.out.println("Editing tomee config file: " + serverXmlFile.getAbsolutePath());
12779       //lets edit the file
12780       //<Connector port="8080" protocol="HTTP/1.1" 
12781       editFile(serverXmlFile, "port=\"([\\d]+)\"", new String[]{"<Connector", "protocol=\"HTTP/1.1\""}, 
12782           new String[]{"SSLEnabled=\"true\""}, Integer.toString(this.tomcatHttpPort), "tomee HTTP port");
12783       //<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
12784       editFile(serverXmlFile, "port=\"([\\d]+)\"", new String[]{"<Connector", "protocol=\"AJP/1.3\""}, null, Integer.toString(jkPort), "tomee JK port");
12785       //<Server port="8005" shutdown="SHUTDOWN">
12786       editFile(serverXmlFile, "port=\"([\\d]+)\"", new String[]{"<Server", "shutdown=\"SHUTDOWN\""}, null, Integer.toString(shutdownPort), "tomee shutdown port");
12787       break;
12788     }
12789 
12790     configureTomcatUriEncoding(serverXmlFile);
12791     
12792   }
12793   /**
12794    * @param serverXmlFile
12795    */
12796   public void configureTomcatUriEncoding(File serverXmlFile) {
12797     //set encoding for connectors
12798     //  /Server/Service/Connector <Connector port="8080" protocol="HTTP/1.1" 
12799     String uriEncodingHttp = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
12800         "/Server/Service/Connector[@protocol='HTTP/1.1']", "URIEncoding");
12801     
12802     // <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
12803     String uriEncodingAjp = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
12804         "/Server/Service/Connector[@protocol='AJP/1.3']", "URIEncoding");
12805 
12806     if (!GrouperInstallerUtils.equals(uriEncodingAjp, "UTF-8") || !GrouperInstallerUtils.equals(uriEncodingHttp, "UTF-8")) {
12807 
12808       boolean defaultSetUriEncoding = GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.ui.setTomcatUriEncoding", true, false);
12809       System.out.print("Do you want to set URIEncoding to UTF-8 in tomcat server.xml <Connector> elements (t|f)? [" 
12810           + (defaultSetUriEncoding ? "t" : "f") + "]: ");
12811       boolean assignUriEncoding = readFromStdInBoolean(defaultSetUriEncoding, "grouperInstaller.autorun.setUriEncodingToUtf8inServerXml");
12812 
12813       if (assignUriEncoding) {
12814         
12815         if (!GrouperInstallerUtils.equals(uriEncodingAjp, "UTF-8")) {
12816           editFile(serverXmlFile, "URIEncoding=\"([^\"]+)\"", new String[]{"<Connector", "protocol=\"AJP/1.3\""}, 
12817               new String[]{"SSLEnabled=\"true\""}, "UTF-8", "tomcat URIEncoding attribute for element <Connector AJP", true, "URIEncoding");
12818           
12819         }
12820         
12821         if (!GrouperInstallerUtils.equals(uriEncodingHttp, "UTF-8")) {
12822           editFile(serverXmlFile, "URIEncoding=\"([^\"]+)\"", new String[]{"<Connector", "protocol=\"HTTP/1.1\""}, 
12823               new String[]{"SSLEnabled=\"true\""}, "UTF-8", "tomcat URIEncoding attribute for element <Connector HTTP", true, "URIEncoding");
12824           
12825         }
12826       }
12827 
12828     }
12829   }
12830 
12831   /**
12832    * 
12833    * @param newEhcacheExampleFile
12834    * @param existingEhcacheExampleFile
12835    * @param existingEhcacheFile
12836    */
12837   public static void mergeEhcacheXmlFiles(File newEhcacheExampleFile, File existingEhcacheExampleFile, File existingEhcacheFile) {
12838     
12839     try {
12840       //lets get the differences of the existing ehcache file and the existing ehcache example file
12841       DocumentBuilderFactory domFactory = GrouperInstallerUtils.xmlDocumentBuilderFactory();
12842       DocumentBuilder builder = domFactory.newDocumentBuilder();
12843       Document existingEhcacheDoc = builder.parse(existingEhcacheFile);
12844       Document existingEhcacheExampleDoc = builder.parse(existingEhcacheExampleFile);
12845 
12846       Element existingDocumentElement = existingEhcacheDoc.getDocumentElement();
12847       Element existingExampleDocumentElement = existingEhcacheExampleDoc.getDocumentElement();
12848 
12849       Map<String, String> diskStoreDifferences = null;
12850       
12851       {
12852         Element existingDiskStoreElement = (Element)existingDocumentElement.getElementsByTagName("diskStore").item(0);
12853         Element existingExampleDiskStoreElement = (Element)existingExampleDocumentElement.getElementsByTagName("diskStore").item(0);
12854         diskStoreDifferences = xmlNodeAttributeDifferences(existingExampleDiskStoreElement, existingDiskStoreElement);
12855       }
12856 
12857       Map<String, String> defaultCacheDifferences = null;
12858 
12859       {
12860         Element existingDefaultCacheElement = (Element)existingDocumentElement.getElementsByTagName("defaultCache").item(0);
12861         Element existingExampleDefaultCacheElement = (Element)existingExampleDocumentElement.getElementsByTagName("defaultCache").item(0);
12862         defaultCacheDifferences = xmlNodeAttributeDifferences(existingExampleDefaultCacheElement, existingDefaultCacheElement);
12863         
12864       }
12865 
12866       XPath xpath = XPathFactory.newInstance().newXPath();
12867       
12868       //map of cache name to the differences in the attributes of the cache
12869       Map<String, Map<String, String>> cacheDifferencesByCacheName = new LinkedHashMap<String, Map<String, String>>();
12870       
12871       {
12872         NodeList existingCacheNodeList = existingDocumentElement.getElementsByTagName("cache");
12873         
12874         //loop through all the caches
12875         for (int i=0;i<existingCacheNodeList.getLength(); i++) {
12876           
12877           Element existingCacheElement = (Element)existingCacheNodeList.item(i);
12878 
12879           String cacheName = existingCacheElement.getAttribute("name");
12880 
12881           //find the example cache
12882           XPathExpression expr = xpath.compile("cache[@name='" + cacheName + "']");
12883           Element existingExampleCacheElement = (Element)expr.evaluate(existingExampleDocumentElement, XPathConstants.NODE);
12884 
12885           //see if they differ
12886           Map<String, String> differences = xmlNodeAttributeDifferences(existingExampleCacheElement, existingCacheElement);
12887           
12888           if (differences != null) {
12889             cacheDifferencesByCacheName.put(cacheName, differences);
12890           }
12891         }
12892         
12893         //note, dont worry if there were caches in the example that werent in the configured one
12894         
12895       }      
12896       
12897       //lets see if there are any other nodes
12898       Set<Element> otherNodes = new LinkedHashSet<Element>();
12899       {
12900         NodeList nodeList = existingDocumentElement.getChildNodes();
12901         
12902         for (int i=0;i<nodeList.getLength();i++) {
12903           Node node = nodeList.item(i);
12904           if (node instanceof Element) {
12905             Element nodeElement = (Element)node;
12906             String nodeName = nodeElement.getNodeName();
12907             if (!GrouperInstallerUtils.equals(nodeName, "cache")
12908                 && !GrouperInstallerUtils.equals(nodeName, "defaultCache")
12909                 && !GrouperInstallerUtils.equals(nodeName, "diskStore")) {
12910               otherNodes.add(nodeElement);
12911             }
12912           }
12913         }
12914       }
12915       
12916       //lets copy the new example to both the example and the configured ehcache file
12917       //assume this is already backed up
12918       GrouperInstallerUtils.copyFile(newEhcacheExampleFile, existingEhcacheExampleFile, true);
12919       GrouperInstallerUtils.copyFile(newEhcacheExampleFile, existingEhcacheFile, true);
12920 
12921       //now lets do our edits
12922       if (GrouperInstallerUtils.length(diskStoreDifferences) > 0) {
12923         
12924         for (String attributeName : diskStoreDifferences.keySet()) {
12925 
12926           String attributeValue = diskStoreDifferences.get(attributeName);
12927 
12928           editXmlFileAttribute(existingEhcacheFile, "diskStore", null, attributeName, attributeValue, 
12929               "ehcache diskStore attribute '" + attributeName + "'");
12930           
12931         }
12932       }
12933       
12934       if (GrouperInstallerUtils.length(defaultCacheDifferences) > 0) {
12935         
12936         for (String attributeName : defaultCacheDifferences.keySet()) {
12937           
12938           String attributeValue = defaultCacheDifferences.get(attributeName);
12939 
12940           editXmlFileAttribute(existingEhcacheFile, "defaultCache", null, attributeName, attributeValue, 
12941               "ehcache defaultCache attribute '" + attributeName + "'");
12942 
12943         }
12944       }
12945 
12946       if (GrouperInstallerUtils.length(cacheDifferencesByCacheName) > 0) {
12947 
12948         existingEhcacheDoc = builder.parse(existingEhcacheFile);
12949         existingDocumentElement = existingEhcacheDoc.getDocumentElement();
12950         
12951         for (String cacheName : cacheDifferencesByCacheName.keySet()) {
12952 
12953           //see if the name exists
12954           //find the example cache
12955           XPathExpression expr = xpath.compile("cache[@name='" + cacheName + "']");
12956 
12957           Element existingCacheElement = (Element)expr.evaluate(existingDocumentElement, XPathConstants.NODE);
12958 
12959           Map<String, String> attributeMap = cacheDifferencesByCacheName.get(cacheName);
12960 
12961           //it exists
12962           if (existingCacheElement != null) {
12963 
12964             Map<String, String> expectedAttribute = new HashMap<String, String>();
12965 
12966             expectedAttribute.put("name", cacheName);
12967             
12968             for (String attributeName : attributeMap.keySet()) {
12969 
12970               String attributeValue = attributeMap.get(attributeName);
12971 
12972               editXmlFileAttribute(existingEhcacheFile, "cache", expectedAttribute, attributeName, attributeValue, 
12973                   "ehcache cache name=" + cacheName + " attribute '" + attributeName + "'");
12974             }
12975           } else {
12976 
12977               String fileContents = GrouperInstallerUtils.readFileIntoString(existingEhcacheFile);
12978 
12979               String newline = GrouperInstallerUtils.newlineFromFile(fileContents);
12980 
12981               int lastTagStart = fileContents.lastIndexOf("</ehcache>");
12982               
12983               if (lastTagStart == -1) {
12984                 throw new RuntimeException("Why is </ehcache> not found???? " + fileContents);
12985               }
12986 
12987               String tag = GrouperInstallerUtils.xmlElementToXml("cache", null, attributeMap);
12988 //              sdf
12989               String newFileContents = fileContents.substring(0, lastTagStart) + tag + newline 
12990                   + fileContents.substring(lastTagStart, fileContents.length());
12991 
12992               System.out.println(" - adding ehcache cache " + cacheName);
12993 
12994               GrouperInstallerUtils.saveStringIntoFile(existingEhcacheFile, newFileContents);
12995 
12996           }
12997 
12998         }
12999       }
13000 
13001       if (GrouperInstallerUtils.length(otherNodes) > 0) {
13002         String fileContents = GrouperInstallerUtils.readFileIntoString(existingEhcacheFile);
13003         
13004         String newline = GrouperInstallerUtils.newlineFromFile(fileContents);
13005 
13006         StringBuilder otherNodesStringBuilder = new StringBuilder();
13007         for (Element element : otherNodes) {
13008           String elementString = GrouperInstallerUtils.xmlToString(element);
13009           // take out the xml header: <?xml version="1.0" encoding="UTF-8"?>
13010           
13011           int elementStart = elementString.indexOf("<" + element.getNodeName());
13012           
13013           elementString = elementString.substring(elementStart);
13014           
13015           otherNodesStringBuilder.append(elementString).append(newline);
13016           System.out.println(" - adding element " + element.getTagName());
13017         }
13018 
13019         int lastTagStart = fileContents.lastIndexOf("</ehcache>");
13020         
13021         if (lastTagStart == -1) {
13022           throw new RuntimeException("Why is </ehcache> not found???? " + fileContents);
13023         }
13024 
13025         String newFileContents = fileContents.substring(0, lastTagStart) + otherNodesStringBuilder.toString()
13026             + fileContents.substring(lastTagStart, fileContents.length());
13027 
13028         GrouperInstallerUtils.saveStringIntoFile(existingEhcacheFile, newFileContents);
13029 
13030       }
13031 
13032 
13033       // test the new file, look for things
13034       existingEhcacheDoc = builder.parse(existingEhcacheFile);
13035       existingDocumentElement = existingEhcacheDoc.getDocumentElement();
13036 
13037       if (GrouperInstallerUtils.length(diskStoreDifferences) > 0) {
13038         Element existingDiskStoreElement = (Element)existingDocumentElement.getElementsByTagName("diskStore").item(0);
13039         for (String attributeName : diskStoreDifferences.keySet()) {
13040           String attributeValue = diskStoreDifferences.get(attributeName);
13041           if (!GrouperInstallerUtils.equals(attributeValue, existingDiskStoreElement.getAttribute(attributeName))) {
13042             throw new RuntimeException("Why is diskStore attribute " + attributeName + " not '" + attributeValue + "'" 
13043                 + existingEhcacheFile.getAbsolutePath());
13044           }
13045         }
13046       }
13047       
13048       if (GrouperInstallerUtils.length(defaultCacheDifferences) > 0) {
13049         Element existingDefaultCacheElement = (Element)existingDocumentElement.getElementsByTagName("defaultCache").item(0);
13050         for (String attributeName : defaultCacheDifferences.keySet()) {
13051           String attributeValue = defaultCacheDifferences.get(attributeName);
13052           if (!GrouperInstallerUtils.equals(attributeValue, existingDefaultCacheElement.getAttribute(attributeName))) {
13053             throw new RuntimeException("Why is defaultCache attribute " + attributeName + " not '" + attributeValue + "'" 
13054                 + existingEhcacheFile.getAbsolutePath());
13055           }
13056         }
13057       }
13058 
13059       if (GrouperInstallerUtils.length(cacheDifferencesByCacheName) > 0) {
13060         for (String cacheName : cacheDifferencesByCacheName.keySet()) {
13061 
13062           //see if the name exists
13063           //find the example cache
13064           XPathExpression expr = xpath.compile("cache[@name='" + cacheName + "']");
13065           Element existingCacheElement = (Element)expr.evaluate(existingDocumentElement, XPathConstants.NODE);
13066 
13067           Map<String, String> attributeMap = cacheDifferencesByCacheName.get(cacheName);
13068           
13069           for (String attributeName : attributeMap.keySet()) {
13070             
13071             String attributeValue = attributeMap.get(attributeName);
13072 
13073             if (!GrouperInstallerUtils.equals(attributeValue, existingCacheElement.getAttribute(attributeName))) {
13074               throw new RuntimeException("Why is cache " + cacheName + " attribute " + attributeName + " not '" + attributeValue + "'" 
13075                   + existingEhcacheFile.getAbsolutePath());
13076             }
13077             
13078           }
13079         }
13080       }
13081 
13082       if (GrouperInstallerUtils.length(otherNodes) > 0) {
13083         for (Element element : otherNodes) {
13084           
13085           NodeList nodeList = existingDocumentElement.getElementsByTagName(element.getNodeName());
13086           if (nodeList == null || nodeList.getLength() == 0 ) {
13087             throw new RuntimeException("Why is new element not there? " + element.getTagName() + ", "
13088                 + existingEhcacheFile.getAbsolutePath());
13089           }
13090         }
13091       }
13092 
13093     } catch (Exception e) {
13094       throw new RuntimeException(e.getMessage(), e);
13095     }
13096   }
13097 
13098   /**
13099    * 
13100    * @param newEhcacheExampleFile
13101    * @param existingEhcacheExampleFile
13102    * @param existingEhcacheFile
13103    * @return hasMerging
13104    */
13105   @SuppressWarnings("unused")
13106   private static boolean mergeEhcacheXmlFiles_XML_NOT_USED(File newEhcacheExampleFile, File existingEhcacheExampleFile, 
13107       File existingEhcacheFile) {
13108     
13109     boolean hasMerging = false;
13110     
13111     try {
13112       //lets get the differences of the existing ehcache file and the existing ehcache example file
13113       DocumentBuilderFactory domFactory = GrouperInstallerUtils.xmlDocumentBuilderFactory();
13114       DocumentBuilder builder = domFactory.newDocumentBuilder();
13115       Document existingEhcacheDoc = builder.parse(existingEhcacheFile);
13116       Document existingEhcacheExampleDoc = builder.parse(existingEhcacheExampleFile);
13117 
13118       Element existingDocumentElement = existingEhcacheDoc.getDocumentElement();
13119       Element existingExampleDocumentElement = existingEhcacheExampleDoc.getDocumentElement();
13120 
13121       Map<String, String> diskStoreDifferences = null;
13122       
13123       {
13124         Element existingDiskStoreElement = (Element)existingDocumentElement.getElementsByTagName("diskStore").item(0);
13125         Element existingExampleDiskStoreElement = (Element)existingExampleDocumentElement.getElementsByTagName("diskStore").item(0);
13126         diskStoreDifferences = xmlNodeAttributeDifferences(existingExampleDiskStoreElement, existingDiskStoreElement);
13127       }
13128 
13129       Map<String, String> defaultCacheDifferences = null;
13130 
13131       {
13132         Element existingDefaultCacheElement = (Element)existingDocumentElement.getElementsByTagName("defaultCache").item(0);
13133         Element existingExampleDefaultCacheElement = (Element)existingExampleDocumentElement.getElementsByTagName("defaultCache").item(0);
13134         defaultCacheDifferences = xmlNodeAttributeDifferences(existingExampleDefaultCacheElement, existingDefaultCacheElement);
13135         
13136       }
13137 
13138       XPath xpath = XPathFactory.newInstance().newXPath();
13139       
13140       //map of cache name to the differences in the attributes of the cache
13141       Map<String, Map<String, String>> cacheDifferencesByCacheName = new LinkedHashMap<String, Map<String, String>>();
13142       
13143       {
13144         NodeList existingCacheNodeList = existingDocumentElement.getElementsByTagName("cache");
13145         
13146         //loop through all the caches
13147         for (int i=0;i<existingCacheNodeList.getLength(); i++) {
13148           
13149           Element existingCacheElement = (Element)existingCacheNodeList.item(i);
13150 
13151           String cacheName = existingCacheElement.getAttribute("name");
13152 
13153           //find the example cache
13154           XPathExpression expr = xpath.compile("cache[@name='" + cacheName + "']");
13155           Element existingExampleCacheElement = (Element)expr.evaluate(existingExampleDocumentElement, XPathConstants.NODE);
13156 
13157           //see if they differ
13158           Map<String, String> differences = xmlNodeAttributeDifferences(existingExampleCacheElement, existingCacheElement);
13159           
13160           if (differences != null) {
13161             cacheDifferencesByCacheName.put(cacheName, differences);
13162           }
13163         }
13164         
13165         //note, dont worry if there were caches in the example that werent in the configured one
13166         
13167       }      
13168       
13169       //lets see if there are any other nodes
13170       Set<Element> otherNodes = new LinkedHashSet<Element>();
13171       {
13172         NodeList nodeList = existingDocumentElement.getChildNodes();
13173         
13174         for (int i=0;i<nodeList.getLength();i++) {
13175           Node node = nodeList.item(i);
13176           if (node instanceof Element) {
13177             Element nodeElement = (Element)node;
13178             String nodeName = nodeElement.getNodeName();
13179             if (!GrouperInstallerUtils.equals(nodeName, "cache")
13180                 && !GrouperInstallerUtils.equals(nodeName, "defaultCache")
13181                 && !GrouperInstallerUtils.equals(nodeName, "diskStore")) {
13182               otherNodes.add(nodeElement);
13183             }
13184           }
13185         }
13186       }
13187       
13188       //lets copy the new example to both the example and the configured ehcache file
13189       //assume this is already backed up
13190       GrouperInstallerUtils.copyFile(newEhcacheExampleFile, existingEhcacheExampleFile, true);
13191 
13192       //this is the new existing ehcache file
13193       existingEhcacheExampleDoc = builder.parse(existingEhcacheExampleFile);
13194       existingExampleDocumentElement = existingEhcacheExampleDoc.getDocumentElement();
13195 
13196       //now lets do our edits
13197       if (GrouperInstallerUtils.length(diskStoreDifferences) > 0) {
13198         
13199         hasMerging = true;
13200 
13201         Element existingExampleDiskStoreElement = (Element)existingExampleDocumentElement.getElementsByTagName("diskStore").item(0);
13202 
13203         for (String attributeName : diskStoreDifferences.keySet()) {
13204 
13205           String attributeValue = diskStoreDifferences.get(attributeName);
13206 
13207           existingExampleDiskStoreElement.setAttribute(attributeName, attributeValue);
13208         }
13209       }
13210       
13211       if (GrouperInstallerUtils.length(defaultCacheDifferences) > 0) {
13212 
13213         hasMerging = true;
13214 
13215         Element existingExampleDefaultCacheElement = (Element)existingExampleDocumentElement.getElementsByTagName("defaultCache").item(0);
13216         
13217         for (String attributeName : defaultCacheDifferences.keySet()) {
13218           
13219           String attributeValue = defaultCacheDifferences.get(attributeName);
13220 
13221           existingExampleDefaultCacheElement.setAttribute(attributeName, attributeValue);
13222           
13223         }
13224       }
13225 
13226       if (GrouperInstallerUtils.length(cacheDifferencesByCacheName) > 0) {
13227         hasMerging = true;
13228         for (String cacheName : cacheDifferencesByCacheName.keySet()) {
13229 
13230           //see if the name exists
13231           //find the example cache
13232           XPathExpression expr = xpath.compile("cache[@name='" + cacheName + "']");
13233           Element existingExampleCacheElement = (Element)expr.evaluate(existingExampleDocumentElement, XPathConstants.NODE);
13234 
13235           Map<String, String> attributeMap = cacheDifferencesByCacheName.get(cacheName);
13236           
13237           //it exists
13238           if (existingExampleCacheElement != null) {
13239             
13240             for (String attributeName : attributeMap.keySet()) {
13241               
13242               String attributeValue = attributeMap.get(attributeName);
13243               existingExampleCacheElement.setAttribute(attributeName, attributeValue);
13244               
13245             }
13246           } else {
13247             
13248             Element existingCacheElement = (Element)expr.evaluate(existingDocumentElement, XPathConstants.NODE);
13249             //move a cache from one document to another
13250             existingExampleDocumentElement.appendChild(existingCacheElement.cloneNode(true));
13251             
13252           }
13253           
13254         }
13255       }
13256 
13257       if (GrouperInstallerUtils.length(otherNodes) > 0) {
13258         hasMerging = true;
13259         for (Element element : otherNodes) {
13260           
13261           //move a cache from one document to another
13262           existingExampleDocumentElement.appendChild(element.cloneNode(true));
13263         }
13264       }
13265 
13266 //      System.out.println("Compare you old ehcache.xml with your new ehcache.xml file: " 
13267 //          + "\n  Old file: "
13268 //          + backedUpEhcacheFile.getAbsolutePath()
13269 //          + "\n  New file: " + existingEhcacheFile.getAbsolutePath());
13270 
13271       // save to file
13272       String xml = GrouperInstallerUtils.xmlToString(existingEhcacheExampleDoc);
13273       GrouperInstallerUtils.saveStringIntoFile(existingEhcacheFile, xml);
13274       
13275       // test the new file, look for things
13276       existingEhcacheDoc = builder.parse(existingEhcacheFile);
13277       existingDocumentElement = existingEhcacheDoc.getDocumentElement();
13278 
13279       if (GrouperInstallerUtils.length(diskStoreDifferences) > 0) {
13280         Element existingDiskStoreElement = (Element)existingDocumentElement.getElementsByTagName("diskStore").item(0);
13281         for (String attributeName : diskStoreDifferences.keySet()) {
13282           String attributeValue = diskStoreDifferences.get(attributeName);
13283           if (!GrouperInstallerUtils.equals(attributeValue, existingDiskStoreElement.getAttribute(attributeName))) {
13284             throw new RuntimeException("Why is diskStore attribute " + attributeName + " not '" + attributeValue + "'" 
13285                 + existingEhcacheFile.getAbsolutePath());
13286           }
13287         }
13288       }
13289       
13290       if (GrouperInstallerUtils.length(defaultCacheDifferences) > 0) {
13291         Element existingDefaultCacheElement = (Element)existingDocumentElement.getElementsByTagName("defaultCache").item(0);
13292         for (String attributeName : defaultCacheDifferences.keySet()) {
13293           String attributeValue = defaultCacheDifferences.get(attributeName);
13294           if (!GrouperInstallerUtils.equals(attributeValue, existingDefaultCacheElement.getAttribute(attributeName))) {
13295             throw new RuntimeException("Why is defaultCache attribute " + attributeName + " not '" + attributeValue + "'" 
13296                 + existingEhcacheFile.getAbsolutePath());
13297           }
13298         }
13299       }
13300 
13301       if (GrouperInstallerUtils.length(cacheDifferencesByCacheName) > 0) {
13302         for (String cacheName : cacheDifferencesByCacheName.keySet()) {
13303 
13304           //see if the name exists
13305           //find the example cache
13306           XPathExpression expr = xpath.compile("cache[@name='" + cacheName + "']");
13307           Element existingCacheElement = (Element)expr.evaluate(existingDocumentElement, XPathConstants.NODE);
13308 
13309           Map<String, String> attributeMap = cacheDifferencesByCacheName.get(cacheName);
13310           
13311           for (String attributeName : attributeMap.keySet()) {
13312             
13313             String attributeValue = attributeMap.get(attributeName);
13314 
13315             if (!GrouperInstallerUtils.equals(attributeValue, existingCacheElement.getAttribute(attributeName))) {
13316               throw new RuntimeException("Why is cache " + cacheName + " attribute " + attributeName + " not '" + attributeValue + "'" 
13317                   + existingEhcacheFile.getAbsolutePath());
13318             }
13319             
13320           }
13321         }
13322       }
13323 
13324       if (GrouperInstallerUtils.length(otherNodes) > 0) {
13325         for (Element element : otherNodes) {
13326           
13327           NodeList nodeList = existingDocumentElement.getElementsByTagName(element.getNodeName());
13328           if (nodeList == null || nodeList.getLength() == 0 ) {
13329             throw new RuntimeException("Why is new element not there? " + element.getTagName() 
13330                 + existingEhcacheFile.getAbsolutePath());
13331           }
13332         }
13333       }
13334 
13335     } catch (Exception e) {
13336       throw new RuntimeException(e.getMessage(), e);
13337     }
13338     return hasMerging;
13339   }
13340   /**
13341    * 
13342    * @param baseElement
13343    * @param configuredElement
13344    * @return the map of differences
13345    */
13346   public static Map<String, String> xmlNodeAttributeDifferences(Element baseElement, Element configuredElement) {
13347     NamedNodeMap configuredNamedNodeMap = configuredElement.getAttributes();
13348     
13349     Map<String, String> result = null;
13350     
13351     //see which attributes are new or changed
13352     for (int i=0;i<configuredNamedNodeMap.getLength();i++) {
13353       Node configuredAttribute = configuredNamedNodeMap.item(i);
13354       Node baseAttribute = baseElement == null ? null : baseElement.getAttributeNode(configuredAttribute.getNodeName());
13355 
13356       String configuredValue = configuredAttribute.getNodeValue();
13357       String baseValue = baseAttribute == null ? null : baseAttribute.getNodeValue();
13358       
13359       if (!GrouperInstallerUtils.equals(configuredValue, baseValue)) {
13360         if (result == null) {
13361           result = new LinkedHashMap<String, String>();
13362         }
13363         result.put(configuredAttribute.getNodeName(), configuredValue);
13364       }
13365     }
13366     
13367     //see which ones are missing
13368     NamedNodeMap baseNamedNodeMap = baseElement == null ? null : baseElement.getAttributes();
13369     
13370     //see which attributes are new or changed
13371     for (int i=0;i<(baseNamedNodeMap == null ? 0 : baseNamedNodeMap.getLength());i++) {
13372       
13373       Node baseAttribute = configuredNamedNodeMap.item(0);
13374       Node configuredAttribute = configuredElement.getAttributeNode(baseAttribute.getNodeName());
13375 
13376       String baseValue = baseAttribute.getNodeValue();
13377       String configuredValue = configuredAttribute == null ? null : configuredAttribute.getNodeValue();
13378       
13379       if (configuredValue == null && !GrouperInstallerUtils.equals(configuredValue, baseValue)) {
13380         if (result == null) {
13381           result = new LinkedHashMap<String, String>();
13382         }
13383         result.put(baseAttribute.getNodeName(), configuredValue);
13384       }
13385     }
13386     
13387     return result;
13388   }
13389   
13390   /**
13391    * 
13392    * @return the file of the directory of API
13393    */
13394   private File downloadApi() {
13395     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
13396     
13397     if (!urlToDownload.endsWith("/")) {
13398       urlToDownload += "/";
13399     }
13400     urlToDownload += "release/";
13401     String apiFileName = "grouper.apiBinary-" + this.version + ".tar.gz";
13402     urlToDownload += this.version + "/" + apiFileName;
13403 
13404     File apiFile = new File(this.grouperTarballDirectoryString + apiFileName);
13405     
13406     downloadFile(urlToDownload, apiFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalApiDownloadTarEtc");
13407 
13408     return apiFile;
13409   }
13410 
13411   /**
13412    * 
13413    * @return the file of the directory of UI
13414    */
13415   private File downloadUi() {
13416     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
13417     
13418     if (!urlToDownload.endsWith("/")) {
13419       urlToDownload += "/";
13420     }
13421     urlToDownload += "release/";
13422 
13423     String uiFileName = "grouper.ui-" + this.version + ".tar.gz";
13424     urlToDownload += this.version + "/" + uiFileName;
13425 
13426     File uiFile = new File(this.grouperTarballDirectoryString + uiFileName);
13427     
13428     downloadFile(urlToDownload, uiFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalUiDownloadTarEtc");
13429 
13430     return uiFile;
13431   }
13432   
13433   /**
13434    * 
13435    * @return the file of the directory of WS
13436    */
13437   private File downloadWs() {
13438     
13439     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
13440     
13441     //String urlToDownload = "http://localhost:8085/grouper/grouperExternal/public/assets/dojo/dijit/themes/claro/images";
13442     
13443     if (!urlToDownload.endsWith("/")) {
13444       urlToDownload += "/";
13445     }
13446     urlToDownload += "release/";
13447 
13448     String wsFileName = "grouper.ws-" + this.version + ".tar.gz";
13449     urlToDownload += this.version + "/" + wsFileName;
13450 
13451     File wsFile = new File(this.grouperTarballDirectoryString + wsFileName);
13452     
13453     System.out.println("wsFile path is "+wsFile.getAbsolutePath());
13454     downloadFile(urlToDownload, wsFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalWsDownloadTarEtc");
13455 
13456     return wsFile;
13457   }
13458 
13459   /**
13460    * 
13461    * @return the file of the directory of ant
13462    */
13463   private File downloadAnt() {
13464     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
13465     
13466     if (!urlToDownload.endsWith("/")) {
13467       urlToDownload += "/";
13468     }
13469 
13470     urlToDownload += "downloads/tools/apache-ant-1.8.2-bin.tar.gz";
13471     
13472     File antFile = new File(this.grouperTarballDirectoryString + "apache-ant-1.8.2-bin.tar.gz");
13473     
13474     downloadFile(urlToDownload, antFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
13475 
13476     return antFile;
13477   }
13478 
13479   /**
13480    * 
13481    * @return the file of the directory of maven
13482    */
13483   private File downloadMaven() {
13484     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
13485     
13486     if (!urlToDownload.endsWith("/")) {
13487       urlToDownload += "/";
13488     }
13489 
13490     urlToDownload += "downloads/tools/apache-maven-3.6.3-bin.tar.gz";
13491     
13492     File mavenFile = new File(this.grouperTarballDirectoryString + "apache-maven-3.6.3-bin.tar.gz");
13493     
13494     downloadFile(urlToDownload, mavenFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
13495 
13496     return mavenFile;
13497   }
13498 
13499   /**
13500    * tomcat version
13501    */
13502   private String tomcatVersion = "8.5.42";
13503   
13504   /**
13505    * 
13506    * @return tomcat version
13507    */
13508   private String tomcatVersion() {
13509     
13510     // this is now hardcoded
13511     if (this.tomcatVersion == null) {
13512       
13513       String defaultTomcatVersion = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.tomcat.version", false);
13514       defaultTomcatVersion = GrouperInstallerUtils.defaultIfBlank(defaultTomcatVersion, "8.5.42");
13515       
13516       System.out.print("Enter the tomcat version (8.5.42 or 8.5.12 or 6.0.35) [" + defaultTomcatVersion + "]: ");
13517       this.tomcatVersion = readFromStdIn("grouperInstaller.autorun.tomcat.version");
13518       
13519       this.tomcatVersion = GrouperInstallerUtils.defaultIfBlank(this.tomcatVersion, defaultTomcatVersion);
13520       
13521       if (!GrouperInstallerUtils.equals(this.tomcatVersion, "8.5.42") && !GrouperInstallerUtils.equals(this.tomcatVersion, "6.0.35")) {
13522         System.out.print("Warning: this *should* be 8.5.42 or 8.5.12 or 6.0.35, hit <Enter> to continue: ");
13523         readFromStdIn("grouperInstaller.autorun.tomcat.version.mismatch");
13524       }
13525       
13526     }
13527     
13528     return this.tomcatVersion;
13529 
13530   }
13531   
13532   /**
13533    * Copy a jar file to another file.  this perserves the file date
13534    * 
13535    * @param sourceFile
13536    * @param destinationFile
13537    * @param onlyIfDifferentContents true if only saving due to different contents.  Note, this is only for non-binary files!
13538    * @param ignoreWhitespace true to ignore whitespace in comparisons
13539    * @return true if contents were saved (thus different if param set)
13540    */
13541   public static boolean copyJarFileIfNotExists(File sourceFile, File destinationFile, boolean onlyIfDifferentContents, boolean ignoreWhitespace) {
13542     
13543     if (!sourceFile.isFile() || !sourceFile.exists()) {
13544       throw new RuntimeException("Why does this not exist???? " + sourceFile.getAbsolutePath());
13545     }
13546     
13547     if (destinationFile.isFile() && destinationFile.exists() && 
13548         GrouperInstallerUtils.equals(GrouperInstallerUtils.fileSha1(destinationFile), GrouperInstallerUtils.fileSha1(sourceFile))) {
13549       System.out.println("Skipping file that exists in destination: " + destinationFile.getAbsolutePath());
13550       return false;
13551     }
13552 
13553     if (onlyIfDifferentContents) {
13554       String sourceContents = GrouperInstallerUtils.readFileIntoString(sourceFile);
13555       return GrouperInstallerUtils.saveStringIntoFile(destinationFile, sourceContents, 
13556           onlyIfDifferentContents, ignoreWhitespace);
13557     }
13558     
13559     File destinationFolder = destinationFile.getParentFile();
13560     
13561     Set<String> relatedBaseNames = GrouperInstallerUtils.jarFileBaseNames(destinationFile.getName());
13562 
13563     boolean hasConflict = false;
13564     for (File destinationCandidateFile : destinationFolder.listFiles()) {
13565       if (!destinationCandidateFile.getName().endsWith(".jar")) {
13566         continue;
13567       }
13568       Set<String> relatedCandidateBaseNames = GrouperInstallerUtils.jarFileBaseNames(destinationCandidateFile.getName());
13569       if (GrouperInstallerUtils.containsAny(relatedBaseNames, relatedCandidateBaseNames)) {
13570         
13571         hasConflict = true;
13572       }
13573     }
13574     
13575     if (hasConflict) {
13576       List<File> relatedFiles = GrouperInstallerUtils.jarFindJar(destinationFolder, sourceFile.getName());
13577       
13578       if (GrouperInstallerUtils.length(relatedFiles) == 1) {
13579         File relatedFile = relatedFiles.iterator().next();
13580         File newerVersion = GrouperInstallerUtils.jarNewerVersion(relatedFile, sourceFile);
13581         if (newerVersion != null) {
13582           
13583           if (newerVersion.equals(sourceFile)) {
13584             System.out.println("There is a conflicting jar: " + sourceFile.getAbsolutePath());
13585             System.out.println("Deleting older jar: " + relatedFile.getAbsolutePath());
13586             GrouperInstallerUtils.fileDelete(relatedFile);
13587             System.out.println("Copying " + sourceFile.getAbsolutePath() + " to " + destinationFile.getAbsolutePath());
13588             GrouperInstallerUtils.copyFile(sourceFile, destinationFile);
13589             return true;
13590           }
13591           System.out.println("There is a conflicting jar for source: " + sourceFile.getAbsolutePath());
13592           System.out.println("Not copying to dest due to this jar is newer: " + relatedFile.getAbsolutePath());
13593           return false;
13594         }
13595         System.out.println("There is a conflicting jar, source jar: " + sourceFile.getAbsolutePath());
13596         System.out.println("Destination jar: " + destinationFile.getAbsolutePath());
13597         System.out.print("Unable to resolve conflict, resolve manually, press <enter> to continue... ");
13598         readFromStdIn("grouperInstaller.autorun.conflictingJarContinue");
13599         return false;
13600       }
13601 
13602     }
13603     
13604     System.out.println("Copying " + sourceFile.getAbsolutePath() + " to " + destinationFile.getAbsolutePath());
13605     GrouperInstallerUtils.copyFile(sourceFile, destinationFile);
13606     return true;
13607   }
13608 
13609   /**
13610    * 
13611    * @return the file of the directory of tomcat
13612    */
13613   private File downloadTomcat() {
13614     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
13615     
13616     if (!urlToDownload.endsWith("/")) {
13617       urlToDownload += "/";
13618     }
13619 
13620     urlToDownload += "downloads/tools/apache-tomcat-" + this.tomcatVersion() + ".tar.gz";
13621     
13622     File tomcatFile = new File(this.grouperTarballDirectoryString + "apache-tomcat-" + this.tomcatVersion() + ".tar.gz");
13623     
13624     downloadFile(urlToDownload, tomcatFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
13625 
13626     return tomcatFile;
13627   }
13628   
13629   public static final String TOMEE_VERSION = "7.0.9";
13630   
13631   /**
13632    * 
13633    * @return the file of the directory of tomee
13634    */
13635   private File downloadTomee() {
13636     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
13637     
13638     //String urlToDownload = "http://localhost:8085/grouper/grouperExternal/public/assets/dojo/dijit/themes/claro/images";
13639     
13640     if (!urlToDownload.endsWith("/")) {
13641       urlToDownload += "/";
13642     }
13643 
13644     urlToDownload += "downloads/tools/apache-tomee-" + TOMEE_VERSION + "-webprofile.tar.gz";
13645     
13646     File tomeeFile = new File(this.grouperTarballDirectoryString + "apache-tomee-" + TOMEE_VERSION + "-webprofile.tar.gz");
13647     
13648     downloadFile(urlToDownload, tomeeFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
13649 
13650     return tomeeFile;
13651   }
13652   
13653   private File downloadGrouperSourceTagFromGithub() {
13654     
13655     File grouperSourceCodeFile = new File(this.grouperTarballDirectoryString + "GROUPER_RELEASE_"+this.version+".tar.gz");
13656     downloadFile("https://github.com/Internet2/grouper/archive/GROUPER_RELEASE_"+this.version+".tar.gz", grouperSourceCodeFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
13657     return grouperSourceCodeFile;
13658   }
13659   
13660   private void deleteJarsFromLibDirs(File webInfDir) {
13661     
13662     List<File> allJarsToBeDeleted = new ArrayList<File>();
13663     
13664     List<File> libDirs = new ArrayList<File>();
13665     libDirs.add(new File(webInfDir+File.separator+"lib"));
13666     libDirs.add(new File(webInfDir+File.separator+"libUiAndDaemon"));
13667     libDirs.add(new File(webInfDir+File.separator+"libWs"));
13668     libDirs.add(new File(webInfDir+File.separator+"libScim"));
13669     
13670     for (File libDir: libDirs) {
13671       File[] filesFromLibToBeDeleted = libDir.listFiles(new FilenameFilter() {
13672         
13673         @Override
13674         public boolean accept(File dir, String name) {
13675           if (name.startsWith("slf4j-api") && name.endsWith("jar")) {
13676             return true;
13677           }
13678           
13679           if (name.startsWith("slf4j-log4j12") && name.endsWith("jar")) {
13680             return true;
13681           }
13682                     
13683           return false;
13684         }
13685       });
13686       allJarsToBeDeleted.addAll(Arrays.asList(filesFromLibToBeDeleted));
13687     }
13688     
13689     for (File jarToBeDeleted: allJarsToBeDeleted) {
13690       GrouperInstallerUtils.fileDelete(jarToBeDeleted);
13691     }
13692     
13693   }
13694   
13695   private void downloadGrouperJarsIntoLibDirectory(File webInfDir) {
13696     String basePath = "https://oss.sonatype.org/service/local/repositories/releases/content/edu/internet2/middleware/grouper/";
13697     
13698     {
13699       File libDir = new File(webInfDir+File.separator+"lib");
13700       
13701       List<String> urlsToDownload = new ArrayList<String>();
13702       urlsToDownload.add(basePath+"grouper/"+this.version+"/grouper-"+this.version+".jar");
13703       urlsToDownload.add(basePath+"grouperClient/"+this.version+"/grouperClient-"+this.version+".jar");
13704       
13705       for (String urlToDownload: urlsToDownload) {
13706         String fileName = urlToDownload.substring(urlToDownload.lastIndexOf(File.separator)+1, urlToDownload.length());
13707         downloadFile(urlToDownload, libDir.getAbsolutePath() + File.separator+ fileName, "grouperInstaller.autorun.buildContainerUseExistingJarIfExists");
13708       }
13709     }
13710     
13711     {
13712       File libUiAndDaemonDir = new File(webInfDir+File.separator+"libUiAndDaemon");
13713       List<String> urlsToDownload = new ArrayList<String>();
13714       urlsToDownload.add(basePath+"grouper-messaging-aws/"+this.version+"/grouper-messaging-aws-"+this.version+".jar");
13715       urlsToDownload.add(basePath+"google-apps-provisioner/"+this.version+"/google-apps-provisioner-"+this.version+".jar");
13716       urlsToDownload.add(basePath+"grouper-messaging-rabbitmq/"+this.version+"/grouper-messaging-rabbitmq-"+this.version+".jar");
13717       urlsToDownload.add(basePath+"grouper-messaging-activemq/"+this.version+"/grouper-messaging-activemq-"+this.version+".jar");
13718       urlsToDownload.add(basePath+"grouper-ui/"+this.version+"/grouper-ui-"+this.version+".jar");
13719       urlsToDownload.add(basePath+"grouper-pspng/"+this.version+"/grouper-pspng-"+this.version+".jar");
13720       urlsToDownload.add(basePath+"grouper-box/"+this.version+"/grouper-box-"+this.version+".jar");
13721       urlsToDownload.add(basePath+"grouper-duo/"+this.version+"/grouper-duo-"+this.version+".jar");
13722       urlsToDownload.add(basePath+"grouper-azure-provisioner/"+this.version+"/grouper-azure-provisioner-"+this.version+".jar");
13723       for (String urlToDownload: urlsToDownload) {
13724         String fileName = urlToDownload.substring(urlToDownload.lastIndexOf(File.separator)+1, urlToDownload.length());
13725         downloadFile(urlToDownload, libUiAndDaemonDir.getAbsolutePath() + File.separator+ fileName, "grouperInstaller.autorun.buildContainerUseExistingJarIfExists");
13726       }
13727     }
13728     
13729     {
13730       File libWsDir = new File(webInfDir+File.separator+"libWs");
13731       String wsUrlToDownload = basePath+"grouper-ws/"+this.version+"/grouper-ws-"+this.version+".jar";
13732       String wsJarfileName = wsUrlToDownload.substring(wsUrlToDownload.lastIndexOf(File.separator)+1, wsUrlToDownload.length());
13733       downloadFile(wsUrlToDownload, libWsDir.getAbsolutePath() + File.separator+ wsJarfileName, "grouperInstaller.autorun.buildContainerUseExistingJarIfExists");
13734     }
13735     
13736     {
13737       File libScimDir = new File(webInfDir+File.separator+"libScim");
13738       String scimUrlToDownload = basePath+"grouper-ws-scim/"+this.version+"/grouper-ws-scim-"+this.version+".jar";
13739       String scimJarfileName = scimUrlToDownload.substring(scimUrlToDownload.lastIndexOf(File.separator)+1, scimUrlToDownload.length());
13740       downloadFile(scimUrlToDownload, libScimDir.getAbsolutePath() + File.separator+ scimJarfileName, "grouperInstaller.autorun.buildContainerUseExistingJarIfExists");
13741     }
13742     
13743   }
13744 
13745   /**
13746    * add quick start subjects
13747    */
13748   private void addQuickstartSubjects() {
13749     
13750     System.out.print("Do you want to add quickstart subjects to DB (t|f)? [t]: ");
13751     boolean addQuickstartSubjects = readFromStdInBoolean(true, "grouperInstaller.autorun.addQuickstartSubjectsToDb");
13752     
13753     if (addQuickstartSubjects) {
13754 
13755       String url = GrouperInstallerUtils.propertiesValue("download.server.url", true);
13756       
13757       if (!url.endsWith("/")) {
13758         url += "/";
13759       }
13760       url += "release/" + this.version + "/subjects.sql";
13761 
13762       String subjectsSqlFileName = this.untarredApiDir.getParent() + File.separator + "subjects.sql";
13763       File subjectsSqlFile = new File(subjectsSqlFileName);
13764       downloadFile(url, subjectsSqlFileName, "grouperInstaller.autorun.useLocalApiDownloadTarEtc");
13765 
13766       List<String> commands = new ArrayList<String>();
13767       
13768       addGshCommands(commands);
13769       commands.add("-registry");
13770       commands.add("-runsqlfile");
13771       commands.add(subjectsSqlFile.getAbsolutePath());
13772       commands.add("-noprompt");
13773       
13774       System.out.println("\n##################################");
13775       System.out.println("Adding sample subjects with command: " + convertCommandsIntoCommand(commands) + "\n");
13776       
13777       CommandResult commandResult = GrouperInstallerUtils.execCommand(
13778           GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
13779           this.untarredApiDir, null, true);
13780       
13781       if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
13782         System.out.println("stderr: " + commandResult.getErrorText());
13783       }
13784       if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
13785         System.out.println("stdout: " + commandResult.getOutputText());
13786       }
13787       
13788       System.out.println("\nEnd adding sample subjects");
13789       System.out.println("##################################\n");
13790 
13791     }
13792   }
13793 
13794   /**
13795    * add quick start subjects
13796    */
13797   private void addQuickstartData() {
13798 
13799     System.out.print("Do you want to add quickstart data to registry (t|f)? [t] ");
13800     boolean addQuickstartData = readFromStdInBoolean(true, "grouperInstaller.autorun.addQuickstartData");
13801 
13802     if (addQuickstartData) {
13803       String url = GrouperInstallerUtils.propertiesValue("download.server.url", true);
13804       
13805       if (!url.endsWith("/")) {
13806         url += "/";
13807       }
13808       url += "release/" + this.version + "/quickstart.xml";
13809       String quickstartFileName = this.untarredApiDir.getParent() + File.separator + "quickstart.xml";
13810       
13811       File quickstartFile = new File(quickstartFileName);
13812       downloadFile(url, quickstartFileName, "grouperInstaller.autorun.useLocalApiDownloadTarEtc");
13813 
13814       List<String> commands = new ArrayList<String>();
13815       
13816       addGshCommands(commands);
13817       commands.add("-xmlimportold");
13818       commands.add("GrouperSystem");
13819       commands.add(quickstartFile.getAbsolutePath());
13820       commands.add("-noprompt");
13821       
13822       System.out.println("\n##################################");
13823       System.out.println("Adding quickstart data with command: " + convertCommandsIntoCommand(commands) + "\n");
13824       
13825       CommandResult commandResult = GrouperInstallerUtils.execCommand(
13826           GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
13827           this.untarredApiDir, null, true);
13828       
13829       if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
13830         System.out.println("stderr: " + commandResult.getErrorText());
13831       }
13832       if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
13833 
13834         System.out.println("stdout: " + commandResult.getOutputText());
13835       }
13836       System.out.println("\nEnd adding quickstart data");
13837       System.out.println("##################################\n");
13838 
13839     }
13840   }
13841   
13842   /**
13843    * if commands have spaces, put quotes around...
13844    * @param commands
13845    * @return the command
13846    */
13847   private static String convertCommandsIntoCommand(List<String> commands) {
13848     StringBuilder result = new StringBuilder();
13849     for (int i=0;i<GrouperInstallerUtils.length(commands); i++) {
13850       String command = GrouperInstallerUtils.defaultString(commands.get(i));
13851       
13852       //if there is a space, put quotes around command
13853       if (command.contains(" ")) {
13854         result.append("\"").append(command).append("\"");
13855       } else {
13856         result.append(command);
13857       }
13858       if (i != GrouperInstallerUtils.length(commands)-1) {
13859         result.append(" ");
13860       }
13861     }
13862     return result.toString();
13863   }
13864   
13865   /**
13866    * 
13867    */
13868   private void initDb() {
13869     System.out.print("Do you want to init the database (delete all existing grouper tables, add new ones) (t|f)? ");
13870     boolean initdb = readFromStdInBoolean(null, "grouperInstaller.autorun.deleteAndInitDatabase");
13871     
13872     if (initdb) {
13873       List<String> commands = new ArrayList<String>();
13874       
13875       addGshCommands(commands);
13876       commands.add("-registry");
13877       commands.add("-drop");
13878       commands.add("-runscript");
13879       commands.add("-noprompt");
13880       
13881       System.out.println("\n##################################");
13882       System.out.println("Initting DB with command: " + convertCommandsIntoCommand(commands) + "\n");
13883       
13884       CommandResult commandResult = GrouperInstallerUtils.execCommand(
13885           GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
13886           this.untarredApiDir, null, true);
13887       
13888       if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
13889         System.out.println("stderr: " + commandResult.getErrorText());
13890       }
13891       if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
13892 
13893         System.out.println("stdout: " + commandResult.getOutputText());
13894       }
13895       System.out.println("\nEnd Initting DB");
13896       System.out.println("##################################\n");
13897       
13898       
13899     }
13900 
13901   }
13902   
13903   /**
13904    * @param prompt
13905    */
13906   private void startLoader(boolean prompt) {
13907     
13908     boolean startLoader = true;
13909     
13910     if (prompt) {
13911       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 " 
13912           + (GrouperInstallerUtils.isWindows() ? "the task manager for java.exe" : "ps -ef | grep gsh | grep loader") + ") (t|f)? [f]: ");
13913       startLoader = readFromStdInBoolean(false, "grouperInstaller.autorun.startGrouperDaemons");
13914     }
13915     
13916     if (startLoader) {
13917       final List<String> commands = new ArrayList<String>();
13918       
13919       addGshCommands(commands);
13920       commands.add("-loader");
13921       
13922       if (!GrouperInstallerUtils.isWindows()) {
13923         
13924         //let this database run forever
13925         commands.add(0, "nohup");
13926         //run in new process
13927         commands.add("> /dev/null 2>&1 &");
13928         
13929         String fullCommand = GrouperInstallerUtils.join(commands.iterator(), ' ');
13930         commands.clear();
13931         commands.add(shCommand());
13932         commands.add("-c");
13933         commands.add(fullCommand);
13934         
13935       }
13936       System.out.println("\n##################################");
13937       System.out.println("Starting the Grouper loader (daemons): " + convertCommandsIntoCommand(commands) + "\n");
13938 
13939       //start in new thread
13940       Thread thread = new Thread(new Runnable() {
13941         
13942         @Override
13943         public void run() {
13944           GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
13945               true, true, null, GrouperInstaller.this.untarredApiDir, 
13946               GrouperInstaller.this.grouperInstallDirectoryString + "grouperLoader", false);
13947         }
13948       });
13949       thread.setDaemon(true);
13950       thread.start();
13951       
13952       System.out.println("\nEnd starting the Grouper loader (daemons)");
13953       System.out.println("##################################\n");
13954       
13955     }
13956 
13957   }
13958 
13959   /**
13960    * gsh command fully qualified
13961    */
13962   private String gshCommand;
13963   
13964   /**
13965    * 
13966    * @return the gsh command
13967    */
13968   private String gshCommand() {
13969 
13970     if (this.gshCommand == null) {
13971 
13972       String gshDir = GrouperInstallerUtils.defaultIfBlank(this.upgradeExistingApplicationDirectoryString, 
13973           this.untarredApiDir.getAbsolutePath() + File.separator);
13974       
13975       String gsh = gshDir + "bin" + File.separator 
13976           + (GrouperInstallerUtils.isWindows() ? "gsh.bat" : "gsh.sh");
13977       
13978       if (new File(gsh).exists()) {
13979         this.gshCommand = gsh;
13980         return gsh;
13981       }
13982 
13983       gsh = gshDir + "WEB-INF" + File.separator + "bin" + File.separator 
13984           + (GrouperInstallerUtils.isWindows() ? "gsh.bat" : "gsh.sh");
13985 
13986       if (new File(gsh).exists()) {
13987         this.gshCommand = gsh;
13988         return gsh;
13989       }
13990       
13991       throw new RuntimeException("Cant find gsh: " + gshDir);
13992     }
13993 
13994     return this.gshCommand;
13995   }
13996 
13997   /**
13998    * 
13999    */
14000   private void checkDatabaseConnection() {
14001     System.out.println("Checking database with query: " + this.giDbUtils.checkConnectionQuery());
14002     Exception exception = this.giDbUtils.checkConnection();
14003     if (exception == null) {
14004       System.out.println("Successfully tested database connection");
14005     } else {
14006       if (GrouperInstallerUtils.getFullStackTrace(exception).contains(ClassNotFoundException.class.getName())) {
14007         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");
14008       } else {
14009         System.out.println("Error: could not connect to the database: ");
14010         exception.printStackTrace();
14011       }
14012     }
14013   }
14014 
14015   /**
14016    * get hsql port
14017    * @return port
14018    */
14019   private int hsqlPort() {
14020     //get right port
14021     int port = 9001;
14022     
14023     //match this, get the port: jdbc:hsqldb:hsql://localhost:9001/grouper
14024     Pattern pattern = Pattern.compile("jdbc:hsqldb:.*:(\\d+)/.*");
14025     Matcher matcher = pattern.matcher(this.dbUrl);
14026     if (matcher.matches()) {
14027       port = GrouperInstallerUtils.intValue(matcher.group(1));
14028     }
14029     return port;
14030   }
14031   
14032   /**
14033    * @param prompt
14034    */
14035   private void startHsqlDb(boolean prompt) {
14036     boolean startdb = true;
14037     
14038     if (prompt) {
14039       System.out.print("Do you want this script to start the hsqldb database (note, it must not be running in able to start) (t|f)? [t]: ");
14040       startdb = readFromStdInBoolean(true, "grouperInstaller.autorun.startHsqlDatabase");
14041     }
14042     if (startdb) {
14043       
14044       //get right port
14045       int port = hsqlPort();
14046 
14047       if (!GrouperInstallerUtils.portAvailable(port, this.defaultIpAddress)) {
14048         shutdownHsql();
14049       }
14050 
14051       if (!GrouperInstallerUtils.portAvailable(port, this.defaultIpAddress)) {
14052         System.out.println("This port does not seem available, even after trying to stop the DB! " + port + "...");
14053         if (!shouldContinue("grouperInstaller.autorun.continueAfterPortNotAvailable")) {
14054           throw new RuntimeException("This port is not available, even after trying to stop the DB! " + port);
14055         }
14056       }
14057       
14058       final List<String> command = new ArrayList<String>();
14059 
14060       command.add(getJavaCommand());
14061       command.add("-cp");
14062       command.add(this.untarredApiDir + File.separator + "lib" + File.separator + "jdbcSamples" + File.separator 
14063           + "*");
14064       //-cp lib\jdbcSamples\hsqldb.jar org.hsqldb.Server -database.0 file:grouper -dbname.0 grouper -port 9001
14065       command.addAll(GrouperInstallerUtils.splitTrimToList("org.hsqldb.Server -database.0 file:" 
14066           + this.untarredApiDir + File.separator + "grouper -dbname.0 grouper -port " + port , " "));
14067       
14068       if (!GrouperInstallerUtils.isWindows()) {
14069 
14070         //let this database run forever
14071         command.add(0, "nohup");
14072         //run in new process
14073         command.add("> /dev/null 2>&1 &");
14074       
14075       }
14076       
14077 //        System.out.println("Starting DB with command: java -cp grouper.apiBinary-" + this.version + File.separator 
14078 //            + "lib" + File.separator + "jdbcSamples" + File.separator 
14079 //            + "hsqldb.jar org.hsqldb.Server -database.0 file:grouper -dbname.0 grouper");
14080 
14081       System.out.println("Starting DB with command: " + GrouperInstallerUtils.join(command.iterator(), " "));
14082 
14083       //start in new thread
14084       Thread thread = new Thread(new Runnable() {
14085         
14086         @Override
14087         public void run() {
14088           GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(command, String.class),
14089               true, true, null, null, 
14090               GrouperInstaller.this.grouperInstallDirectoryString + "hsqlDb", false, false);
14091         }
14092       });
14093       thread.setDaemon(true);
14094       thread.start();
14095       
14096     }
14097     
14098     //lets sleep for a bit to let it start
14099     GrouperInstallerUtils.sleep(2000);
14100     
14101   }
14102 
14103   /** gi db utils */
14104   private GiDbUtils giDbUtils = null;
14105   
14106   /**
14107    * 
14108    */
14109   private void shutdownHsql() {
14110     
14111     try {
14112       this.giDbUtils.executeUpdate("SHUTDOWN", null, false);
14113       System.out.println("Shutting down HSQL before starting it by sending the SQL: SHUTDOWN");
14114     } catch (Exception e) {
14115       //e.printStackTrace();
14116       System.out.println("HSQL was not detected to be running (did not successfully stop it)");
14117     }
14118   }
14119   
14120   
14121   /**
14122    * 
14123    */
14124   private void configureTomeeGrouperWsScimWebapp() {
14125     
14126     File serverXmlFile = new File(this.untarredTomeeDir.getAbsolutePath() 
14127         + File.separator + "conf" + File.separator + "server.xml");
14128     
14129     //C:\mchyzer\grouper\trunk\grouper-installer\grouper.ui-2.0.2\dist\grouper
14130     //
14131     //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
14132     //Server
14133     //Service
14134     //Engine
14135     //Host
14136 
14137     System.out.print("Enter the URL path for the Grouper WS Scim [grouper-ws-scim]: ");
14138     this.tomeeWsScimPath = readFromStdIn("grouperInstaller.autorun.urlPathForGropuerWsScim");
14139     
14140     if (GrouperInstallerUtils.isBlank(this.tomeeWsScimPath)) {
14141       this.tomeeWsScimPath = "grouper-ws-scim";
14142     }
14143 
14144     if (this.tomeeWsScimPath.endsWith("/") || this.tomeeWsScimPath.endsWith("\\")) {
14145       this.tomeeWsScimPath = this.tomeeWsScimPath.substring(0, this.tomeeWsScimPath.length()-1);
14146     }
14147     if (this.tomeeWsScimPath.startsWith("/") || this.tomeeWsScimPath.startsWith("\\")) {
14148       this.tomeeWsScimPath = this.tomeeWsScimPath.substring(1, this.tomeeWsScimPath.length());
14149     }
14150     
14151     String currentDocBase = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
14152         "Server/Service/Engine/Host/Context[@path='/" + this.tomeeWsScimPath + "']", "docBase");
14153 
14154     String shouldBeDocBase = this.untarredWsDir.getAbsolutePath() + File.separator + "grouper-ws-scim" + File.separator + "targetBuiltin" + File.separator + "grouper-ws-scim";
14155 
14156     System.out.println("Editing tomee config file: " + serverXmlFile.getAbsolutePath());
14157     
14158     if (GrouperInstallerUtils.isBlank(currentDocBase)) {
14159 
14160       //need to add it
14161       //<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">
14162       //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
14163       addToXmlFile(serverXmlFile, ">", new String[]{"<Host "}, 
14164           "<Context docBase=\"" + shouldBeDocBase + "\" path=\"/" + this.tomeeWsScimPath + "\" reloadable=\"false\"/>", "tomee context for Grouper WS Scim");
14165 
14166     } else {
14167 
14168       if (!GrouperInstallerUtils.equals(currentDocBase, shouldBeDocBase)) {
14169         
14170         //lets edit the file
14171         //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
14172         editFile(serverXmlFile, "docBase=\"([^\"]+)\"", new String[]{"<Context", "path=\"/" + this.tomeeWsScimPath + "\""}, 
14173             null, shouldBeDocBase, "tomee context for Grouper WS Scim");
14174 
14175       } else {
14176         
14177         System.out.println("  - Context is already set for Grouper WS Scim");
14178         
14179       }
14180       
14181       
14182     }
14183     
14184     currentDocBase = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
14185         "Server/Service/Engine/Host/Context[@path='/" + this.tomeeWsScimPath + "']", "docBase");
14186     
14187     if (!GrouperInstallerUtils.equals(currentDocBase, shouldBeDocBase)) {
14188       System.out.println("Tried to edit server.xml but it didnt work, should have context of: '" 
14189           + shouldBeDocBase + "', but was: '" + currentDocBase + "'");
14190     }
14191     
14192     File[] allFiles = new File(this.untarredApiDir + File.separator + "conf").listFiles(new FilenameFilter() {
14193       
14194       @Override
14195       public boolean accept(File file, String name) {
14196         return name.endsWith(".properties") || name.endsWith(".xml") || name.endsWith(".txt");
14197       }
14198     });
14199     
14200     
14201     for (File fileToCopyFrom : allFiles) {
14202       if (fileToCopyFrom.isFile()) {
14203         File destFile = new File(shouldBeDocBase + File.separator + "WEB-INF" + File.separator + "classes" + File.separator + fileToCopyFrom.getName());
14204         if (!destFile.exists()) {
14205           GrouperInstallerUtils.fileCreate(destFile);
14206         }
14207         GrouperInstallerUtils.copyFile(fileToCopyFrom, destFile, false);
14208       }
14209     }
14210     
14211   }
14212   
14213   /**
14214    * 
14215    */
14216   private void configureTomeeGrouperUberWebapp(File tommeDir, File webAppDir) {
14217     
14218     //GrouperInstallerUtils.toSet("catalina.sh", "startup.sh", "shutdown.sh");
14219     Set<String> shFileNames = new HashSet<String>();
14220 
14221     File binDir = new File(tommeDir.getAbsolutePath() + File.separator + "bin");
14222 
14223     //get all sh files, doing wildcards doesnt work
14224     for (File file : binDir.listFiles()) {
14225       String fileName = GrouperInstallerUtils.defaultString(file.getName());
14226       if (file.isFile() && fileName.endsWith(".sh")) {
14227         shFileNames.add(fileName);
14228       }
14229     }
14230 
14231     for (String command : shFileNames) {
14232       List<String> commands = new ArrayList<String>();
14233       
14234       commands.add("chmod");
14235       commands.add("+x");
14236       //have to do * since all the  sh files need chmod
14237       commands.add(tommeDir.getAbsolutePath() + File.separator + "bin" + File.separator + command);
14238 
14239       System.out.println("Making tomee file executable with command: " + convertCommandsIntoCommand(commands) + "\n");
14240 
14241       CommandResult commandResult = GrouperInstallerUtils.execCommand(
14242           GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
14243           new File(tommeDir.getAbsolutePath() + File.separator + "bin"), null, true);
14244       
14245       if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
14246         System.out.println("stderr: " + commandResult.getErrorText());
14247       }
14248       if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
14249         System.out.println("stdout: " + commandResult.getOutputText());
14250       }
14251     }
14252     
14253     Set<File> shFiles = new LinkedHashSet<File>();
14254     for (String shFileName : shFileNames) {
14255       shFiles.add(new File(shFileName));
14256     }
14257 // do this in container since its too dynamic
14258 //    for (String path : new String[] {"grouper", "grouper-ws", "grouper-ws-scim"}) {
14259 //      // create grouper.xml in conf/Catalina/localhost/grouper.xml
14260 //      File tomeeGrouperFile = new File(tommeDir.getAbsolutePath() + File.separator + "conf" + File.separator +
14261 //          "Catalina" + File.separator + "localhost" + File.separator + path + ".xml");
14262 //      
14263 //      GrouperInstallerUtils.createParentDirectories(tomeeGrouperFile);
14264 //      GrouperInstallerUtils.fileCreate(tomeeGrouperFile);
14265 //      String cookiesFalse = "";
14266 //      if ("grouper-ws".equals(path) || "grouper-ws-scim".equals(path)) {
14267 //        cookiesFalse = " cookies=\"false\" ";
14268 //      }
14269 //      String contentToWrite = "<Context docBase=\"/opt/grouper/grouperWebapp/\" path=\"/" + path + "\" reloadable=\"false\"" 
14270 //            + cookiesFalse + ">\n" + 
14271 //          "<Resources allowLinking=\"true\" />\n" + 
14272 //          "</Context>";
14273 //    
14274 //      try {      
14275 //        Files.write(Paths.get(tomeeGrouperFile.getAbsolutePath()), contentToWrite.toString().getBytes(), StandardOpenOption.APPEND);
14276 //      } catch (Exception e) {
14277 //        System.out.println("Could not write to grouper.xml file.");
14278 //      }
14279 //    }
14280     
14281 //    // edit server.xml in tomee/conf dir
14282 //    File serverXmlFile = new File(tommeDir.getAbsolutePath()
14283 //        + File.separator + "conf" + File.separator + "server.xml");
14284 //    
14285 //    Map<String, String> expectedAttribute = new HashMap<String, String>();
14286 //
14287 //    expectedAttribute.put("port", "8009");
14288 //    expectedAttribute.put("protocol", "AJP/1.3");
14289 //
14290 //    editXmlFileAttribute(serverXmlFile, "Connector", expectedAttribute, "tomcatAuthentication", "false", 
14291 //         "Set tomcatAuthentication to false");
14292 //    editXmlFileAttribute(serverXmlFile, "Connector", expectedAttribute, "URIEncoding", "UTF-8", 
14293 //        "Set URIEncoding to UTF-8");
14294 //    editXmlFileAttribute(serverXmlFile, "Connector", expectedAttribute, "scheme", "https", 
14295 //        "Set scheme to https");
14296 //    editXmlFileAttribute(serverXmlFile, "Connector", expectedAttribute, "secure", "true", 
14297 //        "Set secure to true");
14298     
14299   }
14300   
14301 
14302   /**
14303    * 
14304    */
14305   private void configureTomcatUiWebapp() {
14306     
14307     File serverXmlFile = new File(this.untarredTomcatDir.getAbsolutePath() 
14308         + File.separator + "conf" + File.separator + "server.xml");
14309     
14310     //C:\mchyzer\grouper\trunk\grouper-installer\grouper.ui-2.0.2\dist\grouper
14311     //
14312     //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
14313     //Server
14314     //Service
14315     //Engine
14316     //Host
14317 
14318     System.out.print("Enter the URL path for the UI [grouper]: ");
14319     this.tomcatUiPath = readFromStdIn("grouperInstaller.autorun.urlPathForUi");
14320     
14321     if (GrouperInstallerUtils.isBlank(this.tomcatUiPath)) {
14322       this.tomcatUiPath = "grouper";
14323     }
14324 
14325     if (this.tomcatUiPath.endsWith("/") || this.tomcatUiPath.endsWith("\\")) {
14326       this.tomcatUiPath = this.tomcatUiPath.substring(0, this.tomcatUiPath.length()-1);
14327     }
14328     if (this.tomcatUiPath.startsWith("/") || this.tomcatUiPath.startsWith("\\")) {
14329       this.tomcatUiPath = this.tomcatUiPath.substring(1, this.tomcatUiPath.length());
14330     }
14331     
14332     String currentDocBase = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
14333         "Server/Service/Engine/Host/Context[@path='/" + this.tomcatUiPath + "']", "docBase");
14334 
14335     String shouldBeDocBase = grouperUiBuildToDirName();
14336 
14337     System.out.println("Editing tomcat config file: " + serverXmlFile.getAbsolutePath());
14338     
14339     if (GrouperInstallerUtils.isBlank(currentDocBase)) {
14340 
14341       //need to add it
14342       //<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">
14343       //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
14344       addToXmlFile(serverXmlFile, ">", new String[]{"<Host "}, 
14345           "<Context docBase=\"" + shouldBeDocBase + "\" path=\"/" + this.tomcatUiPath + "\" reloadable=\"false\"/>", "tomcat context for UI");
14346 
14347     } else {
14348 
14349       if (!GrouperInstallerUtils.equals(currentDocBase, shouldBeDocBase)) {
14350         
14351         //lets edit the file
14352         //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
14353         editFile(serverXmlFile, "docBase=\"([^\"]+)\"", new String[]{"<Context", "path=\"/" + this.tomcatUiPath + "\""}, 
14354             null, shouldBeDocBase, "tomcat context for UI");
14355 
14356       } else {
14357         
14358         System.out.println("  - Context is already set for Grouper UI");
14359         
14360       }
14361       
14362       
14363     }
14364     
14365     currentDocBase = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
14366         "Server/Service/Engine/Host/Context[@path='/" + this.tomcatUiPath + "']", "docBase");
14367     
14368     if (!GrouperInstallerUtils.equals(currentDocBase, shouldBeDocBase)) {
14369       System.out.println("Tried to edit server.xml but it didnt work, should have context of: '" 
14370           + shouldBeDocBase + "', but was: '" + currentDocBase + "'");
14371     }
14372     
14373   }
14374 
14375   /**
14376    * 
14377    * @return grouper ui build to name
14378    */
14379   private String grouperUiBuildToDirName() {
14380     return this.untarredUiDir.getAbsolutePath() + File.separator + "dist" + File.separator + "grouper";
14381   }
14382 
14383   /**
14384    * @param isInstallNotUpgrade
14385    */
14386   private void buildWs(boolean isInstallNotUpgrade) {
14387     
14388     File grouperWsBuildToDir = new File(this.grouperWsBuildToDirName());
14389     
14390     if (grouperWsBuildToDir.exists()) {
14391 
14392       boolean rebuildWs = true;
14393       
14394       boolean defaultRebuild = GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.ws.rebuildIfBuilt", true, false);
14395       System.out.print("The Grouper WS has been built in the past, do you want it rebuilt? (t|f) [" 
14396           + (defaultRebuild ? "t" : "f") + "]: ");
14397       rebuildWs = readFromStdInBoolean(defaultRebuild, "grouperInstaller.autorun.rebuildWsIfBuiltAlready");
14398   
14399       if (!rebuildWs) {
14400         return;
14401       }
14402     }
14403     
14404     if (isInstallNotUpgrade) {
14405       //stop tomcat
14406       try {
14407         tomcatBounce("stop");
14408       } catch (Throwable e) {
14409         System.out.println("Couldnt stop tomcat, ignoring...");
14410       }
14411     }
14412     
14413     List<String> commands = new ArrayList<String>();
14414     
14415     addAntCommands(commands);
14416     commands.add("dist");
14417     
14418     System.out.println("\n##################################");
14419     System.out.println("Building WS with command:\n" + this.untarredWsDir.getAbsolutePath() + File.separator + "grouper-ws" + "> " 
14420         + convertCommandsIntoCommand(commands) + "\n");
14421     
14422     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
14423         true, true, null, new File(this.untarredWsDir.getAbsolutePath() + File.separator + "grouper-ws"), null, true);
14424     
14425     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
14426       System.out.println("stderr: " + commandResult.getErrorText());
14427     }
14428     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
14429       System.out.println("stdout: " + commandResult.getOutputText());
14430     }
14431 
14432     if (isInstallNotUpgrade) {
14433       System.out.print("Do you want to set the log dir of WS (t|f)? [t]: ");
14434       boolean setLogDir = readFromStdInBoolean(true, "grouperInstaller.autorun.setWsLogDir");
14435       
14436       if (setLogDir) {
14437         
14438         ////set the log dir
14439         //C:\apps\grouperInstallerTest\grouper.ws-2.0.2\grouper-ws\build\dist\grouper-ws\WEB-INF\classes\log4j.properties
14440         //
14441         //${grouper.home}logs
14442   
14443         String defaultLogDir = this.untarredTomcatDir.getAbsolutePath() + File.separator + "logs" + File.separator + "grouperWs";
14444         System.out.print("Enter the WS log dir: [" + defaultLogDir + "]: ");
14445         String logDir = readFromStdIn("grouperInstaller.autorun.wsLogDir");
14446         logDir = GrouperInstallerUtils.defaultIfBlank(logDir, defaultLogDir);
14447         
14448         //lets replace \\ with /
14449         logDir = GrouperInstallerUtils.replace(logDir, "\\\\", "/");
14450         //lets replace \ with /
14451         logDir = GrouperInstallerUtils.replace(logDir, "\\", "/");
14452   
14453         File log4jFile = new File(grouperWsBuildToDirName() + File.separator + "WEB-INF" + File.separator + "classes"
14454             + File.separator + "log4j.properties");
14455         
14456         System.out.println("Editing file: " + log4jFile.getAbsolutePath());
14457         
14458         editFile(log4jFile, "log4j\\.\\S+\\.File\\s*=\\s*([^\\s]+logs)/grouper_[^\\s]+\\.log", null, 
14459             null, logDir, "WS log directory");
14460         
14461         File logDirFile = new File(defaultLogDir);
14462         if (!logDirFile.exists()) {
14463           System.out.println("Creating log directory: " + logDirFile.getAbsolutePath());
14464           GrouperInstallerUtils.mkdirs(logDirFile);
14465         }
14466         //test log dir
14467         File testLogDirFile = new File(logDirFile.getAbsolutePath() + File.separator + "testFile" + GrouperInstallerUtils.uniqueId() + ".txt");
14468         GrouperInstallerUtils.saveStringIntoFile(testLogDirFile, "test");
14469         if (!testLogDirFile.delete()) {
14470           throw new RuntimeException("Cant delete file: " +  testLogDirFile.getAbsolutePath());
14471         }
14472         System.out.println("Created and deleted a test file successfully in dir: " + logDirFile.getAbsolutePath());
14473       }
14474     }
14475     
14476     System.out.println("\nEnd building Ws");
14477     System.out.println("##################################\n");
14478   
14479     
14480   }
14481 
14482   /**
14483    * 
14484    */
14485   private void configureTomcatWsWebapp() {
14486     
14487     File serverXmlFile = new File(this.untarredTomcatDir.getAbsolutePath() 
14488         + File.separator + "conf" + File.separator + "server.xml");
14489     
14490     //C:\mchyzer\grouper\trunk\grouper-installer\grouper.ui-2.0.2\dist\grouper
14491     //
14492     //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
14493     //Server
14494     //Service
14495     //Engine
14496     //Host
14497   
14498     System.out.print("Enter the URL path for the WS [grouper-ws]: ");
14499     this.tomcatWsPath = readFromStdIn("grouperInstaller.autorun.wsUrlPath");
14500     
14501     if (GrouperInstallerUtils.isBlank(this.tomcatWsPath)) {
14502       this.tomcatWsPath = "grouper-ws";
14503     }
14504   
14505     if (this.tomcatWsPath.endsWith("/") || this.tomcatWsPath.endsWith("\\")) {
14506       this.tomcatWsPath = this.tomcatWsPath.substring(0, this.tomcatWsPath.length()-1);
14507     }
14508     if (this.tomcatWsPath.startsWith("/") || this.tomcatWsPath.startsWith("\\")) {
14509       this.tomcatWsPath = this.tomcatWsPath.substring(1);
14510     }
14511     
14512     String currentDocBase = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
14513         "Server/Service/Engine/Host/Context[@path='/" + this.tomcatWsPath + "']", "docBase");
14514   
14515     //grouper.ws-2.0.2\grouper-ws\build\dist\grouper-ws
14516     
14517     String shouldBeDocBase = grouperWsBuildToDirName();
14518   
14519     System.out.println("Editing tomcat config file: " + serverXmlFile.getAbsolutePath());
14520     
14521     if (GrouperInstallerUtils.isBlank(currentDocBase)) {
14522   
14523       //need to add it
14524       //<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">
14525       //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
14526       addToXmlFile(serverXmlFile, ">", new String[]{"<Host "}, 
14527           "<Context docBase=\"" + shouldBeDocBase + "\" path=\"/" + this.tomcatWsPath + "\" reloadable=\"false\"/>", "tomcat context for WS");
14528   
14529     } else {
14530   
14531       if (!GrouperInstallerUtils.equals(currentDocBase, shouldBeDocBase)) {
14532         
14533         //lets edit the file
14534         //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
14535         editFile(serverXmlFile, "docBase=\"([^\"]+)\"", new String[]{"<Context", "path=\"/" + this.tomcatWsPath + "\""}, 
14536             null, shouldBeDocBase, "tomcat context for WS");
14537   
14538       } else {
14539         
14540         System.out.println("  - Context is already set for Grouper WS");
14541         
14542       }
14543       
14544       
14545     }
14546     
14547     currentDocBase = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
14548         "Server/Service/Engine/Host/Context[@path='/" + this.tomcatWsPath + "']", "docBase");
14549     
14550     if (!GrouperInstallerUtils.equals(currentDocBase, shouldBeDocBase)) {
14551       System.out.println("Tried to edit server.xml but it didnt work, should have context of: '" 
14552           + shouldBeDocBase + "', but was: '" + currentDocBase + "'");
14553     }
14554     
14555   }
14556 
14557   /**
14558    * @return grouper ws build to dir name
14559    */
14560   private String grouperWsBuildToDirName() {
14561     return this.untarredWsDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator 
14562       + "build" + File.separator + "dist" + File.separator + "grouper-ws";
14563   }
14564   
14565   /**
14566      * 
14567      */
14568     private void configureClient() {
14569       //properties file
14570       File localGrouperClientPropertiesFile = new File(this.untarredClientDir.getAbsolutePath() + File.separator 
14571           + "grouper.client.properties");
14572       
14573       //set the grouper property
14574       System.out.println("Editing " + localGrouperClientPropertiesFile.getAbsolutePath() + ": ");
14575       editPropertiesFile(localGrouperClientPropertiesFile, "grouperClient.webService.url", "http://localhost:" 
14576           + this.tomcatHttpPort + "/" + this.tomcatWsPath + "/servicesRest", false);
14577       editPropertiesFile(localGrouperClientPropertiesFile, "grouperClient.webService.login", "GrouperSystem", false);
14578       editPropertiesFile(localGrouperClientPropertiesFile, "grouperClient.webService.password", this.grouperSystemPassword, false);
14579       
14580       
14581 //      grouperClient.webService.url = http://localhost:8200/grouper-ws/servicesRest
14582 //
14583 //      # kerberos principal used to connect to web service
14584 //      grouperClient.webService.login = GrouperSystem
14585 //
14586 //      # password for shared secret authentication to web service
14587 //      # or you can put a filename with an encrypted password
14588 //      grouperClient.webService.password = myNewPass
14589 
14590       
14591     }
14592 
14593   /**
14594    * 
14595    * @return the file of the directory of WS
14596    */
14597   private File downloadClient() {
14598     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
14599     
14600     if (!urlToDownload.endsWith("/")) {
14601       urlToDownload += "/";
14602     }
14603     urlToDownload += "release/";
14604   
14605     String clientFileName = "grouper.clientBinary-" + this.version + ".tar.gz";
14606     urlToDownload += this.version + "/" + clientFileName;
14607   
14608     File clientFile = new File(this.grouperTarballDirectoryString + clientFileName);
14609     
14610     downloadFile(urlToDownload, clientFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalClientDownloadTarEtc");
14611 
14612     return clientFile;
14613   }
14614 
14615   /**
14616    * 
14617    */
14618   private void addGrouperSystemWsGroup() {
14619 
14620     //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);"
14621 
14622     //running with command on command line doenst work on linux since the args with whitespace translate to 
14623     //save the commands to a file, and runt he file
14624     StringBuilder gshCommands = new StringBuilder();
14625     gshCommands.append("grouperSession = GrouperSession.startRootSession();\n");
14626     gshCommands.append("wsGroup = new GroupSave(grouperSession).assignName(\"etc:webServiceClientUsers\").assignCreateParentStemsIfNotExist(true).save();\n");
14627     gshCommands.append("wsGroup.addMember(SubjectFinder.findRootSubject(), false);\n");
14628     
14629     File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "gshAddGrouperSystemWsGroup.gsh");
14630     GrouperInstallerUtils.saveStringIntoFile(gshFile, gshCommands.toString());
14631     
14632     List<String> commands = new ArrayList<String>();
14633 
14634     addGshCommands(commands);
14635     commands.add(gshFile.getAbsolutePath());
14636 
14637     System.out.println("\n##################################");
14638     System.out.println("Adding user GrouperSystem to grouper-ws users group with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
14639 
14640     CommandResult commandResult = GrouperInstallerUtils.execCommand(
14641         GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
14642         this.untarredApiDir, null, true);
14643 
14644     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
14645       System.out.println("stderr: " + commandResult.getErrorText());
14646     }
14647     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
14648       System.out.println("stdout: " + commandResult.getOutputText());
14649     }
14650 
14651 
14652   }
14653 
14654   /**
14655    * 
14656    */
14657   private void runChangeLogTempToChangeLog() {
14658 
14659     boolean defaultBoolean = GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.api.runChangeLogToChangeLogTemp", true, false);
14660     System.out.print("Is it ok to run a script that copies change log temp records to the change log (recommended) (t|f)? [" 
14661         + (defaultBoolean ? "t" : "f") + "]: ");
14662     boolean runScript = readFromStdInBoolean(defaultBoolean, "grouperInstaller.autorun.runChangeLogTempToChangeLog");
14663 
14664     
14665     if (!runScript) {
14666       return;
14667     }
14668     
14669     //running with command on command line doenst work on linux since the args with whitespace translate to 
14670     //save the commands to a file, and runt he file
14671     StringBuilder gshCommands = new StringBuilder();
14672     gshCommands.append("grouperSession = GrouperSession.startRootSession();\n");
14673     gshCommands.append("loaderRunOneJob(\"CHANGE_LOG_changeLogTempToChangeLog\");\n");
14674     
14675     File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "gshChangeLogTempToChangeLog.gsh");
14676     GrouperInstallerUtils.saveStringIntoFile(gshFile, gshCommands.toString());
14677     
14678     List<String> commands = new ArrayList<String>();
14679 
14680     addGshCommands(commands);
14681     commands.add(gshFile.getAbsolutePath());
14682 
14683     System.out.println("\n##################################");
14684     System.out.println("Copying records from change log temp to change log with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
14685 
14686     CommandResult commandResult = GrouperInstallerUtils.execCommand(
14687         GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
14688        new File(this.gshCommand()).getParentFile(), null, true);
14689 
14690     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
14691       System.out.println("stderr: " + commandResult.getErrorText());
14692     }
14693     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
14694       System.out.println("stdout: " + commandResult.getOutputText());
14695     }
14696 
14697 
14698   }
14699 
14700   /**
14701    * 
14702    */
14703   private void runClientCommand() {
14704     System.out.println("##################################");
14705     System.out.println("Running client command:");
14706     System.out.println(this.untarredClientDir.getAbsolutePath() + "> " + getJavaCommand()
14707         + " -jar grouperClient.jar --operation=getMembersWs --groupNames=etc:webServiceClientUsers");
14708     
14709     try {
14710       final List<String> command = new ArrayList<String>();
14711   
14712       command.add(getJavaCommand());
14713       command.add("-jar");
14714       command.addAll(GrouperInstallerUtils.splitTrimToList(
14715           "grouperClient.jar --operation=getMembersWs --groupNames=etc:webServiceClientUsers", " "));
14716               
14717       CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(command, String.class), 
14718           true, true, null, this.untarredClientDir, null, true);
14719   
14720       if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
14721         System.out.println("stderr: " + commandResult.getErrorText());
14722       }
14723       if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
14724         System.out.println("stdout: " + commandResult.getOutputText());
14725       }
14726       System.out.println("Success running client command:");
14727     } catch (Exception e) {
14728       System.out.println("Exception running Grouper client");
14729       e.printStackTrace();
14730       System.out.print("Press <enter> to continue: ");
14731       readFromStdIn("grouperInstaller.autorun.grouperClientErrorContinue");
14732     }
14733     System.out.println("##################################");
14734 
14735   }
14736 
14737   /**
14738    * edit a property in a property file
14739    * @param file
14740    * @param valueRegex 
14741    * @param lineMustHaveRegexes 
14742    * @param lineCantHaveRegexes 
14743    * @param newValue 
14744    * @param description of change for sys out print
14745    * @return true if edited file, or false if not but didnt need to, null if not found
14746    */
14747   public static Boolean editFile(File file, String valueRegex, String[] lineMustHaveRegexes, 
14748       String[] lineCantHaveRegexes, String newValue, String description) {
14749     return editFile(file, valueRegex, lineMustHaveRegexes, lineCantHaveRegexes, newValue, description, false, null);
14750   }
14751 
14752   /**
14753    * edit a property in a property file
14754    * @param file
14755    * @param valueRegex 
14756    * @param lineMustHaveRegexes 
14757    * @param lineCantHaveRegexes 
14758    * @param newValue 
14759    * @param description of change for sys out print
14760    * @param addAttributeIfNotExists if attribute isnt there, then if true, then add the attribute
14761    * @param newAttributeName if adding new attribute, this is the name
14762    * @return true if edited file, or false if not but didnt need to, null if not found
14763    */
14764   public static Boolean editFile(File file, String valueRegex, String[] lineMustHaveRegexes, 
14765       String[] lineCantHaveRegexes, String newValue, String description, boolean addAttributeIfNotExists, String newAttributeName) {
14766     
14767     if (!GrouperInstallerUtils.isBlank(newAttributeName) != addAttributeIfNotExists) {
14768       throw new RuntimeException("newAttributeName cant be null if addAttributeIfNotExists, and must be null if not addAttributeIfNotExists");
14769     }
14770     
14771     if (!file.exists() || file.length() == 0) {
14772       throw new RuntimeException("Why does " + file.getName() + " not exist and have contents? " 
14773           + file.getAbsolutePath());
14774     }
14775     
14776     String fileContents = GrouperInstallerUtils.readFileIntoString(file);
14777     
14778     String newline = GrouperInstallerUtils.newlineFromFile(fileContents);
14779     
14780     String[] lines = GrouperInstallerUtils.splitLines(fileContents);
14781     
14782     Pattern pattern = Pattern.compile(valueRegex);
14783     
14784     Pattern[] lineMustHavePatterns = new Pattern[GrouperInstallerUtils.length(lineMustHaveRegexes)];
14785     
14786     {
14787       int index = 0;
14788       for (String lineMustHaveRegex : GrouperInstallerUtils.nonNull(lineMustHaveRegexes, String.class)) {
14789         Pattern lineMustHavePattern = Pattern.compile(lineMustHaveRegex);
14790         lineMustHavePatterns[index] = lineMustHavePattern;
14791         
14792         index++;
14793       }
14794     }    
14795   
14796     Pattern[] lineCantHavePatterns = new Pattern[GrouperInstallerUtils.length(lineCantHaveRegexes)];
14797     
14798     {
14799       int index = 0;
14800       for (String lineCantHaveRegex : GrouperInstallerUtils.nonNull(lineCantHaveRegexes, String.class)) {
14801         Pattern lineCantHavePattern = Pattern.compile(lineCantHaveRegex);
14802         lineCantHavePatterns[index] = lineCantHavePattern;
14803         
14804         index++;
14805       }
14806     }    
14807   
14808     StringBuilder newfile = new StringBuilder();
14809     
14810     boolean madeChange = false;
14811     boolean noChangeNeeded = false;
14812     
14813     OUTER: for (String line : lines) {
14814       line = GrouperInstallerUtils.defaultString(line);
14815       
14816       //lets see if it satisfies all
14817       for (Pattern lineMustHavePattern : lineMustHavePatterns) {
14818         if (!lineMustHavePattern.matcher(line).find()) {
14819           newfile.append(line).append(newline);
14820           continue OUTER;
14821         }
14822       }
14823       
14824       //lets see if it doesnt have these
14825       for (Pattern lineCantHavePattern : lineCantHavePatterns) {
14826         if (lineCantHavePattern.matcher(line).find()) {
14827           newfile.append(line).append(newline);
14828           continue OUTER;
14829         }
14830       }
14831       
14832       //see if satisfies current
14833       Matcher matcher = pattern.matcher(line);
14834       if (!matcher.find()) {
14835         
14836         if (addAttributeIfNotExists) {
14837           
14838           System.out.println(" - adding " + description + " with value: '" + newValue + "'");
14839           
14840           line = GrouperInstallerUtils.trimEnd(line);
14841           
14842           boolean endsWithCloseTag = false;
14843           boolean endElement = false;
14844           
14845           if (line.endsWith("/>")) {
14846             line = line.substring(0, line.length()-2);
14847             line = GrouperInstallerUtils.trimEnd(line);
14848             endsWithCloseTag = true;
14849           } else if (line.endsWith(">")) {
14850             line = line.substring(0, line.length()-1);
14851             line = GrouperInstallerUtils.trimEnd(line);
14852             endElement = true;
14853           }
14854           
14855           newfile.append(line).append(" ").append(newAttributeName).append("=\"").append(newValue).append("\"");
14856           
14857           if (endsWithCloseTag) {
14858             newfile.append(" />");
14859           } else if (endElement) {
14860             newfile.append(" >");
14861           }
14862           
14863           newfile.append(newline);
14864           madeChange = true;
14865           
14866         } else {
14867         
14868           newfile.append(line).append(newline);
14869         }
14870         continue;
14871       }
14872       
14873       String oldValue = matcher.group(1);
14874       if (GrouperInstallerUtils.equals(newValue, oldValue)) {
14875         System.out.println(" - old " + description + " value is same as new value: " + newValue);
14876         noChangeNeeded = true;
14877         newfile.append(line).append(newline);
14878         continue;
14879       }
14880       
14881       //we need to change the value
14882       System.out.println(" - changing " + description + " from: '" + oldValue + "' to: '" + newValue + "'");
14883       newfile.append(line.substring(0, matcher.start(1)));
14884       newfile.append(newValue);
14885       newfile.append(line.substring(matcher.end(1), line.length()));
14886       newfile.append(newline);
14887       madeChange = true;
14888       continue;
14889     }
14890     
14891     if (!madeChange) {
14892       //true if edited file, or false if not but didnt need to, null if not found
14893       if (noChangeNeeded) {
14894         return false;
14895       }
14896       return null;
14897     }
14898     
14899     GrouperInstallerUtils.writeStringToFile(file, newfile.toString());
14900     
14901     return true;
14902   }
14903 
14904   /**
14905    * add a line to a file.  will replace \n with whatever newline is
14906    * @param file
14907    * @param line (not ending in newline)
14908    * @param lineNumber 1 indexed.  If not exist, add to end of file
14909    * @param description is a description of what was just changed
14910    */
14911   public static void addToFile(File file, String line, int lineNumber, String description) {
14912     if (!file.exists() || file.length() == 0) {
14913       throw new RuntimeException("Why does " + file.getName() + " not exist and have contents? " 
14914           + file.getAbsolutePath());
14915     }
14916     
14917     String fileContents = GrouperInstallerUtils.readFileIntoString(file);
14918     
14919     String newline = GrouperInstallerUtils.newlineFromFile(fileContents);
14920     
14921     String[] lines = GrouperInstallerUtils.splitLines(fileContents);
14922 
14923     line = GrouperInstallerUtils.replace(line, "\n", newline);
14924     
14925     line += newline;
14926     
14927     StringBuilder newfile = new StringBuilder();
14928     
14929     boolean madeChange = false;
14930     
14931     int index = 0;
14932     
14933     for (String fileLine : lines) {
14934       fileLine = GrouperInstallerUtils.defaultString(fileLine);
14935       newfile.append(fileLine).append(newline);
14936       index++;
14937       
14938       if (index >= lineNumber  && !madeChange) {
14939 
14940         System.out.println("Adding " + description + " to file at line number: " + lineNumber);        
14941         
14942         newfile.append(line);
14943         madeChange = true;
14944       }
14945     }
14946     
14947     if (!madeChange) {
14948       System.out.println("Appending " + description + " to end of file");        
14949       newfile.append(line);
14950     }
14951     
14952     GrouperInstallerUtils.writeStringToFile(file, newfile.toString());
14953     
14954   }
14955 
14956   /** tomcat ui path */
14957   private String tomcatUiPath = null;
14958 
14959   /** tomcat ws path */
14960   private String tomcatWsPath = null;
14961   
14962   /** tomee grouper ws scim path */
14963   private String tomeeWsScimPath = null;
14964 
14965   /** untarred dir, this does NOT end in file.separator */
14966   private File untarredClientDir;
14967 
14968   /**
14969    * see if install, upgrade, patch, etc
14970    * @return true if install, or false if upgrade
14971    */
14972   private GrouperInstallerMainFunction grouperInstallerMainFunction() {
14973 
14974     GrouperInstallerMainFunction grouperInstallerMainFunctionLocal = 
14975         (GrouperInstallerMainFunction)promptForEnum(
14976             "Do you want to install ('installContainer') a new grouper container , 'upgrade' an existing installation,\n"
14977                 + "  'patch' an existing installation, 'admin' utilities, 'buildContainer', 'installContainer', or 'createPatch' for Grouper developers\n" 
14978                 + "  (enter: 'installContainer', 'upgrade', 'patch', 'admin', 'createPatch', 'buildContainer', or blank for the default) ",
14979             "grouperInstaller.autorun.actionEgInstallUpgradePatch", GrouperInstallerMainFunction.class, 
14980             GrouperInstallerMainFunction.installContainer, "grouperInstaller.default.installOrUpgrade");
14981     return grouperInstallerMainFunctionLocal;
14982   }
14983 
14984 
14985   
14986   /**
14987    * 
14988    * @return install directory
14989    */
14990   private static String grouperInstallDirectory() {
14991     String grouperInstallDirectoryString;
14992     {
14993       File grouperInstallDirectoryFile = new File("");
14994       String defaultDirectory = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.installDirectory", false);
14995       System.out.print("Enter in the Grouper install directory (note: better if no spaces or special chars) [" 
14996           + (GrouperInstallerUtils.isBlank(defaultDirectory) ? grouperInstallDirectoryFile.getAbsolutePath() : defaultDirectory) + "]: ");
14997       String input = readFromStdIn("grouperInstaller.autorun.installDirectory");
14998       if (!GrouperInstallerUtils.isBlank(input)) {
14999         grouperInstallDirectoryFile = new File(input);
15000         if (!grouperInstallDirectoryFile.exists() || !grouperInstallDirectoryFile.isDirectory()) {
15001           System.out.println("Error: cant find directory: '" + input + "'");
15002           System.exit(1);
15003         }
15004       } else {
15005         if (!GrouperInstallerUtils.isBlank(defaultDirectory)) {
15006           grouperInstallDirectoryFile = new File(defaultDirectory);
15007           if (!grouperInstallDirectoryFile.exists() || !grouperInstallDirectoryFile.isDirectory()) {
15008             System.out.println("Error: cant find directory: '" + input + "'");
15009             System.exit(1);
15010           }
15011         }
15012       }
15013       grouperInstallDirectoryString = grouperInstallDirectoryFile.getAbsolutePath();
15014       if (!grouperInstallDirectoryString.endsWith(File.separator)) {
15015         grouperInstallDirectoryString += File.separator;
15016       }
15017     }
15018     return grouperInstallDirectoryString;
15019   }
15020   
15021   /**
15022    * 
15023    * @return upgrade directory
15024    */
15025   private String grouperUpgradeTempDirectory() {
15026     String localGrouperInstallDirectoryString = null;
15027     {
15028       File grouperInstallDirectoryFile = new File(new File("").getAbsolutePath() + File.separator + "tarballs");
15029       if (!GrouperInstallerUtils.isBlank(this.grouperInstallDirectoryString)) {
15030         grouperInstallDirectoryFile = new File(this.grouperInstallDirectoryString + "tarballs");
15031       }
15032       String defaultDirectory = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.tarballDirectory", false);
15033       if (GrouperInstallerUtils.isBlank(defaultDirectory)) {
15034         defaultDirectory = grouperInstallDirectoryFile.getAbsolutePath();
15035       }
15036       System.out.print("Enter in a Grouper temp directory to download tarballs (note: better if no spaces or special chars) [" 
15037           + defaultDirectory + "]: ");
15038       localGrouperInstallDirectoryString = readFromStdIn("grouperInstaller.autorun.tarballDirectory");
15039       if (!GrouperInstallerUtils.isBlank(localGrouperInstallDirectoryString)) {
15040         grouperInstallDirectoryFile = new File(localGrouperInstallDirectoryString);
15041         if (!grouperInstallDirectoryFile.exists() || !grouperInstallDirectoryFile.isDirectory()) {
15042           System.out.println("Error: cant find directory: '" + grouperInstallDirectoryFile.getAbsolutePath() + "'");
15043           System.exit(1);
15044         }
15045       } else {
15046         localGrouperInstallDirectoryString = defaultDirectory;
15047       }
15048       if (!localGrouperInstallDirectoryString.endsWith(File.separator)) {
15049         localGrouperInstallDirectoryString += File.separator;
15050       }
15051     }
15052     return localGrouperInstallDirectoryString;
15053   }
15054   
15055   /**
15056    * 
15057    * @return grouper container directory
15058    */
15059   private String grouperContainerDirectory() {
15060     String localGrouperContainerDirectoryString = null;
15061     {
15062       File grouperContainerDirectoryFile = new File(new File("").getAbsolutePath() + File.separator + "container");
15063       if (!GrouperInstallerUtils.isBlank(this.grouperInstallDirectoryString)) {
15064         grouperContainerDirectoryFile = new File(this.grouperInstallDirectoryString + "container");
15065       }
15066       String defaultDirectory = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.buildContainerDirectory", false);
15067       if (GrouperInstallerUtils.isBlank(defaultDirectory)) {
15068         defaultDirectory = grouperContainerDirectoryFile.getAbsolutePath();
15069       }
15070       System.out.print("Enter in a directory for output (note: better if no spaces or special chars) [" 
15071           + defaultDirectory + "]: ");
15072       localGrouperContainerDirectoryString = readFromStdIn("grouperInstaller.autorun.buildContainerDirectory");
15073       if (!GrouperInstallerUtils.isBlank(localGrouperContainerDirectoryString)) {
15074         grouperContainerDirectoryFile = new File(localGrouperContainerDirectoryString);
15075         if (!grouperContainerDirectoryFile.exists() || !grouperContainerDirectoryFile.isDirectory()) {
15076           System.out.println("Error: cant find directory: '" + grouperContainerDirectoryFile.getAbsolutePath() + "'");
15077           System.exit(1);
15078         }
15079       } else {
15080         localGrouperContainerDirectoryString = defaultDirectory;
15081       }
15082       if (!localGrouperContainerDirectoryString.endsWith(File.separator)) {
15083         localGrouperContainerDirectoryString += File.separator;
15084       }
15085     }
15086     return localGrouperContainerDirectoryString;
15087   }
15088   
15089   /**
15090    * 
15091    * @return see if operating on a source directory or deployed directory
15092    */
15093   @SuppressWarnings("unused")
15094   private GrouperInstallType sourceOrDeployed() {
15095 
15096     if (this.grouperDirectories.getGrouperInstallType() == null) {
15097              
15098     }
15099     
15100     return this.grouperDirectories.getGrouperInstallType();
15101   }
15102 
15103 
15104   
15105   /**
15106    * 
15107    * @return directory where existing installation exists
15108    */
15109   private String upgradeExistingDirectory() {
15110 
15111     //get the directory where the existing installation is
15112     String tempUpgradeExistingApplicationDirectoryString = this.upgradeExistingApplicationDirectoryString;
15113 
15114     String errorMessage = "Cant find Grouper " + this.appToUpgrade.name() + " properties files or libs, looked in the directory, "
15115         + "/classes/ , /conf/ , /WEB-INF/classes/ , /lib/ , /WEB-INF/lib/ , /lib/grouper/ , /dist/lib/ ";
15116 
15117     try {
15118       String upgradeExistingDirectoryString = null;
15119       for (int i=0;i<10;i++) {
15120         File grouperInstallDirectoryFile = new File("");
15121         String defaultDirectory = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.existingInstalledDirectory", false);
15122         System.out.print("Where is the grouper " + this.appToUpgrade.name() + " installed? " +
15123           (GrouperInstallerUtils.isBlank(defaultDirectory) ? "" : ("[" + defaultDirectory + "]: ")));
15124         upgradeExistingDirectoryString = readFromStdIn("grouperInstaller.autorun.grouperWhereInstalled");
15125         if (!GrouperInstallerUtils.isBlank(upgradeExistingDirectoryString)) {
15126           grouperInstallDirectoryFile = new File(upgradeExistingDirectoryString);
15127           if (!grouperInstallDirectoryFile.exists() || !grouperInstallDirectoryFile.isDirectory()) {
15128             System.out.println("Error: cant find directory: '" + grouperInstallDirectoryFile.getAbsolutePath() + "'");
15129             continue;
15130           }
15131         } else {
15132           upgradeExistingDirectoryString = defaultDirectory;
15133         }
15134         upgradeExistingDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(upgradeExistingDirectoryString);
15135 
15136         this.upgradeExistingApplicationDirectoryString = upgradeExistingDirectoryString;
15137 
15138         //make sure directory is where the app is
15139         if (!this.appToUpgrade.validateExistingDirectory(this)) {
15140           System.out.println(errorMessage);
15141           continue;
15142         }
15143         //find the resources dir
15144         {
15145           File resourcesDirFile = new File(this.upgradeExistingApplicationDirectoryString + "classes" + File.separator);
15146           if (resourcesDirFile.exists()) {
15147             this.upgradeExistingClassesDirectoryString = resourcesDirFile.getAbsolutePath();
15148           } else {
15149             resourcesDirFile = new File(this.upgradeExistingApplicationDirectoryString + "conf" + File.separator);
15150             if (resourcesDirFile.exists()) {
15151               this.upgradeExistingClassesDirectoryString = resourcesDirFile.getAbsolutePath();
15152             } else {
15153               resourcesDirFile = new File(this.upgradeExistingApplicationDirectoryString + "WEB-INF" + File.separator + "classes" + File.separator);
15154               if (resourcesDirFile.exists()) {
15155                 this.upgradeExistingClassesDirectoryString = resourcesDirFile.getAbsolutePath();
15156               } else {
15157                 this.upgradeExistingClassesDirectoryString = this.upgradeExistingApplicationDirectoryString;
15158               }            
15159             }
15160           }
15161           this.upgradeExistingClassesDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.upgradeExistingClassesDirectoryString);
15162         }
15163         
15164         //find the lib dir
15165         {
15166           File libDirFile = new File(this.upgradeExistingApplicationDirectoryString + "lib" + File.separator + "grouper" + File.separator);
15167           if (libDirFile.exists()) {
15168             this.upgradeExistingLibDirectoryString = libDirFile.getAbsolutePath();
15169           } else {
15170             libDirFile = new File(this.upgradeExistingApplicationDirectoryString + "WEB-INF" + File.separator + "lib" + File.separator);
15171             if (libDirFile.exists()) {
15172               this.upgradeExistingLibDirectoryString = libDirFile.getAbsolutePath();
15173             } else {
15174               libDirFile = new File(this.upgradeExistingApplicationDirectoryString + "lib" + File.separator);
15175               if (libDirFile.exists()) {
15176                 this.upgradeExistingLibDirectoryString = libDirFile.getAbsolutePath();
15177               } else {
15178                 this.upgradeExistingLibDirectoryString = this.upgradeExistingApplicationDirectoryString;
15179               }            
15180             }
15181           }
15182           this.upgradeExistingLibDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.upgradeExistingLibDirectoryString);
15183         }
15184 
15185         //find the bin dir
15186         {
15187           File binDirFile = new File(this.upgradeExistingApplicationDirectoryString + "bin" + File.separator);
15188           if (binDirFile.exists()) {
15189             this.upgradeExistingBinDirectoryString = binDirFile.getAbsolutePath();
15190           } else {
15191             binDirFile = new File(this.upgradeExistingApplicationDirectoryString + "WEB-INF" + File.separator + "bin" + File.separator);
15192             if (binDirFile.exists()) {
15193               this.upgradeExistingBinDirectoryString = binDirFile.getAbsolutePath();
15194             } else {
15195               this.upgradeExistingBinDirectoryString = this.upgradeExistingApplicationDirectoryString;
15196             }
15197           }
15198           this.upgradeExistingBinDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.upgradeExistingBinDirectoryString);
15199         }
15200 
15201 
15202         return upgradeExistingDirectoryString;
15203       }
15204       
15205       throw new RuntimeException(errorMessage);
15206       
15207     } finally {
15208       //set this back
15209       this.upgradeExistingApplicationDirectoryString = tempUpgradeExistingApplicationDirectoryString;
15210     }
15211   }
15212 
15213   /**
15214    * where classes are in the upgrade directory, ends in file separator
15215    */
15216   private String upgradeExistingClassesDirectoryString;
15217   
15218   /**
15219    * where jars are in the upgrade directory, ends in file separator
15220    */
15221   private String upgradeExistingLibDirectoryString;
15222 
15223   /**
15224    * where bin files (gsh) are in the upgrade directory, ends in file separator
15225    */
15226   private String upgradeExistingBinDirectoryString;
15227   /**
15228    * 
15229    * @param action upgrade or patch
15230    * @return what we are upgrading
15231    */
15232   private AppToUpgrade grouperAppToUpgradeOrPatch(String action) {
15233 
15234     AppToUpgrade appToUpgradeLocal = 
15235         (AppToUpgrade)promptForEnum(
15236             "What do you want to " + action + "?  api, ui, ws, pspng, or psp? ",
15237             "grouperInstaller.autorun.appToUpgrade", AppToUpgrade.class, AppToUpgrade.API, "grouperInstaller.default.appToUpgrade");
15238     return appToUpgradeLocal;
15239   }
15240 
15241   /**
15242    * add something to an xml file
15243    * @param file
15244    * @param addAfterThisRegex 
15245    * @param mustPassTheseRegexes 
15246    * @param newValue 
15247    * @param description of change for sys out print
15248    */
15249   public static void addToXmlFile(File file, String addAfterThisRegex, String[] mustPassTheseRegexes, String newValue, String description) {
15250     if (!file.exists() || file.length() == 0) {
15251       throw new RuntimeException("Why does " + file.getName() + " not exist and have contents? " 
15252           + file.getAbsolutePath());
15253     }
15254     
15255     String fileContents = GrouperInstallerUtils.readFileIntoString(file);
15256     
15257     String newline = GrouperInstallerUtils.newlineFromFile(fileContents);
15258     
15259     Pattern pattern = Pattern.compile(addAfterThisRegex);
15260 
15261     String[] lines = GrouperInstallerUtils.splitLines(fileContents);
15262     
15263     Pattern[] lineMustPassThesePatterns = new Pattern[GrouperInstallerUtils.length(mustPassTheseRegexes)];
15264     
15265     boolean[] hasPassedTheseRegexes = new boolean[lineMustPassThesePatterns.length];
15266     
15267     for (int i=0;i<hasPassedTheseRegexes.length;i++) {
15268       hasPassedTheseRegexes[i] = false;
15269     }
15270     
15271     {
15272       int index = 0;
15273       for (String lineMustHaveRegex : GrouperInstallerUtils.nonNull(mustPassTheseRegexes, String.class)) {
15274         Pattern lineMustHavePattern = Pattern.compile(lineMustHaveRegex);
15275         lineMustPassThesePatterns[index] = lineMustHavePattern;
15276         
15277         index++;
15278       }
15279     }    
15280 
15281     StringBuilder newfile = new StringBuilder();
15282 
15283     boolean madeChange = false;
15284 
15285     OUTER: for (String line : lines) {
15286       line = GrouperInstallerUtils.defaultString(line);
15287       
15288       //lets see if it satisfies all
15289       for (int i=0;i<lineMustPassThesePatterns.length;i++) {
15290         Pattern lineMustHavePattern = lineMustPassThesePatterns[i];
15291         if (lineMustHavePattern.matcher(line).find()) {
15292           hasPassedTheseRegexes[i] = true;
15293         }
15294       }
15295       
15296       //see if we have passed all the prefixes
15297       for (int i=0;i<hasPassedTheseRegexes.length;i++) {
15298         if (!hasPassedTheseRegexes[i]) {
15299           newfile.append(line).append(newline);
15300           continue OUTER;
15301         }
15302       }
15303       
15304       //see if satisfies current, and only add once
15305       Matcher matcher = pattern.matcher(line);
15306       if (!matcher.find() || madeChange) {
15307         newfile.append(line).append(newline);
15308         continue;
15309       }
15310 
15311       //we need to change the value
15312       System.out.println(" - adding " + description + " line: '" + newValue + "'");
15313       newfile.append(line);
15314       newfile.append(newline);
15315       newfile.append(newValue);
15316       newfile.append(newline);
15317       madeChange = true;
15318     }
15319     if (!madeChange) {
15320       throw new RuntimeException("Couldnt find place to add to server.xml!  Are there newlines that werent there before or something?");
15321     }
15322     
15323     GrouperInstallerUtils.writeStringToFile(file, newfile.toString());
15324     
15325   }
15326 
15327   private static String removeElConfigFromPropertiesFile(String fileContents, String propertyName) {
15328     
15329     if (propertyName.endsWith(".elConfig")) {
15330       return fileContents;
15331     }
15332 
15333     String elConfigPropertyName = propertyName + ".elConfig";
15334     
15335     //lets look for property in file
15336     //this is a newline or form feed then some optional whitespace, and the property name
15337     //then some optional whitespace then an equals, then optional whitespace, then some text
15338     String elConfigPropertyNameRegex = GrouperInstallerUtils.replace(elConfigPropertyName, ".", "\\.");
15339     String regex = "[\\n\\r][ \\t]*(" + elConfigPropertyNameRegex + "[ \\t]*=[ \\t]*[^\\n\\r]*)";
15340     Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
15341     Matcher matcher = pattern.matcher(fileContents);
15342 
15343     if (matcher.find()) {
15344       String previousValue = matcher.group(1);
15345             
15346       int startIndex = matcher.start(1);
15347       
15348       int endIndex = matcher.end(1);
15349       
15350       String newContents = fileContents.substring(0, startIndex);
15351       
15352       //if not the last char
15353       if (endIndex < fileContents.length()-1) {
15354         newContents += fileContents.substring(endIndex, fileContents.length());
15355       }
15356 
15357       //if there is another match, there is a problem
15358       if (matcher.find()) {
15359         throw new RuntimeException("Why are there multiple matches for " + propertyName + " in propertyFile??????");
15360       }
15361 
15362       System.out.println(" - removed property: " 
15363           + elConfigPropertyName);
15364       return newContents;
15365     }
15366     return fileContents;
15367   }
15368   /**
15369    * edit a property in a property file
15370    * @param file
15371    * @param propertyName
15372    * @param propertyValue
15373    * @param createFileIfNotExist 
15374    */
15375   public static void editPropertiesFile(File file, String propertyName, String propertyValue, boolean createFileIfNotExist) {
15376     if (!file.exists()) {
15377       if (createFileIfNotExist) {
15378         System.out.println("Creating file: " + (file == null ? null : file.getAbsolutePath()));
15379         GrouperInstallerUtils.fileCreate(file);
15380       } else {
15381         throw new RuntimeException("Why does " + file.getName() + " not exist and have contents? " 
15382             + file.getAbsolutePath());
15383       }
15384     }
15385     
15386     propertyValue = GrouperInstallerUtils.defaultString(propertyValue);
15387     
15388     String fileContents = GrouperInstallerUtils.readFileIntoString(file);
15389     
15390     String newline = GrouperInstallerUtils.newlineFromFile(fileContents);
15391     
15392     //if it starts with it, add a newline to start so the regexes work
15393     if (!fileContents.startsWith(newline)) {
15394       fileContents = newline + fileContents;
15395     }
15396     
15397     //lets look for property in file
15398     //this is a newline or form feed then some optional whitespace, and the property name
15399     //then some optional whitespace then an equals, then optional whitespace, then some text
15400     String propertyNameRegex = GrouperInstallerUtils.replace(propertyName, ".", "\\.");
15401     String regex = "[\\n\\r][ \\t]*" + propertyNameRegex + "[ \\t]*=[ \\t]*([^\\n\\r]*)";
15402     Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
15403     Matcher matcher = pattern.matcher(fileContents);
15404 
15405     if (matcher.find()) {
15406       String previousValue = matcher.group(1);
15407       
15408       if (GrouperInstallerUtils.trimToEmpty(previousValue).equals(GrouperInstallerUtils.trim(propertyValue))) {
15409         System.out.println(" - property " + propertyName + " already was set to: " + propertyValue + ", not changing file");
15410         return;
15411       }
15412       
15413       int startIndex = matcher.start(1);
15414       
15415       int endIndex = matcher.end(1);
15416       
15417       String newContents = fileContents.substring(0, startIndex) + propertyValue;
15418       
15419       //if not the last char
15420       if (endIndex < fileContents.length()-1) {
15421         newContents += fileContents.substring(endIndex, fileContents.length());
15422       }
15423 
15424       //if there is another match, there is a problem
15425       if (matcher.find()) {
15426         throw new RuntimeException("Why are there multiple matches for " + propertyName + " in propertyFile: " + file.getAbsolutePath() + "??????");
15427       }
15428 
15429       System.out.println(" - set property: " 
15430           + propertyName + " from: " + previousValue + " to: " + propertyValue);
15431       
15432       newContents = removeElConfigFromPropertiesFile(fileContents, newContents);
15433       
15434       GrouperInstallerUtils.writeStringToFile(file, newContents);
15435       return;
15436     }
15437     
15438     //lets see if it is in a comment
15439     //this is a newline or form feed then some optional whitespace, hash, optional whitespace, and the property name
15440     //then some optional whitespace then an equals, then optional whitespace, then some text
15441     regex = ".*[\\n\\r]([ \\t]*#[ \\t]*)" + propertyNameRegex + "[ \\t]*=[ \\t]*([^\\n\\r]*).*";
15442     pattern = Pattern.compile(regex, Pattern.DOTALL);
15443     matcher = pattern.matcher(fileContents);
15444 
15445     if (matcher.matches()) {
15446       String previousValue = matcher.group(2);
15447       
15448       int startIndexHash = matcher.start(1);
15449       
15450       int endIndexHash = matcher.end(1);
15451 
15452       int startIndex = matcher.start(2);
15453       
15454       int endIndex = matcher.end(2);
15455       
15456       String newContents = fileContents.substring(0, startIndexHash) + fileContents.substring(endIndexHash, startIndex)
15457         + propertyValue;
15458       
15459       //if not the last char
15460       if (endIndex < fileContents.length()-1) {
15461         newContents += fileContents.substring(endIndex, fileContents.length());
15462       }
15463       System.out.println(" - uncommented property: " 
15464           + propertyName + " from: " + previousValue + " to: " + propertyValue);
15465       
15466       newContents = removeElConfigFromPropertiesFile(fileContents, newContents);
15467 
15468       GrouperInstallerUtils.writeStringToFile(file, newContents);
15469       
15470       return;
15471     }
15472     
15473     //it must have not existed
15474     //add a newline..
15475     //add to end in case it was already there, now it will be overwritten
15476     String newContents = fileContents + newline + "# added by grouper-installer" + newline + propertyName + " = " + propertyValue + newline;
15477     
15478     newContents = removeElConfigFromPropertiesFile(newContents, propertyName);
15479 
15480     GrouperInstallerUtils.writeStringToFile(file, newContents);
15481 
15482     System.out.println(" - added to end of property file: " + propertyName + " = " + propertyValue);
15483     
15484   }
15485 
15486   /**
15487    * untar a file to a dir
15488    * @param fileName
15489    * @param autorunPropertiesKeyIfFileExistsUseLocal key in properties file to automatically fill in a value
15490    * @param dirToUntarTo or null to keep in same dir as tarfile
15491    * @return the directory where the files are (assuming has a single dir the same name as the archive)
15492    */
15493   private File untar(final String fileName, final String autorunPropertiesKeyIfFileExistsUseLocal,
15494       File dirToUntarTo) {
15495 
15496     if (!fileName.endsWith(".tar")) {
15497       throw new RuntimeException("File doesnt end in .tar: " + fileName);
15498     }
15499     String untarredFileName = fileName.substring(0, fileName.length() - ".tar".length());
15500     
15501     //ant has -bin which is annoying
15502     if (untarredFileName.endsWith("-bin")) {
15503       untarredFileName = untarredFileName.substring(0, untarredFileName.length() - "-bin".length());
15504     }
15505 
15506     if (dirToUntarTo == null) {
15507       dirToUntarTo = new File(untarredFileName).getParentFile();
15508     }
15509     
15510     File untarredFile = new File(dirToUntarTo.getAbsoluteFile() + File.separator + new File(untarredFileName).getName());
15511     untarredFileName = untarredFile.getAbsolutePath();
15512     
15513     if (untarredFile.exists()) {
15514       
15515       if (this.useAllUntarredDirectories != null && this.useAllUntarredDirectories == true) {
15516         return untarredFile;
15517       }
15518       
15519       System.out.print("Untarred dir exists: " + untarredFileName + ", use untarred dir (t|f)? [t]: ");
15520       boolean useUnzippedFile = readFromStdInBoolean(true, autorunPropertiesKeyIfFileExistsUseLocal);
15521       if (useUnzippedFile) {
15522         
15523         if (this.useAllUntarredDirectories == null) {
15524           System.out.print("Would you like to use all existing untarred directories (t|f)? [t]: ");
15525           this.useAllUntarredDirectories = readFromStdInBoolean(true, "grouperInstaller.autorun.useAllUntarredDirectories");
15526         }
15527         
15528         return untarredFile;
15529       }
15530       
15531       System.out.println("Deleting: " + untarredFileName);
15532       GrouperInstallerUtils.deleteRecursiveDirectory(untarredFileName);
15533     }
15534     
15535     System.out.println("Expanding: " + fileName + " to " + untarredFile.getAbsolutePath());
15536     
15537     final File[] result = new File[1];
15538     
15539     final File DIR_TO_UNTAR_TO = dirToUntarTo;
15540     
15541     Runnable runnable = new Runnable() {
15542 
15543       public void run() {
15544         result[0] = untarHelper(fileName, autorunPropertiesKeyIfFileExistsUseLocal, DIR_TO_UNTAR_TO);
15545       }
15546     };
15547 
15548     GrouperInstallerUtils.threadRunWithStatusDots(runnable, true);
15549 
15550     return result[0];
15551 
15552   }
15553 
15554   /**
15555    * untar a file to a dir
15556    * @param fileName
15557    * @param autorunPropertiesKeyIfFileExistsUseLocal key in properties file to automatically fill in a value
15558    * @param dirToUntarTo or null to keep in same dir as tarfile
15559    * @return the directory where the files are (assuming has a single dir the same name as the archive)
15560    */
15561   @SuppressWarnings("resource")
15562   private static File untarHelper(String fileName, String autorunPropertiesKeyIfFileExistsUseLocal, File dirToUntarTo) {
15563     TarArchiveInputStream tarArchiveInputStream = null;
15564     
15565     String untarredFileName = fileName.substring(0, fileName.length() - ".tar".length());
15566     
15567     //ant has -bin which is annoying
15568     if (untarredFileName.endsWith("-bin")) {
15569       untarredFileName = untarredFileName.substring(0, untarredFileName.length() - "-bin".length());
15570     }
15571     
15572     if (dirToUntarTo == null) {
15573       dirToUntarTo = new File(untarredFileName).getParentFile();
15574     }
15575 
15576     File untarredFile = new File(dirToUntarTo.getAbsoluteFile() + File.separator + new File(untarredFileName).getName());
15577 
15578     try {
15579 
15580       tarArchiveInputStream = new TarArchiveInputStream(new FileInputStream(fileName));
15581       
15582       while (true) {
15583   
15584         TarArchiveEntry tarArchiveEntry = tarArchiveInputStream.getNextTarEntry();
15585         if (tarArchiveEntry == null) {
15586           break;
15587         }
15588         
15589         //System.out.println("Entry: " + tarArchiveEntry.getName()
15590         //    + ", isDirectory: " + tarArchiveEntry.isDirectory()
15591         //    + ", isFile: " + tarArchiveEntry.isFile());
15592         String fileEntryName = dirToUntarTo.getAbsolutePath() + File.separator + tarArchiveEntry.getName();
15593         File tarEntryFile = new File(fileEntryName);
15594         
15595         if (tarArchiveEntry.isDirectory()) {
15596           if (!tarEntryFile.exists() && !tarEntryFile.mkdirs()) {
15597             throw new RuntimeException("Cant create dirs: " + tarEntryFile.getAbsolutePath());
15598           }
15599           continue;
15600         }
15601         
15602         byte[] content = new byte[(int)tarArchiveEntry.getSize()];
15603 
15604         int size = tarArchiveInputStream.read(content, 0, content.length);
15605         
15606         //for some reason we get an error when 0 bytes...
15607         if (size != content.length && (!(size == -1 && content.length == 0))) {
15608           throw new RuntimeException("Didnt read the right amount of bytes: " + size 
15609               + ", should have been: " + content.length + " on entry: " + tarArchiveEntry.getName());
15610         }
15611         
15612         ByteArrayInputStream byteArrayInputStream = null;
15613         FileOutputStream fileOutputStream = null;
15614         
15615         try {
15616           
15617           //create parent directories
15618           if (!tarEntryFile.getParentFile().exists() && !tarEntryFile.getParentFile().mkdirs()) {
15619             throw new RuntimeException("Cant create dirs: " + tarEntryFile.getParentFile().getAbsolutePath());
15620           }
15621 
15622           fileOutputStream = new FileOutputStream(tarEntryFile);
15623           byteArrayInputStream = new ByteArrayInputStream(content);
15624           GrouperInstallerUtils.copy(byteArrayInputStream, fileOutputStream);
15625           
15626         } catch (Exception e) {
15627           throw new RuntimeException("Problem with entry: " + tarArchiveEntry.getName(), e);
15628         } finally {
15629           GrouperInstallerUtils.closeQuietly(byteArrayInputStream);
15630           GrouperInstallerUtils.closeQuietly(fileOutputStream);
15631         }
15632         
15633       }
15634     } catch (Exception e) {
15635       throw new RuntimeException("Error untarring: " + fileName, e);
15636     } finally {
15637       GrouperInstallerUtils.closeQuietly(tarArchiveInputStream);
15638     }
15639     return untarredFile;
15640   }
15641 
15642   /**
15643    * unzip a file, this is for .zip files
15644    * @param fileName
15645    * @param autorunPropertiesKeyIfFileExistsUseLocal key in properties file to automatically fill in a value
15646    * @return the unzipped file
15647    */
15648   private static File unzipFromZip(final String fileName, final String autorunPropertiesKeyIfFileExistsUseLocal) {
15649 
15650     if (!fileName.endsWith(".zip")) {
15651       throw new RuntimeException("File doesnt end in .zip: " + fileName);
15652     }
15653     String unzippedFileName = fileName.substring(0, fileName.length() - ".zip".length());
15654 
15655     File unzippedDir = new File(unzippedFileName);
15656 
15657     if (unzippedDir.exists()) {
15658       System.out.print("Unzipped dir exists: " + unzippedFileName + ", use unzipped dir (t|f)? [t]: ");
15659       boolean useUnzippedFile = readFromStdInBoolean(true, autorunPropertiesKeyIfFileExistsUseLocal);
15660       if (useUnzippedFile) {
15661         return unzippedDir;
15662       }
15663       System.out.println("Deleting: " + unzippedFileName);
15664       GrouperInstallerUtils.deleteRecursiveDirectory(unzippedFileName);
15665     } else {
15666       if (!unzippedDir.mkdir()) {
15667         throw new RuntimeException("Cant make dir: " + unzippedDir.getAbsolutePath());
15668       }
15669     }
15670 
15671     System.out.println("Unzipping: " + fileName);
15672 
15673     final File[] result = new File[1];
15674     
15675     Runnable runnable = new Runnable() {
15676 
15677       public void run() {
15678         result[0] = unzipFromZipHelper(fileName, autorunPropertiesKeyIfFileExistsUseLocal);
15679       }
15680     };
15681 
15682     GrouperInstallerUtils.threadRunWithStatusDots(runnable, true);
15683 
15684     return result[0];
15685 
15686   }
15687 
15688   /**
15689    * unzip a file, this is for .zip files
15690    * @param fileName
15691    * @param autorunPropertiesKeyIfFileExistsUseLocal key in properties file to automatically fill in a value
15692    * @return the unzipped file
15693    */
15694   private static File unzipFromZipHelper(String fileName, String autorunPropertiesKeyIfFileExistsUseLocal) {
15695 
15696     String unzippedFileName = fileName.substring(0, fileName.length() - ".zip".length());
15697 
15698     File unzippedDir = new File(unzippedFileName);
15699 
15700     ZipFile zipFile = null;
15701     try {
15702       zipFile = new ZipFile(fileName);
15703       Enumeration<? extends ZipEntry> entries = zipFile.entries();
15704       while (entries.hasMoreElements()) {
15705         ZipEntry entry = entries.nextElement();
15706         File entryDestination = new File(unzippedDir, entry.getName());
15707         if (entry.isDirectory()) {
15708           entryDestination.mkdirs();
15709         } else {
15710           entryDestination.getParentFile().mkdirs();
15711           InputStream in = zipFile.getInputStream(entry);
15712           OutputStream out = new FileOutputStream(entryDestination);
15713           try {
15714             IOUtils.copy(in, out);
15715           } finally {
15716             GrouperInstallerUtils.closeQuietly(in);
15717             GrouperInstallerUtils.closeQuietly(out);
15718           }
15719         }
15720       }
15721     } catch (IOException ioe) {
15722       throw new RuntimeException(ioe);
15723     } finally {
15724       GrouperInstallerUtils.closeQuietly(zipFile);
15725     }
15726 
15727     return unzippedDir;
15728 
15729   }
15730 
15731   /**
15732    * unzip a file to another file, this is for .gz files
15733    * @param fileName
15734    * @param autorunPropertiesKeyIfFileExistsUseLocal key in properties file to automatically fill in a value
15735    * @return the unzipped file
15736    */
15737   private File unzip(final String fileName, final String autorunPropertiesKeyIfFileExistsUseLocal) {
15738 
15739     if (!fileName.endsWith(".gz")) {
15740       throw new RuntimeException("File doesnt end in .gz: " + fileName);
15741     }
15742     String unzippedFileName = fileName.substring(0, fileName.length() - ".gz".length());
15743     
15744     File unzippedFile = new File(unzippedFileName);
15745     if (unzippedFile.exists()) {
15746       
15747       if (this.useAllUnzippedFiles != null && this.useAllUnzippedFiles == true) {
15748         return unzippedFile;
15749       }
15750       
15751       System.out.print("Unzipped file exists: " + unzippedFileName + ", use unzipped file (t|f)? [t]: ");
15752       boolean useUnzippedFile = readFromStdInBoolean(true, autorunPropertiesKeyIfFileExistsUseLocal);
15753       if (useUnzippedFile) {
15754         if (this.useAllUnzippedFiles == null) {
15755           System.out.print("Would you like to use all existing unzipped files (t|f)? [t]: ");
15756           this.useAllUnzippedFiles = readFromStdInBoolean(true, "grouperInstaller.autorun.useAllUnzippedFiles");
15757         }
15758         
15759         return unzippedFile;
15760       }
15761       System.out.println("Deleting: " + unzippedFileName);
15762       if (!unzippedFile.delete()) {
15763         throw new RuntimeException("Cant delete file: " + unzippedFileName);
15764       }
15765     }
15766 
15767     System.out.println("Unzipping: " + fileName);
15768     
15769     final File[] result = new File[1];
15770     
15771     Runnable runnable = new Runnable() {
15772 
15773       public void run() {
15774         result[0] = unzipHelper(fileName, autorunPropertiesKeyIfFileExistsUseLocal);
15775       }
15776     };
15777 
15778     GrouperInstallerUtils.threadRunWithStatusDots(runnable, true);
15779 
15780     return result[0];
15781 
15782   }
15783 
15784   /**
15785    * unzip a file to another file, this is for .gz files
15786    * @param fileName
15787    * @param autorunPropertiesKeyIfFileExistsUseLocal key in properties file to automatically fill in a value
15788    * @return the unzipped file
15789    */
15790   private static File unzipHelper(String fileName, String autorunPropertiesKeyIfFileExistsUseLocal) {
15791 
15792     String unzippedFileName = fileName.substring(0, fileName.length() - ".gz".length());
15793     File unzippedFile = new File(unzippedFileName);
15794 
15795     GzipCompressorInputStream gzipCompressorInputStream = null;
15796     FileOutputStream fileOutputStream = null;
15797     try {
15798       gzipCompressorInputStream = new GzipCompressorInputStream(new BufferedInputStream(new FileInputStream(new File(fileName))));
15799       fileOutputStream = new FileOutputStream(unzippedFile);
15800       GrouperInstallerUtils.copy(gzipCompressorInputStream, fileOutputStream);
15801     } catch (Exception e) {
15802       throw new RuntimeException("Cant unzip file: " + fileName, e);
15803     } finally {
15804       GrouperInstallerUtils.closeQuietly(gzipCompressorInputStream);
15805       GrouperInstallerUtils.closeQuietly(fileOutputStream);
15806     }
15807     return unzippedFile;
15808   }
15809 
15810   /**
15811    * 
15812    * @return the file of the directory of the psp
15813    */
15814   private File downloadPsp() {
15815     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
15816     
15817     if (!urlToDownload.endsWith("/")) {
15818       urlToDownload += "/";
15819     }
15820     urlToDownload += "release/";
15821 
15822     String pspFileName = "grouper.psp-" + this.version + ".tar.gz";
15823     urlToDownload += this.version + "/" + pspFileName;
15824 
15825     File pspFile = new File(this.grouperTarballDirectoryString + pspFileName);
15826     
15827     downloadFile(urlToDownload, pspFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalPspDownloadTarEtc");
15828 
15829     return pspFile;
15830   }
15831   
15832   /**
15833    * 
15834    * @return the file of the directory of the psp
15835    */
15836   private File downloadPspng() {
15837     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
15838     
15839     if (!urlToDownload.endsWith("/")) {
15840       urlToDownload += "/";
15841     }
15842     urlToDownload += "release/";
15843 
15844     String pspFileName = "grouper.pspng-" + this.version + ".tar.gz";
15845     urlToDownload += this.version + "/" + pspFileName;
15846 
15847     File pspFile = new File(this.grouperTarballDirectoryString + pspFileName);
15848     
15849     downloadFile(urlToDownload, pspFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalPspngDownloadTarEtc");
15850 
15851     return pspFile;
15852   }
15853   
15854   /**
15855    * upgrade the ws
15856    */
15857   private void upgradeWs() {
15858   
15859     this.upgradeApiPreRevertPatch();
15860 
15861     System.out.println("You need to revert all patches to upgrade");
15862     this.patchRevertWs();
15863     
15864     System.out.println("\n##################################");
15865     System.out.println("Upgrading WS\n");
15866     
15867     //copy the jars there
15868     System.out.println("\n##################################");
15869     System.out.println("Upgrading WS jars\n");
15870   
15871     this.upgradeJars(new File(this.untarredWsDir + File.separator + 
15872         "grouper-ws" + File.separator + "build" + File.separator + "dist" + File.separator + "grouper-ws"
15873         + File.separator + "WEB-INF" + File.separator + "lib" + File.separator));
15874   
15875     System.out.println("\n##################################");
15876     System.out.println("Upgrading WS files\n");
15877   
15878     //copy files there
15879     this.copyFiles(this.untarredWsDir + File.separator + 
15880         "grouper-ws" + File.separator + "build" + File.separator + "dist" + File.separator + "grouper-ws"
15881         + File.separator,
15882         this.upgradeExistingApplicationDirectoryString,
15883         GrouperInstallerUtils.toSet("WEB-INF/lib", "WEB-INF/web.xml", "WEB-INF/web.wsTomcatAuthn.xml", "WEB-INF/server.wsTomcatAuthn.xml", "WEB-INF/classes",
15884             "WEB-INF/bin/gsh", "WEB-INF/bin/gsh.bat", "WEB-INF/bin/gsh.sh"));
15885 
15886     {
15887       boolean hadChange = false;
15888       for (String gshName : new String[]{"gsh", "gsh.bat", "gsh.sh"}) {
15889         File newGshFile = new File(this.untarredWsDir + File.separator + "grouper-ws" + File.separator 
15890             + "build" + File.separator + "dist" + File.separator + "grouper-ws"
15891             + File.separator + "WEB-INF" + File.separator + "bin" 
15892             + File.separator + gshName);
15893   
15894         File existingGshFile = new File(this.upgradeExistingApplicationDirectoryString 
15895             + File.separator + "WEB-INF" + File.separator + "bin" + File.separator + gshName);
15896   
15897         if (!GrouperInstallerUtils.contentEquals(newGshFile, existingGshFile)) {
15898           this.backupAndCopyFile(newGshFile, existingGshFile, true);
15899           if (!GrouperInstallerUtils.equals("gsh.bat", gshName)) {
15900             hadChange = true;
15901           }
15902         }
15903         
15904       }
15905       if (hadChange) {
15906         //set executable and dos2unix
15907         gshExcutableAndDos2Unix(this.upgradeExistingApplicationDirectoryString + "WEB-INF" 
15908             + File.separator + "bin" 
15909             + File.separator);
15910       }
15911     }
15912     
15913     upgradeWebXml(new File(this.untarredWsDir + File.separator + "grouper-ws" 
15914         + File.separator + "build" + File.separator + "dist" + File.separator + "grouper-ws"
15915         + File.separator + "WEB-INF" + File.separator + "web.xml"),
15916             new File(this.upgradeExistingApplicationDirectoryString 
15917                 + File.separator + "WEB-INF" + File.separator + "web.xml"));
15918     
15919     System.out.println("\n##################################");
15920     System.out.println("Upgrading WS config files\n");
15921 
15922     this.compareUpgradePropertiesFile(this.grouperWsBasePropertiesFile, 
15923         new File(this.untarredWsDir + File.separator + 
15924             "grouper-ws" + File.separator + "build" + File.separator + "dist" + File.separator + "grouper-ws"
15925             + File.separator + "WEB-INF" + File.separator + "classes" + File.separator + "grouper-ws.base.properties"),
15926         this.grouperWsPropertiesFile,
15927         this.grouperWsExamplePropertiesFile, null, "grouperInstaller.autorun.removeRedundantPropetiesFromGrouperWsProperties"
15928       );
15929 
15930     this.upgradeApiPostRevertPatch();
15931     
15932     //patch it
15933     this.patchWs();
15934 
15935   }
15936   /**
15937    * get the patches available to apply that are not already applied
15938    * @param thisAppToUpgrade app to upgrade to check
15939    * @return true if up to date, false if not
15940    */
15941   private boolean patchStatus(AppToUpgrade thisAppToUpgrade) {
15942   
15943     if (thisAppToUpgrade == AppToUpgrade.CLIENT) {
15944       throw new RuntimeException("Cant get status on " + thisAppToUpgrade);
15945     }
15946     
15947     Properties patchesExistingProperties = patchExistingProperties();
15948     
15949     String grouperVersion = this.grouperVersionOfJar().toString();
15950     
15951     grouperVersion = GrouperInstallerUtils.replace(grouperVersion, ".", "_");
15952     
15953     boolean foundNewPatch = false;
15954     
15955     OUTER: for (int i=0;i<1000;i++) {
15956       
15957       //grouper_v2_2_1_api_patch_0.state
15958       String keyBase = "grouper_v" + grouperVersion + "_" + thisAppToUpgrade.name().toLowerCase() + "_patch_" + i;
15959       System.out.println("\n################ Checking patch " + keyBase);
15960       String key = keyBase + ".state";
15961   
15962       String value = patchesExistingProperties.getProperty(key);
15963 
15964       if (!GrouperInstallerUtils.isBlank(value)) {
15965 
15966         GrouperInstallerPatchStatus grouperInstallerPatchStatus = GrouperInstallerPatchStatus.valueOfIgnoreCase(value, true, true);
15967 
15968         switch (grouperInstallerPatchStatus) {
15969           case applied:
15970             
15971             System.out.println("Patch: " + keyBase + ": was applied on: " + patchesExistingProperties.getProperty(keyBase + ".date"));
15972             break;
15973             
15974           case skippedPermanently:
15975             
15976             foundNewPatch = true;
15977             System.out.println("Patch: " + keyBase + ": was skipped permanently on: " + patchesExistingProperties.getProperty(keyBase + ".date"));
15978             break;
15979             
15980           case skippedTemporarily:
15981   
15982             foundNewPatch = true;
15983             System.out.println("Patch: " + keyBase + ": was skipped termporarily on: " + patchesExistingProperties.getProperty(keyBase + ".date"));
15984             break;
15985   
15986           case reverted:
15987   
15988             foundNewPatch = true;
15989             System.out.println("Patch: " + keyBase + ": was reverted on: " + patchesExistingProperties.getProperty(keyBase + ".date"));
15990             break;
15991   
15992           case error:
15993   
15994             foundNewPatch = true;
15995             System.out.println("Patch: " + keyBase + ": had an error installing on: " + patchesExistingProperties.getProperty(keyBase + ".date"));
15996             break;
15997   
15998           default:
15999             throw new RuntimeException("Not expecting: " + grouperInstallerPatchStatus);
16000         }
16001         
16002       }
16003   
16004       //lets see if it exists on the server
16005       File patchUntarredDir = downloadAndUnzipPatch(keyBase);
16006       
16007       //if no more patches
16008       if (patchUntarredDir == null) {
16009         System.out.println("");
16010         break OUTER;
16011       }
16012       
16013       //lets get the description:
16014       //  # will show up on screen so user knows what it is
16015       //  description = This patch fixes GRP-1080: browse folders refresh button only works in chrome, not other browsers
16016       //
16017       //  # patches that this patch is dependant on (comma separated)
16018       //  dependencies = 
16019       //
16020       //  # low, medium, or high risk to applying the patch
16021       //  risk = low
16022       //
16023       //  # is this is a security patch (true or false)
16024       //  security = false
16025       //
16026       //  # if this patch requires restart of processes (true or false)
16027       //  requiresRestart = false
16028       Properties patchProperties = GrouperInstallerUtils.propertiesFromFile(new File(patchUntarredDir.getAbsoluteFile() + File.separator + keyBase + ".properties"));
16029   
16030       foundNewPatch = true;
16031   
16032       // check dependencies
16033       {
16034         String[] dependencies = GrouperInstallerUtils.splitTrim(patchProperties.getProperty("dependencies"), ",");
16035   
16036         for (String dependency : GrouperInstallerUtils.nonNull(dependencies, String.class)) {
16037           if (!this.patchesInstalled.contains(dependency)) {
16038             System.out.println("Cannot install patch " + keyBase + " since it is dependent on a patch which is not installed: " + dependency);
16039           }
16040         }
16041       }
16042       
16043       boolean securityRelated = GrouperInstallerUtils.booleanValue(patchProperties.getProperty("security"), false);
16044       boolean requiresRestart = GrouperInstallerUtils.booleanValue(patchProperties.getProperty("requiresRestart"), true);
16045       
16046       //print description
16047       System.out.println("Patch " + keyBase + " is " + patchProperties.getProperty("risk") + " risk, "
16048           + (securityRelated ? "is a security patch" : "is not a security patch"));
16049       System.out.println("Patch " + keyBase + (requiresRestart ? " requires" : " does not require") + " a restart");
16050       System.out.println(patchProperties.getProperty("description") + "\n");      
16051     }
16052   
16053     if (!foundNewPatch) {
16054       System.out.println("There are no new " + thisAppToUpgrade + " patches to install");
16055       return true;
16056     }
16057     
16058     return false;
16059   }
16060   /**
16061    * build PSPNG
16062    * @param pspngDir
16063    */
16064   private void buildPspng(File pspngDir) {
16065 
16066     if (!pspngDir.exists() || pspngDir.isFile()) {
16067       throw new RuntimeException("Cant find psp: " + pspngDir.getAbsolutePath());
16068     }
16069     
16070     File pspngBuildToDir = new File(pspngDir.getAbsolutePath() 
16071         + File.separator + "target" + File.separator + "classes");
16072     
16073     boolean rebuildPspng = true;
16074     
16075     if (pspngBuildToDir.exists()) {
16076       System.out.print("The PSPNG has been built in the past, do you want it rebuilt? (t|f) [t]: ");
16077       rebuildPspng = readFromStdInBoolean(true, "grouperInstaller.autorun.rebuildPspngAfterHavingBeenBuilt");
16078     }
16079     
16080     if (!rebuildPspng) {
16081       return;
16082     }
16083     
16084     List<String> commands = new ArrayList<String>();
16085     
16086 //    \bin\mvn compile -DskipTests
16087     addMavenCommands(commands);
16088 
16089     //put 'compile -DskipTests' in there so it wont run tests which we dont want to do
16090     // dependency:copy-dependencies package -DskipTests
16091     //not compile
16092     commands.add("dependency:copy-dependencies");
16093     commands.add("package");
16094     commands.add("-DskipTests");
16095     commands.add("-Drat.ignoreErrors=true");
16096     commands.add("-Dlicense.skip=true");
16097         
16098     System.out.println("\n##################################");
16099     System.out.println("Building PSPNG with command:\n" + pspngDir.getAbsolutePath() + "> " 
16100         + convertCommandsIntoCommand(commands) + "\n");
16101 
16102     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
16103         true, true, null, new File(pspngDir.getAbsolutePath()), null, true);
16104     
16105     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
16106       System.out.println("stderr: " + commandResult.getErrorText());
16107     }
16108     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
16109       System.out.println("stdout: " + commandResult.getOutputText());
16110     }
16111 
16112     System.out.println("\nEnd building PSPNG");
16113     System.out.println("##################################\n");
16114     
16115   }
16116 
16117   /**
16118    * 
16119    */
16120   private void upgradeSourcesXmlToProperties() {
16121   
16122     //dont do this if less than 2.3.1
16123     if (new GiGrouperVersionersion(this.version).lessThanArg(new GiGrouperVersion("2.3.1"))) {
16124       return;
16125     }
16126     
16127     //this file is done
16128     File sourcesXmlFile = new File(this.grouperPropertiesFile.getParentFile().getAbsolutePath() + File.separator + "sources.xml");
16129 
16130     if (!sourcesXmlFile.exists()) {
16131       return;
16132     }
16133       
16134     
16135     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]: ");
16136     boolean convert = readFromStdInBoolean(true, "grouperInstaller.autorun.convertSourcesXmlToProperties");
16137   
16138     if (!convert) {
16139       System.out.println("Note: grouper will not run, but whatever you want to do!!!!");
16140     }
16141     File bakFile = null;
16142     if (this.subjectPropertiesFile.exists()) {
16143       //see if there is anything in it
16144       Properties grouperCacheProperties = GrouperInstallerUtils.propertiesFromFile(this.subjectPropertiesFile);
16145       if (grouperCacheProperties.size() > 0) {
16146         bakFile = this.backupAndDeleteFile(this.grouperCachePropertiesFile, true);
16147       }
16148     }
16149     
16150     URL sourcesXmlUrl = null;
16151     
16152     try {
16153       sourcesXmlUrl = sourcesXmlFile.toURI().toURL();
16154     } catch (Exception e) {
16155       throw new RuntimeException("Problem with sources.xml: " + (sourcesXmlFile == null ? null : sourcesXmlFile.getAbsoluteFile()), e);
16156     }
16157     
16158     //convert
16159     convertSourcesXmlToProperties(this.subjectPropertiesFile, sourcesXmlUrl);
16160     
16161     File subjectBakFile = bakFile(this.subjectPropertiesFile);
16162     GrouperInstallerUtils.copyFile(this.subjectPropertiesFile, subjectBakFile, true);
16163     this.backupAndDeleteFile(sourcesXmlFile, true);
16164     
16165     {
16166       File sourcesExampleXmlFile = new File(this.grouperPropertiesFile.getParentFile().getAbsolutePath() + File.separator + "sources.example.xml");
16167       if (sourcesExampleXmlFile.exists()) {
16168         this.backupAndDeleteFile(sourcesExampleXmlFile, true);
16169       }
16170     }
16171     
16172     if (bakFile != null) {
16173       System.out.println("Note, you had settings in your subject.properties (not common), this file has been moved to: " + bakFile.getAbsolutePath());
16174       System.out.println("Merge your settings from that file to " + this.subjectPropertiesFile.getAbsolutePath());
16175       System.out.print("Press <enter> to continue: ");
16176       readFromStdIn("grouperInstaller.autorun.convertSourcesXmlToPropertiesHadPropertiesInFile");
16177     }
16178   }
16179 
16180   /**
16181    * build client API
16182    * @param messagingActiveMqDir
16183    */
16184   private void buildMessagingActivemq(File messagingActiveMqDir) {
16185     if (!messagingActiveMqDir.exists() || messagingActiveMqDir.isFile()) {
16186       throw new RuntimeException("Cant find messaging activemq: " + messagingActiveMqDir.getAbsolutePath());
16187     }
16188     
16189     File messagingActivemqBuildToDir = new File(messagingActiveMqDir.getAbsoluteFile() + File.separator + "dist" + File.separator + "bin");
16190     
16191     boolean rebuildMessagingActivemq = true;
16192     
16193     if (messagingActivemqBuildToDir.exists()) {
16194       System.out.print("Grouper messaging activemq has been built in the past, do you want it rebuilt? (t|f) [t]: ");
16195       rebuildMessagingActivemq = readFromStdInBoolean(true, "grouperInstaller.autorun.rebuildMessagingActivemqAfterHavingBeenBuilt");
16196     }
16197     
16198     if (!rebuildMessagingActivemq) {
16199       return;
16200     }
16201   
16202     List<String> commands = new ArrayList<String>();
16203     
16204     addAntCommands(commands);
16205     
16206     System.out.println("\n##################################");
16207     System.out.println("Building messaging activemq with command:\n" + messagingActiveMqDir.getAbsolutePath() + "> " 
16208         + convertCommandsIntoCommand(commands) + "\n");
16209     
16210     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
16211         true, true, null, messagingActiveMqDir, null, true);
16212     
16213     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
16214       System.out.println("stderr: " + commandResult.getErrorText());
16215     }
16216     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
16217       System.out.println("stdout: " + commandResult.getOutputText());
16218     }
16219   
16220     System.out.println("\nEnd building messaging activemq");
16221     System.out.println("##################################\n");
16222     
16223   }
16224 
16225   /**
16226    * build client API
16227    * @param messagingAwsDir
16228    */
16229   private void buildMessagingAws(File messagingAwsDir) {
16230     if (!messagingAwsDir.exists() || messagingAwsDir.isFile()) {
16231       throw new RuntimeException("Cant find messaging aws: " + messagingAwsDir.getAbsolutePath());
16232     }
16233     
16234     File messagingAwsBuildToDir = new File(messagingAwsDir.getAbsoluteFile() + File.separator + "dist" + File.separator + "bin");
16235     
16236     boolean rebuildMessagingAws = true;
16237     
16238     if (messagingAwsBuildToDir.exists()) {
16239       System.out.print("Grouper messaging aws has been built in the past, do you want it rebuilt? (t|f) [t]: ");
16240       rebuildMessagingAws = readFromStdInBoolean(true, "grouperInstaller.autorun.rebuildMessagingAwsAfterHavingBeenBuilt");
16241     }
16242     
16243     if (!rebuildMessagingAws) {
16244       return;
16245     }
16246   
16247     List<String> commands = new ArrayList<String>();
16248     
16249     addAntCommands(commands);
16250     
16251     System.out.println("\n##################################");
16252     System.out.println("Building messaging aws with command:\n" + messagingAwsDir.getAbsolutePath() + "> " 
16253         + convertCommandsIntoCommand(commands) + "\n");
16254     
16255     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
16256         true, true, null, messagingAwsDir, null, true);
16257     
16258     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
16259       System.out.println("stderr: " + commandResult.getErrorText());
16260     }
16261     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
16262       System.out.println("stdout: " + commandResult.getOutputText());
16263     }
16264   
16265     System.out.println("\nEnd building aws rabbitmq");
16266     System.out.println("##################################\n");
16267     
16268   }
16269 
16270   /**
16271    * 
16272    * @param grouperCacheBasePropertiesFile 
16273    * @param grouperCachePropertiesFile
16274    * @param ehcacheXmlUrl
16275    */
16276   public static void convertEhcacheXmlToProperties(File grouperCacheBasePropertiesFile, File grouperCachePropertiesFile, URL ehcacheXmlUrl) {
16277   
16278     //look at base properties
16279     Properties grouperCacheProperties = grouperCachePropertiesFile.exists() ? 
16280         GrouperInstallerUtils.propertiesFromFile(grouperCachePropertiesFile) : new Properties();
16281   
16282     if (!grouperCacheBasePropertiesFile.exists()) {
16283       throw new RuntimeException(grouperCacheBasePropertiesFile.getAbsolutePath() + " must exist and does not!");
16284     }
16285     
16286     if (grouperCacheProperties.size() > 0) {
16287       throw new RuntimeException(grouperCachePropertiesFile.getAbsolutePath() + " exists and must not.  Delete the file and run this again!");
16288     }
16289   
16290     if (!grouperCachePropertiesFile.getParentFile().exists() || !grouperCachePropertiesFile.getParentFile().isDirectory()) {
16291       throw new RuntimeException(grouperCachePropertiesFile.getParentFile().getAbsolutePath() + " must exist and must be a directory");
16292     }
16293     
16294     //look at base properties
16295     Properties grouperCacheBaseProperties = GrouperInstallerUtils.propertiesFromFile(grouperCacheBasePropertiesFile);
16296     
16297     StringBuilder grouperEhcachePropertiesContents = new StringBuilder();
16298     
16299     grouperEhcachePropertiesContents.append(
16300               "# Copyright 2016 Internet2\n"
16301             + "#\n"
16302             + "# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
16303             + "# you may not use this file except in compliance with the License.\n"
16304             + "# You may obtain a copy of the License at\n"
16305             + "#\n"
16306             + "#   http://www.apache.org/licenses/LICENSE-2.0\n"
16307             + "#\n"
16308             + "# Unless required by applicable law or agreed to in writing, software\n"
16309             + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n"
16310             + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
16311             + "# See the License for the specific language governing permissions and\n"
16312             + "# limitations under the License.\n"
16313             + "\n"
16314             + "#\n"
16315             + "# Grouper Cache Configuration\n"
16316             + "#\n"
16317             + "\n"
16318             + "# The grouper cache config uses Grouper Configuration Overlays (documented on wiki)\n"
16319             + "# By default the configuration is read from grouper.cache.base.properties\n"
16320             + "# (which should not be edited), and the grouper.cache.properties overlays\n"
16321             + "# the base settings.  See the grouper.cache.base.properties for the possible\n"
16322             + "# settings that can be applied to the grouper.cache.properties\n\n"
16323         );
16324   
16325     {
16326       // <diskStore path="java.io.tmpdir"/>
16327       NodeList diskStoreNodeList = GrouperInstallerUtils.xpathEvaluate(ehcacheXmlUrl, "/ehcache/diskStore");
16328       if (diskStoreNodeList.getLength() != 1) {
16329         throw new RuntimeException("Expecting one diskStore element");
16330       }
16331   
16332       Element element = (Element)diskStoreNodeList.item(0);
16333   
16334       NamedNodeMap configuredNamedNodeMap = element.getAttributes();
16335       if (configuredNamedNodeMap.getLength() != 1 || !"path".equals(configuredNamedNodeMap.item(0).getNodeName())) {
16336         throw new RuntimeException("Expecting one diskStore attribute: path");
16337       }
16338       
16339       String path = element.getAttribute("path");
16340       
16341       if (!"java.io.tmpdir".equals(path)) {
16342         grouperEhcachePropertiesContents.append("grouper.cache.diskStorePath = " + path + "\n\n");
16343       }
16344       
16345     }    
16346   
16347     {
16348       //  <defaultCache
16349       //    maxElementsInMemory="1000"
16350       //    eternal="false"
16351       //    timeToIdleSeconds="10"
16352       //    timeToLiveSeconds="10"
16353       //    overflowToDisk="false"
16354       //    statistics="false"
16355       //  />
16356       
16357       NodeList diskStoreNodeList = GrouperInstallerUtils.xpathEvaluate(ehcacheXmlUrl, "/ehcache/defaultCache");
16358       if (diskStoreNodeList.getLength() != 1) {
16359         throw new RuntimeException("Expecting one defaultCache element");
16360       }
16361   
16362       Element element = (Element)diskStoreNodeList.item(0);
16363   
16364       NamedNodeMap configuredNamedNodeMap = element.getAttributes();
16365       
16366       if (configuredNamedNodeMap.getLength() != 6) {
16367         throw new RuntimeException("Expecting defaultCache with these attributes: maxElementsInMemory, "
16368             + "eternal, timeToIdleSeconds, timeToLiveSeconds, overflowToDisk, statistics");
16369       }
16370   
16371       boolean madeChanges = false;
16372       
16373       for (int i=0;i<configuredNamedNodeMap.getLength(); i++) {
16374         
16375         String attributeName = configuredNamedNodeMap.item(i).getNodeName();
16376         String value = element.getAttribute(attributeName);
16377   
16378         if ("maxElementsInMemory".equals(attributeName)) {
16379           if (!"1000".equals(value)) {
16380             grouperEhcachePropertiesContents.append("cache.defaultCache.maxElementsInMemory = " + value + "\n");
16381             madeChanges = true;
16382           }
16383         } else if ("eternal".equals(attributeName)) {
16384           if (!"false".equals(value)) {
16385             grouperEhcachePropertiesContents.append("cache.defaultCache.eternal = " + value + "\n");
16386             madeChanges = true;
16387           }
16388         } else if ("timeToIdleSeconds".equals(attributeName)) {
16389           if (!"10".equals(value)) {
16390             grouperEhcachePropertiesContents.append("cache.defaultCache.timeToIdleSeconds = " + value + "\n");
16391             madeChanges = true;
16392           }
16393           
16394         } else if ("timeToLiveSeconds".equals(attributeName)) {
16395           if (!"10".equals(value)) {
16396             grouperEhcachePropertiesContents.append("cache.defaultCache.timeToLiveSeconds = " + value + "\n");
16397             madeChanges = true;
16398           }
16399           
16400         } else if ("overflowToDisk".equals(attributeName)) {
16401           if (!"false".equals(value)) {
16402             grouperEhcachePropertiesContents.append("cache.defaultCache.overflowToDisk = " + value + "\n");
16403             madeChanges = true;
16404           }
16405           
16406         } else if ("statistics".equals(attributeName)) {
16407           if (!"false".equals(value)) {
16408             grouperEhcachePropertiesContents.append("cache.defaultCache.statistics = " + value + "\n");
16409             madeChanges = true;
16410           }
16411           
16412         } else {
16413           throw new RuntimeException("Not expecting attribuet defaultCache " + attributeName);
16414         }
16415       }
16416   
16417       if (madeChanges) {
16418         grouperEhcachePropertiesContents.append("\n");
16419       }
16420       
16421     }
16422     
16423     NodeList nodeList = GrouperInstallerUtils.xpathEvaluate(ehcacheXmlUrl, "/ehcache/cache");
16424     
16425     Set<String> usedKeys = new HashSet<String>();
16426     
16427     for (int i=0;i<nodeList.getLength();i++) {
16428       
16429       Element element = (Element)nodeList.item(i);
16430   
16431       //  <cache  name="edu.internet2.middleware.grouper.internal.dao.hib3.Hib3MemberDAO.FindBySubject"
16432       //      maxElementsInMemory="5000"
16433       //      eternal="false"
16434       //      timeToIdleSeconds="5"
16435       //      timeToLiveSeconds="10"
16436       //      overflowToDisk="false"  
16437       //      statistics="false"
16438       //  />
16439       
16440       String name = element.getAttribute("name");
16441       Integer maxElementsInMemory = GrouperInstallerUtils.intObjectValue(element.getAttribute("maxElementsInMemory"), true);
16442       Boolean eternal = GrouperInstallerUtils.booleanObjectValue(element.getAttribute("eternal"));
16443       Integer timeToIdleSeconds = GrouperInstallerUtils.intObjectValue(element.getAttribute("timeToIdleSeconds"), true);
16444       Integer timeToLiveSeconds = GrouperInstallerUtils.intObjectValue(element.getAttribute("timeToLiveSeconds"), true);
16445       Boolean overflowToDisk = GrouperInstallerUtils.booleanObjectValue(element.getAttribute("overflowToDisk"));
16446       Boolean statistics = GrouperInstallerUtils.booleanObjectValue(element.getAttribute("statistics"));
16447   
16448       //any attributes we dont expect?
16449       NamedNodeMap configuredNamedNodeMap = element.getAttributes();
16450       //see which attributes are new or changed
16451       for (int j=0;j<configuredNamedNodeMap.getLength();j++) {
16452         Node configuredAttribute = configuredNamedNodeMap.item(j);
16453         if (!configuredAttribute.getNodeName().equals("name")
16454             && !configuredAttribute.getNodeName().equals("maxElementsInMemory")
16455             && !configuredAttribute.getNodeName().equals("eternal")
16456             && !configuredAttribute.getNodeName().equals("timeToIdleSeconds")
16457             && !configuredAttribute.getNodeName().equals("timeToLiveSeconds")
16458             && !configuredAttribute.getNodeName().equals("overflowToDisk")
16459             && !configuredAttribute.getNodeName().equals("statistics")) {
16460           throw new RuntimeException("Cant process attribute: '" + configuredAttribute.getNodeName() + "'");
16461         }
16462       }
16463       
16464       String key = convertEhcacheNameToPropertiesKey(name, usedKeys);
16465       
16466       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.name = edu.internet2.middleware.grouper.internal.dao.hib3.Hib3GroupDAO
16467       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.maxElementsInMemory = 500
16468       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.eternal = false
16469       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.timeToIdleSeconds = 1
16470       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.timeToLiveSeconds = 1
16471       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.overflowToDisk = false
16472       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.statistics = false
16473   
16474       boolean madeChanges = false;
16475       
16476       if (maxElementsInMemory != null && !GrouperInstallerUtils.defaultString((String)grouperCacheBaseProperties.get("cache.name." + key + ".maxElementsInMemory")).equals(maxElementsInMemory.toString())) {
16477         grouperEhcachePropertiesContents.append("cache.name." + key + ".maxElementsInMemory = " + maxElementsInMemory + "\n");
16478         madeChanges = true;
16479       }
16480       if (eternal != null && !GrouperInstallerUtils.defaultString((String)grouperCacheBaseProperties.get("cache.name." + key + ".eternal")).equals(eternal.toString())) {
16481         grouperEhcachePropertiesContents.append("cache.name." + key + ".eternal = " + eternal + "\n");
16482         madeChanges = true;
16483       }
16484       if (timeToIdleSeconds != null && !GrouperInstallerUtils.defaultString((String)grouperCacheBaseProperties.get("cache.name." + key + ".timeToIdleSeconds")).equals(timeToIdleSeconds.toString())) {
16485         grouperEhcachePropertiesContents.append("cache.name." + key + ".timeToIdleSeconds = " + timeToIdleSeconds + "\n");
16486         madeChanges = true;
16487       }
16488       if (timeToLiveSeconds != null && !GrouperInstallerUtils.defaultString((String)grouperCacheBaseProperties.get("cache.name." + key + ".timeToLiveSeconds")).equals(timeToLiveSeconds.toString())) {
16489         grouperEhcachePropertiesContents.append("cache.name." + key + ".timeToLiveSeconds = " + timeToLiveSeconds + "\n");
16490         madeChanges = true;
16491       }
16492       if (overflowToDisk != null && !GrouperInstallerUtils.defaultString((String)grouperCacheBaseProperties.get("cache.name." + key + ".overflowToDisk")).equals(overflowToDisk.toString())) {
16493         grouperEhcachePropertiesContents.append("cache.name." + key + ".overflowToDisk = " + overflowToDisk + "\n");
16494         madeChanges = true;
16495       }
16496       if (statistics != null && !GrouperInstallerUtils.defaultString((String)grouperCacheBaseProperties.get("cache.name." + key + ".statistics")).equals(statistics.toString())) {
16497         grouperEhcachePropertiesContents.append("cache.name." + key + ".statistics = " + statistics + "\n");
16498         madeChanges = true;
16499       }
16500       if (madeChanges) {
16501         grouperEhcachePropertiesContents.append("\n");
16502       }
16503     }
16504   
16505     GrouperInstallerUtils.saveStringIntoFile(grouperCachePropertiesFile, grouperEhcachePropertiesContents.toString());
16506   }
16507 
16508   /**
16509    * get a subelement value.
16510    * e.g. if the node is &lt;source&gt;
16511    * and the sub element is &lt;id&gt;someId&lt;/id&gt;
16512    * It will return "someId" for subElementName "id"
16513    * @param parent
16514    * @param subElementName
16515    * @param required
16516    * @param descriptionForError 
16517    * @return the string or null if not there
16518    */
16519   public static String xmlElementValue(Element parent, String subElementName, boolean required, String descriptionForError) {
16520     
16521     NodeList nodeList = parent.getElementsByTagName(subElementName);
16522     
16523     if (nodeList.getLength() < 1) {
16524       if (required) {
16525         throw new RuntimeException("Cant find subElement <" + subElementName 
16526             + "> in parent element " + parent.getNodeName() + ", " + descriptionForError);
16527       }
16528       return null;
16529     }    
16530     
16531     if (nodeList.getLength() > 1) {
16532       throw new RuntimeException("Too many subElements <" + subElementName 
16533           + "> in parent element " + parent.getNodeName() + ", " 
16534           + nodeList.getLength() + ", " + descriptionForError);
16535     }
16536     return GrouperInstallerUtils.trimToEmpty(nodeList.item(0).getTextContent());
16537   }
16538   
16539   /**
16540    * put in a good comment about this param name
16541    * @param paramName
16542    * @param paramValue
16543    * @param subjectPropertiesContents
16544    */
16545   private static void convertSourcesXmlParamComment(String paramName, StringBuilder subjectPropertiesContents, String paramValue) {
16546     
16547     if (paramName == null) {
16548       throw new NullPointerException("param-name is null");
16549     }
16550     
16551     if (paramName.startsWith("subjectVirtualAttributeVariable_")) {
16552       subjectPropertiesContents.append("\n# when evaluating the virtual attribute EL expression, this variable can be used from this java class.\n"
16553           + "# " + paramName + " variable is the " + paramValue + " class.  Call static methods\n");
16554     } else if (paramName.startsWith("subjectVirtualAttribute_")) {
16555       
16556       Pattern pattern = Pattern.compile("^subjectVirtualAttribute_([\\d]+)_(.*)$");
16557       Matcher matcher = pattern.matcher(paramName);
16558       if (!matcher.matches()) {
16559         throw new RuntimeException(paramName + " is invalid, should be of form: subjectVirtualAttribute_<intIndex>_paramName");
16560       }
16561       
16562       String index = matcher.group(1);
16563       String attributeName = matcher.group(2);
16564       
16565       subjectPropertiesContents.append("\n# This virtual attribute index " + index + " is accessible via: subject.getAttributeValue(\"" + attributeName + "\");\n");
16566       
16567     } else if (GrouperInstallerUtils.equals(paramName, "findSubjectByIdOnCheckConfig")) {
16568       
16569       subjectPropertiesContents.append("\n# if a system check should try to resolve a subject by id on this source\n");
16570 
16571     } else if (GrouperInstallerUtils.equals(paramName, "subjectIdToFindOnCheckConfig")) {
16572       
16573       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");
16574 
16575     } else if (GrouperInstallerUtils.equals(paramName, "findSubjectByIdentifiedOnCheckConfig")) {
16576       
16577       subjectPropertiesContents.append("\n# by default it will do a search by subject identifier\n");
16578 
16579     } else if (GrouperInstallerUtils.equals(paramName, "subjectIdentifierToFindOnCheckConfig")) {
16580       
16581       subjectPropertiesContents.append("\n# by default it will use a random value for subject identifier to lookup, you can specify a value here\n");
16582 
16583     } else if (GrouperInstallerUtils.equals(paramName, "findSubjectByStringOnCheckConfig")) {
16584       
16585       subjectPropertiesContents.append("\n# by default it will search for a subject by string\n");
16586 
16587     } else if (GrouperInstallerUtils.equals(paramName, "stringToFindOnCheckConfig")) {
16588       
16589       subjectPropertiesContents.append("\n# you can specify the search string here or it will be a random value\n");
16590 
16591     } else if (GrouperInstallerUtils.equals(paramName, "sortAttribute0")) {
16592       
16593       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"
16594           + "# you can have up to 5 sort attributes \n");
16595 
16596     } else if (GrouperInstallerUtils.equals(paramName, "sortAttribute1")) {
16597       
16598       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"
16599           + "# you can have up to 5 sort attributes \n");
16600 
16601     } else if (GrouperInstallerUtils.equals(paramName, "sortAttribute2")) {
16602       
16603       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"
16604           + "# you can have up to 5 sort attributes \n");
16605 
16606     } else if (GrouperInstallerUtils.equals(paramName, "sortAttribute3")) {
16607       
16608       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"
16609           + "# you can have up to 5 sort attributes \n");
16610 
16611     } else if (GrouperInstallerUtils.equals(paramName, "sortAttribute4")) {
16612       
16613       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"
16614           + "# you can have up to 5 sort attributes \n");
16615 
16616     } else if (GrouperInstallerUtils.equals(paramName, "searchAttribute0")) {
16617       
16618       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"
16619           + "# you can have up to 5 search attributes \n");
16620 
16621     } else if (GrouperInstallerUtils.equals(paramName, "searchAttribute1")) {
16622       
16623       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"
16624           + "# you can have up to 5 search attributes \n");
16625 
16626     } else if (GrouperInstallerUtils.equals(paramName, "searchAttribute2")) {
16627       
16628       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"
16629           + "# you can have up to 5 search attributes \n");
16630 
16631     } else if (GrouperInstallerUtils.equals(paramName, "searchAttribute3")) {
16632       
16633       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"
16634           + "# you can have up to 5 search attributes \n");
16635 
16636     } else if (GrouperInstallerUtils.equals(paramName, "searchAttribute4")) {
16637       
16638       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"
16639           + "# you can have up to 5 search attributes\n");
16640 
16641     } else if (GrouperInstallerUtils.equals(paramName, "subjectIdentifierAttribute0")) {
16642       
16643       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"
16644           + "# you can have up to max 1 subject identifier\n");
16645 
16646     } else if (GrouperInstallerUtils.equals(paramName, "maxConnectionAge")) {
16647       
16648       subjectPropertiesContents.append("\n# seconds of max connection age\n");
16649 
16650     } else if (GrouperInstallerUtils.equals(paramName, "testConnectionOnCheckout")) {
16651       
16652       subjectPropertiesContents.append("\n# if connections from pool should be tested when checked out from pool\n");
16653 
16654     } else if (GrouperInstallerUtils.equals(paramName, "preferredTestQuery")) {
16655       
16656       subjectPropertiesContents.append("\n# query to use to test the connection when checking out from pool\n");
16657 
16658     } else if (GrouperInstallerUtils.equals(paramName, "idleConnectionTestPeriod")) {
16659       
16660       subjectPropertiesContents.append("\n# seconds between tests of idle connections in pool\n");
16661 
16662     } else if (GrouperInstallerUtils.equals(paramName, "dbDriver")) {
16663       
16664       subjectPropertiesContents.append("\n#       e.g. mysql:           com.mysql.jdbc.Driver\n"
16665           + "#       e.g. p6spy (log sql): com.p6spy.engine.spy.P6SpyDriver\n"
16666           + "#         for p6spy, put the underlying driver in spy.properties\n"
16667           + "#       e.g. oracle:          oracle.jdbc.driver.OracleDriver\n"
16668           + "#       e.g. hsqldb:          org.hsqldb.jdbcDriver\n"
16669           + "#       e.g. postgres:        org.postgresql.Driver\n");
16670 
16671     } else if (GrouperInstallerUtils.equals(paramName, "dbUrl")) {
16672       
16673       subjectPropertiesContents.append("\n#       e.g. mysql:           jdbc:mysql://localhost:3306/grouper\n"
16674           + "#       e.g. p6spy (log sql): [use the URL that your DB requires]\n"
16675           + "#       e.g. oracle:          jdbc:oracle:thin:@server.school.edu:1521:sid\n"
16676           + "#       e.g. hsqldb (a):      jdbc:hsqldb:dist/run/grouper;create=true\n"
16677           + "#       e.g. hsqldb (b):      jdbc:hsqldb:hsql://localhost:9001\n"
16678           + "#       e.g. postgres:        jdbc:postgresql:grouper\n");
16679 
16680     } else if (GrouperInstallerUtils.equals(paramName, "dbUser")) {
16681       
16682       subjectPropertiesContents.append("\n# username when connecting to the database\n");
16683 
16684     } else if (GrouperInstallerUtils.equals(paramName, "dbPwd")) {
16685       
16686       subjectPropertiesContents.append("\n# password when connecting to the database (or file with encrypted password inside)\n");
16687 
16688     } else if (GrouperInstallerUtils.equals(paramName, "maxResults")) {
16689       
16690       subjectPropertiesContents.append("\n# maximum number of results from a search, generally no need to get more than 1000\n");
16691 
16692     } else if (GrouperInstallerUtils.equals(paramName, "dbTableOrView")) {
16693       
16694       subjectPropertiesContents.append("\n# the table or view to query results from.  Note, could prefix with a schema name\n");
16695 
16696     } else if (GrouperInstallerUtils.equals(paramName, "subjectIdCol")) {
16697       
16698       subjectPropertiesContents.append("\n# the column name to get the subjectId from\n");
16699 
16700     } else if (GrouperInstallerUtils.equals(paramName, "nameCol")) {
16701       
16702       subjectPropertiesContents.append("\n# the column name to get the name from\n");
16703 
16704     } else if (GrouperInstallerUtils.equals(paramName, "lowerSearchCol")) {
16705       
16706       subjectPropertiesContents.append("\n# search col where general searches take place, lower case\n");
16707 
16708     } else if (GrouperInstallerUtils.equals(paramName, "defaultSortCol")) {
16709       
16710       subjectPropertiesContents.append("\n# optional col if you want the search results sorted in the API (note, UI might override)\n");
16711 
16712     } else if (paramName.startsWith("subjectIdentifierCol")) {
16713       
16714       subjectPropertiesContents.append("\n# you can count up from 0 to N of columns to search by identifier (which might also include by id)\n");
16715 
16716     } else if (paramName.startsWith("subjectAttributeName")) {
16717       
16718       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");
16719 
16720     } else if (paramName.startsWith("subjectAttributeCol")) {
16721       
16722       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");
16723 
16724     } else if (GrouperInstallerUtils.equals(paramName, "statusDatastoreFieldName")) {
16725       
16726       subjectPropertiesContents.append("\n# STATUS SECTION for searches to filter out inactives and allow\n"
16727           + "# the user to filter by status with e.g. status=all\n"
16728           + "# this is optional, and advanced\n"
16729           + "#\n"
16730           + "# field in database or ldap or endpoint that is the status field\n");
16731 
16732     } else if (GrouperInstallerUtils.equals(paramName, "statusLabel")) {
16733       
16734       subjectPropertiesContents.append("\n# search string from user which represents the status.  e.g. status=active\n");
16735 
16736     } else if (GrouperInstallerUtils.equals(paramName, "statusesFromUser")) {
16737       
16738       subjectPropertiesContents.append("\n# available statuses from screen (if not specified, any will be allowed). comma separated list.\n"
16739           + "# Note, this is optional and you probably dont want to configure it, it is mostly necessary\n"
16740           + "# when you have multiple sources with statuses...  if someone types an invalid status\n"
16741           + "# and you have this configured, it will not filter by it\n");
16742 
16743     } else if (GrouperInstallerUtils.equals(paramName, "statusAllFromUser")) {
16744       
16745       subjectPropertiesContents.append("\n# all label from the user\n");
16746 
16747     } else if (GrouperInstallerUtils.equals(paramName, "statusSearchDefault")) {
16748       
16749       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"
16750           + "# form the user would type in\n");
16751 
16752     } else if (paramName.startsWith("statusTranslateUser")) {
16753       
16754       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"
16755           + "# so the user could enter: status=active, and that could translate to status_col=A.  The 'user' is what the user types in,\n"
16756           + "# the 'datastore' is what is in the datastore.  The user part is not case-sensitive.  Note, this could be a many to one\n");
16757 
16758     } else if (paramName.startsWith("statusTranslateDatastore")) {
16759       
16760       //hmmm, nothing to do here
16761     } else if (GrouperInstallerUtils.equals(paramName, "INITIAL_CONTEXT_FACTORY")) {
16762       
16763       subjectPropertiesContents.append("\n# e.g. com.sun.jndi.ldap.LdapCtxFactory\n");
16764 
16765     } else if (GrouperInstallerUtils.equals(paramName, "PROVIDER_URL")) {
16766       
16767       subjectPropertiesContents.append("\n# e.g. ldap://localhost:389\n");
16768 
16769     } else if (GrouperInstallerUtils.equals(paramName, "SECURITY_AUTHENTICATION")) {
16770       
16771       subjectPropertiesContents.append("\n# e.g. simple, none, sasl_mech\n");
16772 
16773     } else if (GrouperInstallerUtils.equals(paramName, "SECURITY_PRINCIPAL")) {
16774       
16775       subjectPropertiesContents.append("\n# e.g. cn=Manager,dc=example,dc=edu\n");
16776 
16777     } else if (GrouperInstallerUtils.equals(paramName, "SECURITY_CREDENTIALS")) {
16778       
16779       subjectPropertiesContents.append("\n# can be a password or a filename of the encrypted password\n");
16780 
16781     } else if (GrouperInstallerUtils.equals(paramName, "SubjectID_AttributeType")) {
16782       
16783       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");
16784 
16785     } else if (GrouperInstallerUtils.equals(paramName, "SubjectID_formatToLowerCase")) {
16786       
16787       subjectPropertiesContents.append("\n# if the subject id should be changed to lower case after reading from datastore.  true or false\n");
16788 
16789     } else if (GrouperInstallerUtils.equals(paramName, "Name_AttributeType")) {
16790       
16791       subjectPropertiesContents.append("\n# attribute which is the subject name\n");
16792 
16793     } else if (GrouperInstallerUtils.equals(paramName, "Description_AttributeType")) {
16794       
16795       subjectPropertiesContents.append("\n# attribute which is the subject description\n");
16796 
16797     } else if (GrouperInstallerUtils.equals(paramName, "VTLDAP_VALIDATOR")) {
16798       
16799       subjectPropertiesContents.append("\n# LdapValidator provides an interface for validating ldap objects when they are in the pool.\n"
16800           + "# ConnectLdapValidator validates an ldap connection is healthy by testing it is connected.\n"
16801           + "# CompareLdapValidator validates an ldap connection is healthy by performing a compare operation.\n");
16802 
16803     } else if (GrouperInstallerUtils.equals(paramName, "VTLDAP_VALIDATOR_COMPARE_DN")) {
16804       
16805       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");
16806 
16807     } else if (GrouperInstallerUtils.equals(paramName, "VTLDAP_VALIDATOR_COMPARE_SEARCH_FILTER_STRING")) {
16808       
16809       subjectPropertiesContents.append("\n# if VTLDAP_VALIDATOR is CompareLdapValidator, this is the filter string, e.g. ou=People\n");
16810 
16811     } else {
16812       
16813       //hmmm, not sure what to do here, no comment
16814       subjectPropertiesContents.append("\n");
16815 
16816     }
16817 
16818   }
16819   
16820   /**
16821    * valid source param pattern
16822    */
16823   private static Pattern sourcesValidParamPattern = Pattern.compile("^[A-Za-z0-9_]+$");
16824   
16825   /**
16826    * 
16827    * @param subjectPropertiesFile 
16828    * @param sourcesXmlUrl
16829    */
16830   public static void convertSourcesXmlToProperties(File subjectPropertiesFile, URL sourcesXmlUrl) {
16831 
16832     //look at base properties
16833     Properties subjectProperties = subjectPropertiesFile.exists() ? 
16834         GrouperInstallerUtils.propertiesFromFile(subjectPropertiesFile) : new Properties();
16835 
16836     if (subjectPropertiesFile.exists()) {
16837       
16838       //lets see if it just has the default.  the default has no properties
16839       if (subjectProperties.size() > 0) {
16840         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");
16841       }
16842     }
16843     
16844     if (!subjectPropertiesFile.getParentFile().exists() || !subjectPropertiesFile.getParentFile().isDirectory()) {
16845       throw new RuntimeException(subjectPropertiesFile.getParentFile().getAbsolutePath() + " must exist and must be a directory");
16846     }
16847     
16848     StringBuilder subjectPropertiesContents = new StringBuilder();
16849     
16850     subjectPropertiesContents.append(
16851               "# Copyright 2016 Internet2\n"
16852             + "#\n"
16853             + "# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
16854             + "# you may not use this file except in compliance with the License.\n"
16855             + "# You may obtain a copy of the License at\n"
16856             + "#\n"
16857             + "#   http://www.apache.org/licenses/LICENSE-2.0\n"
16858             + "#\n"
16859             + "# Unless required by applicable law or agreed to in writing, software\n"
16860             + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n"
16861             + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
16862             + "# See the License for the specific language governing permissions and\n"
16863             + "# limitations under the License.\n"
16864             + "\n"
16865             + "#\n"
16866             + "# Subject configuration\n"
16867             + "#\n"
16868             + "\n"
16869             + "# The subject properties uses Grouper Configuration Overlays (documented on wiki)\n"
16870             + "# By default the configuration is read from subject.base.properties\n"
16871             + "# (which should not be edited), and the subject.properties overlays\n"
16872             + "# the base settings.  See the subject.base.properties for the possible\n"
16873             + "# settings that can be applied to the subject.properties\n\n"
16874         );
16875 
16876     subjectPropertiesContents.append(
16877         "# enter the location of the sources.xml.  Must start with classpath: or file:\n"
16878         + "# blank means dont use sources.xml, use subject.properties\n"
16879         + "# default is: classpath:sources.xml\n"
16880         + "# e.g. file:/dir1/dir2/sources.xml\n"
16881         + "subject.sources.xml.location = \n\n");
16882       
16883     //   <source adapterClass="edu.internet2.middleware.grouper.GrouperSourceAdapter">
16884     NodeList sourcesNodeList = GrouperInstallerUtils.xpathEvaluate(sourcesXmlUrl, "/sources/source");
16885     
16886     Set<String> usedConfigNames = new HashSet<String>();
16887     
16888     for (int i=0;i<sourcesNodeList.getLength();i++) {
16889 
16890       Element sourceElement = (Element)sourcesNodeList.item(i);
16891       
16892       String configName = null;
16893       String id = null;
16894       {
16895         //  #########################################
16896         //  ## Configuration for source: whateverId
16897         //  #########################################
16898         //  # generally the <configName> is the same as or similar to the source id.  This cannot have special characters
16899         //  # this links together all the configs for this source
16900         //  # subjectApi.source.<configName>.id = sourceId
16901         id = xmlElementValue(sourceElement, "id", true, "source index " + i);
16902         
16903         //these are configured in subject.base.properties
16904         if (GrouperInstallerUtils.equals(id, "g:gsa")
16905             || GrouperInstallerUtils.equals(id, "grouperEntities")) {
16906           continue;
16907         }
16908         configName = convertEhcacheNameToPropertiesKey(id, usedConfigNames);
16909         usedConfigNames.add(configName);
16910         
16911         subjectPropertiesContents.append(
16912             "\n#########################################\n"
16913             + "## Configuration for source id: " + id + "\n"
16914             + "## Source configName: " + configName + "\n"
16915             + "#########################################\n"
16916             + "subjectApi.source." + configName + ".id = " + id + "\n"
16917             );
16918       }
16919 
16920       {
16921         // <name>Grouper: Group Source Adapter</name>
16922         String name = xmlElementValue(sourceElement, "name", true, "source: " + id);
16923         subjectPropertiesContents.append("\n# this is a friendly name for the source\n"
16924             + "subjectApi.source." + configName + ".name = " + name + "\n");
16925       }
16926       
16927       {
16928         // <type>group</type>
16929         NodeList typeNodeList = sourceElement.getElementsByTagName("type");
16930         Set<String> typeSet = new LinkedHashSet<String>();
16931         
16932         for (int typeIndex=0; typeIndex<typeNodeList.getLength(); typeIndex++) {
16933           
16934           typeSet.add(GrouperInstallerUtils.trimToEmpty(typeNodeList.item(typeIndex).getTextContent()));
16935           
16936         }
16937         if (typeNodeList.getLength() > 0) {
16938           
16939           subjectPropertiesContents.append("\n# type is not used all that much.  Can have multiple types, comma separate.  Can be person, group, application\n"
16940               + "subjectApi.source." + configName + ".types = " + GrouperInstallerUtils.join(typeSet.iterator(), ", ") + "\n"
16941               );
16942         }
16943 
16944       }
16945 
16946       {
16947         NamedNodeMap configuredNamedNodeMap = sourceElement.getAttributes();
16948         if (configuredNamedNodeMap.getLength() != 1 || !"adapterClass".equals(configuredNamedNodeMap.item(0).getNodeName())) {
16949           throw new RuntimeException("Expecting one source attribute: adapterClass for source: " + id);
16950         }
16951         
16952         String adapterClass = sourceElement.getAttribute("adapterClass");
16953 
16954         subjectPropertiesContents.append("\n# the adapter class implements the interface: edu.internet2.middleware.subject.Source\n");
16955         subjectPropertiesContents.append("# adapter class must extend: edu.internet2.middleware.subject.provider.BaseSourceAdapter\n");
16956         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");
16957         subjectPropertiesContents.append("# edu.internet2.middleware.grouper.subj.GrouperJdbcSourceAdapter   :  oldest JDBC source.  Put freeform queries in here\n");
16958         subjectPropertiesContents.append("# edu.internet2.middleware.grouper.subj.GrouperJndiSourceAdapter   :  used for LDAP\n");
16959         subjectPropertiesContents.append("subjectApi.source." + configName + ".adapterClass = " + adapterClass + "\n");
16960       }      
16961 
16962       //  # You can flag a source as not throwing exception on a findAll (general search) i.e. if it is
16963       //  # ok if it is down.  Generally you probably won't want to do this.  It defaults to true if omitted.
16964       //  # subjectApi.source.<configName>.param.throwErrorOnFindAllFailure.value = true
16965 
16966       {
16967         //  <init-param>
16968         //    <param-name>subjectVirtualAttribute_0_searchAttribute0</param-name>
16969         //    <param-value>${subject.getAttributeValue('name')},${subject.getAttributeValue('displayName')},${subject.getAttributeValue('alternateName')}</param-value>
16970         //  </init-param>
16971         NodeList initParamNodeList = sourceElement.getElementsByTagName("init-param");
16972         
16973         Set<String> usedParamNames = new HashSet<String>();
16974 
16975         for (int initParamIndex=0; initParamIndex<initParamNodeList.getLength(); initParamIndex++) {
16976           
16977           Element initParamElement = (Element)initParamNodeList.item(initParamIndex);
16978           String paramName = xmlElementValue(initParamElement, "param-name", true, "param-name index " + initParamIndex + " in source " + id);
16979           String paramValue = xmlElementValue(initParamElement, "param-value", true, "param-value " + paramName + " in source " + id);
16980           
16981           String paramConfigKey = convertEhcacheNameToPropertiesKey(paramName, usedParamNames);
16982           convertSourcesXmlParamComment(paramName, subjectPropertiesContents, paramValue);
16983 
16984           //if the param name is invalid, then have a name config
16985           if (!GrouperInstallerUtils.equals(paramName, paramConfigKey)) {
16986             subjectPropertiesContents.append("subjectApi.source." + configName + ".param." + paramConfigKey + ".name = " + paramName + "\n");
16987           }
16988           
16989           //cant have newlines in there, convert to spaces
16990           paramValue = GrouperInstallerUtils.replaceNewlinesWithSpace(paramValue);
16991           
16992           //  # subjectApi.source.<configName>.param.throwErrorOnFindAllFailure.value = true
16993           subjectPropertiesContents.append("subjectApi.source." + configName + ".param." + paramConfigKey + ".value = " + paramValue + "\n");
16994 
16995         }
16996 
16997       }
16998 
16999       {
17000         //  <search>
17001         //    <searchType>searchSubject</searchType>
17002         //    <param>
17003         //      <param-name>sql</param-name>
17004         //                <param-value>
17005         //                  select
17006         //                    s.subjectid as id, s.name as name,
17007         //      </param-value>
17008         //    </param>
17009         //  </search>
17010         NodeList searchNodeList = sourceElement.getElementsByTagName("search");
17011         
17012         for (int searchIndex=0; searchIndex<searchNodeList.getLength(); searchIndex++) {
17013           
17014           Element searchElement = (Element)searchNodeList.item(searchIndex);
17015           
17016           String searchType = xmlElementValue(searchElement, "searchType", true, "search element in the source: " + id);
17017 
17018           NodeList searchParamNodeList = searchElement.getElementsByTagName("param");
17019 
17020           if (GrouperInstallerUtils.equals(searchType, "searchSubject")) {
17021             subjectPropertiesContents.append("\n#searchSubject: find a subject by ID.  ID is generally an opaque and permanent identifier, e.g. 12345678.\n"
17022                 + "#  Each subject has one and only on ID.  Returns one result when searching for one ID.\n");
17023           } else if (GrouperInstallerUtils.equals(searchType, "searchSubjectByIdentifier")) {
17024             subjectPropertiesContents.append("\n#searchSubjectByIdentifier: find a subject by identifier.  Identifier is anything that uniquely\n"
17025                 + "#  identifies the user, e.g. jsmith or jsmith@institution.edu.\n"
17026                 + "#  Subjects can have multiple identifiers.  Note: it is nice to have if identifiers are unique\n"
17027                 + "#  even across sources.  Returns one result when searching for one identifier.\n");
17028           } else if (GrouperInstallerUtils.equals(searchType, "search")) {
17029             subjectPropertiesContents.append("\n#   search: find subjects by free form search.  Returns multiple results.\n");
17030           } else {
17031             System.out.println("Not expecting searchType: '" + searchType + "'");
17032           }
17033 
17034           for (int searchParamIndex=0; searchParamIndex<searchParamNodeList.getLength(); searchParamIndex++) {
17035             
17036             Element searchParamElement = (Element)searchParamNodeList.item(searchParamIndex);
17037             
17038             String paramName = xmlElementValue(searchParamElement, "param-name", true, 
17039                 "search param name element index " + searchParamIndex + " in the source: " + id);
17040           
17041             String paramValue = xmlElementValue(searchParamElement, "param-value", true, 
17042                 "search param value element index " + searchParamIndex + " in the source: " + id);
17043 
17044             // cant have newlines in a properties file
17045             paramValue = GrouperInstallerUtils.replaceNewlinesWithSpace(paramValue);
17046 
17047             //  #
17048             //  # This is how search params are specified.  Note, each source can have different params for each search type
17049             //  # subjectApi.source.<configName>.search.<searchType>.param.<paramName>.value = something
17050             //  #
17051             //  ##############################################
17052             //  #
17053             //  # Searches for edu.internet2.middleware.grouper.subj.GrouperJdbcConnectionProvider
17054             //  #
17055             //  # searchSubject:
17056             //  #
17057             //  # 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}
17058             //  #    inclause allows searching by subject id for multiple ids in one query
17059             //  # subjectApi.source.<configName>.search.searchSubject.param.inclause.value = s.subjectid = ?
17060 
17061             if (!sourcesValidParamPattern.matcher(paramName).matches()) {
17062               throw new RuntimeException("Source " + id + " search " + searchType + " param name is not valid: '" + paramName + "'");
17063             }
17064             if (GrouperInstallerUtils.equals(searchType, "searchSubject")) {
17065               if (GrouperInstallerUtils.equals("sql", paramName)) {
17066                 subjectPropertiesContents.append("\n# sql is the sql to search for the subject by id should use an {inclause}\n");
17067               } else if (GrouperInstallerUtils.equals("inclause", paramName)) {
17068                 subjectPropertiesContents.append("\n# inclause allows searching by subject for multiple ids or identifiers in one query, must have {inclause} in the sql query,\n"
17069                     + "#    this will be subsituted to in clause with the following.  Should use a question mark ? for bind variable\n");
17070               } else if (GrouperInstallerUtils.equals("filter", paramName)) {
17071                   subjectPropertiesContents.append("\n# sql is the sql to search for the subject by id.  %TERM% will be subsituted by the id searched for\n");
17072               }
17073             } else if (GrouperInstallerUtils.equals(searchType, "searchSubjectByIdentifier")) {
17074               if (GrouperInstallerUtils.equals("sql", paramName)) {
17075                 subjectPropertiesContents.append("\n# sql is the sql to search for the subject by identifier should use an {inclause}\n");
17076               } else if (GrouperInstallerUtils.equals("inclause", paramName)) {
17077                 subjectPropertiesContents.append("\n# inclause allows searching by subject for multiple ids or identifiers in one query, must have {inclause} in the sql query,\n"
17078                     + "#    this will be subsituted to in clause with the following.  Should use a question mark ? for bind variable\n");
17079               } else if (GrouperInstallerUtils.equals("filter", paramName)) {
17080                 subjectPropertiesContents.append("\n# sql is the sql to search for the subject by identifier.  %TERM% will be subsituted by the identifier searched for\n");
17081               }
17082             } else if (GrouperInstallerUtils.equals(searchType, "search")) {
17083               if (GrouperInstallerUtils.equals("sql", paramName)) {
17084                 subjectPropertiesContents.append("\n# sql is the sql to search for the subject free-form search.  user question marks for bind variables\n");
17085               } else if (GrouperInstallerUtils.equals("inclause", paramName)) {
17086                 throw new RuntimeException("Should not have incluse for search of type search in source: " + id);
17087               } else if (GrouperInstallerUtils.equals("filter", paramName)) {
17088                 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");
17089               }
17090             }
17091             if (GrouperInstallerUtils.equals("scope", paramName)) {
17092               subjectPropertiesContents.append("\n# Scope Values can be: OBJECT_SCOPE, ONELEVEL_SCOPE, SUBTREE_SCOPE\n");
17093             } else if (GrouperInstallerUtils.equals("base", paramName)) {
17094               subjectPropertiesContents.append("\n# base dn to search in\n");
17095             }
17096             
17097             //  # subjectApi.source.<configName>.search.<searchType>.param.<paramName>.value = something
17098             subjectPropertiesContents.append("subjectApi.source." + configName + ".search." + searchType + ".param." + paramName + ".value = " + paramValue + "\n");
17099             
17100           }
17101         }
17102       }
17103       
17104       {
17105         // # attributes from ldap object to become subject attributes.  comma separated
17106         // <attribute>cn</attribute>
17107         // <attribute>sn</attribute>
17108         NodeList attributeNodeList = sourceElement.getElementsByTagName("attribute");
17109         Set<String> attributeSet = new LinkedHashSet<String>();
17110 
17111         for (int attributeIndex=0; attributeIndex<attributeNodeList.getLength(); attributeIndex++) {
17112 
17113           attributeSet.add(GrouperInstallerUtils.trimToEmpty(attributeNodeList.item(attributeIndex).getTextContent()));
17114         }
17115         if (attributeNodeList.getLength() > 0) {
17116 
17117           subjectPropertiesContents.append("\n# attributes from ldap object to become subject attributes.  comma separated\n"
17118               + "subjectApi.source." + configName + ".attributes = " + GrouperInstallerUtils.join(attributeSet.iterator(), ", ") + "\n");
17119 
17120         }
17121 
17122       }
17123       
17124       {
17125         // # internal attributes are used by grouper only not exposed to code that uses subjects.  comma separated
17126         // <internal-attributes>cn</internal-attributes>
17127         // <internal-attributes>sn</internal-attributes>
17128         NodeList internalAttributeNodeList = sourceElement.getElementsByTagName("internal-attribute");
17129         Set<String> internalAttributeSet = new LinkedHashSet<String>();
17130 
17131         for (int internalAttributeIndex=0; internalAttributeIndex<internalAttributeNodeList.getLength(); internalAttributeIndex++) {
17132 
17133           internalAttributeSet.add(GrouperInstallerUtils.trimToEmpty(internalAttributeNodeList.item(internalAttributeIndex).getTextContent()));
17134 
17135         }
17136         if (internalAttributeNodeList.getLength() > 0) {
17137 
17138           subjectPropertiesContents.append("\n# internal attributes are used by grouper only not exposed to code that uses subjects.  comma separated\n"
17139               + "subjectApi.source." + configName + ".internalAttributes = " + GrouperInstallerUtils.join(internalAttributeSet.iterator(), ", ") + "\n");
17140 
17141         }
17142 
17143       }
17144       //space between sources
17145       subjectPropertiesContents.append("\n");
17146     }
17147 
17148     GrouperInstallerUtils.saveStringIntoFile(subjectPropertiesFile, subjectPropertiesContents.toString());
17149 
17150   }
17151 
17152   /**
17153    * edit an xml file attribute in a xml file
17154    * @param file
17155    * @param elementName
17156    * @param elementMustHaveAttributeAndValue
17157    * @param newValue
17158    * @param description of change for sys out print
17159    * @param newAttributeName if adding new attribute, this is the name
17160    * @return true if edited file, or false if not but didnt need to, null if not found
17161    */
17162   public static Boolean editXmlFileAttribute(File file, String elementName, Map<String, String> elementMustHaveAttributeAndValue, 
17163       String newAttributeName, String newValue, String description) {
17164 
17165     if (!file.exists() || file.length() == 0) {
17166       throw new RuntimeException("Why does " + file.getName() + " not exist and have contents? " 
17167           + file.getAbsolutePath());
17168     }
17169     
17170     String fileContents = GrouperInstallerUtils.readFileIntoString(file);
17171     
17172     boolean inComment = false;
17173     
17174     //lets parse the file and get to the element
17175     OUTER: for (int i=0;i<fileContents.length();i++) {
17176       
17177       //look for start element
17178       char curChar = fileContents.charAt(i);
17179 
17180       Character nextChar = (i+1) < fileContents.length() ? fileContents.charAt(i+1) : null;
17181       Character nextNextChar = (i+2) < fileContents.length() ? fileContents.charAt(i+2) : null;
17182       Character nextNextNextChar = (i+3) < fileContents.length() ? fileContents.charAt(i+3) : null;
17183       
17184       //if we are in comment, see when we are out of comment
17185       if (inComment) {
17186         if (curChar == '-' && nextChar != null && nextChar == '-' && nextNextChar != null && nextNextChar == '>') {
17187           inComment = false;
17188         }
17189         continue;
17190         
17191       }
17192 
17193       //look for a tag or comment
17194       if (curChar != '<') {
17195         continue;
17196       }
17197       
17198       //see if this is a comment
17199       if (nextChar != null && nextChar == '!' && nextNextChar != null && nextNextChar == '-' && nextNextNextChar != null && nextNextNextChar == '-') {
17200         inComment = true;
17201         continue;
17202       }
17203 
17204       //get tagName
17205       String currentElementName = _internalXmlTagName(fileContents, i+1);
17206       
17207       //not the right tag
17208       if (!GrouperInstallerUtils.equals(currentElementName, elementName)) {
17209         continue;
17210       }
17211       
17212       int tagNameStart = fileContents.indexOf(currentElementName, i+1);
17213       
17214       //get the attributes
17215       int tagAttributesStart = tagNameStart + currentElementName.length();
17216       XmlParseAttributesResult xmlParseAttributesResult = _internalXmlParseAttributes(fileContents, tagAttributesStart);
17217       Map<String, String> currentAttributes = xmlParseAttributesResult.getAttributes();
17218       
17219       if (GrouperInstallerUtils.length(elementMustHaveAttributeAndValue) > 0) {
17220         for (String attributeName : elementMustHaveAttributeAndValue.keySet()) {
17221           String expectedValue = elementMustHaveAttributeAndValue.get(attributeName);
17222           String hasValue = currentAttributes.get(attributeName);
17223 
17224           //if we dont have that value, then keep going
17225           if (!GrouperInstallerUtils.equals(expectedValue, hasValue)) {
17226             continue OUTER;
17227           }
17228         }
17229       }
17230       
17231       //we have the tag and it has the expected attributes
17232 
17233       //see if the attribute is even there...
17234       if (!currentAttributes.containsKey(newAttributeName)) {
17235         System.out.println(" - adding " + description + " with value: '" + newValue + "'");
17236         String newFileContents = fileContents.substring(0, tagAttributesStart) + " " + newAttributeName + "=\"" + newValue + 
17237             "\" " + fileContents.substring(tagAttributesStart, fileContents.length());
17238         GrouperInstallerUtils.writeStringToFile(file, newFileContents);
17239         return true;
17240       }
17241 
17242       //does it already have the value?
17243       String currentValue = currentAttributes.get(newAttributeName);
17244       
17245       //value is already there
17246       if (GrouperInstallerUtils.equals(currentValue, newValue)) {
17247         return false;
17248       }
17249 
17250       //it has the wrong value
17251       int startQuote = xmlParseAttributesResult.getAttributeStartIndex().get(newAttributeName);
17252       int endQuote = xmlParseAttributesResult.getAttributeEndIndex().get(newAttributeName);
17253 
17254       System.out.println(" - changing " + description + " from old value: '" + currentValue 
17255           + "' to new value: '" + newValue + "'");
17256 
17257       String newFileContents = fileContents.substring(0, startQuote+1)  + newValue + 
17258           fileContents.substring(endQuote, fileContents.length());
17259       GrouperInstallerUtils.writeStringToFile(file, newFileContents);
17260       return true;
17261 
17262     }
17263 
17264     return null;
17265 
17266   }
17267 
17268   /**
17269    * 
17270    * @param fileContents
17271    * @param tagIndexStart
17272    * @return the tag name
17273    */
17274   private static String _internalXmlTagName(String fileContents, int tagIndexStart) {
17275     StringBuilder tagName = new StringBuilder();
17276     for (int i=tagIndexStart; i<fileContents.length(); i++) {
17277       char curChar = fileContents.charAt(i);
17278       if (tagName.length() == 0 && Character.isWhitespace(curChar)) {
17279         continue;
17280       }
17281       if (Character.isWhitespace(curChar) || '/' == curChar || '>' == curChar) {
17282         return tagName.toString();
17283       }
17284       tagName.append(curChar);
17285     }
17286     throw new RuntimeException("How did I get here???? '" + tagName.toString() + "'");
17287   }
17288   
17289   /**
17290    * xml parse attribute result
17291    */
17292   private static class XmlParseAttributesResult {
17293 
17294     /**
17295      * attributes name to value
17296      */
17297     private Map<String, String> attributes;
17298     
17299     /**
17300      * attribute name to startIndex (of quote)
17301      */
17302     private Map<String, Integer> attributeStartIndex;
17303 
17304     /**
17305      * attribute name to endIndex (of quote)
17306      */
17307     private Map<String, Integer> attributeEndIndex;
17308 
17309     
17310     /**
17311      * attributes name to value
17312      * @return the attributes
17313      */
17314     public Map<String, String> getAttributes() {
17315       return this.attributes;
17316     }
17317 
17318     
17319     /**
17320      * attributes name to value
17321      * @param attributes1 the attributes to set
17322      */
17323     public void setAttributes(Map<String, String> attributes1) {
17324       this.attributes = attributes1;
17325     }
17326 
17327     
17328     /**
17329      * attribute name to startIndex (of quote)
17330      * @return the attributeStartIndex
17331      */
17332     public Map<String, Integer> getAttributeStartIndex() {
17333       return this.attributeStartIndex;
17334     }
17335     
17336     /**
17337      * attribute name to startIndex (of quote)
17338      * @param attributeStartIndex1 the attributeStartIndex to set
17339      */
17340     public void setAttributeStartIndex(Map<String, Integer> attributeStartIndex1) {
17341       this.attributeStartIndex = attributeStartIndex1;
17342     }
17343     
17344     /**
17345      * attribute name to endIndex (of quote)
17346      * @return the attributeEndIndex
17347      */
17348     public Map<String, Integer> getAttributeEndIndex() {
17349       return this.attributeEndIndex;
17350     }
17351     
17352     /**
17353      * attribute name to endIndex (of quote)
17354      * @param attributeEndIndex1 the attributeEndIndex to set
17355      */
17356     public void setAttributeEndIndex(Map<String, Integer> attributeEndIndex1) {
17357       this.attributeEndIndex = attributeEndIndex1;
17358     }
17359     
17360   }
17361 
17362   /**
17363    * 
17364    */
17365   public static enum GrouperInstallerAdminManageServiceAction {
17366   
17367     /** start */
17368     start,
17369     
17370     /** stop */
17371     stop,
17372     
17373     /** restart */
17374     restart,
17375     
17376     /** status */
17377     status;
17378     
17379     /**
17380      * 
17381      * @param string
17382      * @param exceptionIfInvalid
17383      * @param exceptionIfBlank
17384      * @return the action
17385      */
17386     public static GrouperInstallerAdminManageServiceAction valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
17387       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerAdminManageServiceAction.class, string, exceptionIfBlank, exceptionIfInvalid);
17388     }
17389     
17390   }
17391 
17392   /**
17393    * parse attributes
17394    * @param fileContents
17395    * @param tagAttributesStart is the index where the attributes start
17396    * @return the map of attribute names and values
17397    */
17398   private static XmlParseAttributesResult _internalXmlParseAttributes(String fileContents, int tagAttributesStart) {
17399 
17400     XmlParseAttributesResult xmlParseAttributesResult = new XmlParseAttributesResult();
17401 
17402     Map<String, String> attributes = new LinkedHashMap<String, String>();
17403     Map<String, Integer> attributeStartIndex = new LinkedHashMap<String, Integer>();
17404     Map<String, Integer> attributeEndIndex = new LinkedHashMap<String, Integer>();
17405 
17406     xmlParseAttributesResult.setAttributes(attributes);
17407     xmlParseAttributesResult.setAttributeStartIndex(attributeStartIndex);
17408     xmlParseAttributesResult.setAttributeEndIndex(attributeEndIndex);
17409     
17410     boolean inAttributeStartValue = false;
17411     boolean inAttributeStartName = true;
17412     boolean inAttributeName = false;
17413     boolean inAttributeValue = false;
17414     
17415     StringBuilder attributeName = null;
17416     StringBuilder attributeValue = null;
17417     
17418     for (int i=tagAttributesStart; i<fileContents.length(); i++) {
17419       char curChar = fileContents.charAt(i);
17420       boolean isWhitespace = Character.isWhitespace(curChar);
17421 
17422       //waiting for the attribute
17423       if ((inAttributeStartValue || inAttributeStartName) && isWhitespace) {
17424         continue;
17425       }
17426 
17427       //if waiting for value and equals, keep looking
17428       if (inAttributeStartValue && curChar == '=') {
17429         continue;
17430       }
17431 
17432       //waiting to start an attribute name, its not whitespace so do it
17433       if (inAttributeStartName) {
17434         
17435         //we done if we got to this character
17436         if (curChar == '/' || curChar == '>') {
17437           return xmlParseAttributesResult;
17438         }
17439         
17440         inAttributeStartName = false;
17441         inAttributeName = true;
17442         attributeName = new StringBuilder();
17443       }
17444 
17445       //if in an attribute name and whitespace or equals, then we are done
17446       if (inAttributeName && (isWhitespace || curChar == '=' )) {
17447         inAttributeName = false;
17448         inAttributeStartValue = true;
17449         continue;
17450       }
17451 
17452       //getting the attribute name
17453       if (inAttributeName) {
17454         attributeName.append(curChar);
17455         continue;
17456       }
17457 
17458       //if waiting for start value and found quote
17459       if (inAttributeStartValue && curChar == '"') {
17460         inAttributeStartValue = false;
17461         inAttributeValue = true;
17462         attributeValue = new StringBuilder();
17463         attributeStartIndex.put(attributeName.toString(), i);
17464         continue;
17465       }
17466 
17467       //if in attribute value and not quote, append the char
17468       if (inAttributeValue && curChar != '"') {
17469         attributeValue.append(curChar);
17470         continue;
17471       }
17472 
17473       //done with attribute value
17474       if (inAttributeValue && curChar == '"') {
17475         inAttributeValue = false;
17476         inAttributeStartName = true;
17477         if (attributes.containsKey(attributeName.toString())) {
17478           throw new RuntimeException("Duplicate attribute: " + attributeName.toString());
17479         }
17480         attributes.put(attributeName.toString(), attributeValue.toString());
17481         attributeEndIndex.put(attributeName.toString(), i);
17482         continue;
17483       }
17484 
17485       throw new RuntimeException("Why are we here? " + i + ", " + fileContents);
17486     }
17487     return xmlParseAttributesResult;
17488   }
17489   
17490   /** revert patch excludes */
17491   private static Set<String> revertPatchExcludes = new HashSet<String>();
17492   
17493   static {
17494     revertPatchExcludes.add("grouper.cache.properties");
17495     revertPatchExcludes.add("ehcache.xml");
17496     revertPatchExcludes.add("ehcache.example.xml");
17497   }
17498 }