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.FilenameFilter;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.io.InputStreamReader;
28  import java.io.OutputStream;
29  import java.net.MalformedURLException;
30  import java.net.URL;
31  import java.text.ParseException;
32  import java.text.SimpleDateFormat;
33  import java.util.ArrayList;
34  import java.util.Collection;
35  import java.util.Collections;
36  import java.util.Date;
37  import java.util.Enumeration;
38  import java.util.HashMap;
39  import java.util.HashSet;
40  import java.util.Iterator;
41  import java.util.LinkedHashMap;
42  import java.util.LinkedHashSet;
43  import java.util.List;
44  import java.util.Map;
45  import java.util.Properties;
46  import java.util.Set;
47  import java.util.TreeMap;
48  import java.util.TreeSet;
49  import java.util.regex.Matcher;
50  import java.util.regex.Pattern;
51  import java.util.zip.ZipEntry;
52  import java.util.zip.ZipFile;
53  
54  import javax.xml.parsers.DocumentBuilder;
55  import javax.xml.parsers.DocumentBuilderFactory;
56  import javax.xml.xpath.XPath;
57  import javax.xml.xpath.XPathConstants;
58  import javax.xml.xpath.XPathExpression;
59  import javax.xml.xpath.XPathFactory;
60  
61  import org.w3c.dom.Document;
62  import org.w3c.dom.Element;
63  import org.w3c.dom.NamedNodeMap;
64  import org.w3c.dom.Node;
65  import org.w3c.dom.NodeList;
66  
67  import edu.internet2.middleware.grouperInstaller.GrouperInstaller.GrouperDirectories.GrouperInstallType;
68  import edu.internet2.middleware.grouperInstaller.GrouperInstallerIndexFile.PatchFileType;
69  import edu.internet2.middleware.grouperInstaller.util.GiDbUtils;
70  import edu.internet2.middleware.grouperInstaller.util.GrouperInstallerUtils;
71  import edu.internet2.middleware.grouperInstaller.util.GrouperInstallerUtils.CommandResult;
72  import edu.internet2.middleware.grouperInstallerExt.org.apache.commons.compress.archivers.tar.TarArchiveEntry;
73  import edu.internet2.middleware.grouperInstallerExt.org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
74  import edu.internet2.middleware.grouperInstallerExt.org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
75  import edu.internet2.middleware.grouperInstallerExt.org.apache.commons.compress.utils.IOUtils;
76  import edu.internet2.middleware.grouperInstallerExt.org.apache.commons.httpclient.HttpClient;
77  import edu.internet2.middleware.grouperInstallerExt.org.apache.commons.httpclient.methods.GetMethod;
78  
79  /**
80   * Install grouper
81   * @author mchyzer
82   *
83   */
84  public class GrouperInstaller {
85  
86    /**
87     * location of where grouper is an find all the files within
88     */
89    private GrouperDirectories grouperDirectories = new GrouperDirectories();
90    
91    /**
92     * structure and logic to locate where grouper is installed (or will be?)
93     */
94    public static class GrouperDirectories {
95  
96      /**
97       * which type of install are we
98       */
99      public static enum GrouperInstallType {
100 
101       /**
102        * installed Grouper env
103        */
104       installed,
105       
106       /**
107        * source Grouper env
108        */
109       source;
110       
111     }
112 
113     /**
114      * are we an installed directory or source directory
115      */
116     private GrouperInstallType grouperInstallType;
117 
118     
119     /**
120      * are we an installed directory or source directory
121      * @return the grouperInstallType
122      */
123     public GrouperInstallType getGrouperInstallType() {
124       return this.grouperInstallType;
125     }
126 
127     
128     /**
129      * are we an installed directory or source directory
130      * @param grouperInstallType1 the grouperInstallType to set
131      */
132     public void setGrouperInstallType(GrouperInstallType grouperInstallType1) {
133       this.grouperInstallType = grouperInstallType1;
134     }
135 
136     
137     
138   }
139   
140   /**
141    * default ip address to listen for stuff
142    */
143   private String defaultIpAddress = null;
144   
145 
146   /**
147    * if we should continue or not
148    * @return if should continue
149    * @param autorunPropertiesKey key in properties file to automatically fill in a value
150    */
151   private static boolean shouldContinue(String autorunPropertiesKey) {
152     return shouldContinue(null, null, autorunPropertiesKey);
153   }
154   
155   /**
156    * if we should continue or not
157    * @param hint 
158    * @param exitHint 
159    * @param autorunPropertiesKey key in properties file to automatically fill in a value
160    * @return if should continue
161    */
162   private static boolean shouldContinue(String hint, String exitHint, String autorunPropertiesKey) {
163     if (hint == null) {
164       hint = "Do you want to continue ";
165     }
166     if (!hint.endsWith(" ")) {
167       hint += " ";
168     }
169     System.out.print(hint + "(t|f)? [f] ");
170     boolean shouldContinue = readFromStdInBoolean(false, autorunPropertiesKey);
171     if (!shouldContinue) {
172       if (exitHint == null) {
173         exitHint = "OK, will not continue, exiting...";
174       }
175       if (!GrouperInstallerUtils.isBlank(exitHint)) {
176         System.out.println(exitHint);
177       }
178     }
179     return shouldContinue;
180   }
181   
182   /**
183    * read a string from stdin
184    * @param defaultBoolean null for none, or true of false for if the input is blank
185    * @param autorunPropertiesKey key in properties file to automatically fill in a value
186    * @return the string
187    */
188   private static boolean readFromStdInBoolean(Boolean defaultBoolean, String autorunPropertiesKey) {
189     int i=100;
190     //keep trying until we get it
191     while(true) {
192       String input = readFromStdIn(autorunPropertiesKey);
193       if (GrouperInstallerUtils.isBlank(input) && defaultBoolean != null) {
194         return defaultBoolean;
195       }
196       try {
197         boolean inputBoolean = GrouperInstallerUtils.booleanValue(input);
198         return inputBoolean; 
199       } catch (Exception e) {
200         if (defaultBoolean != null) {
201           System.out.print("Expecting t or f or <blank> but received: '" + input + "', please try again: ");
202         } else {
203           System.out.print("Expecting t or f but received: '" + input + "', please try again: ");
204         }
205       }
206       if (i-- < 0) {
207         throw new RuntimeException("Too many tries for finding a boolean!");
208       }
209     }
210   }
211   
212   /**
213    * read a string from stdin
214    * @param autorunPropertiesKey key in properties file to automatically fill in a value
215    * @return the string
216    */
217   private static String readFromStdIn(String autorunPropertiesKey) {
218     
219     String str = null;
220     
221     if (GrouperInstallerUtils.propertiesContainsKey(autorunPropertiesKey)) {
222 
223       str = GrouperInstallerUtils.propertiesValue(autorunPropertiesKey, false);
224       
225       System.out.println("<using autorun property " + autorunPropertiesKey + ">: '" + str + "'");
226       
227     } else if (GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.autorun.useDefaultsAsMuchAsAvailable", false, false)) {
228       
229       System.out.println("<using default which is blank due to grouperInstaller.autorun.useDefaultsAsMuchAsAvailable and " + autorunPropertiesKey + ">: ");
230       
231     } else {
232 
233       if (GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.print.autorunKeys", false, false)) {
234         System.out.print("<" + autorunPropertiesKey + ">: ");
235       }
236       
237       try {
238         BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
239         str = in.readLine();
240       } catch (Exception e) {
241         throw new RuntimeException("Problem", e);
242       }
243 
244     }
245     
246     return GrouperInstallerUtils.trim(str);
247     
248   }
249 
250   /**
251    * download a file, delete the local file if it exists
252    * @param url
253    * @param localFileName
254    * @param autorunUseLocalFilePropertiesKey key in properties file to automatically fill in a value if download exists, default true
255    */
256   private void downloadFile(String url, String localFileName, String autorunUseLocalFilePropertiesKey) {
257     downloadFile(url, localFileName, false, null, autorunUseLocalFilePropertiesKey);
258   }
259 
260   /**
261    * download a file, delete the local file if it exists
262    * @param url
263    * @param localFileName
264    * @param allow404
265    * @param prefixFor404 print message prefix if 404
266    * @param autorunUseLocalFilePropertiesKey key in properties file to automatically fill in a value if download exists, default true
267    * @return true if downloaded, false if not
268    */
269   private boolean downloadFile(final String url, final String localFileName, final boolean allow404, 
270       final String prefixFor404, final String autorunUseLocalFilePropertiesKey) {
271 
272     boolean useLocalFile = false;
273 
274     final File localFile = new File(localFileName);
275 
276     if (localFile.exists()) {
277       
278       if (this.useAllLocalFiles != null && this.useAllLocalFiles == true) {
279         useLocalFile = true;
280       } else {
281         System.out.print("File exists: " + localFile.getAbsolutePath() + ", should we use the local file (t|f)? [t]: ");
282         useLocalFile = readFromStdInBoolean(true, autorunUseLocalFilePropertiesKey);
283         
284         if (useLocalFile && this.useAllLocalFiles == null) {
285           System.out.print("Would you like to use all local files (t|f)? [t]: ");
286           this.useAllLocalFiles = readFromStdInBoolean(true, "grouperInstaller.autorun.useAllLocalFiles");
287         }
288       }
289     }
290     
291     if (useLocalFile) {
292       return true;
293     }
294 
295     if (allow404 && GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.useLocalFilesOnlyForDevelopment", false, false)) {
296       return false;
297     }
298 
299     final boolean[] result = new boolean[1];
300     
301     Runnable runnable = new Runnable() {
302       
303       public void run() {
304         result[0] = downloadFileHelper(url, localFileName, allow404, prefixFor404, autorunUseLocalFilePropertiesKey);
305       }
306     };
307     
308     GrouperInstallerUtils.threadRunWithStatusDots(runnable, true);
309     
310     return result[0];
311   }
312   
313   /**
314    * download a file, delete the local file if it exists
315    * @param url
316    * @param localFileName
317    * @param allow404
318    * @param prefixFor404 print message prefix if 404
319    * @param autorunUseLocalFilePropertiesKey key in properties file to automatically fill in a value if download exists, default true
320    * @return true if downloaded, false if not
321    */
322   private static boolean downloadFileHelper(String url, String localFileName, boolean allow404, 
323       String prefixFor404, String autorunUseLocalFilePropertiesKey) {
324 
325     final File localFile = new File(localFileName);
326 
327     HttpClient httpClient = new HttpClient();
328 
329     String proxyServer = GrouperInstallerUtils.propertiesValue("download.server.proxyServer", false); 
330     
331     if (!GrouperInstallerUtils.isBlank(proxyServer)) {
332       
333       int proxyPort = GrouperInstallerUtils.propertiesValueInt("download.server.proxyPort", -1, true);
334       httpClient.getHostConfiguration().setProxy(proxyServer, proxyPort);
335     }
336     
337     //see if we are working with local files:
338     {
339       File localFileFromUrl = new File(url);
340       if (localFileFromUrl.exists()) {
341         System.out.println("Copying local file: " + url + " to file: " + localFileName);
342         
343         if (localFile.exists()) {
344           
345           System.out.println("File exists: " + localFile.getAbsolutePath() + ", deleting");
346           
347           if (!localFile.delete()) {
348             throw new RuntimeException("Cant delete file: " + localFile.getAbsolutePath() + "!!!!!");
349           }
350         } else {
351           if (!localFile.getParentFile().exists()) {
352             GrouperInstallerUtils.mkdirs(localFile.getParentFile());
353           }
354 
355         }
356         
357         try {
358           FileOutputStream fileOutputStream = new FileOutputStream(localFile);
359           FileInputStream fileInputStream = new FileInputStream(localFileFromUrl);
360   
361           GrouperInstallerUtils.copy(fileInputStream, fileOutputStream);
362   
363           return true;
364         } catch (Exception exception) {
365           String errorMessage = "Error copying file: " + url;
366           System.out.println(errorMessage);
367           throw new RuntimeException(errorMessage, exception);
368         }
369 
370       }
371       
372       //if it doesnt exist, see if the parent dir exists
373       if (localFileFromUrl.getParentFile().exists()) {
374 
375         //the dir is there but no file...   hmmmm
376         if (allow404) {
377           if (GrouperInstallerUtils.isBlank(prefixFor404)) {
378             prefixFor404 = "File not found: ";
379           }
380           System.out.println(prefixFor404 + url);
381           return false;
382         }
383 
384         //weve got a problem
385         
386       }
387     }
388     
389     GetMethod getMethod = null;
390     try {
391       
392       getMethod = new GetMethod(url);
393       
394       int result = httpClient.executeMethod(getMethod);
395       
396       if (allow404 && result == 404) {
397         if (GrouperInstallerUtils.isBlank(prefixFor404)) {
398           prefixFor404 = "File not found: ";
399         }
400         System.out.println(prefixFor404 + url);
401         return false;
402       }
403       
404       System.out.println("Downloading from URL: " + url + " to file: " + localFileName);
405 
406       if (result != 200) {
407         throw new RuntimeException("Expecting 200 but received: " + result);
408       }
409       
410       InputStream inputStream = getMethod.getResponseBodyAsStream();
411 
412       if (localFile.exists()) {
413         
414         System.out.println("File exists: " + localFile.getAbsolutePath() + ", deleting");
415         
416         if (!localFile.delete()) {
417           throw new RuntimeException("Cant delete file: " + localFile.getAbsolutePath() + "!!!!!");
418         }
419       }
420       
421       if (!localFile.getParentFile().exists()) {
422         GrouperInstallerUtils.mkdirs(localFile.getParentFile());
423       }
424       
425       FileOutputStream fileOutputStream = new FileOutputStream(localFile);
426 
427 
428       GrouperInstallerUtils.copy(inputStream, fileOutputStream);
429 
430       return true;
431 
432     } catch (Exception exception) {
433       String errorMessage = "Error connecting to URL: " + url;
434       System.out.println(errorMessage);
435       throw new RuntimeException(errorMessage, exception);
436     }
437   }
438 
439   /**
440    * 
441    * @param ehcacheBaseFile
442    */
443   public void convertEhcacheBaseToProperties(File ehcacheBaseFile) {
444     //File ehcacheBaseBakFile = this.bakFile(ehcacheBaseFile);
445     //GrouperInstallerUtils.copyFile(existingFile, bakFile, true);
446     //System.out.println("Backing up: " + existingFile.getAbsolutePath() + " to: " + bakFile.getAbsolutePath());
447     
448     NodeList nodeList = GrouperInstallerUtils.xpathEvaluate(ehcacheBaseFile, "/ehcache/cache");
449     
450     Set<String> usedKeys = new HashSet<String>();
451     
452     for (int i=0;i<nodeList.getLength();i++) {
453       
454       Element element = (Element)nodeList.item(i);
455 
456       //  <cache  name="edu.internet2.middleware.grouper.internal.dao.hib3.Hib3MemberDAO.FindBySubject"
457       //      maxElementsInMemory="5000"
458       //      eternal="false"
459       //      timeToIdleSeconds="5"
460       //      timeToLiveSeconds="10"
461       //      overflowToDisk="false"  
462       //      statistics="false"
463       //  />
464 
465       
466       String name = element.getAttribute("name");
467       Integer maxElementsInMemory = GrouperInstallerUtils.intObjectValue(element.getAttribute("maxElementsInMemory"), true);
468       Boolean eternal = GrouperInstallerUtils.booleanObjectValue(element.getAttribute("eternal"));
469       Integer timeToIdleSeconds = GrouperInstallerUtils.intObjectValue(element.getAttribute("timeToIdleSeconds"), true);
470       Integer timeToLiveSeconds = GrouperInstallerUtils.intObjectValue(element.getAttribute("timeToLiveSeconds"), true);
471       Boolean overflowToDisk = GrouperInstallerUtils.booleanObjectValue(element.getAttribute("overflowToDisk"));
472       Boolean statistics = GrouperInstallerUtils.booleanObjectValue(element.getAttribute("statistics"));
473 
474       //any attributes we dont expect?
475       NamedNodeMap configuredNamedNodeMap = element.getAttributes();
476       //see which attributes are new or changed
477       for (int j=0;j<configuredNamedNodeMap.getLength();j++) {
478         Node configuredAttribute = configuredNamedNodeMap.item(j);
479         if (!configuredAttribute.getNodeName().equals("name")
480             && !configuredAttribute.getNodeName().equals("maxElementsInMemory")
481             && !configuredAttribute.getNodeName().equals("eternal")
482             && !configuredAttribute.getNodeName().equals("timeToIdleSeconds")
483             && !configuredAttribute.getNodeName().equals("timeToLiveSeconds")
484             && !configuredAttribute.getNodeName().equals("overflowToDisk")
485             && !configuredAttribute.getNodeName().equals("statistics")) {
486           throw new RuntimeException("Cant process attribute: '" + configuredAttribute.getNodeName() + "'");
487         }
488       }
489       
490       String key = convertEhcacheNameToPropertiesKey(name, usedKeys);
491       
492       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.name = edu.internet2.middleware.grouper.internal.dao.hib3.Hib3GroupDAO
493       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.maxElementsInMemory = 500
494       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.eternal = false
495       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.timeToIdleSeconds = 1
496       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.timeToLiveSeconds = 1
497       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.overflowToDisk = false
498       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.statistics = false
499       
500       System.out.println("cache.name." + key + ".name = " + name);
501       if (maxElementsInMemory != null) {
502         System.out.println("cache.name." + key + ".maxElementsInMemory = " + maxElementsInMemory);
503       }
504       if (eternal != null) {
505         System.out.println("cache.name." + key + ".eternal = " + eternal);
506       }
507       if (timeToIdleSeconds != null) {
508         System.out.println("cache.name." + key + ".timeToIdleSeconds = " + timeToIdleSeconds);
509       }
510       if (timeToLiveSeconds != null) {
511         System.out.println("cache.name." + key + ".timeToLiveSeconds = " + timeToLiveSeconds);
512       }
513       if (overflowToDisk != null) {
514         System.out.println("cache.name." + key + ".overflowToDisk = " + overflowToDisk);
515       }
516       if (statistics != null) {
517         System.out.println("cache.name." + key + ".statistics = " + statistics);
518       }
519       System.out.println("");
520     }
521 
522   }
523 
524   /**
525    * convert a name like: edu.internet2.middleware.grouper.internal.dao.hib3.Hib3GroupDAO
526    * to: edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO
527    * @param ehcacheName 
528    * @param usedKeys
529    * @return the key
530    */
531   private static String convertEhcacheNameToPropertiesKey(String ehcacheName, Set<String> usedKeys) {
532     
533     StringBuilder result = new StringBuilder();
534 
535     //strip off this beginning to get the keys a little smaller
536     if (ehcacheName.startsWith("edu.internet2.middleware.grouper.")) {
537       ehcacheName = ehcacheName.substring("edu.internet2.middleware.grouper.".length());
538     }
539     
540     for (int i=0; i<ehcacheName.length(); i++) {
541       
542       char curChar = ehcacheName.charAt(i);
543       
544       if (Character.isLetter(curChar) || Character.isDigit(curChar)) {
545         result.append(curChar);
546       } else {
547         result.append("_");
548       }
549       
550     }
551 
552     String resultString = result.toString();
553     if (!usedKeys.contains(resultString)) {
554       return resultString;
555     }
556     
557     for (int i=2;i<100;i++) {
558       String newResult = resultString + "_" + i;
559       if (!usedKeys.contains(newResult)) {
560         return newResult;
561       }
562     }
563     
564     throw new RuntimeException("Cant find name for " + ehcacheName);
565   }
566   
567   /**
568    * @param args
569    */
570   public static void main(String[] args) {
571 
572     GrouperInstallerrInstaller.html#GrouperInstaller">GrouperInstaller grouperInstaller = new GrouperInstaller();
573 
574     grouperInstaller.mainLogic();
575 
576 //    grouperInstaller.upgradeExistingApplicationDirectoryString = "D:\\temp\\temp\\grouperJarCopyDest\\";
577 //    grouperInstaller.grouperBaseBakDir = "D:\\temp\\temp\\grouperJarBak\\";
578     
579 //    grouperInstaller.upgradeJars(new File("D:\\temp\\temp\\grouperJarCopySource"), new File("D:\\temp\\temp\\grouperJarCopyDest"));
580     
581 //    grouperInstaller.version = "2.4.0";
582 //    
583 //    grouperInstaller.grouperTarballDirectoryString = "D:\\temp\\temp\\grouperInstaller\\";
584 //    
585 //    grouperInstaller.grouperInstallDirectoryString = "D:\\temp\\temp\\grouperInstaller\\install\\";
586 //    
587 //    grouperInstaller.installMessagingAwsSqs();
588     
589     //new GrouperInstaller().convertEhcacheBaseToProperties(new File("/Users/mchyzer/git/grouper_v2_3/grouper/conf/ehcache.example.xml"));
590 
591 //    GrouperInstaller.downloadFile("https://github.com/Internet2/grouper/archive/GROUPER_2_2_BRANCH.zip",
592 //        "C:\\app\\grouperInstallerTarballDir\\GROUPER_2_2_BRANCH.zip", false, null, 
593 //        "grouperInstaller.autorun.createPatchDownloadSourceUseLocalIfExist", true
594 //        );
595 
596 //    GrouperInstaller grouperInstaller = new GrouperInstaller();
597 //
598 //    grouperInstaller.upgradeExistingApplicationDirectoryString = "C:\\app\\grouper_2_1_installer\\grouper.ui-2.1.5\\dist\\grouper\\";
599 //  
600 //    grouperInstaller.grouperBaseBakDir = "C:\\app\\grouperInstallerTarballDir\\bak_UI_2014_10_26_15_06_19_957\\";
601 //      
602 //    grouperInstaller.copyFiles("C:\\app\\grouperInstallerTarballDir\\grouper.ui-2.2.1\\dist\\grouper\\",
603 //        "C:\\app\\grouper_2_1_installer\\grouper.ui-2.1.5\\dist\\grouper\\",
604 //        GrouperInstallerUtils.toSet("WEB-INF/lib", "WEB-INF/web.xml", "WEB-INF/classes"));
605 
606     
607    // copyFiles();
608     
609 //    
610 //    
611 //    grouperInstaller.dbUrl = "jdbc:hsqldb:hsql://localhost:9001/grouper";
612 //    grouperInstaller.dbUser = "sa";
613 //    grouperInstaller.dbPass = "";
614 //    grouperInstaller.giDbUtils = new GiDbUtils(grouperInstaller.dbUrl, grouperInstaller.dbUser, grouperInstaller.dbPass);
615 //    grouperInstaller.untarredApiDir = new File("c:/mchyzer/grouper/trunk/grouper-installer/grouper.apiBinary-2.0.2");
616 //    grouperInstaller.grouperInstallDirectoryString = "C:/mchyzer/grouper/trunk/grouper-installer/";
617 //    
618 //    grouperInstaller.version = "2.0.2";
619 //    grouperInstaller.grouperSystemPassword = "myNewPass";
620 //    
621 //    grouperInstaller.addDriverJarToClasspath();
622 ////      
623 //    grouperInstaller.startHsqlDb();
624 //    grouperInstaller.checkDatabaseConnection();
625 ////      grouperInstaller.initDb();
626 ////      
627 ////      grouperInstaller.addQuickstartSubjects();
628 ////      grouperInstaller.addQuickstartData();
629 //    
630 ////    File uiDir = grouperInstaller.downloadUi();
631 ////    File unzippedUiFile = unzip(uiDir.getAbsolutePath());
632 ////    grouperInstaller.untarredUiDir = untar(unzippedUiFile.getAbsolutePath());
633 //
634 ////      grouperInstaller.configureUi();
635 ////
636 //      File antDir = grouperInstaller.downloadAnt();
637 //      File unzippedAntFile = unzip(antDir.getAbsolutePath());
638 //      grouperInstaller.untarredAntDir = untar(unzippedAntFile.getAbsolutePath());
639 ////
640 ////      grouperInstaller.buildUi();
641 //
642 //    File tomcatDir = grouperInstaller.downloadTomcat();
643 //    File unzippedTomcatFile = unzip(tomcatDir.getAbsolutePath());
644 //    grouperInstaller.untarredTomcatDir = untar(unzippedTomcatFile.getAbsolutePath());
645 //
646 //    grouperInstaller.configureTomcat();
647 //    
648 ////    grouperInstaller.configureTomcatUiWebapp();
649 ////    
650 //    grouperInstaller.tomcatConfigureGrouperSystem();
651 //    
652 ////    grouperInstaller.tomcatBounce("restart");
653 //
654 //    File wsDir = grouperInstaller.downloadWs();
655 //
656 //    File unzippedWsFile = unzip(wsDir.getAbsolutePath());
657 //    grouperInstaller.untarredWsDir = untar(unzippedWsFile.getAbsolutePath());
658 //    grouperInstaller.configureWs();
659 //    grouperInstaller.buildWs();
660 //    
661 //    grouperInstaller.configureTomcatWsWebapp();
662 //    grouperInstaller.tomcatBounce("restart");
663 //
664 //    File clientDir = grouperInstaller.downloadClient();
665 //    
666 //    File unzippedClientFile = unzip(clientDir.getAbsolutePath());
667 //    grouperInstaller.untarredClientDir = untar(unzippedClientFile.getAbsolutePath());
668 //    grouperInstaller.configureClient();
669 //
670 //    grouperInstaller.addGrouperSystemWsGroup();
671 //    
672 //    grouperInstaller.runClientCommand();
673 //    
674 //    //CommandResult commandResult = GrouperInstallerUtils.execCommand("cmd /c cd");
675 //    //String result = commandResult.getOutputText();
676 //    //
677 //    //System.out.println(result);
678 //
679 //
680 //    //editPropertiesFile(new File("C:\\mchyzer\\grouper\\trunk\\grouper-installer\\grouper.apiBinary-2.1.0\\conf\\grouper.hibernate.properties"), 
681 //    //    "hibernate.connection.password", "sdf");
682       
683     //if started hsql, then we need to exit since that thread will not stop
684     System.exit(0);
685   }
686 
687   /** e.g. 2.1.0 */
688   private String version;
689   
690   /**
691    * @param isInstallNotUpgrade install will bounce tomcat, configure, etc
692    * 
693    */
694   private void buildUi(boolean isInstallNotUpgrade) {
695     
696     File grouperUiBuildToDir = new File(this.grouperUiBuildToDirName());
697     
698     boolean rebuildUi = true;
699     
700     if (grouperUiBuildToDir.exists()) {
701       boolean defaultRebuild = GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.ui.rebuildIfBuilt", true, false);
702       System.out.print("The Grouper UI has been built in the past, do you want it rebuilt? (t|f) [" 
703           + (defaultRebuild ? "t" : "f") + "]: ");
704       rebuildUi = readFromStdInBoolean(defaultRebuild, "grouperInstaller.autorun.rebuildUiAfterHavingBeenBuilt");
705     }
706     
707     if (!rebuildUi) {
708       return;
709     }
710 
711     if (isInstallNotUpgrade) {
712       //stop tomcat
713       try {
714         tomcatBounce("stop");
715       } catch (Exception e) {
716         System.out.println("Couldnt stop tomcat, ignoring...");
717       }
718     }
719     
720     List<String> commands = new ArrayList<String>();
721     
722     addAntCommands(commands);
723     commands.add("dist");
724     
725     System.out.println("\n##################################");
726     System.out.println("Building UI with command:\n" + this.untarredUiDir.getAbsolutePath() + "> " 
727         + convertCommandsIntoCommand(commands) + "\n");
728     
729     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
730         true, true, null, this.untarredUiDir, null, true);
731     
732     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
733       System.out.println("stderr: " + commandResult.getErrorText());
734     }
735     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
736       System.out.println("stdout: " + commandResult.getOutputText());
737     }
738     
739     if (isInstallNotUpgrade) {
740       System.out.print("Do you want to set the log dir of UI (t|f)? [t]: ");
741       boolean setLogDir = readFromStdInBoolean(true, "grouperInstaller.autorun.setLogDirOfUi");
742       
743       if (setLogDir) {
744         
745         ////set the log dir
746         //C:\apps\grouperInstallerTest\grouper.ws-2.0.2\grouper-ws\build\dist\grouper-ws\WEB-INF\classes\log4j.properties
747         //
748         //${grouper.home}logs
749   
750         String defaultLogDir = this.untarredTomcatDir.getAbsolutePath() + File.separator + "logs" + File.separator + "grouperUi";
751         System.out.print("Enter the UI log dir: [" + defaultLogDir + "]: ");
752         String logDir = readFromStdIn("grouperInstaller.autorun.uiLogDir");
753         logDir = GrouperInstallerUtils.defaultIfBlank(logDir, defaultLogDir);
754         
755         //lets replace \\ with /
756         logDir = GrouperInstallerUtils.replace(logDir, "\\\\", "/");
757         //lets replace \ with /
758         logDir = GrouperInstallerUtils.replace(logDir, "\\", "/");
759   
760         File log4jFile = new File(grouperUiBuildToDirName() + File.separator + "WEB-INF" + File.separator + "classes"
761             + File.separator + "log4j.properties");
762   
763         System.out.println("Editing file: " + log4jFile.getAbsolutePath());
764   
765         //log4j.appender.grouper_event.File = c:/apps/grouperInstallerTest/grouper.apiBinary-2.0.2/logs/grouper_event.log
766         editFile(log4jFile, "log4j\\.\\S+\\.File\\s*=\\s*([^\\s]+logs)/grouper_[^\\s]+\\.log", null, 
767             null, logDir, "UI log directory");
768   
769         File logDirFile = new File(defaultLogDir);
770         if (!logDirFile.exists()) {
771           System.out.println("Creating log directory: " + logDirFile.getAbsolutePath());
772           GrouperInstallerUtils.mkdirs(logDirFile);
773         }
774         //test log dir
775         File testLogDirFile = new File(logDirFile.getAbsolutePath() + File.separator + "testFile" + GrouperInstallerUtils.uniqueId() + ".txt");
776         GrouperInstallerUtils.saveStringIntoFile(testLogDirFile, "test");
777         if (!testLogDirFile.delete()) {
778           throw new RuntimeException("Cant delete file: " +  testLogDirFile.getAbsolutePath());
779         }
780         System.out.println("Created and deleted a test file successfully in dir: " + logDirFile.getAbsolutePath());
781       }
782     }    
783 
784     
785     System.out.println("\nEnd building UI");
786     System.out.println("##################################\n");
787 
788     
789   }
790 
791   /** ps command */
792   private String psCommandUnix;
793   
794   /**
795    * 
796    * @return the ps command in unix
797    */
798   private String psCommand() {
799     if (GrouperInstallerUtils.isWindows()) {
800       throw new RuntimeException("This is windows, why are you looking for sh???");
801     }
802     if (GrouperInstallerUtils.isBlank(this.psCommandUnix)) {
803       if (new File("/bin/ps").exists()) {
804         this.psCommandUnix = "/bin/ps";
805       } else if (new File("/usr/bin/ps").exists()) {
806         this.psCommandUnix = "/usr/bin/ps";
807       } else if (new File("/usr/local/bin/ps").exists()) {
808         this.psCommandUnix = "/usr/local/bin/ps";
809       } else {
810         throw new RuntimeException("Cant find 'ps' command!");
811       }
812     }
813     return this.psCommandUnix;
814   }
815 
816   /** grep command */
817   private String grepCommand;
818   
819   /**
820    * 
821    * @return the grep command in unix
822    */
823   private String grepCommand() {
824     if (GrouperInstallerUtils.isWindows()) {
825       throw new RuntimeException("This is windows, why are you looking for sh???");
826     }
827     if (GrouperInstallerUtils.isBlank(this.grepCommand)) {
828       if (new File("/bin/grep").exists()) {
829         this.grepCommand = "/bin/grep";
830       } else if (new File("/usr/bin/grep").exists()) {
831         this.grepCommand = "/usr/bin/grep";
832       } else if (new File("/usr/local/bin/grep").exists()) {
833         this.grepCommand = "/usr/local/bin/grep";
834       } else {
835         throw new RuntimeException("Cant find 'grep' command!");
836       }
837     }
838     return this.grepCommand;
839   }
840 
841   /** kill command */
842   private String killCommand;
843   
844   /**
845    * 
846    * @return the kill command in unix
847    */
848   private String killCommand() {
849     if (GrouperInstallerUtils.isWindows()) {
850       throw new RuntimeException("This is windows, why are you looking for sh???");
851     }
852     if (GrouperInstallerUtils.isBlank(this.killCommand)) {
853       if (new File("/bin/kill").exists()) {
854         this.killCommand = "/bin/kill";
855       } else if (new File("/usr/bin/kill").exists()) {
856         this.killCommand = "/usr/bin/kill";
857       } else if (new File("/usr/local/bin/kill").exists()) {
858         this.killCommand = "/usr/local/bin/kill";
859       } else {
860         throw new RuntimeException("Cant find 'kill' command!");
861       }
862     }
863     return this.killCommand;
864   }
865 
866   /** sh command */
867   private String shCommand;
868   
869   /**
870    * 
871    * @return the sh command in unix
872    */
873   private String shCommand() {
874     if (GrouperInstallerUtils.isWindows()) {
875       throw new RuntimeException("This is windows, why are you looking for sh???");
876     }
877     
878     if (!GrouperInstallerUtils.isBlank(this.shCommand)) {
879       return this.shCommand;
880     }
881     
882     String[] attempts = new String[]{
883         "bash", "/bin/bash", 
884         "/sbin/bash", "/usr/local/bin/bash", 
885         "/usr/bin/bash", "/usr/sbin/bash", 
886         "/usr/local/sbin/bash", "sh", "/bin/sh", 
887         "/sbin/sh", "/usr/local/bin/sh", 
888         "/usr/bin/sh", "/usr/sbin/sh", 
889         "/usr/local/sbin/sh"}; 
890     
891     for (String attempt : attempts) {
892     
893       try {
894         CommandResult commandResult = GrouperInstallerUtils.execCommand(
895             attempt, 
896             new String[]{"-version"}, true);
897         String shResult = commandResult.getOutputText();
898         if (GrouperInstallerUtils.isBlank(shResult)) {
899           shResult = commandResult.getErrorText();
900         }
901   
902         //if we get a result, thats good
903         if (!GrouperInstallerUtils.isBlank(shResult)) {
904           this.shCommand = attempt;
905           System.out.println("Using shell command: " + attempt);
906           return this.shCommand;
907         }
908         
909       } catch (Exception e) {
910         //this is ok, keep trying
911       }
912     }
913     //ok, we couldnt find it, 
914     System.out.print("Couldn't find the command 'sh'.  Enter the path of 'sh' (e.g. /bin/sh): ");
915     this.shCommand = readFromStdIn("grouperInstaller.autorun.pathOfShCommandIfNotFound");
916 
917     try {
918       CommandResult commandResult = GrouperInstallerUtils.execCommand(
919           this.shCommand, 
920           new String[]{"-version"}, true);
921       String shResult = commandResult.getOutputText();
922       if (GrouperInstallerUtils.isBlank(shResult)) {
923         shResult = commandResult.getErrorText();
924       }
925 
926       //if we get a result, thats good
927       if (!GrouperInstallerUtils.isBlank(shResult)) {
928         return this.shCommand;
929       }
930       
931     } catch (Exception e) {
932       throw new RuntimeException("Error: couldn't run: " + this.shCommand + " -version!", e);
933     }
934 
935     throw new RuntimeException("Error: couldn't run: " + this.shCommand + " -version!");
936     
937   }
938 
939   /**
940    * 
941    * @param commands
942    */
943   private void addGshCommands(List<String> commands) {
944     if (GrouperInstallerUtils.isWindows()) {
945       commands.add("cmd");
946       commands.add("/c");
947       commands.add(gshCommand());
948     } else {
949       //if you add this it messes up when args have spaces
950       //commands.add(shCommand());
951       commands.add(gshCommand());
952     }
953   }
954 
955   /**
956    * 
957    * @param commands
958    */
959   private void addAntCommands(List<String> commands) {
960     if (GrouperInstallerUtils.isWindows()) {
961       commands.add("cmd");
962       commands.add("/c");
963       commands.add(this.untarredAntDir.getAbsolutePath() + File.separator + "bin" + File.separator + "ant.bat");
964     } else {
965       commands.add(shCommand());
966       commands.add(this.untarredAntDir.getAbsolutePath() + File.separator + "bin" + File.separator + "ant");
967     }
968   }
969 
970   /**
971    * 
972    * @param commands
973    */
974   private void addMavenCommands(List<String> commands) {
975     if (GrouperInstallerUtils.isWindows()) {
976       commands.add("cmd");
977       commands.add("/c");
978       commands.add(this.untarredMavenDir.getAbsolutePath() + File.separator + "bin" + File.separator + "mvn.bat");
979     } else {
980       commands.add(shCommand());
981       commands.add(this.untarredMavenDir.getAbsolutePath() + File.separator + "bin" + File.separator + "mvn");
982     }
983   }
984   
985   /**
986    * @param arg
987    * 
988    */
989   private void tomeeBounce(String arg) {
990     
991     if (!GrouperInstallerUtils.equals("start", arg) && !GrouperInstallerUtils.equals("stop", arg) && !GrouperInstallerUtils.equals("restart", arg)) {
992       throw new RuntimeException("Expecting arg: start|stop|restart but received: " + arg);
993     }
994     
995     if (GrouperInstallerUtils.equals("restart", arg)) {
996       
997       tomeeBounce("stop");
998       tomeeBounce("start");
999       return;
1000     }
1001     
1002     if (GrouperInstallerUtils.equals("stop", arg)) {
1003       
1004       if (GrouperInstallerUtils.portAvailable(this.tomeeHttpPort, this.defaultIpAddress)) {
1005         System.out.println("Tomee is supposed to be listening on port: " + this.tomeeHttpPort + ", port not listening, assuming tomee is not running...");
1006         if (!shouldContinue("Should we " + arg + " tomee anyway?", "", "grouperInstaller.autorun." + arg + "TomeeAnyway")) {
1007           return;
1008         }
1009       }
1010 
1011       
1012     } else {
1013       if (!GrouperInstallerUtils.portAvailable(this.tomeeHttpPort, this.defaultIpAddress)) {
1014         System.out.println("Tomee is supposed to be listening on port: " + this.tomeeHttpPort + ", port is already listening!!!!  Why is this????");
1015         if (!shouldContinue("Should we " + arg + " tomee anyway?", "", "grouperInstaller.autorun." + arg + "TomeeAnyway")) {
1016           return;
1017         }
1018       }
1019       
1020     }
1021     
1022     final List<String> commands = new ArrayList<String>();
1023     
1024     commands.add(GrouperInstallerUtils.javaCommand());
1025     commands.add("-XX:MaxPermSize=150m");
1026     commands.add("-Xmx640m");
1027     
1028     commands.add("-Dcatalina.home=" + this.untarredTomeeDir.getAbsolutePath());
1029     //commands.add("-Djava.util.logging.config.file=" + this.untarredTomcatDir.getAbsolutePath() + File.separator + "conf" + File.separator + "logging.properties");
1030     
1031     commands.add("-cp");
1032     commands.add(this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin" + File.separator + "bootstrap.jar" + File.pathSeparator
1033         + this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin" + File.separator + "tomcat-juli.jar");
1034     commands.add("org.apache.catalina.startup.Bootstrap");
1035     
1036     if (GrouperInstallerUtils.equals("stop", arg)) {
1037       commands.add("stop");
1038     }
1039     
1040     System.out.println("\n##################################");
1041     
1042     String command = "start".equals(arg) ? "startup" : "shutdown";
1043     
1044     System.out.println("Tomee " + arg + " with command (note you need CATALINA_HOME and JAVA_HOME set):\n  "
1045         + this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin" + File.separator + command
1046         + (GrouperInstallerUtils.isWindows() ? ".bat" : ".sh") + "\n");
1047     
1048     //dont wait
1049     boolean waitFor = GrouperInstallerUtils.equals("stop", arg) ? true : false;
1050     
1051     if (waitFor) {
1052       try {
1053         CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
1054             true, true, null, 
1055             new File(this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin"), null, true);
1056         
1057         if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
1058           System.out.println("stderr: " + commandResult.getErrorText());
1059         }
1060         if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
1061           System.out.println("stdout: " + commandResult.getOutputText());
1062         }
1063       } catch (Throwable e) {
1064         e.printStackTrace();
1065         if (!shouldContinue("grouperInstaller.autorun.continueAfterTomeeError")) {
1066           return;
1067         }
1068       }
1069     } else {
1070       //start in new thread
1071       Thread thread = new Thread(new Runnable() {
1072         
1073         @Override
1074         public void run() {
1075           GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
1076               true, true, null, 
1077               new File(GrouperInstaller.this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin"), 
1078               GrouperInstaller.this.untarredTomeeDir.getAbsolutePath() + File.separator + "logs" + File.separator + "catalina", false);
1079         }
1080       });
1081       thread.setDaemon(true);
1082       thread.start();
1083 
1084     }
1085     
1086     System.out.println("\nEnd tomee " + arg + " (note: logs are in " + this.untarredTomeeDir.getAbsolutePath() + File.separator + "logs)");
1087     System.out.println("##################################\n");
1088 
1089     System.out.print("Should we check ports to see if tomee was able to " + arg + " (t|f)? [t]: ");
1090     
1091     boolean shouldCheckTomee = readFromStdInBoolean(true, "grouperInstaller.autorun." + arg + "TomeeCheckPorts");
1092     
1093     if (shouldCheckTomee) {
1094       System.out.print("Waiting for tomee to " + arg +  "...");
1095       boolean success = false;
1096       for (int i=0;i<60;i++) {
1097         GrouperInstallerUtils.sleep(1000);
1098         //check port
1099         boolean portAvailable = GrouperInstallerUtils.portAvailable(this.tomeeHttpPort, this.defaultIpAddress);
1100         if (GrouperInstallerUtils.equals("start", arg)) {
1101           if (!portAvailable) {
1102             success = true;
1103             System.out.println("\nTomee listening on port: " + this.tomeeHttpPort);
1104             break;
1105           }
1106         } else {
1107           if (portAvailable) {
1108             success = true;
1109             System.out.println("\nTomee not listening on port: " + this.tomeeHttpPort);
1110             break;
1111           }
1112         }
1113         System.out.print(".");
1114       }
1115       if (!success) {
1116         System.out.println("Trying to " + arg + " tomee but couldnt properly detect " + arg + " on port " + this.tomeeHttpPort);
1117         System.out.print("Press <enter> to continue... ");
1118         readFromStdIn("grouperInstaller.autorun.tomeePortProblem");
1119       }
1120     } else {
1121       System.out.println("Waiting 10 seconds for tomee to " + arg + "...");
1122       GrouperInstallerUtils.sleep(10000);
1123     }
1124   }
1125 
1126   
1127   /**
1128    * @param arg
1129    * 
1130    */
1131   private void tomcatBounce(String arg) {
1132     
1133     if (!GrouperInstallerUtils.equals("start", arg) && !GrouperInstallerUtils.equals("stop", arg) && !GrouperInstallerUtils.equals("restart", arg)) {
1134       throw new RuntimeException("Expecting arg: start|stop|restart but received: " + arg);
1135     }
1136     
1137     if (GrouperInstallerUtils.equals("restart", arg)) {
1138       
1139       tomcatBounce("stop");
1140       tomcatBounce("start");
1141       return;
1142     }
1143     
1144     if (GrouperInstallerUtils.equals("stop", arg)) {
1145       
1146       if (GrouperInstallerUtils.portAvailable(this.tomcatHttpPort, this.defaultIpAddress)) {
1147         System.out.println("Tomcat is supposed to be listening on port: " + this.tomcatHttpPort + ", port not listening, assuming tomcat is not running...");
1148         if (!shouldContinue("Should we " + arg + " tomcat anyway?", "", "grouperInstaller.autorun." + arg + "TomcatAnyway")) {
1149           return;
1150         }
1151       }
1152 
1153       
1154     } else {
1155       if (!GrouperInstallerUtils.portAvailable(this.tomcatHttpPort, this.defaultIpAddress)) {
1156         System.out.println("Tomcat is supposed to be listening on port: " + this.tomcatHttpPort + ", port is already listening!!!!  Why is this????");
1157         if (!shouldContinue("Should we " + arg + " tomcat anyway?", "", "grouperInstaller.autorun." + arg + "TomcatAnyway")) {
1158           return;
1159         }
1160       }
1161       
1162     }
1163     
1164     final List<String> commands = new ArrayList<String>();
1165     
1166 //    <java jar="${tomcat.home}/bin/bootstrap.jar" fork="true">
1167 //    03          <jvmarg value="-Dcatalina.home=${tomcat.home}"/>
1168 //    04      </java>
1169 //    05  </target>
1170 //    06   
1171 //    07  <target name="tomcat-stop">
1172 //    08      <java jar="${tomcat.home}/bin/bootstrap.jar" fork="true">
1173 //    09          <jvmarg value="-Dcatalina.home=${tomcat.home}"/>
1174 //    10          <arg line="stop"/>
1175 //    11      </java>
1176     
1177     commands.add(GrouperInstallerUtils.javaCommand());
1178     commands.add("-XX:MaxPermSize=150m");
1179     commands.add("-Xmx640m");
1180     
1181     commands.add("-Dcatalina.home=" + this.untarredTomcatDir.getAbsolutePath());
1182     //commands.add("-Djava.util.logging.config.file=" + this.untarredTomcatDir.getAbsolutePath() + File.separator + "conf" + File.separator + "logging.properties");
1183     
1184     //later versions of tomcat need the juli jar...
1185     if (new File(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin" + File.separator + "tomcat-juli.jar").exists()) {
1186       
1187       commands.add("-cp");
1188       commands.add(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin" + File.separator + "bootstrap.jar" + File.pathSeparator
1189           + this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin" + File.separator + "tomcat-juli.jar");
1190       commands.add("org.apache.catalina.startup.Bootstrap");
1191     } else {
1192 
1193       commands.add("-jar");
1194       commands.add(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin" + File.separator + "bootstrap.jar");
1195     }
1196     
1197     if (GrouperInstallerUtils.equals("stop", arg)) {
1198       commands.add("stop");
1199     }
1200     
1201     System.out.println("\n##################################");
1202     
1203     String command = "start".equals(arg) ? "startup" : "shutdown";
1204     
1205     System.out.println("Tomcat " + arg + " with command (note you need CATALINA_HOME and JAVA_HOME set):\n  "
1206         + this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin" + File.separator + command
1207         + (GrouperInstallerUtils.isWindows() ? ".bat" : ".sh") + "\n");
1208     
1209     //dont wait
1210     boolean waitFor = GrouperInstallerUtils.equals("stop", arg) ? true : false;
1211     
1212     if (waitFor) {
1213       try {
1214         CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
1215             true, true, null, 
1216             new File(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin"), null, true);
1217         
1218         if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
1219           System.out.println("stderr: " + commandResult.getErrorText());
1220         }
1221         if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
1222           System.out.println("stdout: " + commandResult.getOutputText());
1223         }
1224       } catch (Throwable e) {
1225         e.printStackTrace();
1226         if (!shouldContinue("grouperInstaller.autorun.continueAfterTomcatError")) {
1227           return;
1228         }
1229       }
1230     } else {
1231       //start in new thread
1232       Thread thread = new Thread(new Runnable() {
1233         
1234         @Override
1235         public void run() {
1236           GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
1237               true, true, null, 
1238               new File(GrouperInstaller.this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin"), 
1239               GrouperInstaller.this.untarredTomcatDir.getAbsolutePath() + File.separator + "logs" + File.separator + "catalina", false);
1240         }
1241       });
1242       thread.setDaemon(true);
1243       thread.start();
1244 
1245     }
1246     
1247     System.out.println("\nEnd tomcat " + arg + " (note: logs are in " + this.untarredTomcatDir.getAbsolutePath() + File.separator + "logs)");
1248     System.out.println("##################################\n");
1249 
1250     System.out.print("Should we check ports to see if tomcat was able to " + arg + " (t|f)? [t]: ");
1251     
1252     boolean shouldCheckTomcat = readFromStdInBoolean(true, "grouperInstaller.autorun." + arg + "TomcatCheckPorts");
1253     
1254     if (shouldCheckTomcat) {
1255       System.out.print("Waiting for tomcat to " + arg +  "...");
1256       boolean success = false;
1257       for (int i=0;i<60;i++) {
1258         GrouperInstallerUtils.sleep(1000);
1259         //check port
1260         boolean portAvailable = GrouperInstallerUtils.portAvailable(this.tomcatHttpPort, this.defaultIpAddress);
1261         if (GrouperInstallerUtils.equals("start", arg)) {
1262           if (!portAvailable) {
1263             success = true;
1264             System.out.println("\nTomcat listening on port: " + this.tomcatHttpPort);
1265             break;
1266           }
1267         } else {
1268           if (portAvailable) {
1269             success = true;
1270             System.out.println("\nTomcat not listening on port: " + this.tomcatHttpPort);
1271             break;
1272           }
1273         }
1274         System.out.print(".");
1275       }
1276       if (!success) {
1277         System.out.println("Trying to " + arg + " tomcat but couldnt properly detect " + arg + " on port " + this.tomcatHttpPort);
1278         System.out.print("Press <enter> to continue... ");
1279         readFromStdIn("grouperInstaller.autorun.tomcatPortProblem");
1280       }
1281     } else {
1282       System.out.println("Waiting 10 seconds for tomcat to " + arg + "...");
1283       GrouperInstallerUtils.sleep(10000);
1284     }
1285   }
1286   
1287   /** db url */
1288   private String dbUrl;
1289 
1290   /** db user */
1291   private String dbUser;
1292 
1293   /** db pass */
1294   private String dbPass;
1295 
1296   /** untarred dir */
1297   private File untarredApiDir;
1298 
1299   /** untarred dir */
1300   private File untarredUiDir;
1301 
1302   /** untarred dir */
1303   private File untarredWsDir;
1304 
1305   /** untarred dir */
1306   private File untarredAntDir;
1307 
1308   /** untarred dir */
1309   private File untarredMavenDir;
1310 
1311   /** untarred dir */
1312   private File untarredTomcatDir;
1313   
1314   /** untarred tomee dir */
1315   private File untarredTomeeDir;
1316 
1317   /** main install dir, must end in file separator */
1318   private String grouperTarballDirectoryString;
1319   
1320   /** main install dir, must end in file separator */
1321   private String grouperInstallDirectoryString;
1322   
1323   /** base bak dir for backing up files that are upgraded, ends in File separator */
1324   private String grouperBaseBakDir;
1325   
1326   /** grouper system password */
1327   private String grouperSystemPassword;
1328   
1329   /**
1330    * 
1331    */
1332   private void tomeeConfigureGrouperSystem() {
1333     
1334 //    while (true) {
1335 //      System.out.print("Enter the GrouperSystem password: ");
1336 //      this.grouperSystemPassword = readFromStdIn("grouperInstaller.autorun.grouperSystemPassword");
1337 //      this.grouperSystemPassword = GrouperInstallerUtils.defaultString(this.grouperSystemPassword);
1338 //      
1339 //      if (!GrouperInstallerUtils.isBlank(this.grouperSystemPassword)) {
1340 //        break;
1341 //      }
1342 //      System.out.println("The GrouperSystem password cannot be blank!");
1343 //    }
1344 
1345     System.out.print("Do you want to set the GrouperSystem password in " + this.untarredTomeeDir + File.separator + "conf" + File.separator + "tomcat-users.xml? [t]: ");
1346     boolean setGrouperSystemPassword = readFromStdInBoolean(true, "grouperInstaller.autorun.setGrouperSystemPasswordInTomeeUsers");
1347     if (setGrouperSystemPassword) {
1348 
1349       //write to the tomee_users file
1350       //get the password
1351       File tomeeUsersXmlFile = new File(this.untarredTomeeDir.getAbsolutePath() + File.separator + "conf" + File.separator + "tomcat-users.xml");
1352       String existingPassword = GrouperInstallerUtils.xpathEvaluateAttribute(tomeeUsersXmlFile, "tomcat-users/user[@username='GrouperSystem']", "password");
1353       
1354       System.out.println("Editing file: " + tomeeUsersXmlFile.getAbsolutePath());
1355 
1356       NodeList existingRole = GrouperInstallerUtils.xpathEvaluate(tomeeUsersXmlFile, "tomcat-users/role");
1357       
1358       //<role rolename="grouper_user"/>
1359       //<user username="GrouperSystem" password="chang3m3" roles="grouper_user"/>
1360 
1361       
1362       if (existingPassword == null) {
1363 
1364         addToXmlFile(tomeeUsersXmlFile, ">",  new String[]{"<tomcat-users"}, "<user username=\"GrouperSystem\" password=\"" 
1365             + this.grouperSystemPassword + "\" roles=\"grouper_user\"/>", "Tomcat user GrouperSystem");
1366          
1367       } else {
1368         
1369         if (GrouperInstallerUtils.equals(existingPassword, this.grouperSystemPassword)) {
1370           System.out.println("  - password is already set to that value, leaving file unchanged...");
1371 
1372         } else {
1373           
1374           editFile(tomeeUsersXmlFile, "password=\"([^\"]*)\"", new String[]{"<user", "username=\"GrouperSystem\""}, 
1375               null, this.grouperSystemPassword, "Tomcat password for user GrouperSystem");
1376           
1377         }
1378         
1379       }
1380 
1381       if (existingRole == null || existingRole.getLength() == 0) {
1382         
1383         //add the role
1384         addToXmlFile(tomeeUsersXmlFile, ">",  new String[]{"<tomcat-users"}, "<role rolename=\"grouper_user\"/>", "Tomcat role grouper_user");
1385         
1386       }
1387     }
1388     
1389   }
1390   
1391   /**
1392    * 
1393    */
1394   private void tomcatConfigureGrouperSystem() {
1395     
1396     while (true) {
1397       System.out.print("Enter the GrouperSystem password: ");
1398       this.grouperSystemPassword = readFromStdIn("grouperInstaller.autorun.grouperSystemPassword");
1399       this.grouperSystemPassword = GrouperInstallerUtils.defaultString(this.grouperSystemPassword);
1400       
1401       if (!GrouperInstallerUtils.isBlank(this.grouperSystemPassword)) {
1402         break;
1403       }
1404       System.out.println("The GrouperSystem password cannot be blank!");
1405     }
1406 
1407     System.out.print("Do you want to set the GrouperSystem password in " + this.untarredTomcatDir + File.separator + "conf" + File.separator + "tomcat-users.xml? [t]: ");
1408     boolean setGrouperSystemPassword = readFromStdInBoolean(true, "grouperInstaller.autorun.setGrouperSystemPasswordInTomcatUsers");
1409     if (setGrouperSystemPassword) {
1410 
1411       //write to the tomcat_users file
1412       //get the password
1413       File tomcatUsersXmlFile = new File(this.untarredTomcatDir.getAbsolutePath() + File.separator + "conf" + File.separator + "tomcat-users.xml");
1414       String existingPassword = GrouperInstallerUtils.xpathEvaluateAttribute(tomcatUsersXmlFile, "tomcat-users/user[@username='GrouperSystem']", "password");
1415       
1416       System.out.println("Editing file: " + tomcatUsersXmlFile.getAbsolutePath());
1417 
1418       NodeList existingRole = GrouperInstallerUtils.xpathEvaluate(tomcatUsersXmlFile, "tomcat-users/role");
1419       
1420       //<role rolename="grouper_user"/>
1421       //<user username="GrouperSystem" password="chang3m3" roles="grouper_user"/>
1422 
1423       
1424       if (existingPassword == null) {
1425 
1426         addToXmlFile(tomcatUsersXmlFile, ">",  new String[]{"<tomcat-users"}, "<user username=\"GrouperSystem\" password=\"" 
1427             + this.grouperSystemPassword + "\" roles=\"grouper_user\"/>", "Tomcat user GrouperSystem");
1428          
1429       } else {
1430         
1431         if (GrouperInstallerUtils.equals(existingPassword, this.grouperSystemPassword)) {
1432           System.out.println("  - password is already set to that value, leaving file unchanged...");
1433 
1434         } else {
1435           
1436           editFile(tomcatUsersXmlFile, "password=\"([^\"]*)\"", new String[]{"<user", "username=\"GrouperSystem\""}, 
1437               null, this.grouperSystemPassword, "Tomcat password for user GrouperSystem");
1438           
1439         }
1440         
1441       }
1442 
1443       if (existingRole == null || existingRole.getLength() == 0) {
1444         
1445         //add the role
1446         addToXmlFile(tomcatUsersXmlFile, ">",  new String[]{"<tomcat-users"}, "<role rolename=\"grouper_user\"/>", "Tomcat role grouper_user");
1447         
1448       }
1449     }
1450     
1451   }
1452   
1453   /**
1454    * 
1455    */
1456   private void configureUi() {
1457     //build properties file
1458     File buildPropertiesFile = new File(this.untarredUiDir.getAbsolutePath() + File.separator + "build.properties");
1459     if (!buildPropertiesFile.exists()) {
1460       File buildPropertiesTemplateFile = new File(this.untarredUiDir.getAbsolutePath() + File.separator + "build.properties.template");
1461       System.out.println("Copying file: " + buildPropertiesTemplateFile.getAbsolutePath() + " to file: " + buildPropertiesFile);
1462       GrouperInstallerUtils.copyFile(buildPropertiesTemplateFile, buildPropertiesFile, true);
1463     }
1464     
1465     //set the grouper property
1466     System.out.println("Editing " + buildPropertiesFile.getAbsolutePath() + ": ");
1467     String apiDir = GrouperInstallerUtils.replace(this.untarredApiDir.getAbsolutePath(),"\\\\", "/");
1468     apiDir = GrouperInstallerUtils.replace(apiDir, "\\", "/");
1469     editPropertiesFile(buildPropertiesFile, "grouper.folder", apiDir, false);
1470     editPropertiesFile(buildPropertiesFile, "should.copy.context.xml.to.metainf", "false", false);
1471     
1472   }
1473   
1474   /**
1475    * 
1476    */
1477   private void configureWs() {
1478     //build properties file
1479     File buildPropertiesFile = new File(this.untarredWsDir.getAbsolutePath() + File.separator 
1480         + "grouper-ws" + File.separator + "build.properties");
1481     if (!buildPropertiesFile.exists()) {
1482       File buildPropertiesTemplateFile = new File(this.untarredWsDir.getAbsolutePath() 
1483           + File.separator + "grouper-ws" + File.separator + "build.example.properties");
1484       System.out.println("Copying file: " + buildPropertiesTemplateFile.getAbsolutePath() + " to file: " + buildPropertiesFile);
1485       GrouperInstallerUtils.copyFile(buildPropertiesTemplateFile, buildPropertiesFile);
1486     }
1487     
1488     //set the grouper property
1489     System.out.println("Editing " + buildPropertiesFile.getAbsolutePath() + ": ");
1490     String apiDir = GrouperInstallerUtils.replace(this.untarredApiDir.getAbsolutePath(),"\\\\", "/");
1491     apiDir = GrouperInstallerUtils.replace(apiDir, "\\", "/");
1492     editPropertiesFile(buildPropertiesFile, "grouper.dir", apiDir, false);
1493     
1494   }
1495 
1496   /**
1497    * main function of grouper installer
1498    */
1499   public static enum GrouperInstallerMainFunction {
1500     
1501     /** install grouper */
1502     admin {
1503 
1504       @Override
1505       public void logic(GrouperInstaller grouperInstaller) {
1506         
1507         grouperInstaller.mainAdminLogic();
1508 
1509       }
1510     },
1511 
1512     /** install grouper */
1513     install {
1514 
1515       @Override
1516       public void logic(GrouperInstaller grouperInstaller) {
1517         
1518         grouperInstaller.mainInstallLogic();
1519 
1520       }
1521     },
1522     
1523     /** upgrade grouper */
1524     upgrade {
1525 
1526       @Override
1527       public void logic(GrouperInstaller grouperInstaller) {
1528         
1529         grouperInstaller.mainUpgradeLogic();
1530 
1531       }
1532     },
1533     
1534     /** create patch */
1535     createPatch {
1536 
1537       @Override
1538       public void logic(GrouperInstaller grouperInstaller) {
1539         
1540         grouperInstaller.mainCreatePatchLogic();
1541 
1542       }
1543     },
1544     
1545     /** see if there are patches available for grouper */
1546     patch {
1547 
1548       @Override
1549       public void logic(GrouperInstaller grouperInstaller) {
1550         
1551         grouperInstaller.mainPatchLogic();
1552 
1553       }
1554     };
1555 
1556     /**
1557      * run the logic for the installer function
1558      * @param grouperInstaller
1559      */
1560     public abstract void logic(GrouperInstaller grouperInstaller);
1561     
1562     /**
1563      * 
1564      * @param string
1565      * @param exceptionIfInvalid
1566      * @param exceptionIfBlank
1567      * @return the action
1568      */
1569     public static GrouperInstallerMainFunction valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
1570       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerMainFunction.class, string, exceptionIfBlank, exceptionIfInvalid);
1571     }
1572 
1573 
1574     /**
1575      * convert a string to the enum
1576      * @param theString
1577      * @param exceptionOnInvalid
1578      * @return the enum
1579      */
1580     public static GrouperInstallerMainFunction valueOfIgnoreCase(String theString, boolean exceptionOnInvalid) {
1581       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerMainFunction.class, theString, false, exceptionOnInvalid);
1582     }
1583   }
1584 
1585   /**
1586    * 
1587    */
1588   private static void validJava() {
1589     //check that java7+
1590     String versionString = System.getProperty("java.version");
1591     // 1.7.03
1592     int dotPos = versionString.indexOf('.');
1593     if (dotPos <= 0) {
1594       throw new RuntimeException("Expecting something like 1.7.03 but was: '" + versionString + "'");
1595     }
1596     //get the second dot
1597     dotPos = versionString.indexOf('.', dotPos+1);
1598     if (dotPos <= 0) {
1599       throw new RuntimeException("Expecting something like 1.7.03 but was: '" + versionString + "'");
1600     }
1601     versionString = versionString.substring(0, dotPos);
1602     double versionDouble = GrouperInstallerUtils.doubleValue(versionString);
1603     
1604     boolean hadError = false;
1605     
1606     if (versionDouble < 1.7) {
1607 
1608       System.out.println("Non-fatal ERROR: grouperInstaller requires to be invoked with at least Java 1.7, but was: " + versionString);
1609       hadError = true;
1610 
1611     }
1612     
1613     //we need a JAVA_HOME of at least java7 too...
1614     String javaHome = System.getenv("JAVA_HOME");
1615     
1616     boolean javaHomeError = false;
1617     
1618     if (GrouperInstallerUtils.isBlank(javaHome)) {
1619       System.out.println("Non-fatal ERROR: you should have the environment variable JAVA_HOME set to a 1.7+ JDK (currently not set)");
1620       javaHomeError = true;
1621       hadError = hadError || javaHomeError;
1622     }
1623 
1624     String command = null;
1625     
1626     if (!javaHomeError) {
1627       command = javaHome + File.separator + "bin" + File.separator + "java";
1628       javaHomeError = validJavaOutput(command, "the environment variable JAVA_HOME", false, false);
1629       hadError = hadError || javaHomeError;
1630     }
1631     
1632     if (!javaHomeError) {
1633       command = javaHome + File.separator + "bin" + File.separator + "javac";
1634       javaHomeError = validJavaOutput(command, "the environment variable JAVA_HOME", true, false);
1635       hadError = hadError || javaHomeError;
1636     }
1637 
1638     javaHomeError = false;
1639     command = "java";
1640     javaHomeError = validJavaOutput(command, "java command in the PATH", false, false);
1641 
1642     hadError = hadError || javaHomeError;
1643     
1644     if (!javaHomeError) {
1645       command = "javac";
1646       hadError = validJavaOutput(command, "javac command in the PATH", true, false) || hadError;
1647     }
1648     
1649     if (hadError) {
1650       System.out.print("Press <enter> to continue... ");
1651       readFromStdIn("grouperInstaller.autorun.javaInvalid");
1652     }
1653   }
1654  
1655   /**
1656    * take a java command (e.g. java, or javac, or %JAVA_HOME%/bin/java and make sure version is valid
1657    * @param what
1658    * @param command
1659    * @param jdkTest 
1660    * @param fatal
1661    * @return if error
1662    */
1663   private static boolean validJavaOutput(String command, String what, boolean jdkTest, boolean fatal) {
1664     
1665     boolean printStackOnError = GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.printStackOnJavaVersionErrors", false, false);
1666 
1667     try {
1668     
1669       List<String> commands = new ArrayList<String>();
1670       
1671       if (GrouperInstallerUtils.isWindows()) {
1672         commands.add(command);
1673       } else {
1674         commands.add(command);
1675       }
1676       
1677       commands.add("-version");
1678         
1679       CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
1680           true, true, null, null, null, false, true, printStackOnError);
1681       
1682       //note this is printed view stderr not stdout
1683       String output = commandResult.getErrorText();
1684       
1685       Pattern javaVersionPattern = Pattern.compile(".*?[^\\d]+(\\d+\\.\\d+).*", Pattern.DOTALL);
1686       Matcher javaVersionMatcher = javaVersionPattern.matcher(output);
1687       if (!javaVersionMatcher.matches()) {
1688         output = commandResult.getOutputText();
1689         javaVersionMatcher = javaVersionPattern.matcher(output);
1690         
1691         if (!javaVersionMatcher.matches()) {
1692           if (jdkTest) {
1693             System.out.println((fatal ? "" : "Non-fatal ") + "ERROR: can't find 'javac' command in " + what + ", Java needs to be a JDK not a JRE!");
1694           }
1695           System.out.println((fatal ? "" : "Non-fatal ") + "ERROR trying to check java output, make sure you have " + what 
1696               + " set to Java JDK (not JRE) 1.7+\n"
1697               + "" + commandResult.getErrorText() + "\n" + commandResult.getOutputText());
1698           if (!fatal) {
1699             return true;
1700           }
1701           System.out.print("Press <enter> to continue... ");
1702           readFromStdIn("grouperInstaller.autorun.javaInvalid");
1703           System.exit(1);
1704         }
1705       }
1706       
1707       String versionString = javaVersionMatcher.group(1);
1708       
1709       double versionDouble = GrouperInstallerUtils.doubleValue(versionString);
1710       if (versionDouble < 1.7) {
1711         System.out.println((fatal ? "" : "Non-fatal ") + "ERROR: " + what + " requires to be invoked with Java 1.7+ JDK (not JRE), but was: " + versionString);
1712         if (!fatal) {
1713           return true;
1714         }
1715         System.out.print("Press <enter> to continue... ");
1716         readFromStdIn("grouperInstaller.autorun.javaInvalid");
1717         System.exit(1);
1718       }
1719       return false;
1720     } catch (RuntimeException re) {
1721 
1722       if (printStackOnError) {
1723         re.printStackTrace();
1724       }
1725 
1726       System.out.println((fatal ? "" : "Non-fatal ") + "ERROR trying to check java output, make sure you have " + what 
1727           + " set to Java JDK (not JRE) 1.7+  " + re.getMessage());
1728       return true;
1729     }
1730   }
1731 
1732   /**
1733    * 
1734    */
1735   private void mainLogic() {
1736     
1737     validJava();
1738     
1739     this.grouperInstallerMainFunction = this.grouperInstallerMainFunction();
1740     
1741     this.grouperInstallerMainFunction.logic(this);
1742     
1743   }
1744   
1745   /**
1746    * what are we doing
1747    */
1748   private GrouperInstallerMainFunction grouperInstallerMainFunction;
1749   
1750   /**
1751    * @param appDir e.g. this.upgradeExistingApplicationDirectoryString
1752    */
1753   private void reportOnConflictingJars(String appDir) {
1754     
1755     System.out.println("\n##################################");
1756     System.out.println("Looking for conflicting jars\n");
1757 
1758     //look for conflicting jars
1759     List<File> allLibraryJars = findAllLibraryFiles(appDir);
1760     
1761     System.out.println("Found " + GrouperInstallerUtils.length(allLibraryJars) + " jars");
1762     
1763     Set<String> alreadyProcessed = new HashSet<String>();
1764     
1765     for (File jarFile : new ArrayList<File>(allLibraryJars)) {
1766       try {
1767         if (!jarFile.exists()) {
1768           allLibraryJars.remove(jarFile);
1769           continue;
1770         }
1771         
1772         Set<String> baseNames = GrouperInstallerUtils.jarFileBaseNames(jarFile.getName());
1773         
1774         //dont print multiple times
1775         if (alreadyProcessed.containsAll(baseNames)) {
1776           continue;
1777         }
1778         
1779         alreadyProcessed.addAll(baseNames);
1780         
1781         List<File> relatedFiles = GrouperInstallerUtils.nonNull(GrouperInstallerUtils.jarFindJar(allLibraryJars, jarFile.getName()));
1782         Iterator<File> relatedFilesIterator = relatedFiles.iterator();
1783         
1784         while (relatedFilesIterator.hasNext()) {
1785           if (jarFile.equals(relatedFilesIterator.next())) {
1786             relatedFilesIterator.remove();
1787           }
1788         }
1789         
1790         if (GrouperInstallerUtils.length(relatedFiles) >= 1) {
1791           
1792           if (relatedFiles.size() == 1) {
1793             File relatedFile = relatedFiles.iterator().next();
1794             File newerVersion = GrouperInstallerUtils.jarNewerVersion(relatedFile, jarFile);
1795             if (newerVersion != null) {
1796               
1797               if (newerVersion.equals(jarFile)) {
1798                 System.out.println("There is a conflicting jar: " + jarFile.getAbsolutePath());
1799                 System.out.println("Deleting older jar: " + relatedFile.getAbsolutePath());
1800                 GrouperInstallerUtils.fileDelete(relatedFile);
1801                 allLibraryJars.remove(relatedFile);
1802               } else {
1803                 System.out.println("There is a conflicting jar: " + relatedFile.getAbsolutePath());
1804                 System.out.println("Deleting older jar: " + jarFile.getAbsolutePath());
1805                 GrouperInstallerUtils.fileDelete(jarFile);
1806                 allLibraryJars.remove(jarFile);
1807               }
1808               System.out.print("Press <enter> to continue... ");
1809               readFromStdIn("grouperInstaller.autorun.conflictingJarContinue");
1810               continue;
1811             }
1812           }
1813           
1814           System.out.println("There is a conflicting jar: " + GrouperInstallerUtils.toStringForLog(relatedFiles));
1815           System.out.println("You should probably delete one of these files (oldest one?) or consult the Grouper team.");
1816           System.out.print("Press <enter> to continue... ");
1817           readFromStdIn("grouperInstaller.autorun.conflictingJarContinue");
1818         }
1819         
1820   //      if (GrouperInstallerUtils.length(relatedFiles) == 0) {
1821   //        System.out.println("Why is jar file not found??? " + jarFile.getAbsolutePath());
1822   //      }
1823       } catch (RuntimeException re) {
1824         GrouperInstallerUtils.injectInException(re, "Problem with jar: " + jarFile.getAbsolutePath());
1825         throw re;
1826       }
1827     }
1828   }
1829   
1830   /**
1831    * which app is being upgraded
1832    */
1833   private AppToUpgrade appToUpgrade;
1834 
1835   /**
1836    * patch grouper
1837    */
1838   private void mainCreatePatchLogic() {
1839 
1840     //####################################
1841     //Find out what directory to upgrade to.  This ends in a file separator
1842     this.grouperTarballDirectoryString = grouperUpgradeTempDirectory();
1843 
1844     //see what we are upgrading: api, ui, ws, client
1845     this.appToUpgrade = grouperAppToUpgradeOrPatch("create a patch for");
1846     
1847     if (this.appToUpgrade == AppToUpgrade.CLIENT) {
1848       throw new RuntimeException("Cant create patches for client, just put the client patch files in an API patch");
1849     }
1850     
1851     String branchToCreatePatchFor = null;
1852     {
1853       String defaultBranchToCreatePatchFor = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.branchToCreatePatchFor", false);
1854       
1855       if (GrouperInstallerUtils.isBlank(defaultBranchToCreatePatchFor)) {
1856         //grouper.version = 2.2.1
1857         // convert to GROUPER_2_2_BRANCH
1858         String grouperVersion = GrouperInstallerUtils.propertiesValue("grouper.version", true);
1859 
1860         grouperVersion = GrouperInstallerUtils.replace(grouperVersion, ".", "_");
1861 
1862         Pattern pattern = Pattern.compile("(\\d+_\\d+_)\\d+");
1863         Matcher matcher = pattern.matcher(grouperVersion);
1864         if (matcher.matches()) {
1865           String majorMinor = matcher.group(1);
1866           defaultBranchToCreatePatchFor = "GROUPER_" + majorMinor + "BRANCH";
1867         }
1868 
1869         
1870       }
1871       
1872       System.out.print("What branch do you want to create a patch for (e.g. GROUPER_2_2_BRANCH)? [" + defaultBranchToCreatePatchFor + "]: ");
1873       branchToCreatePatchFor = readFromStdIn("grouperInstaller.autorun.branchToCreatePatchFor");
1874       if (GrouperInstallerUtils.isBlank(branchToCreatePatchFor)) {
1875         branchToCreatePatchFor = defaultBranchToCreatePatchFor;
1876       }
1877     }
1878 
1879     String branchForPspToCreatePatchFor = null;
1880 
1881     if (this.appToUpgrade == AppToUpgrade.PSP) {
1882       String defaultBranchForPspToCreatePatchFor = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.branchForPspToCreatePatchFor", false);
1883       
1884       if (GrouperInstallerUtils.isBlank(defaultBranchForPspToCreatePatchFor)) {
1885         //grouper.version = 2.2.1
1886         // convert to PSP_2_2_BRANCH
1887         String grouperVersion = GrouperInstallerUtils.propertiesValue("grouper.version", true);
1888 
1889         grouperVersion = GrouperInstallerUtils.replace(grouperVersion, ".", "_");
1890 
1891         Pattern pattern = Pattern.compile("(\\d+_\\d+_)\\d+");
1892         Matcher matcher = pattern.matcher(grouperVersion);
1893         if (matcher.matches()) {
1894           String majorMinor = matcher.group(1);
1895           defaultBranchForPspToCreatePatchFor = "PSP_" + majorMinor + "BRANCH";
1896         }
1897       }
1898       
1899       System.out.print("What PSP branch do you want to create a patch for (e.g. GROUPER_2_2_BRANCH)? [" + defaultBranchForPspToCreatePatchFor + "]: ");
1900       branchForPspToCreatePatchFor = readFromStdIn("grouperInstaller.autorun.branchForPspToCreatePatchFor");
1901       if (GrouperInstallerUtils.isBlank(branchForPspToCreatePatchFor)) {
1902         branchForPspToCreatePatchFor = defaultBranchForPspToCreatePatchFor;
1903       }
1904       
1905     }
1906     
1907     int nextPatchIndex = -1;
1908     
1909     {
1910       String grouperVersion = GrouperInstallerUtils.propertiesValue("grouper.version", true);
1911   
1912       grouperVersion = GrouperInstallerUtils.replace(grouperVersion, ".", "_");
1913   
1914       nextPatchIndex = this.downloadPatches(this.appToUpgrade, grouperVersion);
1915     }
1916     
1917     //see if dir is there: e.g. grouper_v2_2_1_ui_patch_0
1918     String patchName = null;
1919     
1920     {
1921       String grouperVersion = GrouperInstallerUtils.propertiesValue("grouper.version", true);
1922 
1923       grouperVersion = GrouperInstallerUtils.replace(grouperVersion, ".", "_");
1924 
1925       patchName = "grouper_v" + grouperVersion + "_" + this.appToUpgrade.name().toLowerCase() + "_patch_" + nextPatchIndex;
1926     }
1927  
1928     {
1929       System.out.println("Next patch index for " + this.appToUpgrade + " is " + nextPatchIndex + ". ok (" + patchName + ")? (t|f)? [t]:");
1930       boolean continueOn = readFromStdInBoolean(true, "grouperInstaller.autorun.patchIndexIsOk");
1931       if (!continueOn) {
1932         System.out.println("Patch index is not ok");
1933         throw new RuntimeException("Patch index is not ok");
1934       }
1935     }
1936     
1937     downloadAndUnzipGrouperSource(branchToCreatePatchFor);
1938 
1939     File sourceTagDir = null;
1940 
1941     {
1942       String grouperVersion = GrouperInstallerUtils.propertiesValue("grouper.version", true);
1943       String grouperTag = GrouperInstallerUtils.replace(grouperVersion, ".", "_");
1944       System.out.println("Using Grouper tag: " + grouperTag);
1945       downloadAndUnzipGrouperSource("GROUPER_" + grouperTag);
1946       
1947       sourceTagDir = new File(this.grouperTarballDirectoryString + "GROUPER_" + grouperTag
1948           + File.separator + "grouper-GROUPER_" + grouperTag);
1949       
1950     }
1951     
1952     //grouper is in downloadDir/GROUPER_2_2_BRANCH/grouper-GROUPER_2_2_BRANCH
1953     File sourceDir = new File(this.grouperTarballDirectoryString + branchToCreatePatchFor
1954         + File.separator + "grouper-" + branchToCreatePatchFor);
1955 
1956     if (!sourceDir.exists()) {
1957       throw new RuntimeException("Why does source dir not exist??? " + sourceDir);
1958     }
1959 
1960     //grouper is in downloadDir/GROUPER_2_2_BRANCH/grouper-GROUPER_2_2_BRANCH
1961     File pspSourceDir = null;
1962     File pspSourceTagDir = null;
1963 
1964     if (this.appToUpgrade == AppToUpgrade.PSP) {
1965       downloadAndUnzipPspSource(branchForPspToCreatePatchFor);
1966 
1967       pspSourceDir = new File(this.grouperTarballDirectoryString + branchForPspToCreatePatchFor
1968           + File.separator + "grouper-psp-" + branchForPspToCreatePatchFor);
1969 
1970       if (!pspSourceDir.exists()) {
1971         throw new RuntimeException("Why does PSP source dir not exist??? " + pspSourceDir);
1972       }
1973 
1974       String grouperVersion = GrouperInstallerUtils.propertiesValue("grouper.version", true);
1975       System.out.println("Using PSP tag: " + grouperVersion);
1976       downloadAndUnzipPspSource(grouperVersion);
1977 
1978       pspSourceTagDir = new File(this.grouperTarballDirectoryString + grouperVersion
1979           + File.separator + "grouper-psp-" + grouperVersion);
1980 
1981       if (!pspSourceTagDir.exists()) {
1982         throw new RuntimeException("Why does PSP source tag dir not exist??? " + pspSourceTagDir);
1983       }
1984     }
1985     
1986     //get ant and maven
1987     this.downloadAndUnzipAnt();
1988     this.downloadAndUnzipMaven();
1989 
1990     if (this.appToUpgrade == AppToUpgrade.API) {
1991       //have to build client first
1992       this.buildClient(new File(sourceDir + File.separator + "grouper-misc" + File.separator + "grouperClient"));
1993       this.buildClient(new File(sourceTagDir + File.separator + "grouper-misc" + File.separator + "grouperClient"));
1994       
1995     }
1996 
1997     // Grouper API has always been built for everything, but its build is actually broken
1998     // right now, and, in fact, there is no need to build Grouper API for PSPNG
1999     // Therefore: Disabling Grouper API build when building PSPNG so it can at least be patched
2000     if (this.appToUpgrade != AppToUpgrade.PSPNG) {
2001       this.untarredApiDir = new File(sourceDir + File.separator + "grouper");
2002       this.buildGrouperApi(new File(sourceDir + File.separator + "grouper"));
2003       this.untarredApiDir = new File(sourceTagDir + File.separator + "grouper");
2004       this.buildGrouperApi(new File(sourceTagDir + File.separator + "grouper"));
2005     }
2006     
2007     if (this.appToUpgrade == AppToUpgrade.API) {
2008       //other packages, e.g. messaging
2009       this.buildMessagingRabbitmq(new File(sourceDir + File.separator + "grouper-misc" + File.separator + "grouper-messaging-rabbitmq"));
2010       this.buildMessagingRabbitmq(new File(sourceTagDir + File.separator + "grouper-misc" + File.separator + "grouper-messaging-rabbitmq"));
2011 
2012       this.buildMessagingActivemq(new File(sourceDir + File.separator + "grouper-misc" + File.separator + "grouper-messaging-activemq"));
2013       this.buildMessagingActivemq(new File(sourceTagDir + File.separator + "grouper-misc" + File.separator + "grouper-messaging-activemq"));
2014 
2015       this.buildMessagingAws(new File(sourceDir + File.separator + "grouper-misc" + File.separator + "grouper-messaging-aws"));
2016       this.buildMessagingAws(new File(sourceTagDir + File.separator + "grouper-misc" + File.separator + "grouper-messaging-aws"));
2017 
2018       this.buildDuo(new File(sourceDir + File.separator + "grouper-misc" + File.separator + "grouper-duo"));
2019       this.buildDuo(new File(sourceTagDir + File.separator + "grouper-misc" + File.separator + "grouper-duo"));
2020       
2021     }
2022     
2023 
2024     if (this.appToUpgrade == AppToUpgrade.UI) {
2025       //lets build the UI
2026       this.untarredApiDir = new File(sourceDir + File.separator + "grouper");
2027       this.untarredUiDir = new File(sourceDir + File.separator + "grouper-ui");
2028       this.configureUi();
2029       this.buildUi(false);
2030 
2031       this.untarredApiDir = new File(sourceTagDir + File.separator + "grouper");
2032       this.untarredUiDir = new File(sourceTagDir + File.separator + "grouper-ui");
2033       this.configureUi();
2034       this.buildUi(false);
2035 
2036     }
2037     
2038     if (this.appToUpgrade == AppToUpgrade.WS) {
2039       //lets build the WS
2040       this.untarredApiDir = new File(sourceDir + File.separator + "grouper");
2041       this.untarredWsDir = new File(sourceDir + File.separator + "grouper-ws");
2042       this.configureWs();
2043       this.buildWs(false);
2044 
2045       this.untarredApiDir = new File(sourceTagDir + File.separator + "grouper");
2046       this.untarredWsDir = new File(sourceTagDir + File.separator + "grouper-ws");
2047       this.configureWs();
2048       this.buildWs(false);
2049     }    
2050     
2051     if (this.appToUpgrade == AppToUpgrade.PSPNG) {
2052       this.untarredPspngDir = new File(sourceDir + File.separator + "grouper-misc" + File.separator + "grouper-pspng");
2053       this.buildPspng(this.untarredPspngDir);
2054 
2055       this.untarredPspngDir = new File(sourceTagDir + File.separator + "grouper-misc" + File.separator + "grouper-pspng");
2056       this.buildPspng(this.untarredPspngDir);
2057     }    
2058 
2059     if (this.appToUpgrade == AppToUpgrade.PSP) {
2060       this.untarredApiDir = new File(sourceDir + File.separator + "grouper");
2061       this.buildPsp(pspSourceDir);
2062 
2063       this.untarredApiDir = new File(sourceTagDir + File.separator + "grouper");
2064       this.buildPsp(pspSourceTagDir);
2065     }    
2066 
2067     //lets index files
2068     Map<String, GrouperInstallerIndexFile> indexOfFiles = new TreeMap<String, GrouperInstallerIndexFile>();
2069     Map<String, GrouperInstallerIndexFile> indexOfTagFiles = new TreeMap<String, GrouperInstallerIndexFile>();
2070 
2071     patchCreateIndexFiles(indexOfFiles, sourceDir, pspSourceDir);
2072     patchCreateIndexFiles(indexOfTagFiles, sourceTagDir, pspSourceTagDir);
2073 
2074     Set<GrouperInstallerIndexFile> grouperInstallerIndexFilesToAddToPatch = new HashSet<GrouperInstallerIndexFile>();
2075     
2076     //lets get the files from the user
2077     OUTER: for (int i=0;i<10;i++) {
2078       
2079       if (i==9) {
2080         throw new RuntimeException("You need to enter valid files!");
2081       }
2082 
2083       //if subsequent pass, then start fresh
2084       grouperInstallerIndexFilesToAddToPatch.clear();
2085       
2086       System.out.println("\nThe following could be filename if no dupes: Something.java.\n"
2087           + "Could be package path: edu/whatever/Something.java\n"
2088           + "could be path in module: dist/build/edu/internet2/middleware/grouper/changeLog/esb/consumer/EsbEvent.java\n"
2089           + "could be: webapp/WEB-INF/grouperUi2/index/index.jsp");
2090       System.out.println("Enter the comma separated list of files (dont use .class, use .java) to make a patch from: [required]\n");
2091       String filesToMakePatchFromCommaSeparated = readFromStdIn("grouperInstaller.autorun.patchFilesCommaSeparated");
2092       if (GrouperInstallerUtils.isBlank(filesToMakePatchFromCommaSeparated)) {
2093         System.out.println("This is a required field!");
2094         continue;
2095       }
2096       
2097       Set<String> fileKeys = new HashSet<String>(GrouperInstallerUtils.nonNull(
2098           GrouperInstallerUtils.splitTrimToList(filesToMakePatchFromCommaSeparated, ",")));
2099 
2100       for (String fileKey : fileKeys) {
2101         
2102         if (fileKey.endsWith(".class")) {
2103           System.out.println("Do not specify .class files, only .java files (will be compiled): '" + fileKey + "'!!!  please re-enter the list");
2104           continue OUTER;
2105         }
2106         
2107         GrouperInstallerIndexFile grouperInstallerIndexFile = indexOfFiles.get(fileKey);
2108         if (grouperInstallerIndexFile == null) {
2109           grouperInstallerIndexFile = indexOfTagFiles.get(fileKey);
2110           //see if we are deleting
2111           if (grouperInstallerIndexFile == null) {
2112             System.out.println("Cant find file: '" + fileKey + "'!!!  please re-enter the list");
2113             continue OUTER;
2114           }
2115         }
2116         
2117         if (grouperInstallerIndexFile.isHasMultipleFilesBySimpleName()
2118             && GrouperInstallerUtils.equals(fileKey, grouperInstallerIndexFile.getSimpleName())) {
2119           System.out.println("This name is in the index multiple times, please be more specific: '" 
2120               + fileKey + "', " + grouperInstallerIndexFile.getErrors());
2121           continue OUTER;
2122         }
2123         
2124         if (grouperInstallerIndexFile.isHasMultipleFilesByRelativePath()
2125             && GrouperInstallerUtils.equals(fileKey, grouperInstallerIndexFile.getRelativePath())) {
2126           System.out.println("This relative path is in the index multiple times, please be more specific: '" 
2127               + fileKey + "', " + grouperInstallerIndexFile.getErrors());
2128           continue OUTER;
2129         }
2130 
2131         if (grouperInstallerIndexFile.isHasMultipleFilesByPath()
2132             && GrouperInstallerUtils.equals(fileKey, grouperInstallerIndexFile.getPath())) {
2133           System.out.println("This path is in the index multiple times, please be more specific: '" 
2134               + fileKey + "', " + grouperInstallerIndexFile.getErrors());
2135           continue OUTER;
2136         }
2137         
2138         grouperInstallerIndexFilesToAddToPatch.add(grouperInstallerIndexFile);
2139       }
2140       break OUTER;
2141     }
2142     
2143     //ok, we have our list of files
2144     //lets go from java to class
2145     for (GrouperInstallerIndexFile grouperInstallerIndexFile : new HashSet<GrouperInstallerIndexFile>(grouperInstallerIndexFilesToAddToPatch)) {
2146 
2147       if (grouperInstallerIndexFile.getSimpleName().endsWith(".java")) {
2148         
2149         String relativePathJava = grouperInstallerIndexFile.getRelativePath();
2150         String relativePathPrefix = GrouperInstallerUtils.substringBeforeLast(relativePathJava, ".");
2151         String relativePathClass = relativePathPrefix + ".class";
2152 
2153         GrouperInstallerIndexFile grouperInstallerIndexFileClassFile = indexOfFiles.get(relativePathClass);
2154         
2155         //this will happen in a delete
2156         if (grouperInstallerIndexFileClassFile == null) {
2157           continue;
2158         }
2159         
2160         //this shouldnt happen
2161         if (grouperInstallerIndexFileClassFile.isHasMultipleFilesByRelativePath()) {
2162           throw new RuntimeException("Class file has multiple files by relative path???? " + relativePathClass);
2163         }
2164 
2165         //found class file
2166         grouperInstallerIndexFilesToAddToPatch.add(grouperInstallerIndexFileClassFile);
2167         
2168         //lets get all the inner classes
2169         File parentFile = grouperInstallerIndexFileClassFile.getFile().getParentFile();
2170         
2171         //with slash if needed, not sure why a class wouldnt have a package, but handle the case anyways
2172         String parentRelativePathWithSlash = GrouperInstallerUtils.substringBeforeLast(grouperInstallerIndexFileClassFile.getRelativePath(), "/") + "/";
2173         if (!grouperInstallerIndexFileClassFile.getRelativePath().contains("/")) {
2174           parentRelativePathWithSlash = "";
2175         }
2176         String fileNameInnerClassPrefix = GrouperInstallerUtils.substringBeforeLast(
2177             grouperInstallerIndexFileClassFile.getFile().getName(), ".") + "$";
2178         for (File siblingFile : parentFile.listFiles()) {
2179           if (siblingFile.getName().endsWith(".class") && GrouperInstallerUtils.filePathStartsWith(siblingFile.getName(),fileNameInnerClassPrefix)) {
2180             //this is an inner class
2181             String innerClassRelativePath = parentRelativePathWithSlash + siblingFile.getName();
2182             GrouperInstallerIndexFile innerClassIndexFile = indexOfFiles.get(innerClassRelativePath);
2183             if (innerClassIndexFile == null) {
2184               throw new RuntimeException("Cant find inner class index file??? " + innerClassRelativePath);
2185             }
2186             if (innerClassIndexFile.isHasMultipleFilesByRelativePath()) {
2187               throw new RuntimeException("Inner class file has multiple files by relative path??? " + innerClassRelativePath);
2188             }
2189             //found class file
2190             grouperInstallerIndexFilesToAddToPatch.add(innerClassIndexFile);
2191           }
2192         }
2193       }
2194     }
2195 
2196     File patchDir = new File(this.grouperTarballDirectoryString + "patches" + File.separator + patchName);
2197     
2198     if (patchDir.exists()) {
2199       if (patchDir.isFile()) {
2200         throw new RuntimeException("Why is patch directory a file???? " + patchDir.getAbsolutePath());
2201       }
2202       
2203       System.out.println("Local patch dir exists, is it ok to be automatically deleted? (t|f)? [t]:");
2204       boolean continueOn = readFromStdInBoolean(true, "grouperInstaller.autorun.deleteLocalPatchFile");
2205       if (!continueOn) {
2206         System.out.println("Cant continue if not deleting patch dir: " + patchDir.getAbsolutePath());
2207         throw new RuntimeException("Cant continue if not deleting patch dir: " + patchDir.getAbsolutePath());
2208       }
2209       
2210       //delete this dir
2211       GrouperInstallerUtils.deleteRecursiveDirectory(patchDir.getAbsolutePath());
2212       
2213     }
2214 
2215     
2216     //lets look for dependencies
2217     Set<String> dependencyPatchNames = new TreeSet<String>();
2218     
2219     //keep track of files to put in the "old" dir
2220     Map<GrouperInstallerIndexFile, File> indexFileToOldFile = new HashMap<GrouperInstallerIndexFile, File>();
2221     
2222     //go from most recent to oldest
2223     for (int i=nextPatchIndex-1;i>=0;i--) {
2224       
2225       //lets find the patch dir
2226       String currentPatchName = GrouperInstallerUtils.substringBeforeLast(patchName, "_") + "_" + i;
2227       
2228       File currentPatchDir = new File(this.grouperTarballDirectoryString + "patches" + File.separator + currentPatchName);
2229 
2230       Iterator<GrouperInstallerIndexFile> iterator = grouperInstallerIndexFilesToAddToPatch.iterator();
2231       
2232       while (iterator.hasNext()) {
2233         
2234         GrouperInstallerIndexFile indexFileToAdd = iterator.next();
2235         
2236         //dont check twice
2237         if (indexFileToOldFile.containsKey(indexFileToAdd)) {
2238           continue;
2239         }
2240         
2241         GrouperInstallerIndexFile indexFileToAddFromBranch = indexOfFiles.get(indexFileToAdd.getRelativePath());
2242         
2243         //note the old file will be in the patch's new directory
2244         File oldFile = new File(currentPatchDir.getAbsolutePath() + File.separator 
2245             + "new" + File.separator + indexFileToAdd.getPatchFileType().getDirName()
2246             + File.separator + GrouperInstallerUtils.replace(indexFileToAdd.getRelativePath(), "/", File.separator));
2247         if (oldFile.exists() && oldFile.isFile()) {
2248 
2249           if (indexFileToAddFromBranch != null && GrouperInstallerUtils.contentEquals(indexFileToAdd.getFile(), oldFile)) {
2250             System.out.println("New file is same as old file: " + indexFileToAdd.getFile().getAbsolutePath() + ", " 
2251                 + oldFile.getAbsolutePath());
2252             System.out.println("This file will not be included in patch");
2253             //remove from patch
2254             iterator.remove();
2255           } else {
2256 
2257             //this is now a dependency
2258             dependencyPatchNames.add(currentPatchName);
2259             
2260             //link this with the installer index file
2261             indexFileToOldFile.put(indexFileToAdd, oldFile);
2262           }          
2263         }
2264         
2265       }
2266       
2267     }
2268     
2269     {
2270       String patchNameDependenciesString = null;
2271       
2272       OUTER: for (int i=0;i<10;i++) {
2273         if (i==9) {
2274           throw new RuntimeException("Invalid patch names!");
2275         }
2276         if (dependencyPatchNames.size() == 0) {
2277           
2278           System.out.println("No dependency patches are detected, enter any patch names that are "
2279               + "dependencies that you know of (comma separated), or blank for none:\n");
2280           patchNameDependenciesString = readFromStdIn("grouperInstaller.autorun.patchNameDependenciesCommaSeparated");
2281           
2282         } else {
2283     
2284           System.out.println("These " + dependencyPatchNames.size() + " patches are detected: " 
2285               + GrouperInstallerUtils.join(dependencyPatchNames.iterator(), ", "));
2286           System.out.println("Enter any patch names that are dependencies that you know of (comma separated), or blank for none:\n");
2287           patchNameDependenciesString = readFromStdIn("grouperInstaller.autorun.patchNameDependenciesCommaSeparated");
2288     
2289         }
2290         if (!GrouperInstallerUtils.isBlank(patchNameDependenciesString)) {
2291           List<String> patchNameDependeciesFromUser = GrouperInstallerUtils.splitTrimToList(patchNameDependenciesString, ",");
2292           for (String currentPatchName : patchNameDependeciesFromUser) {
2293             if (!patchNameValid(currentPatchName)) {
2294               System.out.println("Invalid patch name! '" + currentPatchName + "', enter them again!");
2295               continue OUTER;
2296             }
2297           }
2298           dependencyPatchNames.addAll(patchNameDependeciesFromUser);
2299         }
2300         break;
2301       }
2302       
2303     }    
2304 
2305     //find old files from the tag
2306     Iterator<GrouperInstallerIndexFile> iterator = grouperInstallerIndexFilesToAddToPatch.iterator();
2307     
2308     while (iterator.hasNext()) {
2309       GrouperInstallerIndexFile currentIndexFile = iterator.next();
2310       //see if its covered in another patch
2311       if (indexFileToOldFile.containsKey(currentIndexFile)) {
2312         continue;
2313       }
2314       
2315       //dont have old files from java or classes, thats only for other patches to do
2316       if (currentIndexFile.getSimpleName().endsWith(".class") || currentIndexFile.getSimpleName().endsWith(".java")) {
2317         continue;
2318       }
2319 
2320       GrouperInstallerIndexFile currentIndexFileFromBranch = indexOfFiles.get(currentIndexFile.getRelativePath());
2321       
2322       //look for the old file
2323       GrouperInstallerIndexFile currentIndexFileFromTag = indexOfTagFiles.get(currentIndexFile.getPath());
2324       if (currentIndexFileFromTag == null) {
2325         currentIndexFileFromTag = indexOfTagFiles.get(currentIndexFile.getRelativePath());
2326       }
2327       if (currentIndexFileFromTag != null) {
2328         if (currentIndexFileFromTag.isHasMultipleFilesByPath()) {
2329           throw new RuntimeException("Why multiple paths???? " + currentIndexFile + ", " + currentIndexFile.getPath());
2330         }
2331         if (currentIndexFileFromBranch != null && GrouperInstallerUtils.contentEquals(currentIndexFileFromTag.getFile(), currentIndexFile.getFile())) {
2332           System.out.println("New file is same as old file: " + currentIndexFile.getFile().getAbsolutePath() + ", " 
2333               + currentIndexFileFromTag.getFile().getAbsolutePath());
2334           System.out.println("This file will not be included in patch");
2335           //remove from patch
2336           iterator.remove();
2337         } else {
2338           //add this as an old file
2339           indexFileToOldFile.put(currentIndexFile, currentIndexFileFromTag.getFile());
2340         }
2341       }
2342     }
2343     
2344     if (grouperInstallerIndexFilesToAddToPatch.size() == 0) {
2345       throw new RuntimeException("There are no files to put in patch!");
2346     }
2347     
2348 
2349     //# will show up on screen so user knows what it is
2350     //description = This patch fixes GRP-1080: browse folders refresh button only works in chrome, not other browsers
2351     System.out.print("\nEnter a description for this patch, e.g. GRP-123: fixes a problem with such and such: [required]\n");
2352     String patchDescription = readFromStdIn("grouperInstaller.autorun.patchDescription");
2353     
2354     if (GrouperInstallerUtils.isBlank(patchDescription)) {
2355       throw new RuntimeException("Cant have a blank description!");
2356     }
2357 
2358     //# (note, will try to get this from patch description, if its there, this can be blank)
2359     Matcher patchJiraKeyMatcher = Pattern.compile(".*(GRP-\\d+).*").matcher(patchDescription);
2360     String defaultPatchJiraKey = "";
2361     if (patchJiraKeyMatcher.matches()) {
2362       defaultPatchJiraKey = patchJiraKeyMatcher.group(1);
2363     }
2364     System.out.print("\nEnter a Jira key (e.g. GRP-123) for this patch: [required] " 
2365         + (GrouperInstallerUtils.isBlank(defaultPatchJiraKey) ? "" : ("[" + defaultPatchJiraKey + "]")) + "\n");
2366     String patchJiraKey = readFromStdIn("grouperInstaller.autorun.patchJiraKey");
2367     
2368     if (GrouperInstallerUtils.isBlank(patchJiraKey)) {
2369       if (!GrouperInstallerUtils.isBlank(defaultPatchJiraKey)) {
2370         patchJiraKey = defaultPatchJiraKey;
2371       } else {
2372         throw new RuntimeException("Cant have a blank jira key!");
2373       }
2374     }
2375     
2376     if (!Pattern.compile("^GRP-\\d+$").matcher(patchJiraKey).matches()) {
2377       throw new RuntimeException("Patch jira key must be valid: '" + patchJiraKey + "'");
2378     }
2379 
2380     String patchRiskLevel = null;
2381     
2382     {
2383       //# low, medium, or high risk to applying the patch
2384       //risk = low
2385       System.out.println("Enter the risk level for the patch: (low|medium|high): [required] ");
2386       String patchRiskLevelInput = readFromStdIn("grouperInstaller.autorun.patchRiskLevel");
2387       
2388       if (GrouperInstallerUtils.equalsIgnoreCase("low", patchRiskLevelInput)) {
2389         patchRiskLevel = "low";
2390       } else if (GrouperInstallerUtils.equalsIgnoreCase("medium", patchRiskLevelInput)) {
2391         patchRiskLevel = "medium";
2392       } else if (GrouperInstallerUtils.equalsIgnoreCase("high", patchRiskLevelInput)) {
2393         patchRiskLevel = "high";
2394       } else {
2395         throw new RuntimeException("Invalid risk level: '" + patchRiskLevelInput + "', expecting low|medium|high");
2396       }
2397       
2398     }
2399     
2400     //# is this is a security patch (true or false)
2401     //security = false
2402     System.out.println("Is this a security patch? (t|f): [t] ");
2403     boolean securityPatch = readFromStdInBoolean(false, "grouperInstaller.autorun.patchSecurity");
2404 
2405     boolean requiresRestart = false;
2406     for (GrouperInstallerIndexFile currentIndexFile : grouperInstallerIndexFilesToAddToPatch) {
2407       if (currentIndexFile.getSimpleName().endsWith(".jar")
2408           || currentIndexFile.getSimpleName().endsWith(".java")) {
2409         requiresRestart = true;
2410       }
2411     }
2412     //# if this patch requires restart of processes (true or false)
2413     //requiresRestart = false
2414     if (requiresRestart) {
2415       System.out.println("It is detected that your patch requires restart");
2416     } else {
2417       System.out.println("It is NOT detected that your patch requires restart, please confirm this, does it require restart (t|f)? [f] ");
2418       requiresRestart = readFromStdInBoolean(false, "grouperInstaller.autorun.overrideDoesntRequireRestart");
2419       
2420       if (requiresRestart) {
2421         System.out.println("Perhaps the maintainer of the Grouper Installer can use this feedback to make a better guess on restart, let them know");
2422         GrouperInstallerUtils.sleep(2000);
2423       }
2424     }
2425     //at this point we can build the patch dir and file and put files in there
2426     
2427     //# patches that this patch is dependant on (comma separated)
2428     //dependencies = 
2429 
2430     //create the dir
2431     GrouperInstallerUtils.mkdirs(patchDir);
2432     
2433     {
2434       String patchPropertiesContents = "# will show up on screen so user knows what it is\n"
2435           + "description = " + patchDescription + "\n"
2436           + "\n"
2437           + "# patches that this patch is dependant on (comma separated)\n"
2438           + "dependencies = " + GrouperInstallerUtils.join(dependencyPatchNames.iterator(), ", ") + "\n"
2439           + "\n"
2440           + "# low, medium, or high risk to applying the patch\n"
2441           + "risk = " + patchRiskLevel + "\n"
2442           + "\n"
2443           + "# is this is a security patch (true or false)\n"
2444           + "security = " + securityPatch + "\n"
2445           + "\n"
2446           + "# if this patch requires restart of processes (true or false)\n"
2447           + "requiresRestart = " + requiresRestart + "\n";
2448       String patchPropertiesFileName = patchDir + File.separator + patchDir.getName() + ".properties";
2449       GrouperInstallerUtils.saveStringIntoFile(new File(patchPropertiesFileName), patchPropertiesContents);
2450     }
2451     
2452     //lets do old files
2453     //start with old files
2454     if (indexFileToOldFile.size() > 0) {
2455       GrouperInstallerUtils.mkdirs(new File(patchDir.getAbsolutePath() + File.separator + "old"));
2456       for (GrouperInstallerIndexFile currentIndexFile : indexFileToOldFile.keySet()) {
2457 
2458         File oldFile = new File(patchDir.getAbsolutePath() + File.separator + "old" + File.separator
2459             + currentIndexFile.getPatchFileType().getDirName() + File.separator
2460             + GrouperInstallerUtils.replace(currentIndexFile.getRelativePath(), "/", File.separator));
2461         
2462         GrouperInstallerUtils.mkdirs(oldFile.getParentFile());
2463         
2464         System.out.println("Copying old file from " + indexFileToOldFile.get(currentIndexFile).getAbsolutePath()
2465             + "\n   to: " + oldFile.getAbsolutePath());
2466         
2467         GrouperInstallerUtils.copyFile(indexFileToOldFile.get(currentIndexFile), oldFile);
2468         
2469       }
2470     }
2471 
2472     //now put new files in place
2473     {
2474       GrouperInstallerUtils.mkdirs(new File(patchDir.getAbsolutePath() + File.separator + "new"));
2475       for (GrouperInstallerIndexFile currentIndexFile : grouperInstallerIndexFilesToAddToPatch) {
2476 
2477         //is it a delete? then there is no new file
2478         if (!indexOfFiles.containsKey(currentIndexFile.getRelativePath())) {
2479           continue;
2480         }
2481         
2482         File newFile = new File(patchDir.getAbsolutePath() + File.separator + "new" + File.separator
2483             + currentIndexFile.getPatchFileType().getDirName() + File.separator
2484             + GrouperInstallerUtils.replace(currentIndexFile.getRelativePath(), "/", File.separator));
2485         
2486         GrouperInstallerUtils.mkdirs(newFile.getParentFile());
2487         
2488         System.out.println("Copying new file from " + currentIndexFile.getFile().getAbsolutePath()
2489             + "\n   to: " + newFile.getAbsolutePath());
2490         
2491         GrouperInstallerUtils.copyFile(currentIndexFile.getFile().getAbsoluteFile(), newFile);
2492         
2493       }
2494     }
2495 
2496     {
2497       //generate the wiki markup
2498       //    <tr>
2499       //      <td>
2500       //        <p>
2501       //          <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>
2502       //        </p>
2503       //      </td>
2504       //      <td>
2505       //        <p>
2506       //          <a href="https://bugs.internet2.edu/jira/browse/GRP-1096">GRP-1096: Use threads for 2.2 upgrade to decrease time of upgrade</a>
2507       //        </p>
2508       //      </td>
2509       //      <td>
2510       //        <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>
2511       //      </td>
2512       //    </tr>
2513       String grouperVersion = GrouperInstallerUtils.propertiesValue("grouper.version", true);
2514 
2515       String wikiMarkup = "    <tr>\n"
2516         + "      <td>\n"
2517         + "        <p>" + new SimpleDateFormat("yyyy/MM/dd").format(new Date()) + "</p>\n"
2518         + "      </td>\n"
2519         + "      <td>\n"
2520         + "        <p>\n"
2521         + "          <a href=\"https://software.internet2.edu/grouper/release/" + grouperVersion + "/patches/" + patchName + ".tar.gz\">" + patchName + "</a>\n"
2522         + "        </p>\n"
2523         + "      </td>\n"
2524         + "      <td>\n"
2525         + "        <p>\n"
2526         + "          <a href=\"https://bugs.internet2.edu/jira/browse/" + patchJiraKey + "\">" + patchDescription + "</a>\n"
2527         + "        </p>\n"
2528         + "      </td>\n"
2529         + "      <td>\n"
2530         + "        <p>";
2531       
2532       boolean isFirst = true;
2533       for (GrouperInstallerIndexFile currentIndexFile : grouperInstallerIndexFilesToAddToPatch) {
2534         
2535         //just do java files
2536         if (currentIndexFile.getSimpleName().endsWith(".class")) {
2537           continue;
2538         }
2539         
2540         //classes/edu/internet2/middleware/grouper/internal/dao/hib3/Hib3StemSetDAO.java 
2541         //<br class=\"atl-forced-newline\"/>
2542 
2543         if (!isFirst) {
2544           wikiMarkup += "<br class=\"atl-forced-newline\"/>";
2545         }
2546         wikiMarkup += currentIndexFile.getPatchFileType().getDirName() + "/" 
2547             + currentIndexFile.getRelativePath();
2548 
2549         isFirst = false;
2550         
2551       }
2552       wikiMarkup += "</p>\n      </td>\n"
2553         + "    </tr>\n";
2554     
2555       System.out.println("Here is the wiki markup for the release notes page, copy and paste that into confluence using the <> button:");
2556       System.out.println("\n" + wikiMarkup + "\n");
2557       System.out.print("Press <enter> to continue... ");
2558       readFromStdIn("grouperInstaller.autorun.patchContinueAfterWikiMarkup");
2559     }
2560     
2561     // tar this up
2562     File tarfile = new File(patchDir.getParentFile() + File.separator + patchName + ".tar");
2563     GrouperInstallerUtils.tar(patchDir, tarfile);
2564     
2565     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]: ");
2566     boolean patchUseTestFileName = readFromStdInBoolean(true, "grouperInstaller.autorun.patchNameFileAsTestVersion");
2567 
2568     File gzipfile = new File(patchDir.getParentFile() + File.separator + patchName + (patchUseTestFileName ? "_test" : "") + ".tar.gz");
2569     GrouperInstallerUtils.gzip(tarfile, gzipfile);
2570 
2571     System.out.println("\nSUCCESS: your patch is here: " + gzipfile.getAbsolutePath());
2572 
2573   }
2574 
2575   /**
2576    * patch pattern
2577    */
2578   private static final Pattern patchNamePattern = Pattern.compile("^grouper_v(\\d+)_(\\d+)_(\\d+)_(api|ws|ui|psp|pspng)_patch_(\\d+)$");
2579 
2580   
2581   /**
2582    * see if valid patch name e.g. grouper_v2_2_1_api_patch_0
2583    * @param patchName
2584    * @return true for valid
2585    */
2586   private static boolean patchNameValid(String patchName) {
2587     //validate patch names
2588     return patchNamePattern.matcher(patchName).matches();
2589 
2590   }
2591   
2592   /**
2593    * index files from a source directory
2594    * @param theIndexOfFiles index of label to the index file object
2595    * @param theSourceDir to look for files in
2596    * @param thePspSourceDir is psp source dir to look for files in
2597    */
2598   private void patchCreateIndexFiles(Map<String, GrouperInstallerIndexFile> theIndexOfFiles, File theSourceDir, File thePspSourceDir) {
2599     System.out.println("\nCreating file index to make patches from " + theSourceDir.getAbsolutePath() + "...\n");
2600     
2601     switch(this.appToUpgrade) {
2602       case CLIENT:
2603         throw new RuntimeException("No patching client, patch API instead");
2604       case API:
2605 
2606         //index the grouper client
2607 // dont think we need lib from client, only api
2608 //        this.patchCreateProcessFiles(indexOfFiles, 
2609 //            new File(sourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouperClient"),
2610 //            new File(sourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouperClient" + File.separator + "lib"),
2611 //            PatchFileType.lib);
2612 
2613         this.patchCreateProcessFiles(theIndexOfFiles, 
2614             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouperClient"),
2615             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouperClient" 
2616                 + File.separator + "dist" + File.separator + "bin"),
2617             PatchFileType.clazz);
2618 
2619         this.patchCreateProcessFiles(theIndexOfFiles, 
2620             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouperClient"),
2621             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouperClient" 
2622                 + File.separator + "src" + File.separator + "java"),
2623             PatchFileType.clazz);
2624 
2625         this.patchCreateProcessFiles(theIndexOfFiles, 
2626             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouperClient"),
2627             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouperClient" 
2628                 + File.separator + "src" + File.separator + "ext"),
2629             PatchFileType.clazz);
2630 
2631         this.patchCreateProcessFiles(theIndexOfFiles, 
2632             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouperClient"),
2633             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouperClient" 
2634                 + File.separator + "conf"),
2635             PatchFileType.clazz);
2636 
2637         // rabbitmq
2638         this.patchCreateProcessFiles(theIndexOfFiles, 
2639             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-rabbitmq"),
2640             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-rabbitmq" 
2641                 + File.separator + "src" + File.separator + "main" + File.separator + "java"),
2642             PatchFileType.clazz);
2643 
2644         this.patchCreateProcessFiles(theIndexOfFiles, 
2645             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-rabbitmq"),
2646             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-rabbitmq" 
2647                 + File.separator + "dist" + File.separator + "bin"),
2648             PatchFileType.clazz);
2649 
2650         this.patchCreateProcessFiles(theIndexOfFiles, 
2651             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-rabbitmq"),
2652             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-rabbitmq"
2653                 + File.separator + "lib"),
2654             PatchFileType.lib);
2655 
2656         //active mq
2657         this.patchCreateProcessFiles(theIndexOfFiles, 
2658             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-activemq"),
2659             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-activemq" 
2660                 + File.separator + "src" + File.separator + "main" + File.separator + "java"),
2661             PatchFileType.clazz);
2662 
2663         this.patchCreateProcessFiles(theIndexOfFiles, 
2664             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-activemq"),
2665             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-activemq" 
2666                 + File.separator + "dist" + File.separator + "bin"),
2667             PatchFileType.clazz);
2668 
2669         this.patchCreateProcessFiles(theIndexOfFiles, 
2670             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-activemq"),
2671             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-activemq"
2672                 + File.separator + "lib"),
2673             PatchFileType.lib);
2674 
2675         //aws
2676         this.patchCreateProcessFiles(theIndexOfFiles, 
2677             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-aws"),
2678             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-aws" 
2679                 + File.separator + "src" + File.separator + "main" + File.separator + "java"),
2680             PatchFileType.clazz);
2681 
2682         this.patchCreateProcessFiles(theIndexOfFiles, 
2683             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-aws"),
2684             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-aws" 
2685                 + File.separator + "dist" + File.separator + "bin"),
2686             PatchFileType.clazz);
2687 
2688         this.patchCreateProcessFiles(theIndexOfFiles, 
2689             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-aws"),
2690             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-messaging-aws"
2691                 + File.separator + "lib"),
2692             PatchFileType.lib);
2693 
2694         // duo
2695         this.patchCreateProcessFiles(theIndexOfFiles, 
2696             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-duo"),
2697             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-duo" 
2698                 + File.separator + "src"),
2699             PatchFileType.clazz);
2700 
2701         this.patchCreateProcessFiles(theIndexOfFiles, 
2702             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-duo"),
2703             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-duo" 
2704                 + File.separator + "dist" + File.separator + "bin"),
2705             PatchFileType.clazz);
2706 
2707         //add grouper api files
2708         this.patchCreateProcessFiles(theIndexOfFiles, 
2709             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper"),
2710             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper" + File.separator + "lib"),
2711             PatchFileType.lib);
2712 
2713         this.patchCreateProcessFiles(theIndexOfFiles, 
2714             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper"),
2715             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper" + File.separator + "dist" 
2716                 + File.separator + "build" + File.separator + "grouper"),
2717             PatchFileType.clazz);
2718 
2719         this.patchCreateProcessFiles(theIndexOfFiles,
2720             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper"),
2721             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper" + File.separator + "conf"),
2722             PatchFileType.clazz);
2723 
2724         this.patchCreateProcessFiles(theIndexOfFiles,
2725             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper"),
2726             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper" + File.separator + "src" 
2727                 + File.separator + "grouper"),
2728             PatchFileType.clazz);
2729 
2730         this.patchCreateProcessFiles(theIndexOfFiles,
2731             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper"),
2732             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper" + File.separator + "src" 
2733                 + File.separator + "esb"),
2734             PatchFileType.clazz);
2735 
2736 // do this at some point
2737 //        this.patchCreateProcessFiles(theIndexOfFiles,
2738 //            new File(theSourceDir.getAbsolutePath() + File.separator + "grouper"),
2739 //            new File(theSourceDir.getAbsolutePath() + File.separator + "grouper" + File.separator + "src" 
2740 //                + File.separator + "test"),
2741 //            PatchFileType.clazz);
2742 
2743         this.patchCreateProcessFiles(theIndexOfFiles,
2744             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper"),
2745             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper" + File.separator + "bin"),
2746             PatchFileType.bin);
2747 
2748 
2749         break;
2750       case UI:
2751         
2752         this.patchCreateProcessFiles(theIndexOfFiles, 
2753             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ui"),
2754             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ui" 
2755                 + File.separator + "java" + File.separator + "lib"),
2756             PatchFileType.lib);
2757         
2758         this.patchCreateProcessFiles(theIndexOfFiles, 
2759             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ui"),
2760             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ui" 
2761                 + File.separator + "java" + File.separator + "src"),
2762             PatchFileType.clazz);
2763         
2764         this.patchCreateProcessFiles(theIndexOfFiles, 
2765             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ui"),
2766             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ui" 
2767                 + File.separator + "conf"),
2768             PatchFileType.clazz);
2769         
2770         this.patchCreateProcessFiles(theIndexOfFiles, 
2771             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ui"),
2772             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ui" 
2773                 + File.separator + "temp" + File.separator + "jarBin"),
2774             PatchFileType.clazz);
2775         
2776         this.patchCreateProcessFiles(theIndexOfFiles, 
2777             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ui"),
2778             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ui" 
2779                 + File.separator + "webapp"),
2780             PatchFileType.file);
2781 
2782         break;
2783       case WS:
2784         
2785         this.patchCreateProcessFiles(theIndexOfFiles, 
2786             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws"),
2787             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws" 
2788                 + File.separator + "lib" + File.separator + "grouper-ws"),
2789             PatchFileType.lib);
2790 
2791         this.patchCreateProcessFiles(theIndexOfFiles, 
2792             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws"),
2793             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws" 
2794                 + File.separator + "lib" + File.separator + "rampart"),
2795             PatchFileType.lib);
2796 
2797         this.patchCreateProcessFiles(theIndexOfFiles, 
2798             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws"),
2799             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws" 
2800                 + File.separator + "build" + File.separator + "grouper-ws"),
2801             PatchFileType.clazz);
2802         
2803         this.patchCreateProcessFiles(theIndexOfFiles, 
2804             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws"),
2805             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws" 
2806                 + File.separator + "conf"),
2807             PatchFileType.clazz);
2808         
2809         // we need to get all the source folders except test, note, each release adds another
2810         File parentSourceDir = new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws"
2811             + File.separator + "src");
2812 
2813         for (File wsSourceDir : parentSourceDir.listFiles()) {
2814           if (wsSourceDir.isFile() || !wsSourceDir.getName().startsWith("grouper")) {
2815             continue;
2816           }
2817           this.patchCreateProcessFiles(theIndexOfFiles, 
2818               new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws"),
2819               new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws" 
2820                   + File.separator + "src" + File.separator + wsSourceDir.getName()),
2821               PatchFileType.clazz);
2822         }
2823 
2824         //files
2825         this.patchCreateProcessFiles(theIndexOfFiles, 
2826             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws"),
2827             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator + "grouper-ws" 
2828                 + File.separator + "webapp"),
2829             PatchFileType.file);
2830         
2831         break;
2832         
2833       case PSP:
2834         this.patchCreateProcessFiles(theIndexOfFiles, 
2835             new File(thePspSourceDir.getAbsolutePath() + File.separator + "psp"),
2836             new File(thePspSourceDir.getAbsolutePath() + File.separator + "psp" + File.separator + "target" 
2837                 + File.separator + "dependency"),
2838             PatchFileType.lib);
2839         this.patchCreateProcessFiles(theIndexOfFiles, 
2840             new File(thePspSourceDir.getAbsolutePath() + File.separator + "psp"),
2841             new File(thePspSourceDir.getAbsolutePath() + File.separator + "psp" + File.separator + "src" 
2842                 + File.separator + "main" + File.separator + "java"),
2843             PatchFileType.clazz);
2844         this.patchCreateProcessFiles(theIndexOfFiles, 
2845             new File(thePspSourceDir.getAbsolutePath() + File.separator + "psp"),
2846             new File(thePspSourceDir.getAbsolutePath() + File.separator + "psp" + File.separator + "src" 
2847                 + File.separator + "main" + File.separator + "resources"),
2848             PatchFileType.clazz);
2849         this.patchCreateProcessFiles(theIndexOfFiles, 
2850             new File(thePspSourceDir.getAbsolutePath() + File.separator + "psp"),
2851             new File(thePspSourceDir.getAbsolutePath() + File.separator + "psp" + File.separator + "target" 
2852                 + File.separator + "classes"),
2853             PatchFileType.clazz);
2854 
2855         break;
2856       case PSPNG:
2857 
2858         this.patchCreateProcessFiles(theIndexOfFiles, 
2859             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-pspng"),
2860             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-pspng" 
2861                 + File.separator + "target" + File.separator + "dependency"),
2862             PatchFileType.lib);
2863 
2864         this.patchCreateProcessFiles(theIndexOfFiles, 
2865             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-pspng"),
2866             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-pspng" 
2867                 + File.separator + "src" + File.separator + "main" + File.separator + "java"),
2868             PatchFileType.clazz);
2869 
2870         this.patchCreateProcessFiles(theIndexOfFiles, 
2871             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-pspng"),
2872             new File(theSourceDir.getAbsolutePath() + File.separator + "grouper-misc" + File.separator + "grouper-pspng" 
2873                 + File.separator + "target" + File.separator + "classes"),
2874             PatchFileType.clazz);
2875 
2876         break;
2877     }
2878     
2879     //print out files for debugging
2880     //for (String key : theIndexOfFiles.keySet()) {
2881     //  if (key.toLowerCase().contains("mygroupsmemberships")) {
2882     //    System.out.println(key + " -> " + theIndexOfFiles.get(key));
2883     //  }
2884     //}
2885 
2886     System.out.println("\nDone creating file index to make patches from " + theSourceDir.getAbsolutePath() + "... found " + theIndexOfFiles.size() + " files\n");
2887 
2888   }
2889   
2890   /**
2891    * @param directory to look in
2892    * @param projectDirectory is the directory where the project is for the files
2893    * @param indexOfFiles the index
2894    * @param patchFileType
2895    */
2896   private void patchCreateProcessFiles(Map<String, GrouperInstallerIndexFile> indexOfFiles, File projectDirectory, File directory, 
2897       PatchFileType patchFileType) {
2898     
2899     this.patchCreateProcessFilesHelper(indexOfFiles, projectDirectory, directory, patchFileType, "");
2900 
2901   }
2902 
2903   /**
2904    * @param directory to look in
2905    * @param projectDirectory is the directory where the project is for the files
2906    * @param indexOfFiles
2907    * @param relativePath in the main path to look in, helps with restrictions
2908    * @param patchFileType
2909    */
2910   private void patchCreateProcessFilesHelper(Map<String, GrouperInstallerIndexFile> indexOfFiles, 
2911       File projectDirectory, File directory, 
2912       PatchFileType patchFileType, String relativePath) {
2913 
2914     try {
2915       //lets spider through directory and add files to index
2916       //get the files into a vector
2917       File[] allFiles = directory.listFiles();
2918   
2919       //loop through the array
2920       for (int i = 0; i < allFiles.length; i++) {
2921   
2922         File currentFileOrDirectory = allFiles[i];
2923         
2924         if (-1 < currentFileOrDirectory.getName().indexOf("..")) {
2925           continue; //dont go to the parent directory
2926         }
2927   
2928         //go to sub directory
2929         String newRelativePath = GrouperInstallerUtils.isBlank(relativePath) ? currentFileOrDirectory.getName() 
2930             : (relativePath + "/" + currentFileOrDirectory.getName());
2931   
2932         if (currentFileOrDirectory.isFile()) {
2933   
2934           boolean addFile = false;
2935           
2936           String fileRelativePath = GrouperInstallerUtils.fileRelativePath(projectDirectory, currentFileOrDirectory);
2937   
2938           switch(patchFileType) {
2939   
2940             case lib:
2941               
2942               if (currentFileOrDirectory.getName().endsWith(".jar")) {
2943                 addFile = true;
2944               }
2945   
2946               break;
2947             case file:
2948               addFile = true;
2949               
2950               if (currentFileOrDirectory.getName().endsWith(".jar")) {
2951                 addFile = false;
2952               }
2953   
2954               if (currentFileOrDirectory.getName().endsWith(".class")) {
2955                 addFile = false;
2956               }
2957   
2958               if (currentFileOrDirectory.getName().endsWith(".java")) {
2959                 addFile = false;
2960               }
2961   
2962               //these are classes not files
2963               if (GrouperInstallerUtils.filePathStartsWith(fileRelativePath,"WEB-INF/classes")) {
2964                 addFile = false;
2965               }
2966   
2967               //these are libs not files
2968               if (GrouperInstallerUtils.filePathStartsWith(fileRelativePath,"WEB-INF/lib")) {
2969                 addFile = false;
2970               }
2971   
2972               break;
2973             default: 
2974               addFile = true;
2975           }
2976   
2977           if (addFile) {
2978             GrouperInstallerIndexFilee.html#GrouperInstallerIndexFile">GrouperInstallerIndexFile grouperInstallerIndexFile = new GrouperInstallerIndexFile();
2979             grouperInstallerIndexFile.setSimpleName(currentFileOrDirectory.getName());
2980             grouperInstallerIndexFile.setRelativePath(newRelativePath);
2981             grouperInstallerIndexFile.setFile(currentFileOrDirectory);
2982             grouperInstallerIndexFile.setPatchFileType(patchFileType);
2983             grouperInstallerIndexFile.setPath(fileRelativePath);
2984             
2985             //add by name
2986             if (patchCreateAddFileToIndex(indexOfFiles, grouperInstallerIndexFile, currentFileOrDirectory.getName())) {
2987               //different file
2988               indexOfFiles.get(currentFileOrDirectory.getName()).setHasMultipleFilesBySimpleName(true);
2989               System.out.println("Note: duplicate file by name: " + currentFileOrDirectory.getAbsolutePath().replace('\\', '/') 
2990                   + ", " + currentFileOrDirectory.getName() + ", " + newRelativePath.replace('\\', '/') + ", " 
2991                   + indexOfFiles.get(currentFileOrDirectory.getName()).getRelativePath().replace('\\', '/')  + ", "
2992                   + grouperInstallerIndexFile.getPath().replace('\\', '/') + ", "
2993                   + indexOfFiles.get(currentFileOrDirectory.getName()).getPath().replace('\\', '/'));
2994             }
2995             
2996             //add by relative path
2997             if (patchCreateAddFileToIndex(indexOfFiles, grouperInstallerIndexFile, newRelativePath)) {
2998               //different file
2999               indexOfFiles.get(currentFileOrDirectory.getName()).setHasMultipleFilesByRelativePath(true);
3000               System.out.println("Note: duplicate file by relative path: " + currentFileOrDirectory.getAbsolutePath().replace('\\', '/') 
3001                   + ", " + currentFileOrDirectory.getName() + ", " + newRelativePath.replace('\\', '/') + ", " 
3002                   + indexOfFiles.get(currentFileOrDirectory.getName()).getRelativePath().replace('\\', '/')  + ", "
3003                   + grouperInstallerIndexFile.getPath().replace('\\', '/') + ", "
3004                   + indexOfFiles.get(currentFileOrDirectory.getName()).getPath().replace('\\', '/'));
3005             }
3006   
3007             //add by path
3008             if (patchCreateAddFileToIndex(indexOfFiles, grouperInstallerIndexFile, grouperInstallerIndexFile.getPath())) {
3009               //different file
3010               indexOfFiles.get(currentFileOrDirectory.getName()).setHasMultipleFilesByPath(true);
3011               System.out.println("Note: duplicate file by path: " + currentFileOrDirectory.getAbsolutePath() .replace('\\', '/') 
3012                   + ", " + currentFileOrDirectory.getName() + ", " + newRelativePath.replace('\\', '/') + ", " 
3013                   + indexOfFiles.get(currentFileOrDirectory.getName()).getRelativePath().replace('\\', '/')  + ", "
3014                   + grouperInstallerIndexFile.getPath().replace('\\', '/') + ", "
3015                   + indexOfFiles.get(currentFileOrDirectory.getName()).getPath().replace('\\', '/'));
3016             }
3017           }
3018           
3019         } else {
3020                   
3021           patchCreateProcessFilesHelper(indexOfFiles, projectDirectory, currentFileOrDirectory, patchFileType, newRelativePath);
3022           
3023         }
3024       }
3025     } catch (Exception e) {
3026       throw new RuntimeException("Problem with directory: " + directory.getAbsolutePath(), e);
3027     }
3028     
3029   }
3030 
3031   /**
3032    * @param indexOfFiles database of files by various lookup names
3033    * @param grouperInstallerIndexFile file to add
3034    * @param key add by this key
3035    * @return true if file already there and different
3036    */
3037   private boolean patchCreateAddFileToIndex(Map<String, GrouperInstallerIndexFile> indexOfFiles, 
3038       GrouperInstallerIndexFile grouperInstallerIndexFile, String key) {
3039     
3040     //convert slashes on key
3041     key = key.replace('\\', '/');
3042     
3043     grouperInstallerIndexFile.getErrors().append("Key: ").append(key).append(", ");
3044     
3045     GrouperInstallerIndexFile currentFileInIndex = indexOfFiles.get(key);
3046     if (currentFileInIndex == null) {
3047       indexOfFiles.put(key, grouperInstallerIndexFile);
3048     } else {
3049       currentFileInIndex.getErrors().append("Key: ").append(key).append(",");
3050       //skip these, who cares, too many dupes
3051       if (!GrouperInstallerUtils.equals(grouperInstallerIndexFile.getSimpleName(), "package-info.java")
3052           && !GrouperInstallerUtils.equals(grouperInstallerIndexFile.getSimpleName(), "package.html")) {
3053         if (!GrouperInstallerUtils.equals(grouperInstallerIndexFile.computeSha1(), currentFileInIndex.computeSha1())) {
3054           return true;
3055         }
3056       }
3057     }
3058     return false;
3059   }
3060   
3061   /**
3062    * build PSP
3063    * @param pspDir
3064    */
3065   private void buildPsp(File pspDir) {
3066     if (!pspDir.exists() || pspDir.isFile()) {
3067       throw new RuntimeException("Cant find psp: " + pspDir.getAbsolutePath());
3068     }
3069     
3070     File pspBuildToDir = new File(pspDir.getAbsolutePath() + File.separator + "psp" 
3071         + File.separator + "target" + File.separator + "classes");
3072     
3073     boolean rebuildPsp = true;
3074     
3075     if (pspBuildToDir.exists()) {
3076       System.out.print("The PSP has been built in the past, do you want it rebuilt? (t|f) [t]: ");
3077       rebuildPsp = readFromStdInBoolean(true, "grouperInstaller.autorun.rebuildPspAfterHavingBeenBuilt");
3078     }
3079     
3080     if (!rebuildPsp) {
3081       return;
3082     }
3083     
3084     List<String> commands = new ArrayList<String>();
3085     
3086 //    \bin\mvn compile -DskipTests
3087     addMavenCommands(commands);
3088 
3089     //put 'compile -DskipTests' in there so it wont run tests which we dont want to do
3090     // dependency:copy-dependencies package -DskipTests
3091     //not compile
3092     commands.add("dependency:copy-dependencies");
3093     commands.add("package");
3094     commands.add("-DskipTests");
3095     commands.add("-Drat.ignoreErrors=true");
3096     commands.add("-Dlicense.skip=true");
3097     
3098     System.out.println("\n##################################");
3099     System.out.println("Building PSP with command:\n" + pspDir.getAbsolutePath() + "> " 
3100         + convertCommandsIntoCommand(commands) + "\n");
3101     
3102     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
3103         true, true, null, new File(pspDir.getAbsolutePath() + File.separator + "psp-parent"), null, true);
3104     
3105     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
3106       System.out.println("stderr: " + commandResult.getErrorText());
3107     }
3108     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
3109       System.out.println("stdout: " + commandResult.getOutputText());
3110     }
3111 
3112     System.out.println("\nEnd building PSP");
3113     System.out.println("##################################\n");
3114     
3115   }
3116   
3117   /**
3118    * build ws scim
3119    */
3120   private void buildWsScim() {
3121     
3122     File grouperWsScimSourcesDir =  new File(this.untarredWsDir.getAbsolutePath() + File.separator + "grouper-ws-scim");
3123     
3124     if (!grouperWsScimSourcesDir.exists() || grouperWsScimSourcesDir.isFile()) {
3125       throw new RuntimeException("Cant find grouper-ws-scim: " + grouperWsScimSourcesDir.getAbsolutePath());
3126     }
3127     
3128     File grouperWsScimBuildToDir = new File(grouperWsScimSourcesDir.getAbsolutePath() + File.separator + "target" + File.separator + "classes");
3129     
3130     boolean rebuildWsScim = true;
3131     
3132     if (grouperWsScimBuildToDir.exists()) {
3133       System.out.print("The Grouper WS Scim has been built in the past, do you want it rebuilt? (t|f) [t]: ");
3134       rebuildWsScim = readFromStdInBoolean(true, "grouperInstaller.autorun.rebuildWsScimAfterHavingBeenBuilt");
3135     }
3136     
3137     if (!rebuildWsScim) {
3138       return;
3139     }
3140     
3141     List<String> commands = new ArrayList<String>();
3142     
3143 //    \bin\mvn compile -DskipTests
3144     addMavenCommands(commands);
3145 
3146     //put 'compile -DskipTests' in there so it wont run tests which we dont want to do
3147     // dependency:copy-dependencies package -DskipTests
3148     //not compile
3149     commands.add("dependency:copy-dependencies");
3150     commands.add("package");
3151     commands.add("-DskipTests");
3152     commands.add("-Drat.ignoreErrors=true");
3153     commands.add("-Dlicense.skip=true");
3154     
3155     System.out.println("\n##################################");
3156     System.out.println("Building Grouper WS Scim with command:\n" + grouperWsScimSourcesDir.getAbsolutePath() + "> " 
3157         + convertCommandsIntoCommand(commands) + "\n");
3158     
3159     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
3160         true, true, null, new File(grouperWsScimSourcesDir.getAbsolutePath()), null, true);
3161     
3162     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
3163       System.out.println("stderr: " + commandResult.getErrorText());
3164     }
3165     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
3166       System.out.println("stdout: " + commandResult.getOutputText());
3167     }
3168 
3169     System.out.println("\nEnd building grouper-ws-scim");
3170     System.out.println("##################################\n");
3171     
3172   }
3173 
3174   /**
3175    * build grouper API
3176    * @param grouperApiDir
3177    */
3178   private void buildGrouperApi(File grouperApiDir) {
3179 
3180     if (!grouperApiDir.exists() || grouperApiDir.isFile()) {
3181       throw new RuntimeException("Cant find grouper api: " + grouperApiDir.getAbsolutePath());
3182     }
3183     
3184     File grouperBuildToDir = new File(grouperApiDir.getAbsolutePath() + File.separator + "dist" + File.separator + "build" 
3185         + File.separator + "grouper");
3186     
3187     boolean rebuildGrouperApi = true;
3188     
3189     if (grouperBuildToDir.exists()) {
3190       System.out.print("The Grouper API has been built in the past, do you want it rebuilt? (t|f) [t]: ");
3191       rebuildGrouperApi = readFromStdInBoolean(true, "grouperInstaller.autorun.rebuildGrouperApiAfterHavingBeenBuilt");
3192     }
3193     
3194     if (!rebuildGrouperApi) {
3195       return;
3196     }
3197     
3198     List<String> commands = new ArrayList<String>();
3199     
3200     addAntCommands(commands);
3201 
3202     //this will run tests which we dont want to do
3203     commands.add("dist");
3204     
3205     System.out.println("\n##################################");
3206     System.out.println("Building grouper API with command:\n" + grouperApiDir.getAbsolutePath() + "> " 
3207         + convertCommandsIntoCommand(commands) + "\n");
3208     
3209     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
3210         true, true, null, grouperApiDir, null, true);
3211     
3212     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
3213       System.out.println("stderr: " + commandResult.getErrorText());
3214     }
3215     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
3216       System.out.println("stdout: " + commandResult.getOutputText());
3217     }
3218 
3219     System.out.println("\nEnd building grouper API");
3220     System.out.println("##################################\n");
3221     
3222   }
3223   
3224 
3225 
3226   /**
3227    * build client API
3228    * @param clientDir
3229    */
3230   private void buildClient(File clientDir) {
3231     if (!clientDir.exists() || clientDir.isFile()) {
3232       throw new RuntimeException("Cant find client: " + clientDir.getAbsolutePath());
3233     }
3234     
3235     File clientBuildToDir = new File(clientDir.getAbsoluteFile() + File.separator + "dist" + File.separator + "bin");
3236     
3237     boolean rebuildClient = true;
3238     
3239     if (clientBuildToDir.exists()) {
3240       System.out.print("The Grouper client has been built in the past, do you want it rebuilt? (t|f) [t]: ");
3241       rebuildClient = readFromStdInBoolean(true, "grouperInstaller.autorun.rebuildClientAfterHavingBeenBuilt");
3242     }
3243     
3244     if (!rebuildClient) {
3245       return;
3246     }
3247 
3248     List<String> commands = new ArrayList<String>();
3249     
3250     addAntCommands(commands);
3251     
3252     System.out.println("\n##################################");
3253     System.out.println("Building client with command:\n" + clientDir.getAbsolutePath() + "> " 
3254         + convertCommandsIntoCommand(commands) + "\n");
3255     
3256     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
3257         true, true, null, clientDir, null, true);
3258     
3259     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
3260       System.out.println("stderr: " + commandResult.getErrorText());
3261     }
3262     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
3263       System.out.println("stdout: " + commandResult.getOutputText());
3264     }
3265 
3266     System.out.println("\nEnd building client");
3267     System.out.println("##################################\n");
3268     
3269   }
3270 
3271   /**
3272    * build client API
3273    * @param duoDir
3274    */
3275   private void buildDuo(File duoDir) {
3276     if (!duoDir.exists() || duoDir.isFile()) {
3277       throw new RuntimeException("Cant find duo: " + duoDir.getAbsolutePath());
3278     }
3279     
3280     File duoBuildToDir = new File(duoDir.getAbsoluteFile() + File.separator + "dist" + File.separator + "bin");
3281     
3282     boolean rebuildDuo = true;
3283     
3284     if (duoBuildToDir.exists()) {
3285       System.out.print("Grouper duo has been built in the past, do you want it rebuilt? (t|f) [t]: ");
3286       rebuildDuo = readFromStdInBoolean(true, "grouperInstaller.autorun.rebuildDuoAfterHavingBeenBuilt");
3287     }
3288     
3289     if (!rebuildDuo) {
3290       return;
3291     }
3292 
3293     List<String> commands = new ArrayList<String>();
3294     
3295     addAntCommands(commands);
3296     
3297     System.out.println("\n##################################");
3298     System.out.println("Building duo with command:\n" + duoDir.getAbsolutePath() + "> " 
3299         + convertCommandsIntoCommand(commands) + "\n");
3300     
3301     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
3302         true, true, null, duoDir, null, true);
3303     
3304     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
3305       System.out.println("stderr: " + commandResult.getErrorText());
3306     }
3307     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
3308       System.out.println("stdout: " + commandResult.getOutputText());
3309     }
3310 
3311     System.out.println("\nEnd building duo");
3312     System.out.println("##################################\n");
3313     
3314   }
3315 
3316   /**
3317    * build client API
3318    * @param messagingRabbitMqDir
3319    */
3320   private void buildMessagingRabbitmq(File messagingRabbitMqDir) {
3321     if (!messagingRabbitMqDir.exists() || messagingRabbitMqDir.isFile()) {
3322       throw new RuntimeException("Cant find messaging rabbitmq: " + messagingRabbitMqDir.getAbsolutePath());
3323     }
3324     
3325     File messaginRabbitmqBuildToDir = new File(messagingRabbitMqDir.getAbsoluteFile() + File.separator + "dist" + File.separator + "bin");
3326     
3327     boolean rebuildMessagingRabbitmq = true;
3328     
3329     if (messaginRabbitmqBuildToDir.exists()) {
3330       System.out.print("Grouper messaging rabbitmq has been built in the past, do you want it rebuilt? (t|f) [t]: ");
3331       rebuildMessagingRabbitmq = readFromStdInBoolean(true, "grouperInstaller.autorun.rebuildMessagingRabbitmqAfterHavingBeenBuilt");
3332     }
3333     
3334     if (!rebuildMessagingRabbitmq) {
3335       return;
3336     }
3337 
3338     List<String> commands = new ArrayList<String>();
3339     
3340     addAntCommands(commands);
3341     
3342     System.out.println("\n##################################");
3343     System.out.println("Building messaging rabbitmq with command:\n" + messagingRabbitMqDir.getAbsolutePath() + "> " 
3344         + convertCommandsIntoCommand(commands) + "\n");
3345     
3346     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
3347         true, true, null, messagingRabbitMqDir, null, true);
3348     
3349     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
3350       System.out.println("stderr: " + commandResult.getErrorText());
3351     }
3352     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
3353       System.out.println("stdout: " + commandResult.getOutputText());
3354     }
3355 
3356     System.out.println("\nEnd building messaging rabbitmq");
3357     System.out.println("##################################\n");
3358     
3359   }
3360 
3361   /**
3362    * admin
3363    */
3364   private void mainAdminLogic() {
3365     
3366     this.version = GrouperInstallerUtils.propertiesValue("grouper.version", true);
3367 
3368     GrouperInstallerAdminAction grouperInstallerAdminAction = 
3369         (GrouperInstallerAdminAction)promptForEnum(
3370             "What admin action do you want to do (manage, upgradeTask, develop)? : ",
3371             "grouperInstaller.autorun.adminAction", GrouperInstallerAdminAction.class);
3372     
3373     switch(grouperInstallerAdminAction) {
3374       case manage:
3375         mainManageLogic();
3376         break;
3377 
3378       case develop:
3379         mainDevelopLogic();
3380         break;
3381 
3382       case upgradeTask:
3383         mainUpgradeTaskLogic();
3384         break;
3385     }
3386         
3387   }
3388 
3389   /**
3390    * admin manage
3391    */
3392   private void mainManageLogic() {
3393     
3394     //####################################
3395     //Find out what directory to install to.  This ends in a file separator
3396     this.grouperInstallDirectoryString = grouperInstallDirectory();
3397 
3398     GrouperInstallerManageAction grouperInstallerManageAction = null;
3399 
3400     while (true) {
3401       grouperInstallerManageAction = 
3402           (GrouperInstallerManageAction)promptForEnum(
3403               "What do you want to manage (logs, services, back, exit)? : ",
3404               "grouperInstaller.autorun.manageAction", GrouperInstallerManageAction.class);
3405 
3406       switch(grouperInstallerManageAction) {
3407         case logs:
3408       
3409           adminManageLogs();
3410   
3411           break;
3412         case services:
3413 
3414           adminManageServices();
3415           
3416           break;
3417         case exit:
3418           
3419           System.exit(0);
3420           
3421           break;
3422         case back:
3423           
3424           this.mainAdminLogic();
3425   
3426           break;
3427       }
3428       
3429       System.out.print("Press <enter> to continue or type 'exit' to end: ");
3430       String result = readFromStdIn("grouperInstaller.autorun.manageContinue");
3431       if (GrouperInstallerUtils.equalsIgnoreCase(result, "exit")) {
3432         System.exit(0);
3433       }
3434       //add some space
3435       System.out.println("");
3436     }
3437   }
3438 
3439   /**
3440    * admin manage
3441    */
3442   private void mainDevelopLogic() {
3443     
3444     GrouperInstallerDevelopAction grouperInstallerDevelopAction = null;
3445 
3446     while (true) {
3447       grouperInstallerDevelopAction = 
3448           (GrouperInstallerDevelopAction)promptForEnum(
3449               "What do you want to develop (translate, back, exit)? : ",
3450               "grouperInstaller.autorun.developAction", GrouperInstallerDevelopAction.class);
3451 
3452       switch(grouperInstallerDevelopAction) {
3453         case translate:
3454 
3455           adminTranslate();
3456 
3457           break;
3458         case exit:
3459 
3460           System.exit(0);
3461 
3462           break;
3463         case back:
3464           
3465           this.mainAdminLogic();
3466   
3467           break;
3468       }
3469       
3470       System.out.print("Press <enter> to continue or type 'exit' to end: ");
3471       String result = readFromStdIn("grouperInstaller.autorun.developContinue");
3472       if (GrouperInstallerUtils.equalsIgnoreCase(result, "exit")) {
3473         System.exit(0);
3474       }
3475       //add some space
3476       System.out.println("");
3477     }
3478   }
3479 
3480   /**
3481    * try 10 times to get enum
3482    * @param prompt
3483    * @param configKey
3484    * @param theClass
3485    * @return the object
3486    */
3487   public static Object promptForEnum(String prompt, String configKey, Class<?> theClass) {
3488     return promptForEnum(prompt, configKey, theClass, null, null);
3489   }
3490 
3491   /**
3492    * try 10 times to get enum
3493    * @param prompt
3494    * @param configKey
3495    * @param enumClass
3496    * @param theDefault
3497    * @param configKeyForDefault
3498    * @return the object
3499    */
3500   public static Object promptForEnum(String prompt, String configKey, Class<?> enumClass, Object theDefault, String configKeyForDefault) {
3501 
3502     //if we are using a config key
3503     if (!GrouperInstallerUtils.isBlank(configKeyForDefault)) {
3504       String defaultAction = GrouperInstallerUtils.propertiesValue(configKeyForDefault, false);
3505       if (!GrouperInstallerUtils.isBlank(defaultAction)) {
3506         theDefault = GrouperInstallerUtils.callMethod(enumClass, null, "valueOfIgnoreCase",
3507             new Class<?>[]{String.class, boolean.class, boolean.class}, new Object[]{defaultAction, true, true});
3508       }
3509       defaultAction = GrouperInstallerUtils.defaultIfBlank(defaultAction, "install");
3510     }
3511     if (theDefault != null) {
3512       prompt += "[" + ((Enum<?>)theDefault).name() + "]: ";
3513     }
3514     
3515     for (int i=0;i<10;i++) {
3516       System.out.print(prompt);
3517       String input = readFromStdIn(configKey);
3518       if (GrouperInstallerUtils.isBlank(input)) {
3519         if (theDefault != null) {
3520           return theDefault;
3521         }
3522         System.out.println("Input is required");
3523         continue;
3524       }
3525 
3526       //call a static method via reflection
3527       Object result = GrouperInstallerUtils.callMethod(enumClass, null, "valueOfIgnoreCase",
3528           new Class<?>[]{String.class, boolean.class, boolean.class}, new Object[]{input, false, false});
3529       if (result != null) {
3530         return result;
3531       } 
3532     }
3533     throw new RuntimeException("Cant find valid answer!!!!");
3534   }
3535   
3536   /**
3537    * 
3538    */
3539   private void adminManageServices() {
3540     
3541     //see what we are upgrading: api, ui, ws, client
3542     GrouperInstallerAdminManageService grouperInstallerAdminManageService = 
3543         (GrouperInstallerAdminManageService)promptForEnum(
3544             "What service do you want to manage?  database, tomcat, grouperDaemon? : ",
3545             "grouperInstaller.autorun.serviceToManage", GrouperInstallerAdminManageService.class);
3546 
3547     GrouperInstallerAdminManageServiceAction grouperInstallerAdminManageServiceAction = 
3548         (GrouperInstallerAdminManageServiceAction)promptForEnum(
3549             "What " + grouperInstallerAdminManageService + " action do you want to perform?  stop, start, restart, status? : ",
3550             "grouperInstaller.autorun.serviceToManageAction", GrouperInstallerAdminManageServiceAction.class);
3551 
3552     switch (grouperInstallerAdminManageService) {
3553       case grouperDaemon:
3554         adminManageGrouperDaemon(grouperInstallerAdminManageServiceAction);
3555         
3556         break;
3557       case database:
3558 
3559         adminManageDatabase(grouperInstallerAdminManageServiceAction);
3560         break;
3561       case tomcat:
3562 
3563         adminManageTomcat(grouperInstallerAdminManageServiceAction);
3564         
3565 
3566         break;
3567 
3568     }
3569     
3570   }
3571 
3572   /**
3573    * translate a ui text file
3574    */
3575   private void adminTranslate() {
3576 
3577     System.out.println("What is the location of the grouper.text.en.us.base.properties file: ");
3578     String grouperTextEnUsBasePropertiesName = readFromStdIn("grouperInstaller.autorun.translate.from");
3579 
3580     if (GrouperInstallerUtils.isBlank(grouperTextEnUsBasePropertiesName)) {
3581       System.out.println("The location of the grouper.text.en.us.base.properties file is required!");
3582       System.exit(1);
3583     }
3584 
3585     File grouperTextEnUsBasePropertiesFile = new File(grouperTextEnUsBasePropertiesName);
3586 
3587     if (grouperTextEnUsBasePropertiesFile.isDirectory()) {
3588       grouperTextEnUsBasePropertiesName = GrouperInstallerUtils.stripLastSlashIfExists(grouperTextEnUsBasePropertiesName);
3589       grouperTextEnUsBasePropertiesName = grouperTextEnUsBasePropertiesName + File.separator + "grouper.text.en.us.base.properties";
3590       grouperTextEnUsBasePropertiesFile = new File(grouperTextEnUsBasePropertiesName);
3591     }
3592 
3593     if (!grouperTextEnUsBasePropertiesFile.isFile() || !grouperTextEnUsBasePropertiesFile.exists()) {
3594       System.out.println("The grouper.text.en.us.base.properties file is not found! " + grouperTextEnUsBasePropertiesFile.getAbsolutePath());
3595       System.exit(1);
3596     }
3597     
3598     System.out.println("What is the location of the translated file: ");
3599     String grouperTranslatedBasePropertiesName = readFromStdIn("grouperInstaller.autorun.translate.to");
3600 
3601     if (GrouperInstallerUtils.isBlank(grouperTextEnUsBasePropertiesName)) {
3602       System.out.println("The location of the translated file is required!");
3603       System.exit(0);
3604     }
3605 
3606     File grouperTranslatedBasePropertiesFile = new File(grouperTranslatedBasePropertiesName);
3607 
3608     if (!grouperTranslatedBasePropertiesFile.isFile() || !grouperTranslatedBasePropertiesFile.exists()) {
3609       System.out.println("The translated file is not found! " + grouperTextEnUsBasePropertiesFile.getAbsolutePath());
3610       System.exit(0);
3611     }
3612     
3613     //backup the existing file
3614     File grouperTranslatedBasePropertiesFileBak = new File(GrouperInstallerUtils.prefixOrSuffix(
3615         grouperTranslatedBasePropertiesFile.getAbsolutePath(), ".properties", true) + "." 
3616         + new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS").format(new Date()) + ".properties");
3617     
3618     // GrouperInstallerUtils.newlineFromFile(GrouperInstallerUtils.readFileIntoString(grouperTranslatedBasePropertiesFile));
3619     String newline = "\n";
3620     
3621     GrouperInstallerUtils.copyFile(grouperTranslatedBasePropertiesFile, grouperTranslatedBasePropertiesFileBak);
3622     System.out.println("The translated file was backed up to: " + grouperTranslatedBasePropertiesFileBak.getAbsolutePath());
3623     
3624     System.out.print("Do you want to edit this file inline (if not will just run a report) (t|f) [t]: ");
3625     boolean editInline = readFromStdInBoolean(true, "grouperInstaller.translate.editInline");
3626    
3627     StringBuilder output = new StringBuilder();
3628     
3629     String grouperTextEnUsBasePropertiesContents = GrouperInstallerUtils.readFileIntoString(grouperTextEnUsBasePropertiesFile);
3630     String grouperTranslatedBasePropertiesContents = GrouperInstallerUtils.readFileIntoString(grouperTranslatedBasePropertiesFile);
3631 
3632     //go through the original properties line by line
3633     String[] grouperTextEnUsBasePropertiesLines = GrouperInstallerUtils.splitLines(grouperTextEnUsBasePropertiesContents);
3634     String[] grouperTranslatedBasePropertiesLines = GrouperInstallerUtils.splitLines(grouperTranslatedBasePropertiesContents);
3635     Properties existingTranslatedLinesByKey = new Properties();
3636 
3637     //make raw properties
3638     for (String grouperTranslatedBasePropertiesLine : grouperTranslatedBasePropertiesLines) {
3639       int equalsIndex = grouperTranslatedBasePropertiesLine.indexOf('=');
3640       if (equalsIndex != -1) {
3641         String propertyName = GrouperInstallerUtils.prefixOrSuffix(grouperTranslatedBasePropertiesLine, "=", true).trim();
3642         String propertyValue = GrouperInstallerUtils.prefixOrSuffix(grouperTranslatedBasePropertiesLine, "=", false).trim();
3643         if (!GrouperInstallerUtils.isBlank(propertyValue)) {
3644           existingTranslatedLinesByKey.put(propertyName, grouperTranslatedBasePropertiesLine);
3645         }
3646       }
3647     }
3648 
3649     StringBuilder propertyAndComments = new StringBuilder();
3650     int diffCount = 0;
3651 
3652     int lineCount = 1;
3653     
3654     for (String grouperTextEnUsBasePropertiesLine: grouperTextEnUsBasePropertiesLines) {
3655       
3656       Map<String, Object> debugMap = new LinkedHashMap<String, Object>();
3657       
3658       grouperTextEnUsBasePropertiesLine = grouperTextEnUsBasePropertiesLine.trim();
3659       
3660       boolean isBlank = GrouperInstallerUtils.isBlank(grouperTextEnUsBasePropertiesLine);
3661       boolean isComment = grouperTextEnUsBasePropertiesLine.trim().startsWith("#");
3662       boolean isProperty = !isBlank && !isComment && grouperTextEnUsBasePropertiesLine.contains("=");
3663       
3664       if (!isBlank && !isComment && !isProperty) {
3665         System.out.print("Line " + lineCount + " is not a blank, comment, or property, hit <enter> to continue");
3666         readFromStdIn("grouperInstaller.autorun.translateIssueContinue");
3667       }
3668       
3669       debugMap.put("isBlank", isBlank);
3670       debugMap.put("isComment", isComment);
3671       debugMap.put("isProperty", isProperty);
3672       
3673       propertyAndComments.append(newline).append(grouperTextEnUsBasePropertiesLine);
3674 
3675       if (!isProperty) {
3676         output.append(grouperTextEnUsBasePropertiesLine).append(newline);
3677         debugMap.put("clearPropertyAndComments", false);
3678       } else {
3679         int equalsIndex = grouperTextEnUsBasePropertiesLine.indexOf('=');
3680         if (equalsIndex == -1) {
3681           //shouldnt happen
3682           throw new RuntimeException("Coding error: " + grouperTextEnUsBasePropertiesLine);
3683         }
3684         
3685         String propertyName = grouperTextEnUsBasePropertiesLine.substring(0, equalsIndex).trim();
3686 
3687         debugMap.put("propertyName", propertyName);
3688 
3689         String translatedPropertyLine = existingTranslatedLinesByKey.getProperty(propertyName);
3690         
3691         debugMap.put("hasTranslation", !GrouperInstallerUtils.isBlank(translatedPropertyLine));
3692 
3693         // see if there is already a translation
3694         if (!GrouperInstallerUtils.isBlank(translatedPropertyLine)) {
3695  
3696           //just append everything to the new file
3697           output.append(translatedPropertyLine).append(newline);
3698           
3699         } else {
3700           diffCount++;
3701 
3702           //there is no translation
3703           if (!editInline) {
3704             System.out.println(diffCount + ": Translate line " + lineCount + ":");
3705           }
3706 
3707           System.out.println("");
3708           System.out.println(propertyAndComments.toString().trim() + newline);
3709           
3710           //there is no translation
3711           if (editInline) {
3712             System.out.print("\n" + diffCount + ": Enter a translation for line " + lineCount + ":");
3713             String translatedValue = readFromStdIn("autorun.translate.value");
3714             
3715             output.append(propertyName).append("=").append(translatedValue).append(newline);
3716 
3717           } else {
3718             
3719             output.append(propertyName).append("=").append(newline);
3720             
3721           }
3722           
3723         }
3724         debugMap.put("clearPropertyAndComments", true);
3725         propertyAndComments = new StringBuilder();
3726         
3727       }
3728       
3729       if (GrouperInstallerUtils.propertiesValueBoolean("printDebugInfo", false, false)) {
3730         System.out.println(GrouperInstallerUtils.mapToString(debugMap));
3731       }
3732       
3733       lineCount++;
3734     }
3735     GrouperInstallerUtils.saveStringIntoFile(grouperTranslatedBasePropertiesFile, output.toString(), true, true);
3736     
3737     if (diffCount == 0) {
3738       System.out.println("The translated file is complete");
3739     } else {
3740       if (!editInline) {
3741         System.out.println("You have " + diffCount + " missing properties, they need translation.");
3742       } else {
3743         System.out.println("You translated " + diffCount + " missing properties.");
3744       }
3745     }
3746     System.exit(0);
3747   }
3748 
3749   /**
3750    * 
3751    * @param grouperInstallerAdminManageServiceAction
3752    */
3753   private void adminManageTomcat(
3754       GrouperInstallerAdminManageServiceAction grouperInstallerAdminManageServiceAction) {
3755     //tomcat dir
3756     File catalinaServerXmlFile = new File(this.grouperInstallDirectoryString + "conf" + File.separator + "server.xml");
3757     if (!catalinaServerXmlFile.exists()) {
3758       //if used the webapps dir
3759       catalinaServerXmlFile = new File(this.grouperInstallDirectoryString + ".." + File.separator + ".." + File.separator + "conf" + File.separator + "server.xml");
3760     }
3761     //normal installer dir
3762     if (!catalinaServerXmlFile.exists()) {
3763       catalinaServerXmlFile = new File(this.grouperInstallDirectoryString + File.separator 
3764           + "apache-tomcat-" + this.tomcatVersion() + "" + File.separator + "conf" + File.separator + "server.xml");
3765     }
3766 
3767     this.untarredTomcatDir = catalinaServerXmlFile.getParentFile().getParentFile();       
3768 
3769     //  /Server/Service/Connector <Connector port="8080" protocol="HTTP/1.1" 
3770     this.tomcatHttpPort = GrouperInstallerUtils.xpathEvaluateAttributeInt(catalinaServerXmlFile, "/Server/Service/Connector[@protocol='HTTP/1.1']", "port", -1);
3771 
3772     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]: ");
3773     this.defaultIpAddress = readFromStdIn("grouperInstaller.autorun.defaultIpAddressForPorts");
3774     
3775     if (GrouperInstallerUtils.isBlank(this.defaultIpAddress)) {
3776       this.defaultIpAddress = "0.0.0.0";
3777     }
3778 
3779     switch (grouperInstallerAdminManageServiceAction) {
3780       case stop:
3781       case start:
3782       case restart:
3783         
3784         tomcatBounce(grouperInstallerAdminManageServiceAction.name().toString());
3785         break;
3786       case status:
3787         
3788         if (!GrouperInstallerUtils.portAvailable(this.tomcatHttpPort, this.defaultIpAddress)) {
3789           System.out.println("Tomcat is running.  It is detected to be listening on port: " + this.tomcatHttpPort);
3790         } else {
3791           System.out.println("Tomcat is stopped.  It is not detected to be listening on port: " + this.tomcatHttpPort);
3792         }
3793         break;
3794     }
3795   }
3796 
3797   /**
3798    * 
3799    * @param grouperInstallerAdminManageServiceAction
3800    */
3801   private void adminManageDatabase(
3802       GrouperInstallerAdminManageServiceAction grouperInstallerAdminManageServiceAction) {
3803     List<File> grouperHibernatePropertiesFiles = GrouperInstallerUtils.fileListRecursive(new File(this.grouperInstallDirectoryString), "grouper.hibernate.properties");
3804     
3805     if (GrouperInstallerUtils.length(grouperHibernatePropertiesFiles) == 0) {
3806       System.out.println("Cant find a grouper.hibernate.properties in the install directory: " + this.grouperInstallDirectoryString);
3807     }
3808 
3809     //lets see which one
3810     File grouperHibernatePropertiesFileLocal = null;
3811     String url = null;
3812     
3813     for (File file : grouperHibernatePropertiesFiles) {
3814       Properties grouperHibernateProperties = GrouperInstallerUtils.propertiesFromFile(file);
3815       String urlFromFile = grouperHibernateProperties.getProperty("hibernate.connection.url");
3816 
3817       if (url == null) {
3818         grouperHibernatePropertiesFileLocal = file;
3819         url = urlFromFile;
3820       }
3821       if (!GrouperInstallerUtils.equals(url, urlFromFile)) {
3822         System.out.println("You have " + grouperHibernatePropertiesFiles.size() 
3823           + " grouper.hibernate.properties files in the install directory "
3824           + this.grouperInstallDirectoryString + " with different urls: " + url + ", " + urlFromFile
3825           + ", sync up your config files or specify an install directory that has one grouper.hibernate.properties"); 
3826         for (File current : grouperHibernatePropertiesFiles) {
3827           System.out.println("\n  " + current.getAbsolutePath());
3828         }
3829       }
3830     }
3831     
3832     Properties grouperHibernateProperties = GrouperInstallerUtils.propertiesFromFile(grouperHibernatePropertiesFileLocal);
3833 
3834     this.dbUrl = url;
3835     this.dbUser = GrouperInstallerUtils.defaultString(grouperHibernateProperties.getProperty("hibernate.connection.username"));
3836     this.dbPass = GrouperInstallerUtils.defaultString(grouperHibernateProperties.getProperty("hibernate.connection.password"));
3837     this.giDbUtils = new GiDbUtils(this.dbUrl, this.dbUser, this.dbPass);
3838     this.giDbUtils.registerDriverOnce(this.grouperInstallDirectoryString);
3839     
3840     System.out.println("grouper.hibernate.properties read from: " + grouperHibernatePropertiesFileLocal.getAbsolutePath());
3841     System.out.println("Database URL (hibernate.connection.url from grouper.hibernate.properties) is: " + this.dbUrl);
3842     
3843     if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.status) {
3844       System.out.println("Trying query: " + this.giDbUtils.checkConnectionQuery());
3845       //check connection to database
3846       Exception exception = this.giDbUtils.checkConnection();
3847       if (exception == null) {
3848         System.out.println("Database is up and connection from Java successful.");
3849       } else {
3850         System.out.print("Database could not be connected to from Java.  Perhaps it is down or there is a network problem?\n"
3851             + "  Do you want to see the stacktrace from the connection error? (t|f) [f]: ");
3852         boolean showStack = readFromStdInBoolean(false, "grouperInstaller.autorun.printStackFromDbConnectionError");
3853         if (showStack) {
3854           exception.printStackTrace();
3855         }
3856       }
3857     } else {          
3858       if (this.dbUrl.contains(":hsqldb:")) {
3859 
3860         this.untarredApiDir = grouperHibernatePropertiesFileLocal;
3861         //find the untarred API dir
3862         int MAX_TRIES = 6;
3863         for (int i=0;i<MAX_TRIES;i++) {
3864           File tryFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "grouper.apiBinary-" + this.version);
3865           if (tryFile.exists()) {
3866             this.untarredApiDir = tryFile;
3867             break;
3868           }
3869           this.untarredApiDir = this.untarredApiDir.getParentFile();
3870           if (i==MAX_TRIES-1) {
3871             System.out.print("Normally the database is started by the installer from the unzipped API directory.  \n"
3872                 + "Based on your inputted install directory, the API directory cannot be found.\n"
3873                 + "HSQL cannot be accessed, maybe try again with a different install directory");
3874             System.exit(1);
3875           }
3876         }
3877         
3878         if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.stop 
3879             || grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
3880           this.shutdownHsql();
3881         }
3882 
3883         if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
3884           GrouperInstallerUtils.sleep(3000);
3885         }
3886 
3887         if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.start 
3888             || grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
3889           this.startHsqlDb(false);
3890 
3891           GrouperInstallerUtils.sleep(3000);
3892 
3893           //check connection to database
3894           if (null == this.giDbUtils.checkConnection()) {
3895             System.out.println("Success: database is up, connection successful.");
3896           } else {
3897             System.out.println("ERROR: database is down... could not start");
3898           }
3899         } else {
3900           //check connection to database
3901           if (null == this.giDbUtils.checkConnection()) {
3902             System.out.println("ERROR: database is up... could not stop.");
3903           } else {
3904             System.out.println("Success: database is down.");
3905           }
3906           
3907         }
3908  
3909       } else {
3910         
3911         System.out.println("Error: you are using an external database, (URL above), you need to " + grouperInstallerAdminManageServiceAction + " that database yourself");
3912         
3913       }
3914 
3915     }
3916   }
3917 
3918   /**
3919    * 
3920    * @param grouperInstallerAdminManageServiceAction
3921    */
3922   private void adminManageGrouperDaemon(
3923       GrouperInstallerAdminManageServiceAction grouperInstallerAdminManageServiceAction) {
3924     boolean done = false;
3925     if (!GrouperInstallerUtils.isWindows()) {
3926       
3927       System.out.println("In unix you should have a /etc/init.d or launchctl script which manages the grouper daemon (see details on wiki).");
3928       System.out.print("If you have a service configured please enter name or <enter> to continue without a service: ");
3929       String daemonName = readFromStdIn("grouperInstaller.autorun.grouperDaemonNameOrContinue");
3930       if (!GrouperInstallerUtils.isBlank(daemonName)) {
3931         done = true;
3932         boolean isService = true;
3933         String command = "/sbin/service";
3934         if (!new File(command).exists()) {
3935           command = "/usr/sbin/service";
3936         }
3937         if (!new File(command).exists()) {
3938           command = "/bin/launchctl";
3939           isService = false;
3940         }
3941         if (!new File(command).exists()) {
3942           System.out.println("Cannot find servie command, looked for /sbin/service, /usr/sbin/service, and /bin/launchctl.  "
3943               + "Your version of unix services is not supported.  Contact the Grouper support team.");
3944           System.exit(1);
3945         }
3946         if (isService) {
3947           List<String> commands = new ArrayList<String>();
3948           commands.add(command);
3949           commands.add(daemonName);
3950           commands.add(grouperInstallerAdminManageServiceAction.name());
3951           
3952           System.out.println(grouperInstallerAdminManageServiceAction + " " + daemonName
3953               + " with command: " + convertCommandsIntoCommand(commands) + "\n");
3954 
3955           GrouperInstallerUtils.execCommand(
3956               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
3957               new File(this.grouperInstallDirectoryString), null, false, false, true);
3958         } else {
3959           // <pid> <status> mytask
3960           if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.status) {
3961             // launchctl list | grep mytask
3962             List<String> commandsToRun = new ArrayList<String>();
3963             commandsToRun.add(shCommand());
3964             commandsToRun.add("-c");
3965             commandsToRun.add(command + " list | " + grepCommand() + " " + daemonName);
3966             
3967             System.out.println(grouperInstallerAdminManageServiceAction + " " + daemonName
3968                 + " with command: " + convertCommandsIntoCommand(commandsToRun) + "\n");
3969 
3970             GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commandsToRun, String.class), true, true, null, 
3971                 new File(this.grouperInstallDirectoryString), null, false, false, true);
3972             
3973           } else {
3974             // launchctl start|stop mytask
3975             if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.stop 
3976                 || grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
3977               //stop daemon
3978               List<String> commands = new ArrayList<String>();
3979               commands.add(command);
3980               commands.add("stop");
3981               commands.add(daemonName);
3982               
3983               System.out.println("stopping " + daemonName
3984                   + " with command: " + convertCommandsIntoCommand(commands) + "\n");
3985 
3986               GrouperInstallerUtils.execCommand(
3987                   GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
3988                   new File(this.grouperInstallDirectoryString), null, false, false, true);
3989               
3990             }
3991  
3992             if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
3993               GrouperInstallerUtils.sleep(3000);
3994             }
3995  
3996             if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.start 
3997                 || grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
3998               //start daemon
3999               List<String> commands = new ArrayList<String>();
4000               commands.add(command);
4001               commands.add("start");
4002               commands.add(daemonName);
4003               
4004               System.out.println("starting " + daemonName
4005                   + " with command: " + convertCommandsIntoCommand(commands) + "\n");
4006 
4007               GrouperInstallerUtils.execCommand(
4008                   GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
4009                   new File(this.grouperInstallDirectoryString), null, false, false, true);
4010               
4011               GrouperInstallerUtils.sleep(5000);
4012             }
4013           }              
4014         }
4015       }
4016     }
4017 
4018     if (!done) {
4019       
4020       if (new File(this.grouperInstallDirectoryString + "grouper.apiBinary-" + this.version).exists()) {
4021         this.untarredApiDir = new File(this.grouperInstallDirectoryString + "grouper.apiBinary-" + this.version);
4022       }
4023       
4024       //this is for loader dir
4025       if (new File(this.grouperInstallDirectoryString + "WEB-INF").exists()) {
4026         this.upgradeExistingApplicationDirectoryString = this.grouperInstallDirectoryString;
4027       } else if (new File(this.grouperInstallDirectoryString + "bin").exists()) {
4028         this.upgradeExistingApplicationDirectoryString = this.grouperInstallDirectoryString;
4029       }
4030       
4031       String gshCommandLocal = gshCommand();
4032       if (gshCommandLocal.endsWith(".sh")) {
4033         gshCommandLocal = gshCommandLocal.substring(0, gshCommandLocal.length()-".sh".length());
4034       }
4035       if (gshCommandLocal.endsWith(".bat")) {
4036         gshCommandLocal = gshCommandLocal.substring(0, gshCommandLocal.length()-".bat".length());
4037       }
4038       
4039       if (!GrouperInstallerUtils.isWindows()) {
4040         if (gshCommandLocal.contains(" ")) {
4041           System.out.println("On unix the gsh command cannot contain whitespace!");
4042           System.exit(1);
4043         }
4044       }
4045       
4046       // ps -ef | grep -- -loader | grep -v grep
4047       List<String> psCommands = new ArrayList<String>();
4048       psCommands.add(shCommand());
4049       psCommands.add("-c");
4050       psCommands.add( psCommand() + " -ef | " + grepCommand() + " " + gshCommandLocal + " | " 
4051           + grepCommand() + " -- -loader | " + grepCommand() + " -v grep");
4052 
4053       //unix
4054       //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
4055       //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
4056       
4057       //mac
4058       //0     1     0   0 Sun06PM ??         1:15.38 /sbin/launchd
4059       //0    45     1   0 Sun06PM ??         0:06.80 /usr/sbin/syslogd
4060       
4061       Pattern pidPattern = Pattern.compile("^[^\\s]+\\s+([^\\s]+)\\s+.*$");
4062       
4063       if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.stop 
4064           || grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
4065 
4066         if (GrouperInstallerUtils.isWindows()) {
4067           System.out.print("In windows you need to find the java process in task manager and kill it, press <enter> to continue... ");
4068           readFromStdIn("grouperInstaller.autorun.enterToContinueWindowsCantKillProcess");
4069         } else {
4070 
4071           System.out.println("Stopping the grouper daemon is not an exact science, be careful!");
4072           System.out.println("This script will find the process id of the daemon and kill it.  Make it is correct!");
4073           System.out.println("Finding the grouper daemon process with: " + convertCommandsIntoCommand(psCommands));
4074 
4075           //stop daemon
4076           CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(psCommands, String.class), false, true, null, 
4077              new File(this.grouperInstallDirectoryString), null, false, false, true);
4078           if (commandResult.getExitCode() != 0 && !GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
4079             throw new RuntimeException("Could not execute: " + convertCommandsIntoCommand(psCommands) 
4080                + "\n" + commandResult.getErrorText()
4081                + "\n" + commandResult.getOutputText());
4082           }
4083           String outputText = GrouperInstallerUtils.defaultString(commandResult.getOutputText());
4084           if (GrouperInstallerUtils.isBlank(outputText)) {
4085             System.out.println("Cannot find the grouper daemon process, it is not running");
4086           } else {
4087             outputText = outputText.replace('\r', '\n');
4088             outputText = GrouperInstallerUtils.replace(outputText, "\r", "\n");
4089             List<String> lines = GrouperInstallerUtils.splitTrimToList(outputText, "\n");
4090             int MAX_LINES = 2;
4091             if (lines.size() > MAX_LINES) {
4092               System.out.println("Found more output than expected, please examine the services on your system and kill them manually");
4093               for (String line : lines) {
4094                 System.out.println(line);
4095               }
4096             } else {
4097               Set<String> pidsDone = new HashSet<String>();
4098               for (int i=0; i<MAX_LINES; i++) {
4099                 // ^[^\s]+\s+([^\s]+)\s+.*$
4100                 // start, then first thing, then spaces, then second thing is the pic, then spaces, then whatever and end string
4101                 Matcher matcher = pidPattern.matcher(lines.get(0));
4102                 if (matcher.matches()) {
4103                   String pid = matcher.group(1);
4104                   if (pidsDone.contains(pid)) {
4105                     System.out.println("Could not kill pid " + pid);
4106                     System.exit(1);
4107                   }
4108                   List<String> killCommandList = GrouperInstallerUtils.splitTrimToList(killCommand() + " -KILL " + pid, " ");
4109                   System.out.println("The command to kill the daemon is: " + convertCommandsIntoCommand(killCommandList));
4110                   System.out.print("Found pid " + pid + ", do you want this script to kill it? (t|f) [t]: ");
4111                   boolean killDaemon = readFromStdInBoolean(true, "grouperInstaller.autorun.killPidOfDaemon");
4112                   
4113                   if (killDaemon) {
4114 
4115                     //keep track that we tried this one
4116                     pidsDone.add(pid);
4117                     
4118                     commandResult = GrouperInstallerUtils.execCommand(
4119                         GrouperInstallerUtils.toArray(killCommandList, String.class), true, true, null, 
4120                        null, null, true, false, true);
4121                     
4122                     GrouperInstallerUtils.sleep(5000);
4123 
4124                     //get next line, hopefully first one isnt there anymore, maybe not second either...
4125                     commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(psCommands, String.class), false, true, null, 
4126                        null, null, false, false, true);
4127 
4128                     if (commandResult.getExitCode() != 0 && !GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
4129                       throw new RuntimeException("Could not execute: " + convertCommandsIntoCommand(psCommands) 
4130                          + "\n" + commandResult.getErrorText()
4131                          + "\n" + commandResult.getOutputText());
4132                     }
4133                     
4134                     outputText = GrouperInstallerUtils.defaultString(commandResult.getOutputText());
4135                     if (GrouperInstallerUtils.isBlank(outputText)) {
4136                       break;
4137                     }
4138                     
4139                     outputText = outputText.replace('\r', '\n');
4140                     outputText = GrouperInstallerUtils.replace(outputText, "\r", "\n");
4141                     lines = GrouperInstallerUtils.splitTrimToList(outputText, "\n");
4142                     
4143                   } else {
4144                     break;
4145                   }
4146                 }
4147               }
4148             }
4149           }
4150         }
4151       }
4152 
4153       if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
4154         GrouperInstallerUtils.sleep(3000);
4155       }
4156 
4157       if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.start 
4158           || grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.restart) {
4159         //start
4160         startLoader(false);
4161         GrouperInstallerUtils.sleep(5000);
4162       }
4163 
4164       if (grouperInstallerAdminManageServiceAction == GrouperInstallerAdminManageServiceAction.status && GrouperInstallerUtils.isWindows()) {
4165         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).");
4166       } else {
4167                    
4168         //no matter what, status, or other, do a status
4169         if (!GrouperInstallerUtils.isWindows()) {
4170           //stop daemon
4171           System.out.println("Finding the grouper daemon process with: " + convertCommandsIntoCommand(psCommands));
4172           CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(psCommands, String.class), false, true, null, 
4173              null, null, false, false, true);
4174           if (commandResult.getExitCode() != 0 && !GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
4175             throw new RuntimeException("Could not execute: " + convertCommandsIntoCommand(psCommands) 
4176                + "\n" + commandResult.getErrorText()
4177                + "\n" + commandResult.getOutputText());
4178           }
4179           String outputText = GrouperInstallerUtils.defaultString(commandResult.getOutputText());
4180           if (GrouperInstallerUtils.isBlank(outputText)) {
4181             System.out.println("Cannot find the grouper daemon process, it is not running");
4182           } else {
4183             outputText = outputText.replace('\r', '\n');
4184             outputText = GrouperInstallerUtils.replace(outputText, "\r", "\n");
4185             List<String> lines = GrouperInstallerUtils.splitTrimToList(outputText, "\n");
4186             System.out.println("Grouper loader is running, here is the process output:");
4187             for (String line : lines) {
4188               System.out.println(line);
4189             }
4190           }
4191         }
4192       }
4193     }
4194   }
4195 
4196   /**
4197    * admin manage logs
4198    */
4199   private void adminManageLogs() {
4200     //see what we are upgrading: api, ui, ws, client
4201     this.appToUpgrade = grouperAppToUpgradeOrPatch("look at logs for");
4202 
4203     System.out.println("Find where the application is running, then find the log4j.properties in the classpath.");
4204     
4205     switch (this.appToUpgrade) {
4206       case PSP:
4207       case PSPNG:
4208         System.out.println("This runs in the API, so logging for the API will be examined.");
4209         //pass through to API
4210       case API:
4211         System.out.println("The API (generally invoked via GSH) logs to where the log4.properties specifies.");
4212         File log4jPropertiesFile = new File(this.grouperInstallDirectoryString + "grouper.apiBinary-" + this.version + File.separator 
4213             + "conf" + File.separator + "log4j.properties");
4214         
4215         if (!log4jPropertiesFile.exists()) {
4216           
4217           List<File> allFiles = GrouperInstallerUtils.fileListRecursive(new File(this.grouperInstallDirectoryString));
4218           log4jPropertiesFile = null;
4219           boolean multipleFound = false;
4220           for (File file : allFiles) {
4221             if ("log4j.properties".equals(file.getName())) {
4222               if (log4jPropertiesFile != null) {
4223                 multipleFound = true;
4224                 log4jPropertiesFile = null;
4225                 break;
4226               }
4227               log4jPropertiesFile = file;
4228             }
4229           }
4230           if (multipleFound || log4jPropertiesFile == null) {
4231             System.out.print("What is the absolute path of the log4j.properties? : ");
4232             String log4jPropertiesLocation = readFromStdIn("grouperInstaller.autorun.log4jPropertiesLocation");
4233             log4jPropertiesFile = new File(log4jPropertiesLocation);
4234             if (!log4jPropertiesFile.exists()) {
4235               System.out.println("Bad location: " + log4jPropertiesFile.getAbsolutePath());
4236               System.exit(1);
4237             }
4238           }
4239         }
4240         
4241         File logFile = new File(this.grouperInstallDirectoryString  
4242             + "logs" + File.separator + "grouper_error.log");
4243         String grouperHomeWithSlash = this.grouperInstallDirectoryString;
4244 
4245         if (!logFile.exists()) {
4246           logFile = new File(this.grouperInstallDirectoryString + "grouper.apiBinary-" + this.version + File.separator 
4247               + "logs" + File.separator + "grouper_error.log");
4248           grouperHomeWithSlash = this.grouperInstallDirectoryString + "grouper.apiBinary-" + this.version + File.separator;
4249         }              
4250         System.out.println("By default the installer configures the log file to be: " + logFile.getAbsolutePath());
4251         
4252         
4253         analyzeLogFile(log4jPropertiesFile, grouperHomeWithSlash, null, null);
4254         break;
4255       case CLIENT:
4256         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.");
4257         break;
4258       case WS:
4259       case UI:
4260         File catalinaLogFile = new File(this.grouperInstallDirectoryString + "logs");
4261         if (!catalinaLogFile.exists()) {
4262           //if used the webapps dir
4263           catalinaLogFile = new File(this.grouperInstallDirectoryString + ".." + File.separator + ".." + File.separator + "logs");
4264         }
4265         if (!catalinaLogFile.exists()) {
4266           catalinaLogFile = new File(this.grouperInstallDirectoryString + File.separator 
4267               + "apache-tomcat-" + this.tomcatVersion() + "" + File.separator + "logs");
4268         }
4269         
4270         System.out.println("Tomcat logs STDOUT and STDERR to the catalinaErr.log "
4271             + "and catalinaOut.log logfiles, which should be here: " + catalinaLogFile.getAbsolutePath());
4272         if (!catalinaLogFile.exists()) {
4273           System.out.println("Warning: that directory does not exist, so you will need to locate the logs directory for tomcat.");
4274         }
4275         System.out.println("Locate the " + this.appToUpgrade + " application files.");
4276         System.out.println("By default the installer has the " + this.appToUpgrade + " running based on the tomcat server.xml, "
4277             + "but could also run in the webapps dir.");
4278         
4279         File serverXmlFile = new File(catalinaLogFile.getParentFile().getAbsolutePath() + File.separator + "conf" + File.separator + "server.xml");
4280         
4281         if (!serverXmlFile.exists()) {
4282           System.out.println("server.xml not found: " + serverXmlFile.getAbsolutePath());
4283         } else {
4284           //<Context docBase="/Users/mchyzer/tmp/grouperInstaller/grouper.ws-2.3.0/grouper-ws/build/dist/grouper-ws" path="/grouper-ws" reloadable="false"/>
4285           //<Context docBase="/Users/mchyzer/tmp/grouperInstaller/grouper.ui-2.3.0/dist/grouper" path="/grouper" reloadable="false"/>
4286 
4287           System.out.println("The server.xml is located: " + serverXmlFile.getAbsolutePath());
4288 
4289           String tomcatPath = this.appToUpgrade == AppToUpgrade.UI ? "grouper" : "grouper-ws";
4290 
4291           System.out.print("What is the URL starting path? [" + tomcatPath + "]: ");
4292           String newTomcatPath = readFromStdIn(this.appToUpgrade == AppToUpgrade.UI ? "grouperInstaller.autorun.urlPathForUi" : "grouperInstaller.autorun.urlPathForWs");
4293           
4294           if (!GrouperInstallerUtils.isBlank(newTomcatPath)) {
4295             tomcatPath = newTomcatPath;
4296           }
4297 
4298           if (tomcatPath.endsWith("/") || tomcatPath.endsWith("\\")) {
4299             tomcatPath = tomcatPath.substring(0, tomcatPath.length()-1);
4300           }
4301           if (tomcatPath.startsWith("/") || tomcatPath.startsWith("\\")) {
4302             tomcatPath = tomcatPath.substring(1, tomcatPath.length());
4303           }                  
4304           String currentDocBase = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
4305               "Server/Service/Engine/Host/Context[@path='/" + tomcatPath + "']", "docBase");
4306 
4307           if (this.appToUpgrade == AppToUpgrade.UI) {
4308             //<Context docBase="/Users/mchyzer/tmp/grouperInstaller/grouper.ui-2.3.0/dist/grouper" path="/grouper" reloadable="false"/>
4309             System.out.println("Looking for an entry in the server.xml that looks like this:");
4310             System.out.println("  <Context docBase=\""
4311                 + GrouperInstallerUtils.defaultIfBlank(currentDocBase, this.grouperInstallDirectoryString 
4312                     + "grouper.ui-" + this.version + File.separator + "dist" + File.separator 
4313                     + "grouper")
4314                 + "\" path=\"/" + tomcatPath + "\" reloadable=\"false\"/>");
4315 
4316           } else if (this.appToUpgrade == AppToUpgrade.WS) {
4317             //<Context docBase="/Users/mchyzer/tmp/grouperInstaller/grouper.ws-2.3.0/grouper-ws/build/dist/grouper-ws" path="/grouper-ws" reloadable="false"/>
4318             System.out.println("Looking for an entry in the server.xml that looks like this:");
4319             System.out.println("  <Context docBase=\""
4320                 + GrouperInstallerUtils.defaultIfBlank(currentDocBase, this.grouperInstallDirectoryString 
4321                     + "grouper.ws-" + this.version + File.separator + "grouper-ws" 
4322                     + File.separator + "build" + File.separator + "dist" + File.separator 
4323                     + "grouper-ws")
4324                 + "\" path=\"/" + tomcatPath + "\" reloadable=\"false\"/>");
4325           }
4326           
4327           if (!GrouperInstallerUtils.isBlank(currentDocBase)) {
4328             System.out.println("The docBase for the " + tomcatPath + " entry in the server.xml is: " + currentDocBase);
4329           } else {
4330             //check webapps
4331             System.out.println("The docBase could not be found in the server.xml, check in the tomcat" 
4332                 + File.separator + "webapps directory");
4333             currentDocBase = catalinaLogFile.getParentFile().getAbsolutePath() + File.separator + "webapps" + File.separator + tomcatPath;
4334             if (!new File(currentDocBase).exists()) {
4335               System.out.println("Cant find where grouper is linked from tomcat, looked in server.xml and the webapps directory");
4336               currentDocBase = null;
4337             }
4338           }
4339           if (currentDocBase != null) {
4340             log4jPropertiesFile = new File(currentDocBase + File.separator + "WEB-INF" + File.separator + "classes" + File.separator + "log4j.properties");
4341             
4342             analyzeLogFile(log4jPropertiesFile, "${grouper.home}" + File.separator, new File(catalinaLogFile + File.separator + "catalinaOut.log"),
4343                 new File(catalinaLogFile + File.separator + "catalinaErr.log"));
4344           }
4345         }
4346         
4347         break;
4348         
4349       default: 
4350         throw new RuntimeException("Not expecting appToUpgrade: " + this.appToUpgrade + "!");
4351     }
4352     
4353   }
4354   
4355   /**
4356    * 
4357    * @param log4jPropertiesFile
4358    * @param grouperHomeWithSlash
4359    * @param stdoutLocation
4360    * @param stderrLocation
4361    */
4362   private void analyzeLogFile(File log4jPropertiesFile, String grouperHomeWithSlash, File stdoutLocation, File stderrLocation) {
4363     System.out.println("The log4j.properties is located in: " 
4364         + log4jPropertiesFile.getAbsolutePath());
4365 
4366     if (!log4jPropertiesFile.exists()) {
4367     
4368       System.out.println("Error, the log4j.properties file could not be found.");
4369 
4370     } else {
4371       
4372       System.out.println("Examine the log4j.properties to see where it is logging");
4373       
4374       Properties log4jProperties = GrouperInstallerUtils.propertiesFromFile(log4jPropertiesFile);
4375       
4376       //= ERROR, grouper_error")
4377       String rootLoggerValue = log4jProperties.getProperty("log4j.rootLogger");              
4378       
4379       System.out.println("Generally the log4j.rootLogger property shows where logs go, it is set to: " + rootLoggerValue);
4380       
4381       Pattern pattern = Pattern.compile("\\s*[A-Z]+\\s*,\\s*(\\w+)\\s*");
4382       Matcher matcher = pattern.matcher(rootLoggerValue);
4383       if (!matcher.matches()) {
4384         System.out.println("Examine the log4j.properties for more information");
4385       } else {
4386         String logger = matcher.group(1);
4387         System.out.println("The log4j.rootLogger property in log4j.properties is set to: " + logger);
4388         //log4j.appender.grouper_error = org.apache.log4j.DailyRollingFileAppender
4389         //log4j.appender.grouper_error.File = ${grouper.home}logs/grouper_error.log
4390         String logFileName = log4jProperties.getProperty("log4j.appender." + logger + ".File");
4391         if (!GrouperInstallerUtils.isBlank(logFileName)) {
4392           System.out.println("There is a property in log4j.properties: log4j.appender." + logger + ".File = " + logFileName);
4393           if (logFileName.contains("${grouper.home}")) {
4394             logFileName = GrouperInstallerUtils.replace(logFileName, "${grouper.home}", grouperHomeWithSlash);
4395           }
4396           System.out.println("Grouper log should be: " + logFileName);
4397         } else {
4398           //log4j.appender.grouper_stdout = org.apache.log4j.ConsoleAppender
4399           String appender = log4jProperties.getProperty("log4j.appender." + logger);
4400           //log4j.appender.grouper_stderr.Target                    = System.err
4401           String target = log4jProperties.getProperty("log4j.appender." + logger + ".Target");
4402           String targetFriendly = null;
4403           if (GrouperInstallerUtils.equals(target, "System.err")) {
4404             targetFriendly = "STDERR";
4405           } else if (GrouperInstallerUtils.equals(target, "System.out")) {
4406             targetFriendly = "STDOUT";
4407           }
4408           if (GrouperInstallerUtils.equals(appender, "org.apache.log4j.ConsoleAppender") && targetFriendly != null) {
4409             System.out.println("Since log4j.properties log4j.appender." + logger + " = org.apache.log4j.ConsoleAppender you are logging to " + targetFriendly);
4410             if (GrouperInstallerUtils.equals(target, "System.err") && stderrLocation != null) {
4411               System.out.println("Grouper logs should be in " + stderrLocation.getAbsolutePath());
4412             } else if (GrouperInstallerUtils.equals(target, "System.out") && stdoutLocation != null) {
4413               System.out.println("Grouper logs should be in " + stdoutLocation.getAbsolutePath());
4414             }
4415           } else {
4416             System.out.println("Examine the log4j.properties for more information");
4417           }
4418         }
4419       }
4420     }
4421   }
4422   
4423   /**
4424    * admin
4425    */
4426   private void mainUpgradeTaskLogic() {
4427     
4428     GrouperInstallerUpgradeTaskAction grouperInstallerConvertAction = 
4429         (GrouperInstallerUpgradeTaskAction)promptForEnum(
4430             "What upgrade task do you want to do (convertEhcacheXmlToProperties, convertSourcesXmlToProperties, analyzeAndFixJars)? : ",
4431             "grouperInstaller.autorun.upgradeTaskAction", GrouperInstallerUpgradeTaskAction.class);
4432 
4433     switch(grouperInstallerConvertAction) {
4434       case convertEhcacheXmlToProperties:
4435 
4436         System.out.println("Note, you need to convert the ehcache.xml file for each Grouper runtime, e.g. loader, WS, UI.");
4437         System.out.println("Note, you need to be running Grouper 2.3.0 with API patch 35 installed.");
4438         System.out.print("Enter the location of the ehcache.xml file: ");
4439         String convertEhcacheXmlLocation = readFromStdIn("grouperInstaller.autorun.convertEhcacheXmlLocation");
4440 
4441         File ehcacheXmlFile = new File(convertEhcacheXmlLocation);
4442         if (!ehcacheXmlFile.exists()) {
4443           System.out.println("Cant find ehcache.xml: " + ehcacheXmlFile.getAbsolutePath());
4444           System.exit(1);
4445         }
4446 
4447         File grouperCacheBaseProperties = new File(ehcacheXmlFile.getParentFile().getAbsolutePath() + File.separator + "grouper.cache.base.properties");
4448 
4449         {
4450           System.out.print("Enter the location of the grouper.cache.base.properties file [" + grouperCacheBaseProperties.getAbsolutePath() + "]: ");
4451           String grouperCacheBasePropertiesLocation = readFromStdIn("grouperInstaller.autorun.convertEhcacheBasePropertiesLocation");
4452   
4453           if (!GrouperInstallerUtils.isBlank(grouperCacheBasePropertiesLocation)) {
4454             grouperCacheBaseProperties = new File(grouperCacheBasePropertiesLocation);
4455           }
4456         }
4457         
4458         File grouperCacheProperties = new File(ehcacheXmlFile.getParentFile().getAbsolutePath() + File.separator + "grouper.cache.properties");
4459 
4460         {
4461           System.out.print("Enter the location of the grouper.cache.properties file (to be created)  [" + grouperCacheProperties.getAbsolutePath() + "]: ");
4462           String grouperCachePropertiesLocation = readFromStdIn("grouperInstaller.autorun.convertEhcachePropertiesLocation");
4463   
4464           if (!GrouperInstallerUtils.isBlank(grouperCachePropertiesLocation)) {
4465             grouperCacheProperties = new File(grouperCachePropertiesLocation);
4466           }
4467         }
4468         
4469         try {
4470           convertEhcacheXmlToProperties(grouperCacheBaseProperties, grouperCacheProperties,
4471               ehcacheXmlFile.toURI().toURL());
4472         } catch (MalformedURLException mue) {
4473           throw new RuntimeException("Malformed url on " + convertEhcacheXmlLocation);
4474         }
4475 
4476         System.out.println("File was written: " + grouperCacheProperties.getAbsolutePath());
4477 
4478         break;
4479         
4480       case analyzeAndFixJars:
4481         
4482         //Find out what directory to install to.  This ends in a file separator
4483         this.grouperInstallDirectoryString = grouperInstallDirectory();
4484 
4485         reportOnConflictingJars(this.grouperInstallDirectoryString);
4486         
4487         break;
4488         
4489       case convertSourcesXmlToProperties:
4490 
4491         System.out.println("Note, you need to convert the sources.xml file for each Grouper runtime, e.g. loader, WS, UI.");
4492         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.");
4493         System.out.print("Enter the location of the sources.xml file: ");
4494         String convertSourcesXmlLocation = readFromStdIn("grouperInstaller.autorun.convertSourceXmlLocation");
4495 
4496         File sourcesXmlFile = new File(convertSourcesXmlLocation);
4497         if (!sourcesXmlFile.exists()) {
4498           System.out.println("Cant find sources.xml: " + sourcesXmlFile.getAbsolutePath());
4499           System.exit(1);
4500         }
4501 
4502         File subjectProperties = new File(sourcesXmlFile.getParentFile().getAbsolutePath() + File.separator + "subject.properties");
4503 
4504         {
4505           System.out.print("Enter the location of the subject.properties file [" + subjectProperties.getAbsolutePath() + "]: ");
4506           String grouperCacheBasePropertiesLocation = readFromStdIn("grouperInstaller.autorun.convertSubjectPropertiesLocation");
4507   
4508           if (!GrouperInstallerUtils.isBlank(grouperCacheBasePropertiesLocation)) {
4509             subjectProperties = new File(grouperCacheBasePropertiesLocation);
4510           }
4511         }
4512         
4513         try {
4514           convertSourcesXmlToProperties(subjectProperties, sourcesXmlFile.toURI().toURL());
4515         } catch (MalformedURLException mue) {
4516           throw new RuntimeException("Malformed url on " + convertSourcesXmlLocation);
4517         }
4518 
4519         System.out.println("File was written: " + subjectProperties.getAbsolutePath());
4520         System.out.println("You should archive your sources.xml and remove it from your project since it is now unused:\n  " 
4521             + sourcesXmlFile.getAbsolutePath());
4522 
4523         break;
4524     }
4525 
4526   }
4527   
4528   /**
4529    * patch grouper
4530    */
4531   private void mainPatchLogic() {
4532     
4533     //####################################
4534     //Find out what directory to upgrade to.  This ends in a file separator
4535     this.grouperTarballDirectoryString = grouperUpgradeTempDirectory();
4536     
4537     //see what we are upgrading: api, ui, ws, client
4538     this.appToUpgrade = grouperAppToUpgradeOrPatch("patch");
4539 
4540     //get the directory where the existing installation is
4541     this.upgradeExistingApplicationDirectoryString = upgradeExistingDirectory();
4542 
4543     GrouperInstallerPatchAction grouperInstallerPatchAction = 
4544         (GrouperInstallerPatchAction)promptForEnum(
4545             "What do you want to do with patches (install, revert, status, fixIndexFile)? ",
4546             "grouperInstaller.autorun.patchAction", GrouperInstallerPatchAction.class, GrouperInstallerPatchAction.install, null);
4547     
4548     switch(grouperInstallerPatchAction) {
4549       case install:
4550         
4551         fixIndexFileIfOk();
4552 
4553         //loop through applications, check patches
4554         this.appToUpgrade.patch(this);
4555 
4556         break;
4557         
4558       case revert:
4559         
4560         fixIndexFileIfOk();
4561 
4562         //look through applications, check for reverts
4563         this.appToUpgrade.revertPatch(this);
4564         break;
4565         
4566       case status:
4567         
4568         fixIndexFileIfOk();
4569 
4570         //print out status for applications
4571         this.appToUpgrade.patchStatus(this);
4572         break;
4573         
4574       case fixIndexFile:
4575         
4576         //print out status for applications
4577         this.appToUpgrade.fixIndexFile(this);
4578         break;
4579         
4580       default:
4581         throw new RuntimeException("Invalid patch action: " + grouperInstallerPatchAction);  
4582     }
4583     
4584   }
4585 
4586   /**
4587    * 
4588    */
4589   public static enum GrouperInstallerPatchAction {
4590 
4591     /** fix index file */
4592     fixIndexFile,
4593     
4594     /** install patches */
4595     install,
4596 
4597     /**
4598      * revert patches
4599      */
4600     revert,
4601     
4602     /**
4603      * get status on patches
4604      */
4605     status;
4606     
4607     /**
4608      * 
4609      * @param string
4610      * @param exceptionIfInvalid
4611      * @param exceptionIfBlank
4612      * @return the action
4613      */
4614     public static GrouperInstallerPatchAction valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
4615       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerPatchAction.class, string, exceptionIfBlank, exceptionIfInvalid);
4616     }
4617     
4618   }
4619   
4620   /**
4621    * 
4622    */
4623   public static enum GrouperInstallerAdminAction {
4624 
4625     /** manage */
4626     manage,
4627     
4628     /** develop */
4629     develop,
4630     
4631     /** convert */
4632     upgradeTask;
4633     
4634     /**
4635      * 
4636      * @param string
4637      * @param exceptionIfInvalid
4638      * @param exceptionIfBlank
4639      * @return the action
4640      */
4641     public static GrouperInstallerAdminAction valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
4642       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerAdminAction.class, string, exceptionIfBlank, exceptionIfInvalid);
4643     }
4644     
4645   }
4646   
4647   /**
4648    * 
4649    */
4650   public static enum GrouperInstallerUpgradeTaskAction {
4651 
4652     /** analyze and fix jars */
4653     analyzeAndFixJars,
4654     
4655     /** convert */
4656     convertEhcacheXmlToProperties,
4657     
4658     /** convert sources xml */
4659     convertSourcesXmlToProperties;
4660     
4661     /**
4662      * 
4663      * @param string
4664      * @param exceptionIfInvalid
4665      * @param exceptionIfBlank
4666      * @return the action
4667      */
4668     public static GrouperInstallerUpgradeTaskAction valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
4669       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerUpgradeTaskAction.class, string, exceptionIfBlank, exceptionIfInvalid);
4670     }
4671     
4672   }
4673   
4674   /**
4675    * 
4676    */
4677   public static enum GrouperInstallerAdminManageService {
4678 
4679     /** tomcat */
4680     tomcat,
4681     
4682     /** database (hsqldb) */
4683     database,
4684     
4685     /** daemon (loader) */
4686     grouperDaemon;
4687     
4688     /**
4689      * 
4690      * @param string
4691      * @param exceptionIfInvalid
4692      * @param exceptionIfBlank
4693      * @return the action
4694      */
4695     public static GrouperInstallerAdminManageService valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
4696       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerAdminManageService.class, string, exceptionIfBlank, exceptionIfInvalid);
4697     }
4698     
4699   }
4700   
4701   /**
4702    * 
4703    */
4704   public static enum GrouperInstallerManageAction {
4705 
4706     /** logs */
4707     logs,
4708 
4709     /** back to admin */
4710     back,
4711 
4712     /** exit */
4713     exit,
4714 
4715     /** services */
4716     services;
4717     
4718     /**
4719      * 
4720      * @param string
4721      * @param exceptionIfInvalid
4722      * @param exceptionIfBlank
4723      * @return the action
4724      */
4725     public static GrouperInstallerManageAction valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
4726       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerManageAction.class, string, exceptionIfBlank, exceptionIfInvalid);
4727     }
4728     
4729   }
4730   
4731   /**
4732    * 
4733    */
4734   public static enum GrouperInstallerDevelopAction {
4735 
4736     /** logs */
4737     translate,
4738 
4739     /** back to admin */
4740     back,
4741 
4742     /** exit */
4743     exit;
4744 
4745     /**
4746      * 
4747      * @param string
4748      * @param exceptionIfInvalid
4749      * @param exceptionIfBlank
4750      * @return the action
4751      */
4752     public static GrouperInstallerDevelopAction valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
4753       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerDevelopAction.class, string, exceptionIfBlank, exceptionIfInvalid);
4754     }
4755     
4756   }
4757   
4758   /**
4759    * get the existing properties file of patches
4760    * @return the file for patches
4761    */
4762   private Properties patchExistingProperties() {
4763     File patchExistingPropertiesFile = this.patchExistingPropertiesFile();
4764     if (patchExistingPropertiesFile == null || !patchExistingPropertiesFile.exists()) {
4765       return new Properties();
4766     }
4767     return GrouperInstallerUtils.propertiesFromFile(patchExistingPropertiesFile);
4768    }
4769 
4770   /**
4771    * get the existing properties file of patches
4772    * @return the file for patches
4773    */
4774   private File patchExistingPropertiesFile() {
4775     
4776     //dont cache this in a variable since the upgrade existing application variable
4777     File patchExistingPropertiesFile = null;
4778     //if theres a web-inf, put it there, if not, put it in regular...
4779     if (new File(this.upgradeExistingApplicationDirectoryString + "WEB-INF").exists()) {
4780       patchExistingPropertiesFile = new File(this.upgradeExistingApplicationDirectoryString 
4781           + "WEB-INF" + File.separator + "grouperPatchStatus.properties");
4782     } else {
4783       patchExistingPropertiesFile = new File(this.upgradeExistingApplicationDirectoryString 
4784           + "grouperPatchStatus.properties");
4785     }
4786     return patchExistingPropertiesFile;
4787   }
4788   
4789   /**
4790    * 
4791    */
4792   private void mainUpgradeLogic() {
4793 
4794     System.out.print("You should backup your files and database before you start.  Press <enter> to continue. ");
4795     readFromStdIn("grouperInstaller.autorun.backupFiles");
4796     
4797     System.out.println("\n##################################");
4798     System.out.println("Gather upgrade information\n");
4799 
4800     //####################################
4801     //Find out what directory to upgrade to.  This ends in a file separator
4802     this.grouperTarballDirectoryString = grouperUpgradeTempDirectory();
4803     
4804     //GRP-1429: grouperInstaller tarballs dir fails on upgrade
4805     //set this here since on upgrade some things download to this dir...
4806     this.grouperInstallDirectoryString = this.grouperTarballDirectoryString;
4807     
4808     //see what we are upgrading: api, ui, ws, client
4809     this.appToUpgrade = grouperAppToUpgradeOrPatch("upgrade");
4810 
4811     for (int i=0;i<10;i++) {
4812       System.out.println("Are there any running processes using this installation?  tomcats?  loader?  psp?  etc?  (t|f)? [f]:");
4813       boolean runningProcesses = readFromStdInBoolean(true, "grouperInstaller.autorun.runningProcesses");
4814       if (runningProcesses) {
4815         break;
4816       }
4817       System.out.println("Please stop any processes using this installation...");
4818       //lets sleep for a bit to let it start
4819       GrouperInstallerUtils.sleep(2000);
4820     }
4821     
4822     //get the directory where the existing installation is
4823     this.upgradeExistingApplicationDirectoryString = upgradeExistingDirectory();
4824 
4825     this.version = GrouperInstallerUtils.propertiesValue("grouper.version", true);
4826     System.out.println("Upgrading to grouper " + this.appToUpgrade.name() + " version: " + this.version);
4827  
4828     fixIndexFileIfOk();
4829     
4830     System.out.println("\n##################################");
4831     System.out.println("Download and build grouper packages\n");
4832 
4833     //download new files
4834     this.appToUpgrade.downloadAndBuildGrouperProjects(this);
4835 
4836     System.out.println("End download and build grouper packages\n");
4837     System.out.println("\n##################################");
4838 
4839     this.grouperBaseBakDir = this.grouperTarballDirectoryString + "bak_" + this.appToUpgrade + "_" 
4840         + new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS").format(new Date()) + File.separator; 
4841 
4842     GrouperInstallerUtils.tempFilePathForJars = this.grouperBaseBakDir 
4843         + "jarToDelete" + File.separator;
4844 
4845     //when we revert patches, default should be true
4846     this.revertAllPatchesDefault = true;
4847     try {
4848       this.appToUpgrade.upgradeApp(this);
4849     } finally {
4850       this.revertAllPatchesDefault = false;
4851     }
4852 
4853     this.reportOnConflictingJars(this.upgradeExistingApplicationDirectoryString);
4854     
4855     System.out.println("\nGrouper is upgraded from " + (this.originalGrouperJarVersion == null ? null : this.originalGrouperJarVersion) 
4856         + " to " + GrouperInstallerUtils.propertiesValue("grouper.version", true) +  "\n");
4857 
4858     //this file keeps track of partial upgrades
4859     GrouperInstallerUtils.fileDelete(this.grouperUpgradeOriginalVersionFile);
4860 
4861     //reset this so that patches go against new version
4862     this.grouperVersionOfJar = null;
4863     
4864   }
4865 
4866   /**
4867    * 
4868    */
4869   private void fixIndexFileIfOk() {
4870     Properties patchesExistingProperties = patchExistingProperties();
4871     
4872     //see what is already there
4873     String existingDate = patchesExistingProperties.getProperty("grouperInstallerLastFixedIndexFile.date");
4874 
4875     boolean defaultToFixIndex = true;
4876     
4877     if (!GrouperInstallerUtils.isBlank(existingDate)) {
4878       try {
4879         Date theDate = GrouperInstallerUtils.dateMinutesSecondsFormat.parse(existingDate);
4880         //this is when the installer was fixed to do the index file correctly
4881         if (theDate.getTime() > GrouperInstallerUtils.dateValue("20150929").getTime()) {
4882           defaultToFixIndex = false;
4883         }
4884       } catch (ParseException pe) {
4885         System.out.println("Cant parse date: " + existingDate);
4886       }
4887     }
4888 
4889     //if we are affecting 2.2.2+ then dont recommend this
4890     if (defaultToFixIndex) {
4891       
4892       //see the version
4893       String grouperVersion = this.grouperVersionOfJar().toString();
4894 
4895       GiGrouperVersion giGrouperVersion = GiGrouperVersion.valueOfIgnoreCase(grouperVersion);
4896       
4897       if (giGrouperVersion.greaterOrEqualToArg(GiGrouperVersion.valueOfIgnoreCase("2.2.2"))) {
4898         defaultToFixIndex = false;
4899       }
4900       
4901     }
4902     
4903     
4904     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") + "]: ");
4905     boolean fixIndexFile = readFromStdInBoolean(defaultToFixIndex, "grouperInstaller.autorun.fixIndexFile");
4906     if (fixIndexFile) {
4907       this.appToUpgrade.fixIndexFile(this);
4908     }
4909   }
4910 
4911   /**
4912    * upgrade the client
4913    */
4914   private void upgradeClient() {
4915 
4916     System.out.println("\n##################################");
4917     System.out.println("Upgrading grouper client\n");
4918 
4919     this.compareAndReplaceJar(this.grouperClientJar, new File(this.untarredClientDir + File.separator + "grouperClient.jar"), true, null);
4920 
4921     this.compareUpgradePropertiesFile(this.grouperClientBasePropertiesFile, 
4922       new File(this.untarredClientDir + File.separator + "grouper.client.base.properties"),
4923       this.grouperClientPropertiesFile,
4924       this.grouperClientExamplePropertiesFile,
4925       GrouperInstallerUtils.toSet("grouperClient.webService.client.version"),
4926       "grouperInstaller.autorun.removeRedundantPropetiesFromGrouperClient"
4927     );
4928 
4929     
4930   }
4931 
4932 
4933   /**
4934    * upgrade the ui
4935    */
4936   private void upgradeUi() {
4937 
4938     this.upgradeApiPreRevertPatch();
4939 
4940     System.out.println("You need to revert all patches to upgrade");
4941     this.patchRevertUi();
4942         
4943     System.out.println("\n##################################");
4944     System.out.println("Upgrading UI\n");
4945     
4946     //copy the jars there
4947     System.out.println("\n##################################");
4948     System.out.println("Upgrading UI jars\n");
4949 
4950     this.upgradeJars(new File(this.untarredUiDir + File.separator + "dist" + File.separator + "grouper" + File.separator 
4951         + "WEB-INF" + File.separator + "lib" + File.separator));
4952 
4953     System.out.println("\n##################################");
4954     System.out.println("Upgrading UI files\n");
4955 
4956     //copy files there
4957     this.copyFiles(this.untarredUiDir + File.separator + "dist" + File.separator + "grouper" + File.separator,
4958         this.upgradeExistingApplicationDirectoryString,
4959         GrouperInstallerUtils.toSet("WEB-INF/lib", "WEB-INF/web.xml", "WEB-INF/classes",
4960             "WEB-INF/bin/gsh", "WEB-INF/bin/gsh.bat", "WEB-INF/bin/gsh.sh"));
4961 
4962     {
4963       boolean hadChange = false;
4964       for (String gshName : new String[]{"gsh", "gsh.bat", "gsh.sh"}) {
4965         File newGshFile = new File(this.untarredUiDir + File.separator + "dist" 
4966             + File.separator + "grouper" + File.separator + "WEB-INF" + File.separator + "bin" 
4967             + File.separator + gshName);
4968 
4969         File existingGshFile = new File(this.upgradeExistingApplicationDirectoryString 
4970             + "WEB-INF" + File.separator + "bin" + File.separator + gshName);
4971 
4972         if (!GrouperInstallerUtils.contentEquals(newGshFile, existingGshFile)) {
4973           this.backupAndCopyFile(newGshFile, existingGshFile, true);
4974           if (!GrouperInstallerUtils.equals("gsh.bat", gshName)) {
4975             hadChange = true;
4976           }
4977         }
4978         
4979       }
4980       if (hadChange) {
4981         //set executable and dos2unix
4982         gshExcutableAndDos2Unix(this.untarredUiDir + File.separator + "dist" 
4983             + File.separator + "grouper" + File.separator + "WEB-INF" + File.separator + "bin" 
4984             + File.separator);
4985       }
4986     }
4987     
4988     upgradeWebXml(new File(this.untarredUiDir + File.separator + "dist" 
4989             + File.separator + "grouper" + File.separator + "WEB-INF" + File.separator + "web.xml"),
4990             new File(this.upgradeExistingApplicationDirectoryString 
4991                 + File.separator + "WEB-INF" + File.separator + "web.xml"));
4992     
4993     System.out.println("\n##################################");
4994     System.out.println("Upgrading UI config files\n");
4995 
4996     this.changeConfig("WEB-INF/classes/resources/grouper/nav.properties", 
4997         "WEB-INF/classes/grouperText/grouper.text.en.us.base.properties",
4998         "WEB-INF/classes/grouperText/grouper.text.en.us.properties", null, GiGrouperVersion.valueOfIgnoreCase("2.2.0"), true, 
4999         "grouperInstaller.autorun.continueAfterNavProperties",
5000         "grouperInstaller.autorun.removeOldKeysFromNavProperties");
5001 
5002     this.changeConfig("WEB-INF/classes/resources/grouper/media.properties", 
5003         "WEB-INF/classes/grouper-ui.base.properties",
5004         "WEB-INF/classes/grouper-ui.properties", null, GiGrouperVersion.valueOfIgnoreCase("2.2.0"), false,
5005         "grouperInstaller.autorun.continueAfterMediaProperties",
5006         "grouperInstaller.autorun.removeOldKeysFromMediaProperties");
5007 
5008     {
5009       File newBaseOwaspFile = new File(this.untarredUiDir + File.separator + "dist" 
5010           + File.separator + "grouper" + File.separator + "WEB-INF" + File.separator + "classes" 
5011           + File.separator + "Owasp.CsrfGuard.properties");
5012 
5013       File newOwaspFile = new File(this.untarredUiDir + File.separator + "dist" 
5014           + File.separator + "grouper" + File.separator + "WEB-INF" + File.separator + "classes" 
5015           + File.separator + "Owasp.CsrfGuard.overlay.properties");
5016 
5017       if (this.owaspCsrfGuardBaseFile == null) {
5018         this.owaspCsrfGuardBaseFile = new File(this.upgradeExistingClassesDirectoryString + newBaseOwaspFile.getName());
5019       }
5020       
5021       if (this.owaspCsrfGuardFile == null) {
5022         this.owaspCsrfGuardFile = new File(this.upgradeExistingClassesDirectoryString + newOwaspFile.getName());
5023       }
5024       
5025       this.backupAndCopyFile(newBaseOwaspFile, this.owaspCsrfGuardBaseFile, true);
5026 
5027       boolean editedOwaspOverlay = this.owaspCsrfGuardFile != null && this.owaspCsrfGuardFile.exists();
5028 
5029       File bakFile = this.backupAndCopyFile(newOwaspFile, this.owaspCsrfGuardFile, true);
5030 
5031       if (bakFile != null && editedOwaspOverlay) {
5032         if (!GrouperInstallerUtils.contentEquals(this.owaspCsrfGuardFile, newOwaspFile)) {
5033           System.out.println("If you have edited the Owasp.CsrfGuard.overlay.properties please merge the changes to the new file");
5034           System.out.println("Press <enter> when done");
5035           readFromStdIn("grouperInstaller.autorun.continueAfterEditedOwaspCsrfGuard");
5036         }
5037       }
5038     }
5039     
5040     this.upgradeApiPostRevertPatch();
5041 
5042     //patch it
5043     this.patchUi();
5044     
5045   }
5046 
5047   /**
5048    * upgrade the psp
5049    */
5050   private void upgradePsp() {
5051 
5052     this.upgradeApiPreRevertPatch();
5053 
5054     System.out.println("You need to revert all patches to upgrade");
5055     this.patchRevertPsp();
5056     
5057     System.out.println("\n##################################");
5058     System.out.println("Upgrading PSP\n");
5059     
5060     //copy the jars there
5061     System.out.println("\n##################################");
5062     System.out.println("Upgrading PSP jars\n");
5063 
5064     this.upgradeJars(new File(this.untarredPspDir + File.separator + "lib" + File.separator + "custom" + File.separator),
5065         new File(new File(this.upgradeExistingLibDirectoryString).getParentFile().getAbsolutePath() + File.separator + "custom"));
5066 
5067     System.out.println("\n##################################");
5068     System.out.println("Upgrading PSP files\n");
5069 
5070     //copy files there (this is the conf examples)
5071     this.copyFiles(this.untarredPspDir + File.separator + "conf" + File.separator,
5072         this.upgradeExistingApplicationDirectoryString + "conf" + File.separator, null);
5073 
5074     this.upgradeApiPostRevertPatch();
5075     
5076     //patch it
5077     this.patchPsp();
5078   }
5079 
5080   /**
5081    * upgrade the pspng
5082    */
5083   private void upgradePspng() {
5084 
5085     this.upgradeApiPreRevertPatch();
5086 
5087     System.out.println("You need to revert all patches to upgrade");
5088     this.patchRevertPspng();
5089     
5090     System.out.println("\n##################################");
5091     System.out.println("Upgrading PSPNG\n");
5092     
5093     //copy the jars there
5094     System.out.println("\n##################################");
5095     System.out.println("Upgrading PSPNG jars\n");
5096 
5097     this.upgradeJars(new File(this.untarredPspngDir + File.separator + "lib" + File.separator + "custom" + File.separator),
5098         new File(new File(this.upgradeExistingLibDirectoryString).getParentFile().getAbsolutePath() + File.separator + "custom"));
5099 
5100     System.out.println("\n##################################");
5101     System.out.println("Upgrading PSPNG files\n");
5102 
5103     //copy files there (this is the conf examples)
5104     this.copyFiles(this.untarredPspngDir + File.separator + "conf" + File.separator,
5105         this.upgradeExistingApplicationDirectoryString + "conf" + File.separator, null);
5106 
5107     this.upgradeApiPostRevertPatch();
5108     
5109     //patch it
5110     this.patchPspng();
5111   }
5112 
5113   
5114   /**
5115    * upgrade a web.xml file
5116    * @param newWebXml
5117    * @param existingWebXml
5118    */
5119   public void upgradeWebXml(File newWebXml, File existingWebXml) {
5120     
5121     File bakFile = backupAndCopyFile(newWebXml, existingWebXml, true);
5122     
5123     if (bakFile != null) {
5124       //it existed
5125       NodeList nodeList = GrouperInstallerUtils.xpathEvaluate(bakFile, "/web-app/security-constraint");
5126       boolean tookOutAuthn = false;
5127       if (nodeList == null || nodeList.getLength() == 0) {
5128         //take out authn from web.xml
5129         String webXmlContents = GrouperInstallerUtils.readFileIntoString(existingWebXml);
5130         int startAuthnIndex = webXmlContents.indexOf("<security-constraint>");
5131         int endAuthnIndex = webXmlContents.indexOf("</security-role>");
5132         if (startAuthnIndex != -1 && endAuthnIndex != -1 && endAuthnIndex > startAuthnIndex) {
5133           endAuthnIndex = endAuthnIndex + "</security-role>".length();
5134           //authn is there
5135           webXmlContents = webXmlContents.substring(0, startAuthnIndex) + webXmlContents.substring(endAuthnIndex, webXmlContents.length());
5136           GrouperInstallerUtils.saveStringIntoFile(existingWebXml, webXmlContents);
5137           tookOutAuthn = true;
5138           System.out.println("Taking out basic authentication from " + existingWebXml + " since it wasnt there before");
5139         }
5140       }
5141       System.out.println("If you customized the web.xml please merge your changes back in "
5142           + (tookOutAuthn ? "\n  Note: basic authentication was removed from the new web.xml to be consistent with the old web.xml" : "")
5143           + "\n  New file: " + existingWebXml.getAbsolutePath() + ", bak file:" + bakFile.getAbsolutePath() );
5144       System.out.println("Press the <enter> key to continue");
5145       readFromStdIn("grouperInstaller.autorun.continueAfterMergeWebXml");
5146       
5147       if (tookOutAuthn) {
5148         GrouperInstallerUtils.xpathEvaluate(existingWebXml, "/web-app");        
5149       }
5150     }
5151   }
5152 
5153   
5154   /**
5155    * @param legacyPropertiesFileRelativePath legacy file we are converting from
5156    * @param propertiesFileRelativePath 
5157    * @param propertiesToIgnore
5158    * @param basePropertiesFileRelativePath
5159    * @param versionMigrationHappened
5160    * @param removeOldCopy
5161    * @param autorunPropertiesKey key in properties file to automatically fill in a value
5162    * @param autorunPropertiesKeyRemoveOldKeys key in properties file to automatically fill in a value to remove old keys
5163    */
5164   @SuppressWarnings("unchecked")
5165   private void changeConfig(String legacyPropertiesFileRelativePath,
5166       String basePropertiesFileRelativePath,
5167       String propertiesFileRelativePath,
5168       Set<String> propertiesToIgnore,
5169       GiGrouperVersion versionMigrationHappened, boolean removeOldCopy, 
5170       String autorunPropertiesKey, String autorunPropertiesKeyRemoveOldKeys) {
5171 
5172     File legacyPropertiesFile = new File(this.upgradeExistingApplicationDirectoryString + legacyPropertiesFileRelativePath);
5173     File newBasePropertiesFile = new File(this.untarredUiDir + File.separator + "dist" 
5174         + File.separator + "grouper" + File.separator + basePropertiesFileRelativePath);
5175     File existingBasePropertiesFile = new File(this.upgradeExistingApplicationDirectoryString + basePropertiesFileRelativePath);
5176     File existingPropertiesFile = new File(this.upgradeExistingApplicationDirectoryString + propertiesFileRelativePath);
5177 
5178     this.compareUpgradePropertiesFile(existingBasePropertiesFile, newBasePropertiesFile, 
5179         existingPropertiesFile, null, propertiesToIgnore, autorunPropertiesKeyRemoveOldKeys);
5180 
5181     //look for existing properties in legacy file, and if there are properties in the base, then remove them
5182     if (legacyPropertiesFile.exists()) {
5183       
5184       Properties existingBaseProperties = GrouperInstallerUtils.propertiesFromFile(existingBasePropertiesFile);
5185       Properties existingProperties = GrouperInstallerUtils.propertiesFromFile(existingPropertiesFile);
5186       Properties legacyProperties = GrouperInstallerUtils.propertiesFromFile(legacyPropertiesFile);
5187       Set<String> propertyNamesToRemove = new LinkedHashSet<String>();
5188       Set<String> propertyNamesWrongValue = new LinkedHashSet<String>();
5189 
5190       for (String propertyName : (Set<String>)(Object)existingBaseProperties.keySet()) {
5191         if (legacyProperties.containsKey(propertyName)) {
5192           
5193           Object existingValue = existingProperties.containsKey(propertyName) ?
5194              existingProperties.get(propertyName) : existingBaseProperties.get(propertyName);
5195           
5196           //it might be in the override, what about other overrides?  who knows
5197           if (!GrouperInstallerUtils.equals(existingValue, 
5198               legacyProperties.get(propertyName))) {
5199 
5200             propertyNamesWrongValue.add(propertyName);
5201           }
5202           propertyNamesToRemove.add(propertyName);
5203         }
5204       }
5205       
5206       //if we found some, see if we can remove them
5207       if (propertyNamesToRemove.size() > 0) {
5208         
5209         if (propertyNamesWrongValue.size() > 0) {
5210 
5211           //these are properties that we different in the previous legacy file
5212           System.out.println(legacyPropertiesFileRelativePath + " has properties that have a different value than\n  the new place they are managed: "
5213               + basePropertiesFileRelativePath + ",\n  and the everride(s) which could be: " + propertiesFileRelativePath);
5214           System.out.println("Review these properties and merge the values, this could have happened due to changes in Grouper:");
5215           for (String propertyName: propertyNamesWrongValue) {
5216             System.out.println(" - " + propertyName);
5217           }
5218           System.out.println("When you are done merging press <enter>");
5219           readFromStdIn(autorunPropertiesKey);
5220 
5221         }
5222 
5223         if (removeOldCopy) {
5224           
5225           System.out.println(legacyPropertiesFileRelativePath + " is not used anymore by grouper, can it be backed up and removed (t|f)? [t]: ");
5226           boolean removeLegacy = readFromStdInBoolean(true, autorunPropertiesKeyRemoveOldKeys);
5227           if (removeLegacy) {
5228             File backupLegacy = bakFile(legacyPropertiesFile);
5229             GrouperInstallerUtils.copyFile(legacyPropertiesFile, backupLegacy, true);
5230             GrouperInstallerUtils.fileDelete(legacyPropertiesFile);
5231             System.out.println("File as removed.  Backup path: " + backupLegacy.getAbsolutePath());
5232           }
5233           
5234         } else {
5235           System.out.println(legacyPropertiesFileRelativePath + " has properties that can be removed since they are now managed in "
5236               + basePropertiesFileRelativePath);
5237           System.out.print("Would you like to have the properties automatically removed from " 
5238               + legacyPropertiesFile.getName() + " (t|f)? [t]: ");
5239           boolean removeRedundantProperties = readFromStdInBoolean(true, autorunPropertiesKeyRemoveOldKeys);
5240           
5241           if (removeRedundantProperties) {
5242             removeRedundantProperties(legacyPropertiesFile, propertyNamesToRemove);
5243           }
5244         }
5245       }
5246     }
5247   }
5248 
5249   
5250   /**
5251    * copy files if they are different from one place to another, print out statuses
5252    * @param fromDirString where to copy files from
5253    * @param toDirString where to copy files to
5254    * @param relativePathsToIgnore
5255    */
5256   public void copyFiles(String fromDirString, String toDirString, 
5257       Set<String> relativePathsToIgnore) {
5258     
5259     fromDirString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(fromDirString);
5260     toDirString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(toDirString);
5261 
5262     {
5263       //lets massage all the paths so they dont start or end with slash and
5264       //so they have File.separator instead of the wrong slash
5265       Set<String> tempRelativePathsToIgnore = new HashSet<String>();
5266       for (String path : GrouperInstallerUtils.nonNull(relativePathsToIgnore)) {
5267         path = GrouperInstallerUtils.fileMassagePathsNoLeadingOrTrailing(path);
5268         tempRelativePathsToIgnore.add(path);
5269       }
5270       relativePathsToIgnore = tempRelativePathsToIgnore;
5271     }
5272 
5273     int insertCount = 0;
5274     int updateCount = 0;
5275     Map<String, Boolean> relativeFilePathsChangedAndIfInsert = new LinkedHashMap<String, Boolean>();
5276 
5277     List<File> allFiles = GrouperInstallerUtils.fileListRecursive(new File(fromDirString));
5278     for (File fileToCopyFrom : allFiles) {
5279       String relativePath = null;
5280       {
5281         //get the relative path with no leading or trailing slash
5282         String path = fileToCopyFrom.getAbsolutePath();
5283         if (!GrouperInstallerUtils.filePathStartsWith(path,fromDirString)) {
5284           throw new RuntimeException("Why does path not start with fromDirString: " + path + ", " + fromDirString);
5285         }
5286         relativePath = path.substring(fromDirString.length());
5287         relativePath = GrouperInstallerUtils.fileMassagePathsNoLeadingOrTrailing(relativePath);
5288       }
5289       boolean ignore = false;
5290       
5291       //ignore paths passed in
5292       for (String pathToIgnore : relativePathsToIgnore) {
5293         if (GrouperInstallerUtils.filePathStartsWith(relativePath,pathToIgnore)) {
5294           ignore = true;
5295           break;
5296         }
5297       }
5298       
5299       if (!ignore) {
5300         
5301         //File to copy to
5302         File fileToCopyTo = new File(toDirString + relativePath);
5303         if (fileToCopyTo.exists()) {
5304           //compare contents
5305           if (GrouperInstallerUtils.contentEquals(fileToCopyFrom, fileToCopyTo)) {
5306             continue;
5307           }
5308           //not equals, make backup
5309           updateCount++;
5310 
5311           relativeFilePathsChangedAndIfInsert.put(relativePath, false);
5312 
5313           this.backupAndCopyFile(fileToCopyFrom, fileToCopyTo, false);
5314 
5315           continue;
5316         }
5317         
5318         //insert
5319         insertCount++;
5320         relativeFilePathsChangedAndIfInsert.put(relativePath, true);
5321         GrouperInstallerUtils.copyFile(fileToCopyFrom, fileToCopyTo, false);
5322         
5323       }
5324     }
5325 
5326     System.out.println("Upgrading files from: " + fromDirString + "\n  to: " + toDirString 
5327         + (GrouperInstallerUtils.length(relativePathsToIgnore) == 0 ? "" : 
5328         ("\n  ignoring paths: " + GrouperInstallerUtils.join(relativePathsToIgnore.iterator(), ", "))));
5329     System.out.println("Compared " + allFiles.size() + " files and found " 
5330         + insertCount + " adds and " + updateCount + " updates");
5331 
5332     if (insertCount > 0 || updateCount > 0) {
5333       
5334       System.out.println((insertCount + updateCount) + " files were backed up to: " + this.grouperBaseBakDir);
5335 
5336       boolean listFiles = insertCount + updateCount <= 10;
5337       if (!listFiles) {
5338         System.out.println("Do you want to see the list of files changed (t|f)? [f]: ");
5339         listFiles = readFromStdInBoolean(false, "grouperInstaller.autorun.viewListOfFilesChangedInCopy");
5340       }
5341 
5342       if (listFiles) {
5343 
5344         for (String relativeFilePathChanged : relativeFilePathsChangedAndIfInsert.keySet()) {
5345           boolean isInsert = relativeFilePathsChangedAndIfInsert.get(relativeFilePathChanged);
5346           System.out.println(relativeFilePathChanged + " was " + (isInsert ? "added" : "updated"));
5347         }
5348       }
5349     }
5350     
5351   }
5352 
5353   /**
5354    *
5355    * @param sourceDir directory to copy files from
5356    * @param targetDir directory to copy files to
5357    * @param filesToCopyFromSource list of files to copy, if exist in source and differ in target
5358    */
5359   private void syncFilesInDirWithBackup(String sourceDir, String targetDir, String[] filesToCopyFromSource) {
5360     for (String filename : filesToCopyFromSource) {
5361       File srcFile = new File(sourceDir + filename);
5362       File targetFile = new File(targetDir + filename);
5363 
5364       if (srcFile.isFile() && !GrouperInstallerUtils.contentEquals(srcFile, targetFile)) {
5365         try {
5366           @SuppressWarnings("unused")
5367           File bakFile = backupAndCopyFile(srcFile, targetFile, true);
5368         } catch (Exception e) {
5369           System.out.println(" - failed to copy newer bin file " + filename + ": " + e.getMessage());
5370         }
5371       }
5372     }
5373   }
5374 
5375   /**
5376    * upgrade the api
5377    */
5378   private void upgradeApi() {
5379     this.upgradeApiPreRevertPatch();
5380 
5381     System.out.println("You need to revert all patches to upgrade");
5382     this.patchRevertApi();
5383     
5384     this.upgradeApiPostRevertPatch();
5385   }
5386 
5387 
5388   /**
5389    * upgrade the api
5390    */
5391   private void upgradeApiPreRevertPatch() {
5392 
5393     //make sure existing gsh is executable and dos2unix
5394     gshExcutableAndDos2Unix(new File(gshCommand()).getParentFile().getAbsolutePath(), "existing");
5395     
5396     this.runChangeLogTempToChangeLog();
5397   }
5398   
5399   
5400   /**
5401    * upgrade the api
5402    */
5403   private void upgradeApiPostRevertPatch() {
5404 
5405     //revert patches
5406     this.upgradeClient();
5407 
5408     System.out.println("\n##################################");
5409     System.out.println("Upgrading API\n");
5410 
5411     //lets get the version of the existing jar
5412     this.originalGrouperJarVersionOrUpgradeFileVersion();
5413 
5414     this.compareAndReplaceJar(this.grouperJar, 
5415         new File(this.untarredApiDir + File.separator + "dist" + File.separator 
5416             + "lib" + File.separator + "grouper.jar"), true, null);
5417     
5418     if (this.appToUpgrade == AppToUpgrade.API) {
5419       boolean hadChange = false;
5420       for (String gshName : new String[]{"gsh", "gsh.bat", "gsh.sh"}) {
5421         File newGshFile = new File(this.untarredApiDir + File.separator + "bin" 
5422             + File.separator + gshName);
5423 
5424         File existingGshFile = new File(this.upgradeExistingApplicationDirectoryString 
5425             + "bin" + File.separator + gshName);
5426 
5427         if (!GrouperInstallerUtils.contentEquals(newGshFile, existingGshFile)) {
5428           this.backupAndCopyFile(newGshFile, existingGshFile, true);
5429           if (!GrouperInstallerUtils.equals("gsh.bat", gshName)) {
5430             hadChange = true;
5431           }
5432         }
5433         
5434       }
5435       if (hadChange) {
5436         //set executable and dos2unix
5437         gshExcutableAndDos2Unix(this.untarredApiDir + File.separator + "bin" 
5438             + File.separator);
5439       }
5440     }
5441 
5442     System.out.println("\n##################################");
5443     System.out.println("Upgrading API config files\n");
5444 
5445     this.compareUpgradePropertiesFile(this.grouperBasePropertiesFile, 
5446       new File(this.untarredApiDir + File.separator + "conf" + File.separator + "grouper.base.properties"),
5447       this.grouperPropertiesFile,
5448       this.grouperExamplePropertiesFile, null, 
5449       "grouperInstaller.autorun.removeRedundantPropetiesFromGrouperProperties"
5450     );
5451       
5452     this.compareUpgradePropertiesFile(this.grouperHibernateBasePropertiesFile, 
5453         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "grouper.hibernate.base.properties"),
5454         this.grouperHibernatePropertiesFile,
5455         this.grouperHibernateExamplePropertiesFile, null, 
5456         "grouperInstaller.autorun.removeRedundantPropetiesFromGrouperHibernateProperties"
5457       );
5458         
5459     this.compareUpgradePropertiesFile(this.grouperLoaderBasePropertiesFile, 
5460         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "grouper-loader.base.properties"),
5461         this.grouperLoaderPropertiesFile,
5462         this.grouperLoaderExamplePropertiesFile, null,
5463         "grouperInstaller.autorun.removeRedundantPropetiesFromGrouperLoaderProperties"
5464       );
5465     
5466     this.compareUpgradePropertiesFile(this.subjectBasePropertiesFile, 
5467         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "subject.base.properties"),
5468         this.subjectPropertiesFile,
5469         null, null,
5470         "grouperInstaller.autorun.removeRedundantPropetiesFromSubjectProperties"
5471       );
5472     
5473     this.compareUpgradePropertiesFile(this.grouperCacheBasePropertiesFile, 
5474         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "grouper.cache.base.properties"),
5475         this.grouperCachePropertiesFile,
5476         null, null,
5477         "grouperInstaller.autorun.removeRedundantPropetiesFromGrouperCacheProperties"
5478       );
5479 
5480     this.upgradeEhcacheXml();
5481     this.upgradeEhcacheXmlToProperties();
5482     this.upgradeSourcesXmlToProperties();
5483     
5484     this.compareAndCopyFile(this.grouperUtf8File, 
5485         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "grouperUtf8.txt"),
5486         true,
5487         new File(this.upgradeExistingClassesDirectoryString)
5488         );
5489     
5490     this.compareAndCopyFile(this.gshFileLoadPropertiesFile, 
5491         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "GSHFileLoad.properties"),
5492         true,
5493         new File(this.upgradeExistingClassesDirectoryString)
5494         );
5495     
5496     this.compareAndCopyFile(this.groovyshProfileFile, 
5497         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "groovysh.profile"),
5498         true,
5499         new File(this.upgradeExistingClassesDirectoryString)
5500         );
5501     
5502     this.compareAndCopyFile(this.grouperClientUsageExampleFile, 
5503         new File(this.untarredApiDir + File.separator + "conf" + File.separator + "grouper.client.usage.example.txt"),
5504         true,
5505         new File(this.upgradeExistingClassesDirectoryString)
5506         );
5507     
5508     //do this only if less than 2.3.1
5509     if (new GiGrouperVersionersion(this.version).lessThanArg(new GiGrouperVersion("2.3.1"))) {
5510       System.out.println("\nYou should compare " + this.grouperPropertiesFile.getParentFile().getAbsolutePath() + File.separator + "sources.xml"
5511           + "\n  with " + this.untarredApiDir + File.separator + "conf" + File.separator + "sources.xml");
5512       System.out.print("Press <enter> to continue after you have merged the sources.xml. ");
5513       readFromStdIn("grouperInstaller.autorun.continueAfterMergingSourcesXml");
5514     }
5515     
5516     System.out.println("\n##################################");
5517     System.out.println("Upgrading API jars\n");
5518 
5519     this.upgradeJars(new File(this.untarredApiDir + File.separator + "lib" 
5520       + File.separator + "grouper" + File.separator));
5521     
5522     if (this.appToUpgrade.isApiOrganized()) {
5523 
5524       //if we need to put the jars in the jdbcSamples dir...
5525       this.upgradeJars(new File(this.untarredApiDir + File.separator + "lib" 
5526           + File.separator + "jdbcSamples" + File.separator), 
5527           new File(new File(this.upgradeExistingLibDirectoryString).getParentFile().getAbsolutePath() + File.separator + "jdbcSamples"));
5528     
5529     } else {
5530     
5531       this.upgradeJars(new File(this.untarredApiDir + File.separator + "lib" 
5532           + File.separator + "jdbcSamples" + File.separator));
5533     
5534     }
5535 
5536     {
5537       File subjectJar = new File(this.upgradeExistingLibDirectoryString + "subject.jar");
5538       if (subjectJar.exists()) {
5539         this.backupAndDeleteFile(subjectJar, true);
5540       }
5541     }
5542     
5543     {
5544       File oroJar = new File(this.upgradeExistingLibDirectoryString + "jakarta-oro.jar");
5545       if (oroJar.exists()) {
5546         this.backupAndDeleteFile(oroJar, true);
5547       }
5548     }
5549     
5550     System.out.println("\n##################################");
5551     System.out.println("Patch API\n");
5552 
5553     //patch it
5554     this.patchApi();
5555 
5556     //make sure log4j is debugging sql statements
5557     log4jDebugSql(this.upgradeExistingClassesDirectoryString + "log4j.properties");
5558     
5559     //verify that grouper.hibernate.properties doesn't have legacy properties
5560     removeLegacyHibernateProperties(this.upgradeExistingClassesDirectoryString + "grouper.hibernate.properties");
5561     
5562     System.out.println("\n##################################");
5563     System.out.println("Upgrading DB (registry)\n");
5564 
5565     this.apiUpgradeDbVersion(true);
5566 
5567     this.apiUpgradeAdditionalGshScripts();
5568 
5569   }
5570 
5571   /**
5572    * run additional GSH scripts based on what we are upgrading from...
5573    */
5574   private void apiUpgradeAdditionalGshScripts() {
5575     GiGrouperVersion giGrouperVersion = this.originalGrouperJarVersionOrUpgradeFileVersion();
5576     if (giGrouperVersion == null) {
5577       System.out.println("Grouper jar file: " + (this.grouperJar == null ? null : this.grouperJar.getAbsolutePath()));
5578       System.out.println("ERROR, cannot find grouper version in grouper jar file, do you want to continue? (t|f)? [f]: ");
5579       boolean continueScript = readFromStdInBoolean(false, "grouperInstaller.autorun.shouldContinueAfterNoGrouperVersionFound");
5580       if (!continueScript) {
5581         System.exit(1);
5582       }
5583     }
5584 
5585     boolean lessThan2_0 = this.originalGrouperJarVersion.lessThanArg(new GiGrouperVersion("2.0.0"));
5586     {
5587       String runUsduAutorun = null;
5588       if (lessThan2_0) {
5589         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]: ");
5590         runUsduAutorun = "grouperInstaller.autorun.runUsduPre2.0.0";
5591       } else {
5592         System.out.println("You are upgrading from after API version 2.0.0, so you dont have to do this,\n  "
5593             + "but do you want to run Unresolvable Subject Deletion Utility (USDU) (not recommended) (t|f)? [f]: ");
5594         runUsduAutorun = "grouperInstaller.autorun.runUsduPost2.0.0";
5595       }
5596       boolean runScript = readFromStdInBoolean(lessThan2_0, runUsduAutorun);
5597       
5598       if (runScript) {
5599         
5600         //running with command on command line doenst work on linux since the args with whitespace translate to 
5601         //save the commands to a file, and runt he file
5602         StringBuilder gshCommands = new StringBuilder();
5603   
5604         //gsh 0% GrouperSession.startRootSession()
5605         //edu.internet2.middleware.grouper.GrouperSession: 6f94c99d5b0948a3be96f94f00ab4d87,'GrouperSystem','application'
5606         //gsh 1% // run USDU to resolve all the subjects with type=person
5607         //gsh 3% usdu()
5608   
5609         gshCommands.append("grouperSession = GrouperSession.startRootSession();\n");
5610         gshCommands.append("usdu();\n");
5611   
5612         File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "gshUsdu.gsh");
5613         GrouperInstallerUtils.saveStringIntoFile(gshFile, gshCommands.toString());
5614         
5615         List<String> commands = new ArrayList<String>();
5616   
5617         addGshCommands(commands);
5618         commands.add(gshFile.getAbsolutePath());
5619   
5620         System.out.println("\n##################################");
5621         System.out.println("Running USDU with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
5622   
5623         GrouperInstallerUtils.execCommand(
5624             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
5625            new File(this.gshCommand()).getParentFile(), null, true);
5626   
5627       }
5628     }
5629     
5630     {
5631 
5632       String autorunResolveGroupSubjects = null;
5633       if (lessThan2_0) {
5634         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]: ");
5635         autorunResolveGroupSubjects = "grouperInstaller.autorun.resolveGroupSubjectsPre2.0.0";
5636       } else {
5637         System.out.println("You are upgrading from after API version 2.0.0, so you dont have to do this,\n  "
5638             + "but do you want to resolve all group subjects (not recommended) (t|f)? [f]: ");
5639         autorunResolveGroupSubjects = "grouperInstaller.autorun.resolveGroupSubjectsPost2.0.0";
5640       }
5641       boolean runScript = readFromStdInBoolean(lessThan2_0, autorunResolveGroupSubjects);
5642       
5643       if (runScript) {
5644         
5645         //running with command on command line doenst work on linux since the args with whitespace translate to 
5646         //save the commands to a file, and runt he file
5647         StringBuilder gshCommands = new StringBuilder();
5648   
5649         //gsh 5% GrouperSession.startRootSession();
5650         //edu.internet2.middleware.grouper.GrouperSession: 4163fb08b3b24922b55a14010d48e121,'GrouperSystem','application'
5651         //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); }
5652   
5653         gshCommands.append("grouperSession = GrouperSession.startRootSession();\n");
5654         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");
5655   
5656         File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "gshGroupUsdu.gsh");
5657         GrouperInstallerUtils.saveStringIntoFile(gshFile, gshCommands.toString());
5658         
5659         List<String> commands = new ArrayList<String>();
5660   
5661         addGshCommands(commands);
5662         commands.add(gshFile.getAbsolutePath());
5663   
5664         System.out.println("\n##################################");
5665         System.out.println("Resolving group subjects with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
5666 
5667         GrouperInstallerUtils.execCommand(
5668             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
5669            new File(this.gshCommand()).getParentFile(), null, true);
5670       }
5671     }
5672 
5673     {
5674       boolean lessThan2_1 = giGrouperVersion.lessThanArg(new GiGrouperVersion("2.1.0"));
5675       String autorunSeeRuleCheckType = null;
5676       if (lessThan2_1) {
5677         System.out.println("You are upgrading from pre API version 2.1.0, do you want to "
5678             + "see if you have rules with ruleCheckType: flattenedPermission* (recommended) (t|f)? [t]: ");
5679         autorunSeeRuleCheckType = "grouperInstaller.autorun.seeRulesFlattenedPermissionsPre2.1.0";
5680       } else {
5681         System.out.println("You are upgrading from after API version 2.1.0, so you dont have to do this,\n  "
5682             + "but do you want to see if you have rules with ruleCheckType: flattenedPermission* (not recommended) (t|f)? [f]: ");
5683         autorunSeeRuleCheckType = "grouperInstaller.autorun.seeRulesFlattenedPermissionsPost2.1.0";
5684       }
5685       boolean runScript = readFromStdInBoolean(lessThan2_1, autorunSeeRuleCheckType);
5686       
5687       if (runScript) {
5688         
5689         //running with command on command line doenst work on linux since the args with whitespace translate to 
5690         //save the commands to a file, and runt he file
5691         StringBuilder gshCommands = new StringBuilder();
5692     
5693         gshCommands.append("\"Count: \" + HibernateSession.bySqlStatic().select(int.class, \"SELECT count(*) FROM grouper_rules_v WHERE rule_check_type LIKE 'flattenedPermission%'\");\n");
5694   
5695         File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "gshRuleFlattenedPermissionCount.gsh");
5696         GrouperInstallerUtils.saveStringIntoFile(gshFile, gshCommands.toString());
5697         
5698         List<String> commands = new ArrayList<String>();
5699   
5700         addGshCommands(commands);
5701         commands.add(gshFile.getAbsolutePath());
5702   
5703         System.out.println("\n##################################");
5704         System.out.println("Counting flattenedPermission rules with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
5705 
5706         CommandResult commandResult = GrouperInstallerUtils.execCommand(
5707           GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
5708           new File(this.gshCommand()).getParentFile(), null, true);
5709 
5710         if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
5711           System.out.println("stderr: " + commandResult.getErrorText());
5712         }
5713         if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
5714           System.out.println("stdout: " + commandResult.getOutputText());
5715         }
5716 
5717         String result = commandResult.getOutputText().trim();
5718         String[] lines = GrouperInstallerUtils.splitLines(result);
5719         {
5720           Pattern pattern = Pattern.compile("^Count: ([0-9]+)$");
5721           int count = -1;
5722           for (String line : lines) {
5723             Matcher matcher = pattern.matcher(line);
5724             if (matcher.matches()) {
5725               count = GrouperInstallerUtils.intValue(matcher.group(1));
5726               break;
5727             }
5728           }
5729           if (count == -1) {
5730             System.out.println("Error getting count of rules, would you like to continue (t|f)? [t]:");
5731             if (!readFromStdInBoolean(true, "grouperInstaller.autorun.shouldContinueAfterErrorCountFlattenedRules")) {
5732               System.exit(1);
5733             }
5734           } else {
5735             if (count > 0) {
5736               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]: ");
5737               
5738               if (!readFromStdInBoolean(true, "grouperInstaller.autorun.shouldContinueAfterFoundFlattenedRules")) {
5739                 System.exit(1);
5740               }
5741             }
5742           }
5743         }
5744       }
5745     }
5746 
5747     {
5748       boolean lessThan2_2_0 = giGrouperVersion.lessThanArg(new GiGrouperVersion("2.2.0"));
5749       String autorunRun2_2gshScript = null;
5750       if (lessThan2_2_0) {
5751         System.out.println("You are upgrading from pre API version 2.2.0, "
5752             + "do you want to run the 2.2 upgrade GSH script (recommended) (t|f)? [t]: ");
5753         autorunRun2_2gshScript = "grouperInstaller.autorun.run2.2gshUpgradeScriptPre2.2.0";
5754       } else {
5755         System.out.println("You are upgrading from after API version 2.2.0, so you dont have to do this,\n  "
5756             + "but do you want to run the 2.2 upgrade GSH script (not recommended) (t|f)? [f]: ");
5757         autorunRun2_2gshScript = "grouperInstaller.autorun.run2.2gshUpgradeScriptPost2.2.0";
5758       }
5759       boolean runScript = readFromStdInBoolean(lessThan2_2_0, autorunRun2_2gshScript);
5760       
5761       if (runScript) {
5762         
5763         File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "misc" + File.separator + "postGrouper2_2Upgrade.gsh");
5764         
5765         List<String> commands = new ArrayList<String>();
5766   
5767         addGshCommands(commands);
5768         commands.add(gshFile.getAbsolutePath());
5769   
5770         System.out.println("\n##################################");
5771         System.out.println("Running 2.2 upgrade GSH with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
5772   
5773         GrouperInstallerUtils.execCommand(
5774             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
5775            new File(this.gshCommand()).getParentFile(), null, true);
5776   
5777       }
5778       
5779     }
5780 
5781     {
5782       boolean lessThan2_2_1 = giGrouperVersion.lessThanArg(new GiGrouperVersion("2.2.1"));
5783       String autorunRun2_2_1gshUpgradeScript = null;
5784       if (lessThan2_2_1) {
5785         System.out.println("You are upgrading from pre API version 2.2.1, do you want to "
5786             + "run the 2.2.1 upgrade GSH script (recommended) (t|f)? [t]: ");
5787         autorunRun2_2_1gshUpgradeScript = "grouperInstaller.autorun.run2.2.1gshUpgradeScriptPre2.2.1";
5788       } else {
5789         System.out.println("You are upgrading from after API version 2.2.1, so you dont have to do this,\n  "
5790             + "but do you want to run the 2.2.1 upgrade GSH script (not recommended) (t|f)? [f]: ");
5791         autorunRun2_2_1gshUpgradeScript = "grouperInstaller.autorun.run2.2.1gshUpgradeScriptPost2.2.1";
5792       }
5793       boolean runScript = readFromStdInBoolean(lessThan2_2_1, autorunRun2_2_1gshUpgradeScript);
5794       
5795       if (runScript) {
5796         
5797         File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "misc" + File.separator + "postGrouper2_2_1Upgrade.gsh");
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("Running 2.2.1 upgrade GSH 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     {
5815       boolean lessThan2_3_0 = giGrouperVersion.lessThanArg(new GiGrouperVersion("2.3.0"));
5816       String autorunRun2_3_0gshUpgradeScript = null;
5817       if (lessThan2_3_0) {
5818         System.out.println("You are upgrading from pre API version 2.3.0, do you want to "
5819             + "run the 2.3.0 upgrade GSH script (recommended) (t|f)? [t]: ");
5820         autorunRun2_3_0gshUpgradeScript = "grouperInstaller.autorun.run2.3.0gshUpgradeScriptPre2.3.0";
5821       } else {
5822         System.out.println("You are upgrading from after API version 2.3.0, so you dont have to do this,\n  "
5823             + "but do you want to run the 2.3.0 upgrade GSH script (not recommended) (t|f)? [f]: ");
5824         autorunRun2_3_0gshUpgradeScript = "grouperInstaller.autorun.run2.3.0gshUpgradeScriptPost2.3.0";
5825       }
5826       boolean runScript = readFromStdInBoolean(lessThan2_3_0, autorunRun2_3_0gshUpgradeScript);
5827       
5828       if (runScript) {
5829         
5830         File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "misc" + File.separator + "postGrouper2_3_0Upgrade.gsh");
5831         
5832         List<String> commands = new ArrayList<String>();
5833 
5834         addGshCommands(commands);
5835         commands.add(gshFile.getAbsolutePath());
5836 
5837         System.out.println("\n##################################");
5838         System.out.println("Running 2.3.0 upgrade GSH with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
5839 
5840         GrouperInstallerUtils.execCommand(
5841             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
5842            new File(this.gshCommand()).getParentFile(), null, true);
5843 
5844       }
5845     }
5846 
5847   }
5848 
5849   /**
5850    * grouper version of jar
5851    */
5852   private GiGrouperVersion grouperVersionOfJar = null;
5853   
5854   /**
5855    * 
5856    * @return the version
5857    */
5858   private GiGrouperVersion grouperVersionOfJar() {
5859     if (this.grouperVersionOfJar == null) {
5860       String grouperJarVersionString = null;
5861       if (this.grouperJar != null && this.grouperJar.exists()) {
5862         grouperJarVersionString = GrouperInstallerUtils.jarVersion(this.grouperJar);
5863         
5864       } else if (this.grouperClientJar != null && this.grouperClientJar.exists()) {
5865         grouperJarVersionString = GrouperInstallerUtils.jarVersion(this.grouperClientJar);
5866         
5867       }
5868       if (!GrouperInstallerUtils.isBlank(grouperJarVersionString)) {
5869         this.grouperVersionOfJar = new GiGrouperVersion(grouperJarVersionString);
5870       }
5871   
5872       if (this.grouperVersionOfJar == null) {
5873         throw new RuntimeException("Cant find version of grouper! " + this.grouperJar + ", " + this.grouperClientJar);
5874       }
5875     }
5876     
5877     return this.grouperVersionOfJar;
5878   }
5879   
5880   /**
5881    * grouper jar version e.g. 2.1.5
5882    */
5883   private GiGrouperVersion originalGrouperJarVersion = null;
5884   
5885   /**
5886    * keep trac if we have found it or not
5887    */
5888   private boolean originalGrouperJarVersionRetrieved = false;
5889   
5890   /**
5891    * get the version of the grouper jar
5892    * @return the version or null if couldnt be found
5893    */
5894   private GiGrouperVersion originalGrouperJarVersionOrUpgradeFileVersion() {
5895 
5896     if (!this.originalGrouperJarVersionRetrieved) {
5897 
5898       this.originalGrouperJarVersionRetrieved = true;
5899       
5900       //lets see if an upgrade went halfway through
5901       this.grouperUpgradeOriginalVersionFile = new File(this.upgradeExistingApplicationDirectoryString + "grouperUpgradeOriginalVersion.txt");
5902 
5903       this.originalGrouperJarVersion = this.grouperVersionOfJar();
5904       
5905       if (this.grouperUpgradeOriginalVersionFile.exists()) {
5906         String grouperJarVersionString = GrouperInstallerUtils.readFileIntoString(this.grouperUpgradeOriginalVersionFile);
5907         GiGrouperVersionrsion.html#GiGrouperVersion">GiGrouperVersion fileGrouperJarVersion = new GiGrouperVersion(grouperJarVersionString);
5908         
5909         if (fileGrouperJarVersion != this.originalGrouperJarVersion) {
5910           
5911           System.out.println("It is detected that an upgrade did not complete from version " + fileGrouperJarVersion);
5912           this.originalGrouperJarVersion = fileGrouperJarVersion;
5913         }
5914       } else {
5915         GrouperInstallerUtils.writeStringToFile(this.grouperUpgradeOriginalVersionFile, this.originalGrouperJarVersion.toString());
5916       }
5917     }
5918     
5919     return this.originalGrouperJarVersion;
5920   }
5921   
5922   /**
5923    * file where version is kept for partial upgrades
5924    */
5925   private File grouperUpgradeOriginalVersionFile;
5926   
5927   /**
5928    * @param firstTime if first time
5929    */
5930   private void apiUpgradeDbVersion(boolean firstTime) {
5931     
5932     if (!GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.api.checkDdlVersion", true, false)) {
5933       System.out.println("Not checking DDL version since grouper.installer.properties: grouperInstaller.default.api.checkDdlVersion = false");
5934       return;
5935     }
5936 
5937     List<String> commands = new ArrayList<String>();
5938 
5939     addGshCommands(commands);
5940     commands.add("-registry");
5941     commands.add("-check");
5942     commands.add("-noprompt");
5943 
5944     System.out.println("\n##################################");
5945     System.out.println("Checking API database version with command: " + convertCommandsIntoCommand(commands) + "\n");
5946 
5947     CommandResult commandResult = GrouperInstallerUtils.execCommand(
5948         GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
5949         new File(this.gshCommand()).getParentFile(), null, true);
5950     
5951     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
5952       System.out.println("stdout: " + commandResult.getOutputText());
5953     }
5954     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
5955       System.out.println("stderr: " + commandResult.getErrorText());
5956     }
5957 
5958     String result = commandResult.getErrorText().trim();
5959     
5960     // Grouper ddl object type 'Grouper' has dbVersion: 27 and java version: 28
5961     // NOTE: Grouper database schema DDL may require updates, but the temp change log must 
5962     // be empty to perform an upgrade.  To process the temp change log, start up your current 
5963     // version of GSH and run: loaderRunOneJob("CHANGE_LOG_changeLogTempToChangeLog")
5964     if (result != null && result.contains("CHANGE_LOG_changeLogTempToChangeLog")) {
5965       System.out.println("You must run the change log temp to change log before upgrading.  You can start the upgrader again and run it.");
5966       System.exit(1);
5967     }
5968     
5969     String[] lines = GrouperInstallerUtils.splitLines(result);
5970     {
5971       boolean okWithVersion = false;
5972       boolean notOkWithVersion = false;
5973       for (String line : lines) {
5974         line = line.toLowerCase();
5975         //expecting stderr: NOTE: database table/object structure (ddl) is up to date
5976         if (line.contains("ddl") && line.contains("up to date") && line.contains("note:")) {
5977           okWithVersion = true;
5978         }
5979         //cant have this line
5980         if (line.contains("requires updates")) {
5981           notOkWithVersion = true;
5982         }
5983       }
5984       if (okWithVersion && !notOkWithVersion) {
5985         return;
5986       }
5987     }
5988 
5989     if (!firstTime) {
5990       System.out.println("Error: we tried to upgrade the database but it didnt work, would you like to continue skipping DDL (t|f)? ");
5991       boolean continueOn = readFromStdInBoolean(null, "grouperInstaller.autorun.shouldContinueIfErrorUpgradingDatabase");
5992       if (continueOn) {
5993         return;
5994       }
5995     }
5996     
5997     //we need to upgrade the DDL
5998     //Grouper ddl object type 'Grouper' has dbVersion: 26 and java version: 28
5999     //Grouper database schema DDL requires updates
6000     //(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),
6001     //script file is:
6002     //C:\app\grouper_2_2_0_installer\grouper.apiBinary-2.2.0\ddlScripts\grouperDdl_20141014_10_17_12_577.sql
6003     //Note: this script was not executed due to option passed in
6004     //To run script via gsh, carefully review it, then run this:
6005     //gsh -registry -runsqlfile C:\\app\\grouper_2_2_0_installer\\grouper.apiBinary-2.2.0\\ddlScripts\\grouperDdl_20141014_10_17_12_577.sql
6006 
6007     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]: ");
6008     boolean runIt = readFromStdInBoolean(true, "grouperInstaller.autorun.shouldRunDdlScript");
6009     
6010     if (runIt) {
6011 
6012       boolean foundScript = false;
6013 
6014       for (String line : lines) {
6015         if (line.contains("-registry -runsqlfile")) {
6016           
6017           String regexPattern = "^[^\\s]+\\s+-registry -runsqlfile (.*)$";
6018           Pattern pattern = Pattern.compile(regexPattern);
6019           
6020           Matcher matcher = pattern.matcher(line);
6021           
6022           if (!matcher.matches()) {
6023             throw new RuntimeException("Expected " + regexPattern + " but received: " + line);
6024           }
6025 
6026           String fileName = matcher.group(1);
6027           
6028           commands = new ArrayList<String>();
6029           
6030           addGshCommands(commands);
6031           commands.add("-registry");
6032           commands.add("-noprompt");
6033           commands.add("-runsqlfile");
6034           commands.add(fileName);
6035           
6036           foundScript = true;
6037           
6038           System.out.println("\n##################################");
6039           System.out.println("Upgrading database with command: " + convertCommandsIntoCommand(commands) + "\n");
6040 
6041           commandResult = GrouperInstallerUtils.execCommand(
6042               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
6043              new File(this.gshCommand()).getParentFile(), null, true);
6044 
6045           //no out/err since printing as we go
6046           System.out.println("\nDone upgrading database");
6047           System.out.println("\n##################################\n");
6048         }
6049       }
6050       //cant find script, thats ok, just check and go
6051       if (!foundScript) {
6052         throw new RuntimeException("didnt find script to to run: " + result);
6053       }
6054 
6055       //check again to make sure ok
6056       apiUpgradeDbVersion(false);
6057     }
6058   }
6059 
6060   /**
6061    * upgrade jars from a directory
6062    * @param fromDir jars from this directory
6063    */
6064   private void upgradeJars(File fromDir) {
6065     this.upgradeJars(fromDir, new File(this.upgradeExistingLibDirectoryString));
6066   }
6067   
6068   /**
6069    * upgrade jars from a directory
6070    * @param fromDir jars from this directory
6071    * @param toDir is where jars go if not there
6072    */
6073   private void upgradeJars(File fromDir, File toDir) {
6074 
6075     //for each jar in the directory
6076     if (!fromDir.exists() || !fromDir.isDirectory()) {
6077       throw new RuntimeException("Why does jar directory not exist? " + fromDir);
6078     }
6079     
6080     int changes = 0;
6081     
6082     // sort the files to get them a little more reproducible
6083     File[] fromFiles = GrouperInstallerUtils.nonNull(fromDir.listFiles(), File.class);
6084     List<File> fromFilesList = GrouperInstallerUtils.toList(fromFiles);
6085     Collections.sort(fromFilesList);
6086     for (File jarFile : fromFilesList) {
6087       
6088       //only do jar files
6089       if (!jarFile.getName().endsWith(".jar")) {
6090         continue;
6091       }
6092       
6093       // File existingJar = this.findLibraryFile(jarFile.getName(), false);
6094 
6095       List<File> relatedJars = null;
6096       
6097       relatedJars = GrouperInstallerUtils.jarFindJar(toDir, jarFile.getName());
6098       
6099       boolean foundFile = false;
6100       if (GrouperInstallerUtils.length(relatedJars) > 0) {
6101         
6102         for (File relatedJar : relatedJars) {
6103           if (!relatedJar.exists()) {
6104             continue;
6105           }
6106           if (GrouperInstallerUtils.fileSha1(relatedJar).equals(GrouperInstallerUtils.fileSha1(jarFile))) {
6107             if (relatedJar.getName().equals(jarFile.getName())) {
6108               foundFile = true;
6109               continue;
6110             }
6111           }
6112           
6113           File bakFile = bakFile(relatedJar);
6114           
6115           System.out.println("Deleting " + relatedJar.getAbsolutePath() + ", backed up to: " + bakFile.getAbsolutePath());
6116           changes++;
6117           boolean moved = GrouperInstallerUtils.fileMove(relatedJar, bakFile, false);
6118           if (!moved) {
6119             System.out.println("Non-fatal error: could not delete file: " + relatedJar.getAbsolutePath() 
6120                 + ",\ndelete this file when all processed are terminated.  Press <enter> to acknowledge this.");
6121             readFromStdIn("grouperInstaller.autorun.continueAfterCantDeleteJar");
6122           }
6123         }
6124       }
6125       if (!foundFile) {
6126         changes += this.compareAndReplaceJar(null, jarFile, false, toDir) ? 1 : 0;
6127       }
6128           
6129       
6130 //      if (existingJar == null) {
6131 //        //see if one exists by another version
6132 //        if (GrouperInstallerUtils.length(relatedJars) == 1) {
6133 //          existingJar = relatedJars.get(0);
6134 //        }
6135 //      }
6136 
6137     }
6138 
6139     System.out.println("Upgraded " + changes + " jar files from: " + fromDir.getAbsolutePath()
6140         + "\n  to: " + toDir.getAbsolutePath());
6141     
6142   }
6143   
6144   /**
6145    * 
6146    */
6147   private void upgradeEhcacheXml() {
6148 
6149     //ehcache, prompt to see if do it (if difference than example, and if old example different than new example?
6150     File newEhcacheExample = new File(this.untarredApiDir + File.separator + "conf" + File.separator + "ehcache.xml");
6151 
6152     //this file is done
6153     if (!newEhcacheExample.exists() || this.ehcacheFile == null || !this.ehcacheFile.exists()) {
6154       return;
6155     }
6156     
6157     //lets see if different
6158     String existingEhcacheContents = GrouperInstallerUtils.readFileIntoString(this.ehcacheFile);
6159     String existingExampleEhcacheContents = GrouperInstallerUtils.readFileIntoString(this.ehcacheExampleFile);
6160     String newEhcacheContents = GrouperInstallerUtils.readFileIntoString(newEhcacheExample);
6161     
6162     //if existing is the same as new...
6163     if (GrouperInstallerUtils.equals(existingEhcacheContents, newEhcacheContents)) {
6164       //make sure example is up to date
6165       if (this.ehcacheExampleFile != null && !GrouperInstallerUtils.equals(existingExampleEhcacheContents, newEhcacheContents)) {
6166         this.backupAndCopyFile(newEhcacheExample, this.ehcacheExampleFile, true);
6167       }
6168 
6169       //we are all good
6170       return;
6171     }
6172 
6173     //lets backup the example and regular file
6174     File ehcacheBakFile = bakFile(this.ehcacheFile);
6175     GrouperInstallerUtils.copyFile(this.ehcacheFile, ehcacheBakFile, true);
6176 
6177     boolean mergeFiles = true;
6178     
6179     if (this.ehcacheExampleFile != null) {
6180       File ehcacheExampleBakFile = bakFile(this.ehcacheExampleFile);
6181   
6182       GrouperInstallerUtils.copyFile(this.ehcacheExampleFile, ehcacheExampleBakFile, true);
6183     } else {
6184       GrouperInstallerUtils.copyFile(newEhcacheExample, this.ehcacheFile, true);
6185       mergeFiles = false;
6186     }
6187 
6188     if (mergeFiles) {
6189       //if the ehcache is the same as the example, lets just copy
6190       if (GrouperInstallerUtils.equals(existingEhcacheContents, existingExampleEhcacheContents)) {
6191         this.backupAndCopyFile(newEhcacheExample, this.ehcacheFile, false);
6192         if (this.ehcacheExampleFile != null) {
6193           this.backupAndCopyFile(newEhcacheExample, this.ehcacheExampleFile, false);
6194         }
6195         return;
6196       }
6197 
6198       //the ehcache file is different from the example and different from the new one, so merge it in
6199       mergeEhcacheXmlFiles(newEhcacheExample, this.ehcacheExampleFile, this.ehcacheFile);
6200     }
6201 
6202     System.out.println("Compare you old ehcache.xml with the new ehcache.xml file: " 
6203         + "\n  Old file: "
6204         + ehcacheBakFile.getAbsolutePath()
6205         + "\n  New file: " + this.ehcacheFile.getAbsolutePath()
6206         + "\n  Press <enter> when done");
6207     readFromStdIn("grouperInstaller.autorun.continueAfterCompareEhcache");
6208 
6209   }
6210 
6211   /**
6212    * 
6213    */
6214   private void upgradeEhcacheXmlToProperties() {
6215 
6216     //dont do this is less than 2.3.1
6217     if (new GiGrouperVersionersion(this.version).lessThanArg(new GiGrouperVersion("2.3.1"))) {
6218       return;
6219     }
6220     
6221     // the file may have been added during the install
6222     if (this.grouperCacheBasePropertiesFile == null) {
6223       this.grouperCacheBasePropertiesFile = findClasspathFile("grouper.cache.base.properties", false);
6224     }
6225     
6226     //this file is done
6227     if ((this.ehcacheFile == null || !this.ehcacheFile.exists())
6228         && this.grouperCacheBasePropertiesFile.exists() && this.grouperCachePropertiesFile.exists()) {
6229       return;
6230     }
6231     
6232     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]: ");
6233     boolean convert = readFromStdInBoolean(true, "grouperInstaller.autorun.convertEhcacheXmlToProperties");
6234 
6235     if (!convert) {
6236       System.out.println("Note: grouper will not run, but whatever you want to do!!!!");
6237     }
6238     
6239     // the file may have been added during the install
6240     if (this.grouperCachePropertiesFile == null) {
6241       this.grouperCachePropertiesFile = findClasspathFile("grouper.cache.properties", false);
6242     }
6243     
6244     if (this.grouperCachePropertiesFile.exists()) {
6245       //see if there is anything in it
6246       Properties grouperCacheProperties = GrouperInstallerUtils.propertiesFromFile(this.grouperCachePropertiesFile);
6247       if (grouperCacheProperties.size() > 0) {
6248         this.backupAndDeleteFile(this.grouperCachePropertiesFile, true);
6249       } else {
6250         GrouperInstallerUtils.fileDelete(this.grouperCachePropertiesFile);
6251       }
6252     }
6253 
6254     URL ehcacheXmlUrl = null;
6255     
6256     try {
6257       ehcacheXmlUrl = this.ehcacheFile.toURI().toURL();
6258     } catch (Exception e) {
6259       throw new RuntimeException("Problem with ehcache.xml: " + (this.ehcacheFile == null ? null : this.ehcacheFile.getAbsoluteFile()), e);
6260     }
6261     
6262     //convert
6263     convertEhcacheXmlToProperties(this.grouperCacheBasePropertiesFile, this.grouperCachePropertiesFile, ehcacheXmlUrl);
6264     
6265     File bakFile = bakFile(this.grouperCachePropertiesFile);
6266     GrouperInstallerUtils.copyFile(this.grouperCachePropertiesFile, bakFile, true);
6267     this.backupAndDeleteFile(this.ehcacheFile, true);
6268     this.backupAndDeleteFile(this.ehcacheExampleFile, true);
6269     
6270   }
6271 
6272   /**
6273    * 
6274    * @param newFile
6275    * @param existingFile
6276    * @param printDetails
6277    * @return the bakFile
6278    */
6279   public File backupAndCopyFile(File newFile, File existingFile, boolean printDetails) {
6280     
6281     if (!GrouperInstallerUtils.contentEquals(newFile, existingFile)) {
6282       
6283       File bakFile = null;
6284           
6285       boolean fileExists = existingFile.exists();
6286       if (fileExists) {
6287         bakFile = bakFile(existingFile);
6288         GrouperInstallerUtils.copyFile(existingFile, bakFile, true);
6289         if (printDetails) {
6290           System.out.println("Backing up: " + existingFile.getAbsolutePath() + " to: " + bakFile.getAbsolutePath());
6291         }
6292       }
6293       if (printDetails) {
6294         System.out.println("Copying " + (fileExists ? "new file" : "upgraded file") + ": " + newFile.getAbsolutePath() + " to: " + existingFile.getAbsolutePath());
6295       }
6296       GrouperInstallerUtils.copyFile(newFile, existingFile, false);
6297       return bakFile;
6298       
6299     }
6300 
6301     if (printDetails) {
6302       System.out.println(existingFile.getAbsolutePath() + " has not been updated so it was not changed");
6303     }
6304     
6305     return null;
6306   }
6307 
6308   /**
6309    * @param file
6310    * @param printDetails
6311    * @return the bakFile
6312    */
6313   public File backupAndDeleteFile(File file, boolean printDetails) {
6314 
6315     if (file != null && file.exists()) {
6316 
6317       File bakFile = null;
6318 
6319       bakFile = bakFile(file);
6320       GrouperInstallerUtils.copyFile(file, bakFile, true);
6321       if (printDetails) {
6322         System.out.println("Backing up: " + file.getAbsolutePath() + " to: " + bakFile.getAbsolutePath());
6323       }
6324       if (printDetails) {
6325         System.out.println("Deleting file: " + file.getAbsolutePath());
6326       }
6327       GrouperInstallerUtils.fileDelete(file);
6328       return bakFile;
6329 
6330     }
6331 
6332     if (printDetails) {
6333       System.out.println(file + " did not exist so it was not deleted");
6334     }
6335 
6336     return null;
6337   }
6338 
6339   /**
6340    * 
6341    * @param existingFile
6342    * @return the bak file
6343    */
6344   public File bakFile(File existingFile) {
6345     String existingFilePath = existingFile.getAbsolutePath();
6346     if (!GrouperInstallerUtils.filePathStartsWith(existingFilePath, this.upgradeExistingApplicationDirectoryString)) {
6347       throw new RuntimeException("Why does existing path not start with upgrade path??? " 
6348           + existingFilePath + ", " + this.upgradeExistingApplicationDirectoryString);
6349     }
6350     
6351     String bakString = this.grouperBaseBakDir 
6352         + existingFilePath.substring(this.upgradeExistingApplicationDirectoryString.length());
6353 
6354     File bakFile = new File(bakString);
6355     return bakFile;
6356   }
6357   
6358   /**
6359    * @param existingBasePropertiesFile 
6360    * @param newBasePropertiesFile 
6361    * @param existingPropertiesFile 
6362    * @param existingExamplePropertiesFile 
6363    * @param propertiesToIgnore
6364    * @param autorunPropertiesKeyRemoveRedundantProperties key in properties file to automatically fill in a value
6365    */
6366   private void compareUpgradePropertiesFile(File existingBasePropertiesFile, 
6367       File newBasePropertiesFile,
6368       File existingPropertiesFile,
6369       File existingExamplePropertiesFile,
6370       Set<String> propertiesToIgnore, String autorunPropertiesKeyRemoveRedundantProperties) {
6371 
6372     boolean hadChange = false;
6373     
6374     if (!newBasePropertiesFile.exists() || !newBasePropertiesFile.isFile()) {
6375       throw new RuntimeException("Why does this file not exist? " + newBasePropertiesFile.getAbsolutePath());
6376     }
6377     
6378     //if there is an existing base properties file, compare and replace and done
6379     if (existingBasePropertiesFile != null && existingBasePropertiesFile.exists() && existingBasePropertiesFile.isFile()) {
6380       
6381       String existingBaseContents = GrouperInstallerUtils.readFileIntoString(existingBasePropertiesFile);
6382       String newBaseContents = GrouperInstallerUtils.readFileIntoString(newBasePropertiesFile);
6383       
6384       if (!GrouperInstallerUtils.equals(existingBaseContents, newBaseContents)) {
6385         
6386         String existingBasePropertiesFilePath = existingBasePropertiesFile.getAbsolutePath();
6387         if (!GrouperInstallerUtils.filePathStartsWith(existingBasePropertiesFilePath, this.upgradeExistingApplicationDirectoryString)) {
6388           throw new RuntimeException("Why does existing path not start with upgrade path??? " 
6389               + existingBasePropertiesFilePath + ", " + this.upgradeExistingApplicationDirectoryString);
6390         }
6391         
6392         String bakBasePropertiesString = this.grouperBaseBakDir + existingBasePropertiesFilePath.substring(this.upgradeExistingApplicationDirectoryString.length());
6393 
6394         File bakBasePropertiesFile = new File(bakBasePropertiesString);
6395         
6396         //make sure parents exist
6397         GrouperInstallerUtils.createParentDirectories(bakBasePropertiesFile);
6398 
6399         System.out.println(existingBasePropertiesFile.getName() + " has changes and was upgraded.\n  It is backed up to " 
6400             + bakBasePropertiesFile.getAbsolutePath());
6401         
6402         hadChange = true;
6403         
6404         GrouperInstallerUtils.fileMove(existingBasePropertiesFile, bakBasePropertiesFile);
6405         
6406         GrouperInstallerUtils.copyFile(newBasePropertiesFile, existingBasePropertiesFile, true);
6407 
6408       }
6409       
6410     } else {
6411       
6412       hadChange = true;
6413       
6414       System.out.println(newBasePropertiesFile.getName() + " didn't exist and was installed.");
6415       
6416       //its null, but we dont have the path...
6417       if (existingBasePropertiesFile == null) {
6418         existingBasePropertiesFile = new File(this.upgradeExistingClassesDirectoryString + newBasePropertiesFile.getName());
6419       }
6420       GrouperInstallerUtils.copyFile(newBasePropertiesFile, existingBasePropertiesFile, true);
6421     }
6422     
6423     // if there is an example there, it can be removed
6424     if (existingExamplePropertiesFile != null && existingExamplePropertiesFile.exists() && existingExamplePropertiesFile.isFile()) {
6425 
6426       String existingExamplePropertiesFilePath = existingExamplePropertiesFile.getAbsolutePath();
6427       if (!GrouperInstallerUtils.filePathStartsWith(existingExamplePropertiesFilePath, this.upgradeExistingApplicationDirectoryString)) {
6428         throw new RuntimeException("Why does existing path not start with upgrade path??? " 
6429             + existingExamplePropertiesFilePath + ", " + this.upgradeExistingApplicationDirectoryString);
6430       }
6431       
6432       String bakExamplePropertiesString = this.grouperBaseBakDir 
6433           + existingExamplePropertiesFilePath.substring(this.upgradeExistingApplicationDirectoryString.length());
6434 
6435       File bakExamplePropertiesFile = new File(bakExamplePropertiesString);
6436       
6437       //make sure parents exist
6438       GrouperInstallerUtils.createParentDirectories(bakExamplePropertiesFile);
6439 
6440       System.out.println(existingExamplePropertiesFile.getName() + " is not needed and was deleted.\n  It is backed up to " 
6441           + bakExamplePropertiesFile.getAbsolutePath());
6442 
6443       GrouperInstallerUtils.fileMove(existingExamplePropertiesFile, bakExamplePropertiesFile);
6444     
6445     }
6446 
6447     if (existingPropertiesFile != null && existingPropertiesFile.exists() && existingPropertiesFile.isFile()) {
6448       
6449       // now then, if there is a properties file, we can look for duplicate configs, and remove them...
6450       Set<String> duplicateConfigPropertyNames = configPropertyDuplicates(newBasePropertiesFile, existingPropertiesFile);
6451 
6452       if (GrouperInstallerUtils.length(propertiesToIgnore) > 0 && GrouperInstallerUtils.length(duplicateConfigPropertyNames) > 0) {
6453         duplicateConfigPropertyNames.addAll(propertiesToIgnore);
6454       }
6455       
6456       if (GrouperInstallerUtils.length(duplicateConfigPropertyNames) > 0) {
6457 
6458         hadChange = true;
6459         
6460         System.out.println(existingPropertiesFile.getName() + " has " + duplicateConfigPropertyNames.size() 
6461             + " properties that can be removed since the values are the same in "
6462             + newBasePropertiesFile.getName());
6463 
6464         System.out.println("Would you like to have the " + duplicateConfigPropertyNames.size() 
6465             + " redundant properties automatically removed from " 
6466             + existingPropertiesFile.getName() + " (t|f)? [t]: ");
6467         boolean removeRedundantProperties = readFromStdInBoolean(true, autorunPropertiesKeyRemoveRedundantProperties);
6468         
6469         if (removeRedundantProperties) {
6470 
6471           String existingPropertiesFilePath = existingPropertiesFile.getAbsolutePath();
6472           if (!GrouperInstallerUtils.filePathStartsWith(existingPropertiesFilePath, this.upgradeExistingApplicationDirectoryString)) {
6473             throw new RuntimeException("Why does existing path not start with upgrade path??? " 
6474                 + existingPropertiesFilePath + ", " + this.upgradeExistingApplicationDirectoryString);
6475           }
6476           
6477           String bakPropertiesString = this.grouperBaseBakDir 
6478               + existingPropertiesFilePath.substring(this.upgradeExistingApplicationDirectoryString.length());
6479 
6480           File bakPropertiesFile = new File(bakPropertiesString);
6481           
6482           //make sure parents exist
6483           GrouperInstallerUtils.createParentDirectories(bakPropertiesFile);
6484 
6485           System.out.println(existingPropertiesFile.getName() + " had redundant properties removed after being backed up to " 
6486               + bakPropertiesFile.getAbsolutePath());
6487 
6488           GrouperInstallerUtils.copyFile(existingPropertiesFile, bakPropertiesFile, true);
6489           
6490           removeRedundantProperties(existingPropertiesFile, duplicateConfigPropertyNames);
6491           
6492         }
6493       }
6494     } else {
6495       
6496       hadChange = true;
6497       
6498       //if we didnt have a properties file, create one
6499       //file is null...
6500       String contents = "\n# The " + newBasePropertiesFile.getName().replace(".base", "") 
6501           + " file uses Grouper Configuration Overlays (documented on wiki)\n"
6502           + "# By default the configuration is read from " + newBasePropertiesFile.getName() + "\n"
6503           + "# (which should not be edited), and the " +newBasePropertiesFile.getName().replace(".base", "") + " overlays\n"
6504           + "# the base settings.  See the " + newBasePropertiesFile.getName() + " for the possible\n"
6505           + "# settings that can be applied to the " + newBasePropertiesFile.getName().replace(".base", "") + "\n\n";
6506 
6507       File file = null;
6508       
6509       if (existingPropertiesFile != null) {
6510         file = existingPropertiesFile;
6511       } else if (existingBasePropertiesFile != null) {
6512         file = new File(existingBasePropertiesFile.getAbsolutePath().replace(".base", ""));
6513 //      } else {
6514 //        String fileName =  existingPropertiesFile != null ? existingPropertiesFile.getAbsolutePath() : this.upgradeExistingClassesDirectoryString + newBasePropertiesFile.getName().replace(".base", "");
6515 //        file = new File(fileName);
6516       }
6517       
6518       System.out.println("Created overlay config file: " + file.getAbsolutePath());
6519       
6520       GrouperInstallerUtils.saveStringIntoFile(file, contents);
6521     }
6522     
6523     if (!hadChange) {
6524       System.out.println("Found no changes in " + existingBasePropertiesFile.getAbsolutePath());
6525     }
6526     
6527   }
6528 
6529   /**
6530    * remove duplicate properties
6531    * @param propertiesFile
6532    * @param duplicatePropertyNames
6533    */
6534   private static void removeRedundantProperties(File propertiesFile, Set<String> duplicatePropertyNames) {
6535     
6536     String fileContents = GrouperInstallerUtils.readFileIntoString(propertiesFile);
6537     
6538     String newline = GrouperInstallerUtils.newlineFromFile(fileContents);
6539 
6540     StringBuilder newContents = new StringBuilder();
6541 
6542     String[] lines = GrouperInstallerUtils.splitLines(fileContents);
6543     
6544     boolean inStartComments = true;
6545     boolean inHeaderComments = false;
6546 
6547     StringBuilder captureHeader = new StringBuilder();
6548     StringBuilder propertyAndComments = new StringBuilder();
6549     
6550     for (String line: lines) {
6551       
6552       line = line.trim();
6553       
6554       boolean isBlank = GrouperInstallerUtils.isBlank(line);
6555       boolean isComment = line.startsWith("#");
6556       boolean isSingleComment = line.startsWith("#") && !line.startsWith("##");
6557       boolean isHeaderComment = line.contains("#####");
6558       boolean isProperty = !isBlank && !isComment;
6559       
6560       //if in header then we are done with the start comments
6561       if (isHeaderComment) {
6562         inStartComments = false;
6563       }
6564 
6565       //we want to keep the start comments
6566       if (inStartComments) {
6567         
6568         if (isBlank || isComment) {
6569           newContents.append(line).append(newline);
6570           continue;
6571         }
6572         inStartComments = false;
6573       }
6574 
6575       //we are done with headers
6576       if (isProperty || isBlank || isSingleComment) {
6577         inHeaderComments = false;
6578       }
6579 
6580       if (isHeaderComment) {
6581         //if header and in headers, then we arent in headers
6582         if (inHeaderComments) {
6583           inHeaderComments = false;
6584         } else {
6585           //if this is a header, and we arent in headers, then we are in headers
6586           inHeaderComments = true;          
6587           captureHeader.setLength(0);
6588         }
6589       }
6590 
6591       if (isHeaderComment || inHeaderComments) {
6592         propertyAndComments.setLength(0);
6593         captureHeader.append(line).append(newline);
6594         continue;
6595       }
6596       
6597       if (isProperty) {
6598         
6599         //get the property
6600         int equalsIndex = line.indexOf('=');
6601         if (equalsIndex == -1) {
6602           //uh... ignore this... 
6603           System.out.println("Invalid line removed from properties file: " + propertiesFile.getAbsolutePath() + ":\n  " + line);
6604           continue;
6605         }
6606         
6607         String propertyName = line.substring(0, equalsIndex).trim();
6608         //unescape colons...
6609         if (duplicatePropertyNames.contains(propertyName) || duplicatePropertyNames.contains(propertyName.replace("\\:", ":"))) {
6610           propertyAndComments.setLength(0);
6611           //remove it!
6612           continue;
6613         }
6614 
6615         //keep it
6616         propertyAndComments.append(line).append(newline);
6617 
6618         //we need a header if there is one
6619         if (captureHeader.length() > 0) {
6620           newContents.append(newline);
6621           newContents.append(captureHeader);
6622           captureHeader.setLength(0);
6623         }
6624 
6625         //append the property and contents
6626         newContents.append(propertyAndComments);
6627 
6628         propertyAndComments.setLength(0);
6629         continue;
6630       }
6631       
6632       //must be whitespace or comment...
6633       propertyAndComments.append(line).append(newline);
6634     }
6635     
6636     GrouperInstallerUtils.saveStringIntoFile(propertiesFile, newContents.toString());
6637     
6638   }
6639   
6640   /**
6641    * 
6642    * @param file1
6643    * @param file2
6644    * @return the property names which are the same
6645    */
6646   @SuppressWarnings("unchecked")
6647   public static Set<String> configPropertyDuplicates(File file1, File file2) {
6648     Properties file1properties = GrouperInstallerUtils.propertiesFromFile(file1);
6649     Properties file2properties = GrouperInstallerUtils.propertiesFromFile(file2);
6650     
6651     Set<String> duplicatePropertyNames = new LinkedHashSet<String>();
6652     
6653     for (String propertyName : (Set<String>)(Object)file2properties.keySet()) {
6654       
6655       String file1Value = GrouperInstallerUtils.trimToEmpty(file1properties.getProperty(propertyName));
6656       String file2Value = GrouperInstallerUtils.trimToEmpty(file2properties.getProperty(propertyName));
6657       
6658       if (GrouperInstallerUtils.equals(file1Value, file2Value)) {
6659         duplicatePropertyNames.add(propertyName);
6660       }
6661       
6662     }
6663     return duplicatePropertyNames;
6664   }
6665   
6666   
6667   /**
6668    * the location of the existing installation, must end in file separator
6669    */
6670   private String upgradeExistingApplicationDirectoryString;
6671   
6672   /**
6673    * 
6674    */
6675   private static enum AppToUpgrade {
6676     
6677     /**
6678      * upgrading the UI
6679      */
6680     UI {
6681 
6682       @Override
6683       public void patchStatus(GrouperInstaller grouperInstaller) {
6684         grouperInstaller.patchStatusUi();
6685       }
6686 
6687       @Override
6688       public void patch(GrouperInstaller grouperInstaller) {
6689         grouperInstaller.patchUi();
6690       }
6691 
6692       @Override
6693       public void revertPatch(GrouperInstaller grouperInstaller) {
6694         grouperInstaller.patchRevertUi();
6695       }
6696 
6697       @Override
6698       public boolean validateExistingDirectory(GrouperInstaller grouperInstaller) {
6699         //API and client are in the UI
6700         if (!API.validateExistingDirectory(grouperInstaller)) {
6701           return false;
6702         }
6703         
6704         //no need to check if it exists... its new in 2.2
6705 
6706         //grouperInstaller.mediaPropertiesFile = grouperInstaller.findClasspathFile("media.properties", false);
6707 
6708         //media should be there, but not forever
6709         
6710         return true;
6711       }
6712 
6713       @Override
6714       public void downloadAndBuildGrouperProjects(GrouperInstaller grouperInstaller) {
6715         API.downloadAndBuildGrouperProjects(grouperInstaller);
6716         
6717         //####################################
6718         //download and configure ui
6719         grouperInstaller.downloadAndConfigureUi();
6720 
6721         //####################################
6722         //get ant
6723         grouperInstaller.downloadAndUnzipAnt();
6724 
6725         //####################################
6726         //build UI
6727         grouperInstaller.buildUi(false);
6728 
6729         File serverXml = null;
6730         for (int i=0;i<10;i++) {
6731           String defaultServerXml = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.ui.server.xml", false);
6732           System.out.println("What is the location of your tomcat server.xml for the UI?  "
6733               + "Note, if you dont use tomcat just leave it blank or type 'blank': " 
6734               + (GrouperInstallerUtils.isBlank(defaultServerXml) ? "" : ("[" + defaultServerXml + "]: ")));
6735           String serverXmlLocation = readFromStdIn("grouperInstaller.autorun.locationOfTomcatServerXml");
6736           
6737           if (GrouperInstallerUtils.equals(defaultServerXml, "blank")) {
6738             defaultServerXml = null;
6739             break;
6740           }
6741           
6742           if (GrouperInstallerUtils.isBlank(serverXmlLocation)) {
6743             if (GrouperInstallerUtils.isNotBlank(defaultServerXml)) {
6744               serverXmlLocation = defaultServerXml;
6745             } else {
6746               break;
6747             }
6748           }
6749           serverXml = new File(serverXmlLocation);
6750           if (serverXml.exists() && serverXml.isFile()) {
6751             break;
6752           }
6753           if (i != 9) {
6754             System.out.println("Error: server.xml cant be found, try again.");
6755           }
6756         }
6757         if (serverXml != null && serverXml.exists() && serverXml.isFile()) {
6758           grouperInstaller.configureTomcatUriEncoding(serverXml);
6759         }
6760                 
6761       }
6762 
6763       @Override
6764       public void upgradeApp(GrouperInstaller grouperInstaller) {
6765         grouperInstaller.upgradeUi();
6766       }
6767 
6768       @Override
6769       public void fixIndexFile(GrouperInstaller grouperInstaller) {
6770         grouperInstaller.fixIndexFileUi();
6771       }
6772 
6773       @Override
6774       public boolean isApiOrganized() {
6775         return false;
6776       }
6777     },
6778     
6779     /**
6780      * upgrading the API
6781      */
6782     API {
6783 
6784       @Override
6785       public boolean validateExistingDirectory(GrouperInstaller grouperInstaller) {
6786 
6787         //client is in the API
6788         if (!CLIENT.validateExistingDirectory(grouperInstaller)) {
6789           return false;
6790         }
6791         
6792         grouperInstaller.subjectPropertiesFile = grouperInstaller.findClasspathFile("subject.properties", false);
6793         grouperInstaller.subjectBasePropertiesFile = grouperInstaller.findClasspathFile("subject.base.properties", false);
6794 
6795         grouperInstaller.grouperUtf8File = grouperInstaller.findClasspathFile("grouperUtf8.txt", false);
6796         grouperInstaller.gshFileLoadPropertiesFile = grouperInstaller.findClasspathFile("GSHFileLoad.properties", false);
6797         grouperInstaller.grouperClientUsageExampleFile = grouperInstaller.findClasspathFile("grouper.client.usage.example.txt", false);
6798         grouperInstaller.groovyshProfileFile = grouperInstaller.findClasspathFile("groovysh.profile", false);
6799 
6800         //no need to check if it exists... its new in 2.2
6801         
6802         grouperInstaller.grouperPropertiesFile = grouperInstaller.findClasspathFile("grouper.properties", false);
6803         grouperInstaller.grouperBasePropertiesFile = grouperInstaller.findClasspathFile("grouper.base.properties", false);
6804         grouperInstaller.grouperExamplePropertiesFile = grouperInstaller.findClasspathFile("grouper.example.properties", false);
6805 
6806         if (grouperInstaller.grouperBasePropertiesFile == null 
6807             && grouperInstaller.grouperPropertiesFile == null 
6808             && grouperInstaller.grouperExamplePropertiesFile == null) {
6809           return false;
6810         }
6811         
6812         grouperInstaller.grouperHibernatePropertiesFile = grouperInstaller.findClasspathFile("grouper.hibernate.properties", false);
6813         grouperInstaller.grouperHibernateBasePropertiesFile = grouperInstaller.findClasspathFile("grouper.hibernate.base.properties", false);
6814         grouperInstaller.grouperHibernateExamplePropertiesFile = grouperInstaller.findClasspathFile("grouper.hibernate.example.properties", false);
6815 
6816         if (grouperInstaller.grouperHibernateBasePropertiesFile == null 
6817             && grouperInstaller.grouperHibernatePropertiesFile == null 
6818             && grouperInstaller.grouperHibernateExamplePropertiesFile == null) {
6819           return false;
6820         }
6821         
6822         grouperInstaller.grouperLoaderPropertiesFile = grouperInstaller.findClasspathFile("grouper-loader.properties", false);
6823         grouperInstaller.grouperLoaderBasePropertiesFile = grouperInstaller.findClasspathFile("grouper-loader.base.properties", false);
6824         grouperInstaller.grouperLoaderExamplePropertiesFile = grouperInstaller.findClasspathFile("grouper-loader.example.properties", false);
6825 
6826         if (grouperInstaller.grouperLoaderBasePropertiesFile == null 
6827             && grouperInstaller.grouperLoaderPropertiesFile == null 
6828             && grouperInstaller.grouperLoaderExamplePropertiesFile == null) {
6829           return false;
6830         }
6831         
6832         grouperInstaller.grouperCachePropertiesFile = grouperInstaller.findClasspathFile("grouper.cache.properties", false);
6833         grouperInstaller.grouperCacheBasePropertiesFile = grouperInstaller.findClasspathFile("grouper.cache.base.properties", false);
6834 
6835 //        //these must exist after 2.3.1+
6836 //        if (grouperInstaller.grouperCacheBasePropertiesFile == null 
6837 //            && grouperInstaller.grouperCachePropertiesFile == null 
6838 //            && new GiGrouperVersion(grouperInstaller.version).greaterOrEqualToArg(new GiGrouperVersion("2.3.1"))
6839 //            ) {
6840 //          return false;
6841 //        }
6842 
6843         //this must exist
6844         grouperInstaller.grouperJar = grouperInstaller.findLibraryFile("grouper.jar", false);
6845         if (grouperInstaller.grouperJar == null) {
6846           return false;
6847         }
6848 
6849         grouperInstaller.ehcacheFile = grouperInstaller.findClasspathFile("ehcache.xml", false);
6850         grouperInstaller.ehcacheExampleFile = grouperInstaller.findClasspathFile("ehcache.example.xml", false);        
6851         
6852         //all good
6853         return true;
6854       }
6855 
6856       @Override
6857       public void downloadAndBuildGrouperProjects(GrouperInstaller grouperInstaller) {
6858         CLIENT.downloadAndBuildGrouperProjects(grouperInstaller);
6859         
6860         //download api and set executable and dos2unix etc
6861         grouperInstaller.downloadAndConfigureApi();
6862 
6863       }
6864 
6865       @Override
6866       public void upgradeApp(GrouperInstaller grouperInstaller) {
6867         grouperInstaller.upgradeApi();
6868       }
6869 
6870       @Override
6871       public void patch(GrouperInstaller grouperInstaller) {
6872         grouperInstaller.patchApi();
6873       }
6874 
6875       @Override
6876       public void revertPatch(GrouperInstaller grouperInstaller) {
6877         grouperInstaller.patchRevertApi();
6878       }
6879 
6880       @Override
6881       public void patchStatus(GrouperInstaller grouperInstaller) {
6882         grouperInstaller.patchStatusApi();
6883       }
6884 
6885       @Override
6886       public void fixIndexFile(GrouperInstaller grouperInstaller) {
6887         grouperInstaller.fixIndexFileApi();
6888       }
6889 
6890       @Override
6891       public boolean isApiOrganized() {
6892         return true;
6893       }
6894     },
6895 
6896     /**
6897      * upgrading the client
6898      */
6899     CLIENT {
6900 
6901       @Override
6902       public void patchStatus(GrouperInstaller grouperInstaller) {
6903         throw new RuntimeException("Cant patch status client.  Client patches will be in the API if applicable");
6904       }
6905 
6906       @Override
6907       public void patch(GrouperInstaller grouperInstaller) {
6908         throw new RuntimeException("Cant patch client.  Client patches will be in the API if applicable");
6909       }
6910 
6911       @Override
6912       public void revertPatch(GrouperInstaller grouperInstaller) {
6913         throw new RuntimeException("Cant revert client.  Client patches will be in the API if applicable");
6914       }
6915 
6916       @Override
6917       public boolean validateExistingDirectory(GrouperInstaller grouperInstaller) {
6918 
6919         grouperInstaller.grouperClientPropertiesFile = grouperInstaller.findClasspathFile("grouper.client.properties", false);
6920         grouperInstaller.grouperClientBasePropertiesFile = grouperInstaller.findClasspathFile("grouper.client.base.properties", false);
6921         grouperInstaller.grouperClientExamplePropertiesFile = grouperInstaller.findClasspathFile("grouper.client.example.properties", false);
6922 
6923         if (grouperInstaller.grouperClientBasePropertiesFile == null 
6924             && grouperInstaller.grouperClientPropertiesFile == null 
6925             && grouperInstaller.grouperClientExamplePropertiesFile == null) {
6926           if (grouperInstaller.appToUpgrade == CLIENT) {
6927             return false;
6928           }
6929         }
6930         
6931         //this must exist
6932         grouperInstaller.grouperClientJar = grouperInstaller.findLibraryFile("grouperClient.jar", false);
6933         if (grouperInstaller.grouperClientJar == null) {
6934           if (grouperInstaller.appToUpgrade == CLIENT) {
6935             return false;
6936           }
6937         }
6938         
6939         //all good
6940         return true;
6941       }
6942 
6943       @Override
6944       public void downloadAndBuildGrouperProjects(GrouperInstaller grouperInstaller) {
6945         grouperInstaller.downloadAndBuildClient();
6946       }
6947 
6948       @Override
6949       public void upgradeApp(GrouperInstaller grouperInstaller) {
6950         grouperInstaller.upgradeClient();
6951       }
6952       
6953       @Override
6954       public void fixIndexFile(GrouperInstaller grouperInstaller) {
6955         throw new RuntimeException("Not implemented");
6956       }
6957 
6958       @Override
6959       public boolean isApiOrganized() {
6960         return false;
6961       }
6962     },
6963 
6964     /**
6965      * upgrading the WS
6966      */
6967     WS {
6968 
6969       @Override
6970       public void patchStatus(GrouperInstaller grouperInstaller) {
6971         grouperInstaller.patchStatusWs();
6972       }
6973 
6974       @Override
6975       public void patch(GrouperInstaller grouperInstaller) {
6976         grouperInstaller.patchWs();
6977       }
6978 
6979       @Override
6980       public void revertPatch(GrouperInstaller grouperInstaller) {
6981         grouperInstaller.patchRevertWs();
6982       }
6983 
6984       @Override
6985       public boolean validateExistingDirectory(GrouperInstaller grouperInstaller) {
6986         //API and client are in the UI
6987         if (!API.validateExistingDirectory(grouperInstaller)) {
6988           return false;
6989         }
6990 
6991         grouperInstaller.grouperWsPropertiesFile = grouperInstaller.findClasspathFile("grouper-ws.properties", false);
6992         grouperInstaller.grouperWsBasePropertiesFile = grouperInstaller.findClasspathFile("grouper-ws.base.properties", false);
6993         grouperInstaller.grouperWsExamplePropertiesFile = grouperInstaller.findClasspathFile("grouper-ws.example.properties", false);
6994 
6995         if (grouperInstaller.grouperWsBasePropertiesFile == null 
6996             && grouperInstaller.grouperWsPropertiesFile == null 
6997             && grouperInstaller.grouperWsExamplePropertiesFile == null) {
6998           return false;
6999         }
7000 
7001         return true;
7002       }
7003 
7004       @Override
7005       public void downloadAndBuildGrouperProjects(GrouperInstaller grouperInstaller) {
7006         API.downloadAndBuildGrouperProjects(grouperInstaller);
7007         
7008         //####################################
7009         //download and configure ws
7010         grouperInstaller.downloadAndUntarWs();
7011         
7012         //####################################
7013         //configure where api is
7014         grouperInstaller.configureWs();
7015 
7016         //####################################
7017         //get ant
7018         grouperInstaller.downloadAndUnzipAnt();
7019 
7020         //####################################
7021         //build Ws
7022         grouperInstaller.buildWs(false);
7023 
7024       }
7025 
7026       @Override
7027       public void upgradeApp(GrouperInstaller grouperInstaller) {
7028         grouperInstaller.upgradeWs();
7029       }
7030 
7031       @Override
7032       public void fixIndexFile(GrouperInstaller grouperInstaller) {
7033         grouperInstaller.fixIndexFileWs();
7034       }
7035 
7036       @Override
7037       public boolean isApiOrganized() {
7038         return false;
7039       }
7040     }, 
7041     
7042     /**
7043      * upgrading the UI
7044      */
7045     PSP {
7046 
7047       @Override
7048       public void patchStatus(GrouperInstaller grouperInstaller) {
7049         grouperInstaller.patchStatusPsp();
7050       }
7051 
7052       @Override
7053       public void patch(GrouperInstaller grouperInstaller) {
7054         grouperInstaller.patchPsp();
7055       }
7056 
7057       @Override
7058       public void revertPatch(GrouperInstaller grouperInstaller) {
7059         grouperInstaller.patchRevertPsp();
7060       }
7061 
7062       @Override
7063       public boolean validateExistingDirectory(GrouperInstaller grouperInstaller) {
7064         //API and client are in the UI
7065         if (!API.validateExistingDirectory(grouperInstaller)) {
7066           return false;
7067         }
7068         
7069         File customLibDir = new File(grouperInstaller.upgradeExistingApplicationDirectoryString + "lib" + File.separator + "custom");
7070         if (!customLibDir.exists()) {
7071           return false;
7072         }
7073 
7074         //see if psp jar is there
7075         List<File> files = GrouperInstallerUtils.jarFindJar(customLibDir, "psp.jar");
7076                 
7077         if (GrouperInstallerUtils.length(files) == 0) {
7078           return false;
7079         }
7080 
7081         return true;
7082       }
7083     
7084       @Override
7085       public void downloadAndBuildGrouperProjects(GrouperInstaller grouperInstaller) {
7086         API.downloadAndBuildGrouperProjects(grouperInstaller);
7087         
7088         //####################################
7089         //download and configure psp
7090         grouperInstaller.downloadAndBuildPsp();
7091     
7092       }
7093     
7094       @Override
7095       public void upgradeApp(GrouperInstaller grouperInstaller) {
7096         grouperInstaller.upgradePsp();
7097       }
7098 
7099       @Override
7100       public void fixIndexFile(GrouperInstaller grouperInstaller) {
7101         grouperInstaller.fixIndexFilePsp();
7102       }
7103       @Override
7104       public boolean isApiOrganized() {
7105         return true;
7106       }
7107     }, 
7108     
7109     /**
7110      * upgrading the UI
7111      */
7112     PSPNG {
7113     
7114       @Override
7115       public void patchStatus(GrouperInstaller grouperInstaller) {
7116         grouperInstaller.patchStatusPspng();
7117       }
7118     
7119       @Override
7120       public void patch(GrouperInstaller grouperInstaller) {
7121         grouperInstaller.patchPspng();
7122       }
7123     
7124       @Override
7125       public void revertPatch(GrouperInstaller grouperInstaller) {
7126         grouperInstaller.patchRevertPspng();
7127       }
7128     
7129       @Override
7130       public boolean validateExistingDirectory(GrouperInstaller grouperInstaller) {
7131         //API and client are in the UI
7132         if (!API.validateExistingDirectory(grouperInstaller)) {
7133           return false;
7134         }
7135         
7136         File customLibDir = new File(grouperInstaller.upgradeExistingApplicationDirectoryString + "lib" + File.separator + "custom");
7137         if (!customLibDir.exists()) {
7138           return false;
7139         }
7140     
7141         //see if psp jar is there
7142         String grouperVersion = grouperInstaller.grouperVersionOfJar().toString();
7143 
7144         List<File> files = GrouperInstallerUtils.jarFindJar(customLibDir, "grouper-pspng-" + grouperVersion + ".jar");
7145 
7146         if (GrouperInstallerUtils.length(files) == 0) {
7147           return false;
7148         }
7149     
7150         return true;
7151       }
7152     
7153       @Override
7154       public void downloadAndBuildGrouperProjects(GrouperInstaller grouperInstaller) {
7155         API.downloadAndBuildGrouperProjects(grouperInstaller);
7156         
7157         //####################################
7158         //download and configure psp
7159         grouperInstaller.downloadAndBuildPspng();
7160     
7161       }
7162     
7163       @Override
7164       public void upgradeApp(GrouperInstaller grouperInstaller) {
7165         grouperInstaller.upgradePspng();
7166       }
7167     
7168       @Override
7169       public void fixIndexFile(GrouperInstaller grouperInstaller) {
7170         grouperInstaller.fixIndexFilePspng();
7171       }
7172       @Override
7173       public boolean isApiOrganized() {
7174         return true;
7175       }
7176     };
7177 
7178     /**
7179      * if the organization is API organzied (e.g. has lib/jdbcSamples dir)
7180      * @return true/false
7181      */
7182     public abstract boolean isApiOrganized();
7183     
7184     /**
7185      * validate that the existing directory is valid, and find all the file paths
7186      * @param grouperInstaller 
7187      * @return true if valid, false if not
7188      */
7189     public abstract boolean validateExistingDirectory(GrouperInstaller grouperInstaller);
7190     
7191     /**
7192      * based on what is being upgraded, download and build the grouper projects
7193      * @param grouperInstaller
7194      */
7195     public abstract void downloadAndBuildGrouperProjects(GrouperInstaller grouperInstaller);
7196     
7197     /**
7198      * upgrade this app
7199      * @param grouperInstaller
7200      */
7201     public abstract void upgradeApp(GrouperInstaller grouperInstaller);
7202     
7203     /**
7204      * patch this app
7205      * @param grouperInstaller
7206      */
7207     public abstract void patch(GrouperInstaller grouperInstaller);
7208     
7209     /**
7210      * revert patch this app
7211      * @param grouperInstaller
7212      */
7213     public abstract void revertPatch(GrouperInstaller grouperInstaller);
7214     
7215     /**
7216      * patch status for this app
7217      * @param grouperInstaller
7218      */
7219     public abstract void patchStatus(GrouperInstaller grouperInstaller);
7220     
7221     /**
7222      * fix index file for this app
7223      * @param grouperInstaller
7224      */
7225     public abstract void fixIndexFile(GrouperInstaller grouperInstaller);
7226     
7227     /**
7228      * 
7229      * @param string
7230      * @param exceptionIfInvalid
7231      * @param exceptionIfBlank
7232      * @return the action
7233      */
7234     @SuppressWarnings("unused")
7235     public static AppToUpgrade valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
7236       return GrouperInstallerUtils.enumValueOfIgnoreCase(AppToUpgrade.class, string, exceptionIfBlank, exceptionIfInvalid);
7237     }
7238 
7239   }
7240 
7241   /**
7242    * patch names installed (used for dependency checking), without the file ending e.g. grouper_v2_2_1_api_patch_0
7243    */
7244   private Set<String> patchesInstalled = new HashSet<String>();
7245   
7246   /**
7247    * if grouper is stopped
7248    */
7249   private boolean grouperStopped = false;
7250 
7251   /**
7252    * if should revert all
7253    */
7254   private Boolean revertAllPatches = null;
7255   
7256   /**
7257    * if we should use all local files
7258    */
7259   private Boolean useAllLocalFiles = null;
7260   
7261   /**
7262    * if we should use all unzipped files
7263    */
7264   private Boolean useAllUnzippedFiles = null;
7265   
7266   /**
7267    * if we should use all untarred directories
7268    */
7269   private Boolean useAllUntarredDirectories = null;
7270   
7271   /**
7272    * default for revert all patches
7273    */
7274   private boolean revertAllPatchesDefault = false;
7275   
7276   /**
7277    * if should revert all
7278    */
7279   private Boolean installAllPatches = null;
7280   
7281   /**
7282    * if should install some patches
7283    */
7284   private Boolean installPatchesUpToACertainPatchLevel = null;
7285   
7286   /**
7287    * if should install up to patch levels, comma separated
7288    * e.g. grouper_v2_3_0_api_patch_9, grouper_v2_3_0_ui_patch_10, grouper_v2_3_0_ws_patch_5
7289    */
7290   private String installPatchesUpToThesePatchLevels = null;
7291   
7292   /**
7293    * if should install certain specified
7294    */
7295   private Boolean installCertainSpecifiedPatches = null;
7296   
7297   /**
7298    * if should install up to patch levels, comma separated
7299    * e.g. grouper_v2_3_0_api_patch_0, grouper_v2_3_0_api_patch_1, grouper_v2_3_0_ui_patch_0
7300    */
7301   private String installCertainSpecifiedPatchesList = null;
7302   
7303   /**
7304    * if should revert certain specified
7305    */
7306   private Boolean revertCertainSpecifiedPatches = null;
7307   
7308   /**
7309    * if should revert up to patch levels, comma separated
7310    * e.g. grouper_v2_3_0_api_patch_0, grouper_v2_3_0_api_patch_1, grouper_v2_3_0_ui_patch_0
7311    */
7312   private String revertCertainSpecifiedPatchesList = null;
7313   
7314   /**
7315    * revert patches for an app
7316    * @param thisAppToRevert
7317    * @param originalAppToUpgrade 
7318    * @return if reverted
7319    */
7320   private boolean revertPatches(AppToUpgrade thisAppToRevert, AppToUpgrade originalAppToUpgrade) {
7321 
7322     if (thisAppToRevert == AppToUpgrade.CLIENT) {
7323       throw new RuntimeException("Cant revert " + thisAppToRevert);
7324     }
7325     
7326     Properties patchesExistingProperties = patchExistingProperties();
7327     
7328     String grouperVersion = this.grouperVersionOfJar().toString();
7329 
7330     grouperVersion = GrouperInstallerUtils.replace(grouperVersion, ".", "_");
7331 
7332     Map<Integer, String> patchNumberToNameBase = new LinkedHashMap<Integer, String>();
7333     
7334     boolean foundPatch = false;
7335 
7336     Map<String, Set<String>> installedPatchDependencies = new HashMap<String, Set<String>>();
7337     
7338     File patchExistingPropertiesFile = patchExistingPropertiesFile();
7339     
7340     for (int i=1000;i>=0;i--) {
7341       
7342       //grouper_v2_2_1_api_patch_0.state
7343       String keyBase = "grouper_v" + grouperVersion + "_" + thisAppToRevert.name().toLowerCase() + "_patch_" + i;
7344       String key = keyBase + ".state";
7345 
7346       patchNumberToNameBase.put(i, keyBase);
7347       
7348       String value = patchesExistingProperties.getProperty(key);
7349 
7350       if (!GrouperInstallerUtils.isBlank(value)) {
7351         
7352         System.out.println("\n################ Checking patch " + keyBase);
7353 
7354         GrouperInstallerPatchStatus grouperInstallerPatchStatus = GrouperInstallerPatchStatus.valueOfIgnoreCase(value, true, true);
7355         
7356         switch (grouperInstallerPatchStatus) {
7357           case skippedPermanently:
7358             
7359             System.out.println("Patch: " + keyBase + ": was skipped permanently on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7360             continue;
7361 
7362           case skippedTemporarily:
7363 
7364             System.out.println("Patch: " + keyBase + ": was skipped termporarily on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7365             continue;
7366 
7367           case reverted:
7368 
7369             System.out.println("Patch: " + keyBase + ": was removed on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7370             continue;
7371 
7372           case error:
7373 
7374             System.out.println("Patch: " + keyBase + ": had an error installing on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7375             continue;
7376 
7377           case applied:
7378             
7379             System.out.println("Patch: " + keyBase + ": was applied on: " + patchesExistingProperties.getProperty(keyBase + ".date")  + "\n");
7380             this.patchesInstalled.add(keyBase);
7381             break;
7382 
7383           default:
7384             throw new RuntimeException("Not expecting: " + grouperInstallerPatchStatus);
7385         }
7386 
7387       } else {
7388         continue;
7389       }
7390 
7391       if (!this.patchesInstalled.contains(keyBase)) {
7392         System.out.println("\n");
7393         continue;
7394       }
7395 
7396       //lets see if it exists on the server
7397       File patchUntarredDir = downloadAndUnzipPatch(keyBase);
7398       
7399       //if no more patches
7400       if (patchUntarredDir == null) {
7401         System.out.print("Error: cant find directory for patch: " + keyBase + ", press <enter> to continue. ");
7402         readFromStdIn("grouperInstaller.autorun.continueAfterCantFindPatchDir");
7403         continue;
7404       }
7405 
7406       //lets get the description:
7407       //  # will show up on screen so user knows what it is
7408       //  description = This patch fixes GRP-1080: browse folders refresh button only works in chrome, not other browsers
7409       //
7410       //  # patches that this patch is dependant on (comma separated)
7411       //  dependencies = 
7412       //
7413       //  # low, medium, or high risk to applying the patch
7414       //  risk = low
7415       //
7416       //  # is this is a security patch (true or false)
7417       //  security = false
7418       //
7419       //  # if this patch requires restart of processes (true or false)
7420       //  requiresRestart = false
7421       Properties patchProperties = GrouperInstallerUtils.propertiesFromFile(new File(patchUntarredDir.getAbsoluteFile() + File.separator + keyBase + ".properties"));
7422 
7423       foundPatch = true;
7424 
7425       // check dependencies
7426       {
7427         List<String> dependencies = GrouperInstallerUtils.splitTrimToList(patchProperties.getProperty("dependencies"), ",");
7428         Set<String> dependenciesSet = new HashSet<String>(GrouperInstallerUtils.nonNull(dependencies));
7429         installedPatchDependencies.put(keyBase, dependenciesSet);
7430       }
7431 
7432       boolean securityRelated = GrouperInstallerUtils.booleanValue(patchProperties.getProperty("security"), false);
7433       boolean requiresRestart = GrouperInstallerUtils.booleanValue(patchProperties.getProperty("requiresRestart"), true);
7434 
7435       if (this.revertAllPatches == null) {
7436         System.out.println("Would you like to revert all patches (t|f)? [" + (this.revertAllPatchesDefault ? "t" : "f") + "]: ");
7437         this.revertAllPatches = readFromStdInBoolean(this.revertAllPatchesDefault, "grouperInstaller.autorun.revertAllPatches");
7438       }
7439       
7440       if (!this.revertAllPatches && this.revertCertainSpecifiedPatches == null) {
7441         System.out.println("Would you like to revert certain specified patches? (t|f)? [f]: ");
7442         this.revertCertainSpecifiedPatches = readFromStdInBoolean(false, "grouperInstaller.autorun.revertCertainSpecifiedPatches");
7443 
7444         if (this.revertCertainSpecifiedPatches) {
7445 
7446           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)? : ");
7447           this.revertCertainSpecifiedPatchesList = readFromStdIn("grouperInstaller.autorun.revertCertainSpecifiedPatchesList");
7448         }
7449       }
7450       if (this.revertCertainSpecifiedPatches == null) {
7451         this.revertCertainSpecifiedPatches = false;
7452       }
7453 
7454       //print description
7455       System.out.println("Patch " + keyBase + " is " + patchProperties.getProperty("risk") + " risk, "
7456           + (securityRelated ? "is a security patch" : "is not a security patch"));
7457       System.out.println(patchProperties.getProperty("description"));
7458       
7459       Boolean revertPatch = null;
7460       
7461       if (this.revertAllPatches) {
7462         revertPatch = true;
7463       } else if (this.revertCertainSpecifiedPatches) {
7464         if (revertPatch == null) {
7465           revertPatch = shouldRevertCertainSpecifiedPatches(keyBase);
7466         }
7467       } else {
7468         System.out.print("Would you like to revert patch " + keyBase + " (t|f)? [f]: ");
7469         revertPatch = readFromStdInBoolean(false, "grouperInstaller.autorun.revertPatch");
7470       }
7471 
7472 
7473       if (!revertPatch) {
7474         System.out.println("");
7475         continue;
7476       }
7477 
7478       //check dependencies
7479       for (String patchName : installedPatchDependencies.keySet()) {
7480         
7481         Set<String> dependencies = GrouperInstallerUtils.nonNull(installedPatchDependencies.get(patchName));
7482         
7483         if (dependencies.contains(keyBase)) {
7484           System.out.println("Error: cant revert " + keyBase + " because an installed patch is dependent on it: " + patchName);
7485           System.exit(1);
7486         }
7487       }
7488 
7489       if (requiresRestart && !this.grouperStopped) {
7490         System.out.print("This patch requires all processes that user Grouper to be stopped.\n  "
7491             + "Please stop these processes if they are running and press <enter> to continue... ");
7492         this.grouperStopped = true;
7493         readFromStdIn("grouperInstaller.autorun.continueAfterStoppingGrouperProcesses");
7494       }
7495       
7496       Map<String, String> patchDirToApplicationPath = new LinkedHashMap<String, String>();
7497       patchDirToApplicationPath.put("files", this.upgradeExistingApplicationDirectoryString);
7498       patchDirToApplicationPath.put("classes", this.upgradeExistingClassesDirectoryString);
7499       patchDirToApplicationPath.put("lib", this.upgradeExistingLibDirectoryString);
7500       patchDirToApplicationPath.put("bin", this.upgradeExistingBinDirectoryString);
7501 
7502       boolean patchHasProblem = false;
7503       
7504       //we are reverting this patch, lets see if the files are there...
7505       //this.upgradeExistingApplicationDirectoryString
7506       //patchUntarredDir
7507       File newDir = new File(patchUntarredDir.getAbsolutePath() + File.separator + "new");
7508       File oldDir = new File(patchUntarredDir.getAbsolutePath() + File.separator + "old");
7509       {
7510 
7511         for (String patchDir : patchDirToApplicationPath.keySet()) {
7512 
7513           String applicationPath = patchDirToApplicationPath.get(patchDir);
7514 
7515           File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
7516           File oldDirFiles = new File(oldDir.getAbsoluteFile() + File.separator + patchDir);
7517           
7518           if (newDirFiles.exists() && newDirFiles.isDirectory()) {
7519 
7520             // relative, e.g. WEB-INF/jsp/someFile.jsp
7521             Set<String> newFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(newDirFiles);
7522 
7523             for (String newFilePath : GrouperInstallerUtils.nonNull(newFileRelativePaths)) {
7524               File newFileInPatch = new File(newDirFiles.getAbsolutePath() + File.separator + newFilePath);
7525               File oldFileInPatch = new File(oldDirFiles.getAbsolutePath() + File.separator + newFilePath);
7526               
7527               if (revertPatchExcludes.contains(newFileInPatch.getName())) {
7528                 System.out.println("Skipping revert for file: " + newFileInPatch.getName());
7529                 continue;
7530               }
7531 
7532               File newFileInGrouper = new File(applicationPath + newFilePath);
7533 
7534               newFileInGrouper = fixLibraryFileIfFoundAndDifferent(newFileInGrouper, originalAppToUpgrade);
7535               
7536               if (!newFileInGrouper.exists() || !newFileInGrouper.isFile() 
7537                   || (!GrouperInstallerUtils.contentEquals(newFileInPatch, newFileInGrouper)
7538                       //its ok if the patch is already reverted?
7539                       && !GrouperInstallerUtils.contentEquals(oldFileInPatch, newFileInGrouper))) {
7540                 
7541                 // if it's just an example file and it didn't previously exist, then it's fine??
7542                 if (!newFileInGrouper.exists() && newFileInGrouper.getName().contains(".example.")) {
7543                   System.out.println("Grouper file " + newFileInGrouper.getAbsolutePath() + " doesn't exist.  Reverting patch anyways since this is an example file.");
7544                 } else {
7545                 
7546                   System.out.print("Problem reverting patch since this patch file:\n  " + newFileInPatch.getAbsolutePath() 
7547                       + "\n  is not the same as what the patch expects:\n  " + newFileInGrouper.getAbsolutePath()
7548                       + "\n  Do you want to force revert this patch (t|f)? [f]: ");
7549                   
7550                   boolean forceRevertPatch = readFromStdInBoolean(false, "grouperInstaller.autorun.forceRevertPatch");
7551                   
7552                   if (!forceRevertPatch) {
7553                     System.out.println("\nCannot revert patch since this patch file:\n  " + newFileInPatch.getAbsolutePath() 
7554                         + "\n  is not the same as what the patch expects:\n  " + newFileInGrouper.getAbsolutePath());
7555                     patchHasProblem = true;
7556                   }
7557                 }
7558               }
7559             }
7560           }
7561         }
7562       }
7563 
7564       {
7565         //deletes
7566         for (String patchDir : patchDirToApplicationPath.keySet()) {
7567 
7568           String applicationPath = patchDirToApplicationPath.get(patchDir);
7569 
7570           File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
7571           File oldDirFiles = new File(oldDir.getAbsoluteFile() + File.separator + patchDir);
7572           
7573           if (oldDirFiles.exists() && oldDirFiles.isDirectory()) {
7574 
7575             // relative, e.g. WEB-INF/jsp/someFile.jsp
7576             Set<String> oldFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(oldDirFiles);
7577 
7578             for (String oldFilePath : GrouperInstallerUtils.nonNull(oldFileRelativePaths)) {
7579               File newFileInPatch = new File(newDirFiles.getAbsolutePath() + File.separator + oldFilePath);
7580               File oldFileInPatch = new File(oldDirFiles.getAbsolutePath() + File.separator + oldFilePath);
7581 
7582               //if there is a new file, then its not a delete
7583               if (newFileInPatch.exists()) {
7584                 continue;
7585               }
7586               
7587               if (revertPatchExcludes.contains(newFileInPatch.getName())) {
7588                 System.out.println("Skipping revert for file: " + newFileInPatch.getName());
7589                 continue;
7590               }
7591               
7592               File newFileInGrouper = new File(applicationPath + oldFilePath);
7593               
7594               newFileInGrouper = fixLibraryFileIfFoundAndDifferent(newFileInGrouper, originalAppToUpgrade);
7595 
7596               if (newFileInGrouper.exists() && newFileInGrouper.isFile() 
7597                   && !GrouperInstallerUtils.contentEquals(oldFileInPatch, newFileInGrouper)) {
7598                 
7599                 System.out.print("Problem reverting patch since this patch file:\n  " + oldFileInPatch.getAbsolutePath() 
7600                     + "\n  is not the same as what the patch expects (shouldnt exist):\n  " + newFileInGrouper.getAbsolutePath()
7601                     + "\n  Do you want to force revert this patch (t|f)? [f]: ");
7602                 
7603                 boolean forceRevertPatch = readFromStdInBoolean(true, "grouperInstaller.autorun.forceRevertPatch");
7604                 
7605                 if (!forceRevertPatch) {
7606                   System.out.println("\nCannot revert patch since this patch file:\n  " + newFileInPatch.getAbsolutePath() 
7607                       + "\n  is not the same as what the patch expects:\n  " + newFileInGrouper.getAbsolutePath());
7608                   patchHasProblem = true;
7609                 }
7610               }
7611             }
7612           }
7613         }
7614       }
7615 
7616       if (patchHasProblem) {
7617         System.out.println("Cannot continue since patch has problem");
7618         System.exit(1);
7619       }
7620       
7621       //so far so good, all the new files are all good, revert the patch
7622       for (String patchDir : patchDirToApplicationPath.keySet()) {
7623         
7624         String applicationPath = patchDirToApplicationPath.get(patchDir);
7625 
7626         File oldDirFiles = new File(oldDir.getAbsoluteFile() + File.separator + patchDir);
7627         File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
7628         
7629         if (newDirFiles.exists() && newDirFiles.isDirectory()) {
7630         
7631           // relative, e.g. WEB-INF/jsp/someFile.jsp
7632           Set<String> newFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(newDirFiles);
7633           
7634           for (String newFilePath : GrouperInstallerUtils.nonNull(newFileRelativePaths)) {
7635 
7636             File oldFileInPatch = new File(oldDirFiles.getAbsolutePath() + File.separator + newFilePath);
7637 
7638             File newFileInGrouper = new File(applicationPath + newFilePath);
7639             
7640             if (revertPatchExcludes.contains(oldFileInPatch.getName())) {
7641               continue;
7642             }
7643             
7644             newFileInGrouper = fixLibraryFileIfFoundAndDifferent(newFileInGrouper, originalAppToUpgrade);
7645 
7646             if (oldFileInPatch.exists() && oldFileInPatch.isFile()) {
7647               System.out.println("Reverting file: " + newFileInGrouper.getAbsolutePath());
7648               GrouperInstallerUtils.copyFile(oldFileInPatch, newFileInGrouper, false);
7649             } else {
7650               System.out.println("Reverting (deleting) file: " + newFileInGrouper.getAbsolutePath());
7651               GrouperInstallerUtils.fileDelete(newFileInGrouper);
7652             }
7653           }
7654         }
7655       }
7656       
7657       //so far so good, revert the deletes
7658       for (String patchDir : patchDirToApplicationPath.keySet()) {
7659         
7660         String applicationPath = patchDirToApplicationPath.get(patchDir);
7661 
7662         File oldDirFiles = new File(oldDir.getAbsoluteFile() + File.separator + patchDir);
7663         File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
7664         
7665         if (oldDirFiles.exists() && oldDirFiles.isDirectory()) {
7666         
7667           // relative, e.g. WEB-INF/jsp/someFile.jsp
7668           Set<String> oldFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(oldDirFiles);
7669           
7670           for (String oldFilePath : GrouperInstallerUtils.nonNull(oldFileRelativePaths)) {
7671 
7672             File oldFileInPatch = new File(oldDirFiles.getAbsolutePath() + File.separator + oldFilePath);
7673             File newFileInPatch = new File(newDirFiles.getAbsolutePath() + File.separator + oldFilePath);
7674 
7675             if (newFileInPatch.exists()) {
7676               continue;
7677             }
7678             
7679             if (revertPatchExcludes.contains(oldFileInPatch.getName())) {
7680               continue;
7681             }
7682             
7683             File newFileInGrouper = new File(applicationPath + oldFilePath);
7684             
7685             newFileInGrouper = fixLibraryFileIfFoundAndDifferent(newFileInGrouper, originalAppToUpgrade);
7686 
7687             if (oldFileInPatch.exists() && oldFileInPatch.isFile()) {
7688               System.out.println("Reverting deleted file: " + newFileInGrouper.getAbsolutePath());
7689               GrouperInstallerUtils.copyFile(oldFileInPatch, newFileInGrouper, false);
7690             }
7691           }
7692         }
7693       }
7694       
7695       //fixLibDir(patchDirToApplicationPath.get("lib"), originalAppToUpgrade);
7696       
7697       this.patchesInstalled.remove(keyBase);
7698       installedPatchDependencies.remove(keyBase);
7699       System.out.println("Patch successfully reverted: " + keyBase);
7700 
7701       editPropertiesFile(patchExistingPropertiesFile, keyBase + ".date", 
7702           GrouperInstallerUtils.dateMinutesSecondsFormat.format(new Date()), false);
7703       editPropertiesFile(patchExistingPropertiesFile, keyBase + ".state", 
7704           GrouperInstallerPatchStatus.reverted.name(), false);
7705 
7706       System.out.println("");
7707     }
7708 
7709     if (!foundPatch) {
7710       System.out.println("There are no new " + thisAppToRevert + " patches to revert\n");
7711       return false;
7712     }
7713     
7714     return true;
7715       
7716   }
7717   
7718   /**
7719    * this makes sure libs are in the right spot, though might be risky so dont do it
7720    * @param libDirWithSlash
7721    * @param originalAppToUpgrade
7722    */
7723   private void fixLibDir(String libDirWithSlash, AppToUpgrade originalAppToUpgrade) {
7724     if (originalAppToUpgrade.isApiOrganized()) {
7725       FilenameFilter apiFilenameFilter = new FilenameFilter() {
7726         
7727         public boolean accept(File dir, String name) {
7728           
7729           // any jars in "lib"
7730           if (GrouperInstallerUtils.equals("lib", dir.getName()) && name.endsWith(".jar")) {
7731             return true;
7732           }
7733           return false;
7734         }
7735       };
7736       //make sure all libs have something between lib and grouper
7737       for (File file : new File(libDirWithSlash).listFiles(apiFilenameFilter)) {
7738         // move this to grouper dir
7739         final File newFile = new File(file.getParentFile().getAbsolutePath() + File.separator + "grouper" + File.separator + file.getName());
7740         GrouperInstallerUtils.fileMove(file, newFile);
7741         System.out.println("Moving jar: " + file.getAbsolutePath() + " to " + newFile.getAbsolutePath());
7742       }
7743     } else {
7744       for (File file : GrouperInstallerUtils.fileListRecursive(new File(libDirWithSlash))) {
7745         // any jars not in "lib" but parent dir of dir is lib
7746         if (file.getName().endsWith(".jar") && !GrouperInstallerUtils.equals("lib", file.getParentFile().getName()) && GrouperInstallerUtils.equals("lib", file.getParentFile().getParentFile().getName())) {
7747           // move this to grouper dir
7748           final File newFile = new File(file.getParentFile().getParentFile().getAbsolutePath() + File.separator + file.getName());
7749           GrouperInstallerUtils.fileMove(file, newFile);
7750           System.out.println("Moving jar: " + file.getAbsolutePath() + " to " + newFile.getAbsolutePath());
7751           
7752         }
7753       }
7754     }
7755   }
7756   
7757   /**
7758    * get the patches available to apply that are not already applied
7759    * @param thisAppToUpgrade app to upgrade to check
7760    * @param originalAppToUpgrade 
7761    * @return if patches were installed
7762    */
7763   private boolean downloadAndInstallPatches(AppToUpgrade thisAppToUpgrade, AppToUpgrade originalAppToUpgrade) {
7764 
7765     if (thisAppToUpgrade == AppToUpgrade.CLIENT) {
7766       throw new RuntimeException("Cant install patches for " + thisAppToUpgrade);
7767     }
7768     
7769     Properties patchesExistingProperties = patchExistingProperties();
7770 
7771     String grouperVersion = GrouperInstallerUtils.propertiesValue("grouper.version", true);
7772 
7773     grouperVersion = GrouperInstallerUtils.replace(grouperVersion, ".", "_");
7774 
7775     Map<Integer, String> patchNumberToNameBase = new LinkedHashMap<Integer, String>();
7776     
7777     boolean foundNewPatch = false;
7778     
7779     File patchExistingPropertiesFile = patchExistingPropertiesFile();
7780     
7781     OUTER: for (int i=0;i<1000;i++) {
7782       
7783       //grouper_v2_2_1_api_patch_0.state
7784       String keyBase = "grouper_v" + grouperVersion + "_" + thisAppToUpgrade.name().toLowerCase() + "_patch_" + i;
7785       System.out.println("\n################ Checking patch " + keyBase);
7786       String key = keyBase + ".state";
7787 
7788       patchNumberToNameBase.put(i, keyBase);
7789       
7790       String value = patchesExistingProperties.getProperty(key);
7791 
7792       if (!GrouperInstallerUtils.isBlank(value)) {
7793         
7794         GrouperInstallerPatchStatus grouperInstallerPatchStatus = GrouperInstallerPatchStatus.valueOfIgnoreCase(value, true, true);
7795         
7796         switch (grouperInstallerPatchStatus) {
7797           case applied:
7798             
7799             System.out.println("Patch: " + keyBase + ": was applied on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7800             this.patchesInstalled.add(keyBase);
7801             
7802             continue;
7803 
7804           case skippedPermanently:
7805             
7806             System.out.println("Patch: " + keyBase + ": was skipped permanently on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7807             continue;
7808 
7809           case skippedTemporarily:
7810 
7811             System.out.println("Patch: " + keyBase + ": was skipped termporarily on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7812 
7813             break;
7814 
7815           case reverted:
7816 
7817             System.out.println("Patch: " + keyBase + ": was reverted on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7818 
7819             break;
7820 
7821           case error:
7822 
7823             System.out.println("Patch: " + keyBase + ": had an error installing on: " + patchesExistingProperties.getProperty(keyBase + ".date") + "\n");
7824 
7825             break;
7826 
7827           default:
7828             throw new RuntimeException("Not expecting: " + grouperInstallerPatchStatus);
7829         }
7830         
7831       }
7832 
7833       //lets see if it exists on the server
7834       File patchUntarredDir = downloadAndUnzipPatch(keyBase);
7835       
7836       //if no more patches
7837       if (patchUntarredDir == null) {
7838         System.out.println("");
7839         break OUTER;
7840       }
7841       
7842       //lets get the description:
7843       //  # will show up on screen so user knows what it is
7844       //  description = This patch fixes GRP-1080: browse folders refresh button only works in chrome, not other browsers
7845       //
7846       //  # patches that this patch is dependant on (comma separated)
7847       //  dependencies = 
7848       //
7849       //  # low, medium, or high risk to applying the patch
7850       //  risk = low
7851       //
7852       //  # is this is a security patch (true or false)
7853       //  security = false
7854       //
7855       //  # if this patch requires restart of processes (true or false)
7856       //  requiresRestart = false
7857       Properties patchProperties = GrouperInstallerUtils.propertiesFromFile(new File(patchUntarredDir.getAbsoluteFile() + File.separator + keyBase + ".properties"));
7858 
7859       foundNewPatch = true;
7860 
7861       Boolean installPatch = null;
7862       
7863       if (this.installPatchesUpToACertainPatchLevel != null && this.installPatchesUpToACertainPatchLevel) {
7864         if (!GrouperInstallerUtils.isBlank(this.installPatchesUpToThesePatchLevels)) {
7865           
7866           installPatch = shouldInstallPatchUpToLevel(keyBase);
7867           
7868           if (!installPatch) {
7869             break OUTER;
7870           }
7871         }
7872       }
7873       if (this.installCertainSpecifiedPatches != null && this.installCertainSpecifiedPatches) {
7874         if (!GrouperInstallerUtils.isBlank(this.installCertainSpecifiedPatchesList)) {
7875           
7876           installPatch = shouldInstallCertainSpecifiedPatches(keyBase);
7877           
7878         }
7879       }
7880      
7881       // check dependencies
7882       if (installPatch == null || installPatch == true){
7883         String[] dependencies = GrouperInstallerUtils.splitTrim(patchProperties.getProperty("dependencies"), ",");
7884   
7885         boolean invalidDependency = false;
7886         for (String dependency : GrouperInstallerUtils.nonNull(dependencies, String.class)) {
7887           if (!this.patchesInstalled.contains(dependency)) {
7888             System.out.println("Cannot install patch " + keyBase + " since it is dependent on a patch which is not installed: " + dependency);
7889             invalidDependency = true;
7890           }
7891         }
7892         if (invalidDependency) {
7893           System.out.println("Press <enter> to continue. ");
7894           readFromStdIn("grouperInstaller.autorun.continueAfterPatchDependencyFails");
7895           continue OUTER;
7896         }
7897       }
7898       
7899       boolean securityRelated = GrouperInstallerUtils.booleanValue(patchProperties.getProperty("security"), false);
7900       boolean requiresRestart = GrouperInstallerUtils.booleanValue(patchProperties.getProperty("requiresRestart"), true);
7901       
7902       if (this.installAllPatches == null) {
7903         System.out.println("Would you like to install all patches (t|f)? [t]: ");
7904         this.installAllPatches = readFromStdInBoolean(true, "grouperInstaller.autorun.installAllPatches");
7905 
7906         if (!this.installAllPatches && this.installPatchesUpToACertainPatchLevel == null ) {
7907           System.out.println("Would you like to install patches up to a certain patch level? (t|f)? [f]: ");
7908           this.installPatchesUpToACertainPatchLevel = readFromStdInBoolean(false, "grouperInstaller.autorun.installPatchesUpToACertainPatchLevel");
7909           
7910           if (this.installPatchesUpToACertainPatchLevel) {
7911 
7912             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)? : ");
7913             this.installPatchesUpToThesePatchLevels = readFromStdIn("grouperInstaller.autorun.installPatchesUpToThesePatchLevels");
7914 
7915           }
7916           
7917         }
7918         
7919         if (this.installPatchesUpToACertainPatchLevel == null) {
7920           this.installPatchesUpToACertainPatchLevel = false;
7921         }
7922         
7923         if (!this.installAllPatches && !this.installPatchesUpToACertainPatchLevel && this.installCertainSpecifiedPatches == null) {
7924           System.out.println("Would you like to install certain specified patches? (t|f)? [f]: ");
7925           this.installCertainSpecifiedPatches = readFromStdInBoolean(false, "grouperInstaller.autorun.installCertainSpecifiedPatches");
7926 
7927           if (this.installCertainSpecifiedPatches) {
7928 
7929             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)? : ");
7930             this.installCertainSpecifiedPatchesList = readFromStdIn("grouperInstaller.autorun.installCertainSpecifiedPatchesList");
7931           }
7932         }
7933         if (this.installCertainSpecifiedPatches == null) {
7934           this.installCertainSpecifiedPatches = false;
7935         }
7936       }
7937 
7938       //print description
7939       System.out.println("Patch " + keyBase + " is " + patchProperties.getProperty("risk") + " risk, "
7940           + (securityRelated ? "is a security patch" : "is not a security patch"));
7941       System.out.println(patchProperties.getProperty("description"));
7942 
7943       if (this.installAllPatches) {
7944         installPatch = true;
7945       } else if (this.installPatchesUpToACertainPatchLevel) {
7946         if (installPatch == null) {
7947           installPatch = shouldInstallPatchUpToLevel(keyBase);
7948         }
7949       } else if (this.installCertainSpecifiedPatches) {
7950         if (installPatch == null) {
7951           installPatch = shouldInstallCertainSpecifiedPatches(keyBase);
7952         }
7953       } else {
7954         System.out.println("Would you like to install patch " + keyBase + " (t|f)? [t]: ");
7955         installPatch = readFromStdInBoolean(true, "grouperInstaller.autorun.installPatch");
7956       }
7957 
7958       //keep track that we skipped this in the patch properties file
7959       editPropertiesFile(patchExistingPropertiesFile, keyBase + ".date", 
7960           GrouperInstallerUtils.dateMinutesSecondsFormat.format(new Date()), true);
7961 
7962       //if we arent installing the patch
7963       if (!installPatch) {
7964         
7965         boolean temporary = false;
7966         
7967         //if installing up to a patch level, and not specifying about next time, make it temporary
7968         if (this.installPatchesUpToACertainPatchLevel && GrouperInstallerUtils.isBlank(GrouperInstallerUtils.propertiesValue("grouperInstaller.autorun.promptAboutPatchNextTime", false))) {
7969           temporary = true;
7970         } else if (this.installCertainSpecifiedPatches && GrouperInstallerUtils.isBlank(GrouperInstallerUtils.propertiesValue("grouperInstaller.autorun.promptAboutPatchNextTime", false))) {
7971           temporary = true;
7972 
7973         } else {
7974           System.out.println("Would you like to be prompted about this patch next time? (t|f)? [t]: ");
7975 
7976           temporary = readFromStdInBoolean(true, "grouperInstaller.autorun.promptAboutPatchNextTime");
7977         }
7978 
7979         GrouperInstallerPatchStatus grouperInstallerPatchStatus = null;
7980 
7981         if (temporary) {
7982           grouperInstallerPatchStatus = GrouperInstallerPatchStatus.skippedTemporarily;
7983         } else {
7984           grouperInstallerPatchStatus = GrouperInstallerPatchStatus.skippedPermanently;
7985         }
7986 
7987         editPropertiesFile(patchExistingPropertiesFile, keyBase + ".state", 
7988             grouperInstallerPatchStatus.name(), true);
7989         System.out.println("");
7990         continue OUTER;
7991       }
7992 
7993       if (requiresRestart && !this.grouperStopped) {
7994         System.out.println("This patch requires all processes that user Grouper to be stopped.\n  "
7995             + "Please stop these processes if they are running and press <enter> to continue...");
7996         this.grouperStopped = true;
7997         readFromStdIn("grouperInstaller.autorun.continueAfterPatchStopProcesses");
7998       }
7999       
8000       Map<String, String> patchDirToApplicationPath = new LinkedHashMap<String, String>();
8001       patchDirToApplicationPath.put("files", this.upgradeExistingApplicationDirectoryString);
8002       patchDirToApplicationPath.put("classes", this.upgradeExistingClassesDirectoryString);
8003       patchDirToApplicationPath.put("lib", this.upgradeExistingLibDirectoryString);
8004       patchDirToApplicationPath.put("bin", this.upgradeExistingBinDirectoryString);
8005 
8006       boolean patchHasProblem = false;
8007       
8008       //we are installing this patch, lets see if the files are there...
8009       //this.upgradeExistingApplicationDirectoryString
8010       //patchUntarredDir
8011       File oldDir = new File(patchUntarredDir.getAbsolutePath() + File.separator + "old");
8012       File newDir = new File(patchUntarredDir.getAbsolutePath() + File.separator + "new");
8013       {
8014 
8015         for (String patchDir : patchDirToApplicationPath.keySet()) {
8016           
8017           String applicationPath = patchDirToApplicationPath.get(patchDir);
8018 
8019           File oldDirFiles = new File(oldDir.getAbsoluteFile() + File.separator + patchDir);
8020           File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
8021           
8022           if (oldDirFiles.exists() && oldDirFiles.isDirectory()) {
8023           
8024             // relative, e.g. WEB-INF/jsp/someFile.jsp
8025             Set<String> oldFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(oldDirFiles);
8026             
8027             for (String oldFilePath : GrouperInstallerUtils.nonNull(oldFileRelativePaths)) {
8028               File oldFileInPatch = new File(oldDirFiles.getAbsolutePath() + File.separator + oldFilePath);
8029               File newFileInPatch = new File(newDirFiles.getAbsolutePath() + File.separator + oldFilePath);
8030 
8031               oldFilePath = patchFixFilePath(applicationPath, patchDir, oldFilePath, originalAppToUpgrade);
8032 
8033               File oldFileInGrouper = new File(applicationPath + oldFilePath);
8034   
8035               oldFileInGrouper = fixLibraryFileIfFoundAndDifferent(oldFileInGrouper, originalAppToUpgrade);
8036 
8037               if (!oldFileInPatch.exists() || !oldFileInPatch.isFile()) {
8038                 throw new RuntimeException("Why does file not exist or not file??? " + oldFileInPatch.getAbsolutePath());
8039               }
8040               boolean deletedNewPatchFile = !newFileInPatch.exists();
8041               boolean deletedGrouperFile = !oldFileInGrouper.exists();
8042               //if both deleted thats ok
8043               if ((!deletedGrouperFile || !deletedNewPatchFile) &&
8044                  ( !oldFileInGrouper.exists() || !oldFileInGrouper.isFile() 
8045                   || (!GrouperInstallerUtils.contentEquals(oldFileInPatch, oldFileInGrouper)
8046                       //patch is already applied?  thats ok i guess
8047                       && !GrouperInstallerUtils.contentEquals(newFileInPatch, oldFileInGrouper)))) {
8048                 
8049                 System.out.println("Problem applying patch since this patch old file:\n  " + oldFileInPatch.getAbsolutePath() 
8050                     + "\n  is not the same as what the patch expects:\n  " + oldFileInGrouper.getAbsolutePath()
8051                     + "\n  Do you want to force install this patch (t|f)? [f]: ");
8052                 
8053                 boolean forceInstallPatch = readFromStdInBoolean(false, "grouperInstaller.autorun.forceInstallPatch");
8054                 
8055                 if (!forceInstallPatch) {
8056                   System.out.println("Cannot apply patch since this patch file:\n  " + oldFileInPatch.getAbsolutePath() 
8057                       + "\n  is not the same as what the patch expects:\n  " + oldFileInGrouper.getAbsolutePath());
8058                   patchHasProblem = true;
8059                 }
8060               }
8061             }
8062           }
8063         }
8064       }
8065 
8066       //lets make sure that files which are new which dont have an old version do not exist in the application
8067       for (String patchDir : patchDirToApplicationPath.keySet()) {
8068         
8069         String applicationPath = patchDirToApplicationPath.get(patchDir);
8070 
8071         File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
8072         File oldDirFiles = new File(oldDir.getAbsoluteFile() + File.separator + patchDir);
8073         
8074         if (newDirFiles.exists() && newDirFiles.isDirectory()) {
8075         
8076           // relative, e.g. WEB-INF/jsp/someFile.jsp
8077           Set<String> newFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(newDirFiles);
8078 
8079           Set<String> oldFileRelativePaths = (oldDirFiles.exists() && oldDirFiles.isDirectory()) ? 
8080               GrouperInstallerUtils.fileDescendantRelativePaths(oldDirFiles) : new HashSet<String>();
8081 
8082           for (String newFilePath : GrouperInstallerUtils.nonNull(newFileRelativePaths)) {
8083 
8084             File newFileInPatch = new File(newDirFiles.getAbsoluteFile() + File.separator + newFilePath);
8085             
8086             newFilePath = patchFixFilePath(applicationPath, patchDir, newFilePath, originalAppToUpgrade);
8087 
8088             File oldFileInGrouper = new File(applicationPath + newFilePath);
8089 
8090             oldFileInGrouper = fixLibraryFileIfFoundAndDifferent(oldFileInGrouper, originalAppToUpgrade);
8091 
8092             if (!newFileInPatch.isFile()) {
8093               continue;
8094             }
8095             
8096             //if there wasnt a corresponding old file path
8097             if (!oldFileRelativePaths.contains(newFilePath) && !GrouperInstallerUtils.contentEquals(oldFileInGrouper, newFileInPatch)) {
8098 
8099               //then the file shouldnt exist
8100               if (oldFileInGrouper.exists()) {
8101 
8102                 System.out.println("Problem applying patch since this file:\n  " + oldFileInGrouper.getAbsolutePath() 
8103                   + "\n  should not exist yet\n  Do you want to force install this patch (t|f)? [f]: ");
8104             
8105                 boolean forceInstallPatch = readFromStdInBoolean(false, "grouperInstaller.autorun.forceInstallPatch");
8106                 
8107                 if (!forceInstallPatch) {
8108                 
8109                 
8110                   System.out.println("Cannot apply patch since this patch file:\n  " + newFileInPatch.getAbsolutePath() 
8111                       + "\n  is supposed to be new, but it already exists:\n  " + oldFileInGrouper.getAbsolutePath());
8112                   patchHasProblem = true;
8113 
8114                 }
8115               }
8116             }
8117           }
8118         }
8119       }
8120 
8121       if (patchHasProblem) {
8122         editPropertiesFile(patchExistingPropertiesFile, keyBase + ".state", 
8123             GrouperInstallerPatchStatus.error.name(), true);
8124 
8125         continue OUTER;
8126       }
8127 
8128       //so far so good, all the old files are all good, install the patch
8129       for (String patchDir : patchDirToApplicationPath.keySet()) {
8130         
8131         String applicationPath = patchDirToApplicationPath.get(patchDir);
8132 
8133         File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
8134         
8135         if (newDirFiles.exists() && newDirFiles.isDirectory()) {
8136         
8137           // relative, e.g. WEB-INF/jsp/someFile.jsp
8138           Set<String> newFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(newDirFiles);
8139           
8140           for (String newFilePath : GrouperInstallerUtils.nonNull(newFileRelativePaths)) {
8141             // adjust for jars in web apps
8142             //patchDir (e.g. lib), newFilePath (e.g. something.jar or grouper/something.jar), originalAppToUpgrade (e.g. UI)
8143             File newFileInPatch = new File(newDirFiles.getAbsolutePath() + File.separator + newFilePath);
8144             if (!newFileInPatch.isFile()) {
8145               continue;
8146             }
8147             newFilePath = patchFixFilePath(applicationPath, patchDir, newFilePath, originalAppToUpgrade);
8148             File oldFileInGrouper = new File(applicationPath + newFilePath);
8149             oldFileInGrouper = fixLibraryFileIfFoundAndDifferent(oldFileInGrouper, originalAppToUpgrade);
8150 
8151             if (!oldFileInGrouper.exists() && !oldFileInGrouper.getParentFile().exists()) {
8152               GrouperInstallerUtils.mkdirs(oldFileInGrouper.getParentFile());
8153             }
8154             System.out.println("Applying file: " + oldFileInGrouper.getAbsolutePath());
8155             GrouperInstallerUtils.copyFile(newFileInPatch, oldFileInGrouper, false);
8156           }
8157         }
8158         
8159         File oldDirFiles = new File(oldDir.getAbsoluteFile() + File.separator + patchDir);
8160         
8161         if (oldDirFiles.exists() && oldDirFiles.isDirectory()) {
8162         
8163           // relative, e.g. WEB-INF/jsp/someFile.jsp
8164           Set<String> oldFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(oldDirFiles);
8165           
8166           for (String oldFilePath : GrouperInstallerUtils.nonNull(oldFileRelativePaths)) {
8167             File oldFileInPatch = new File(oldDirFiles.getAbsolutePath() + File.separator + oldFilePath);
8168             File newFileInPatch = new File(newDirFiles.getAbsolutePath() + File.separator + oldFilePath);
8169             File oldFileInGrouper = new File(applicationPath + oldFilePath);
8170             oldFileInGrouper = fixLibraryFileIfFoundAndDifferent(oldFileInGrouper, originalAppToUpgrade);
8171 
8172             if (oldFileInPatch.exists() && !newFileInPatch.exists() && oldFileInGrouper.exists() && oldFileInGrouper.isFile()) {
8173 
8174               System.out.println("Deleting file: " + oldFileInGrouper.getAbsolutePath());
8175               GrouperInstallerUtils.fileDelete(oldFileInGrouper);
8176               
8177             }
8178           }
8179         }
8180       }
8181       
8182       //fixLibDir(patchDirToApplicationPath.get("lib"), originalAppToUpgrade);
8183 
8184       this.patchesInstalled.add(keyBase);
8185       System.out.println("Patch successfully applied: " + keyBase);
8186       
8187       editPropertiesFile(patchExistingPropertiesFile, keyBase + ".state", 
8188           GrouperInstallerPatchStatus.applied.name(), true);
8189       System.out.println("");
8190     }
8191 
8192     if (!foundNewPatch) {
8193       System.out.println("There are no new " + thisAppToUpgrade + " patches to install\n");
8194       return false;
8195     } 
8196     return true;
8197   }
8198 
8199   /**
8200    * <pre>
8201    * patch file extra grouper prefix pattern
8202    * ^grouper[/\\][^/\\]+[/\\]([^/\\]+)$
8203    * starts with grouper, then a slash, then capture a dir and jar filename
8204    * </pre>
8205    */
8206   private static Pattern patchFileExtraGrouperPrefixPattern = Pattern.compile("^grouper[/\\\\]([^/\\\\]+[/\\\\][^/\\\\]+)$");
8207   
8208   /**
8209    * applicationPath (e.g. lib), newFilePath (e.g. something.jar or grouper/something.jar), originalAppToUpgrade (e.g. UI)
8210    * @param applicationPath
8211    * @param patchDir
8212    * @param newFilePath
8213    * @param originalAppToUpgrade
8214    * @return String of new newFilePath
8215    */
8216   public String patchFixFilePath(String applicationPath, String patchDir, String newFilePath, AppToUpgrade originalAppToUpgrade) {
8217 
8218     if ("lib".equals(patchDir)) {
8219       // if this is the api then there should be something between lib and the jar
8220       String jarName = newFilePath;
8221       {
8222 //        Matcher matcher = patchFileExtraGrouperPrefixPattern.matcher(newFilePath);
8223 //        if (matcher.matches()) {
8224 //          jarName = matcher.group(1);
8225 //        }
8226         jarName = GrouperInstallerUtils.suffixAfterChar(newFilePath.replace("\\", "/"), '/');
8227 
8228       }
8229       
8230 
8231       if (originalAppToUpgrade.isApiOrganized()) {
8232         
8233         String noSlashApplicationPath = GrouperInstallerUtils.stripLastSlashIfExists(applicationPath);
8234         // if the application path has "grouper" already, then just put jarname on it
8235         if (!noSlashApplicationPath.endsWith("lib")) {
8236           newFilePath = jarName;
8237         } else {
8238           //if application is just lib, then make sure there is something in front of the jarname
8239           if (GrouperInstallerUtils.equals(newFilePath, jarName)) {
8240             newFilePath = "grouper/" + jarName;
8241           }
8242         }
8243          
8244       } else {
8245         // if this is a UI or WS, then we only want the jar
8246         newFilePath = jarName;
8247       }
8248     }
8249     return newFilePath;
8250   }
8251 
8252   
8253   /**
8254    * @param keyBase
8255    * @return if should revert patch
8256    */
8257   private boolean shouldRevertCertainSpecifiedPatches(String keyBase) {
8258     List<String> revertUpToThesePatchLevelsList = GrouperInstallerUtils.splitTrimToList(this.revertCertainSpecifiedPatchesList, ",");
8259     return revertUpToThesePatchLevelsList.contains(keyBase);
8260   }
8261 
8262   /**
8263    * @param keyBase
8264    * @return if should install patch
8265    */
8266   private boolean shouldInstallCertainSpecifiedPatches(String keyBase) {
8267     
8268     List<String> installUpToThesePatchLevelsList = GrouperInstallerUtils.splitTrimToList(this.installCertainSpecifiedPatchesList, ",");
8269     return installUpToThesePatchLevelsList.contains(keyBase);
8270   }
8271   
8272   /**
8273    * @param keyBase
8274    * @return if should install patch
8275    */
8276   private boolean shouldInstallPatchUpToLevel(String keyBase) {
8277     boolean installPatch = false;
8278 
8279     //e.g. ^grouper_v(\\d+)_(\\d+)_(\\d+)_(api|ws|ui|psp|pspng)_patch_(\\d+)$
8280     Matcher patchNameMatcher = patchNamePattern.matcher(keyBase);
8281     if (!patchNameMatcher.matches()) {
8282       throw new RuntimeException("Invalid patch name: " + keyBase);
8283     }
8284     
8285     String grouperVersionInstallPatch = patchNameMatcher.group(1) + "." + patchNameMatcher.group(2) + "." + patchNameMatcher.group(3);
8286     String systemInstallPatch = patchNameMatcher.group(4);
8287     int numberInstallPatch = GrouperInstallerUtils.intValue(patchNameMatcher.group(5));
8288 
8289     
8290     String[] installUpToThesePatchLevels = GrouperInstallerUtils.splitTrim(this.installPatchesUpToThesePatchLevels, ",");
8291     for (String patchName : installUpToThesePatchLevels) {
8292 
8293       //e.g. ^grouper_v(\\d+)_(\\d+)_(\\d+)_(api|ws|ui|psp|pspng)_patch_(\\d+)$
8294       patchNameMatcher = patchNamePattern.matcher(patchName);
8295       if (!patchNameMatcher.matches()) {
8296         throw new RuntimeException("Invalid patch name: " + patchName);
8297       }
8298       
8299       String grouperVersionUpToPatch = patchNameMatcher.group(1) + "." + patchNameMatcher.group(2) + "." + patchNameMatcher.group(3);
8300       String systemUpToPatch = patchNameMatcher.group(4);
8301       int numberUpToPatch = GrouperInstallerUtils.intValue(patchNameMatcher.group(5));
8302 
8303       if (GrouperInstallerUtils.equals(systemInstallPatch, systemUpToPatch)
8304           && GrouperInstallerUtils.equals(grouperVersionInstallPatch, grouperVersionUpToPatch)
8305           && numberInstallPatch <= numberUpToPatch) {
8306         installPatch = true;
8307         break;
8308       }
8309       
8310     }
8311     return installPatch;
8312   }
8313   
8314   /**
8315    * fix the index file
8316    * @param thisAppToUpgrade app to upgrade to check
8317    */
8318   private void fixIndexFile(AppToUpgrade thisAppToUpgrade) {
8319 
8320     if (thisAppToUpgrade == AppToUpgrade.CLIENT) {
8321       throw new RuntimeException("Cant fix index file for " + thisAppToUpgrade);
8322     }
8323     
8324     Properties patchesExistingProperties = patchExistingProperties();
8325 
8326     String grouperVersion = this.grouperVersionOfJar().toString();
8327 
8328     grouperVersion = GrouperInstallerUtils.replace(grouperVersion, ".", "_");
8329 
8330     //lets download all patches
8331     int nextPatchIndex = downloadPatches(thisAppToUpgrade, grouperVersion);
8332     
8333     File patchExistingPropertiesFile = patchExistingPropertiesFile();
8334 
8335     Map<String, String> patchDirToApplicationPath = new LinkedHashMap<String, String>();
8336     patchDirToApplicationPath.put("files", this.upgradeExistingApplicationDirectoryString);
8337     patchDirToApplicationPath.put("classes", this.upgradeExistingClassesDirectoryString);
8338     patchDirToApplicationPath.put("lib", this.upgradeExistingLibDirectoryString);
8339     patchDirToApplicationPath.put("bin", this.upgradeExistingBinDirectoryString);
8340 
8341     //map of full patch file name to the patch number that is installed
8342     Map<String, Integer> fileInMoreRecentPatchMap = new HashMap<String, Integer>();
8343 
8344     boolean patchesOverallOk = true;
8345     
8346     //process patches from greatest to least
8347     for (int i=nextPatchIndex-1;i>=0;i--) {
8348       
8349       //grouper_v2_2_1_api_patch_0.state
8350       String patchName = "grouper_v" + grouperVersion + "_" + thisAppToUpgrade.name().toLowerCase() + "_patch_" + i;
8351 
8352       String key = patchName + ".state";
8353 
8354       //see what is already there
8355       String existingState = patchesExistingProperties.getProperty(key);
8356       
8357       GrouperInstallerPatchStatus grouperInstallerPatchStatus = GrouperInstallerPatchStatus.valueOfIgnoreCase(existingState, false, true);
8358 
8359       File patchUntarredDir = new File(this.grouperTarballDirectoryString + "patches" + File.separator + patchName);
8360 
8361       //keep track that we skipped this in the patch properties file
8362       //editPropertiesFile(patchExistingPropertiesFile, keyBase + ".date", 
8363       //    GrouperInstallerUtils.dateMinutesSecondsFormat.format(new Date()));
8364       //editPropertiesFile(patchExistingPropertiesFile, keyBase + ".state", 
8365       //    grouperInstallerPatchStatus.name());
8366 
8367       boolean patchHasProblem = false;
8368       boolean patchHasAtLeastOneFile = false;
8369       boolean patchHasAtLeastOneFileInAnotherPatch = false;
8370       Set<String> patchErrors = new LinkedHashSet<String>();
8371       
8372       //keep track of patch paths (full path in patch)
8373       Set<String> patchPaths = new HashSet<String>();
8374       
8375       //we are installing this patch, lets see if the files are there...
8376       //this.upgradeExistingApplicationDirectoryString
8377       //patchUntarredDir
8378       File newDir = new File(patchUntarredDir.getAbsolutePath() + File.separator + "new");
8379       //loop through lib, classes, files
8380       for (String patchDir : patchDirToApplicationPath.keySet()) {
8381 
8382         String applicationPath = patchDirToApplicationPath.get(patchDir);
8383 
8384         File newDirFiles = new File(newDir.getAbsoluteFile() + File.separator + patchDir);
8385 
8386         // relative, e.g. WEB-INF/jsp/someFile.jsp
8387         Set<String> newFileRelativePaths = GrouperInstallerUtils.fileDescendantRelativePaths(newDirFiles);
8388         // go through all files of the patches in the new dir
8389         for (String newFilePath : GrouperInstallerUtils.nonNull(newFileRelativePaths)) {
8390 
8391           String patchPath = patchDir + File.separator + newFilePath;
8392           
8393           Integer existsInPatchVersion = fileInMoreRecentPatchMap.get(patchPath);
8394           
8395           //if this file was in a newer patch, then thats ok
8396           if (existsInPatchVersion != null) {
8397             //this file is ok, its in a more recent patch
8398             patchHasAtLeastOneFileInAnotherPatch = true;
8399             continue;
8400           }
8401 
8402           File newFileInGrouper = new File(applicationPath + newFilePath);
8403 
8404           File newFileInPatch = new File(newDirFiles.getAbsolutePath() + File.separator + newFilePath);
8405           
8406           //see if the contents of the patch match those in grouper
8407           if (!GrouperInstallerUtils.contentEquals(newFileInPatch, newFileInGrouper)) {
8408 
8409             patchErrors.add("Problem in patch:\n  " + newFileInPatch.getAbsolutePath() 
8410                 + "\n  is not the same as what the patch expects:\n  " + newFileInGrouper.getAbsolutePath());
8411             patchHasProblem = true;
8412           } else {
8413 
8414             patchPaths.add(patchPath);
8415 
8416             patchHasAtLeastOneFile = true;
8417           }
8418         }
8419       }
8420         
8421       //is any file installed?  or if there are only files in other patches... hmm
8422       if (patchHasAtLeastOneFile || (patchHasAtLeastOneFileInAnotherPatch && !patchHasProblem )) {
8423         
8424         //add files in this patch to the list
8425         for (String patchPath : patchPaths) {
8426           fileInMoreRecentPatchMap.put(patchPath, i);
8427         }
8428         
8429         //one or more of the files in the patch had a problem
8430         if (patchHasProblem) {
8431           for (String patchError: patchErrors) {
8432             System.out.println(patchError);
8433           }
8434           if (grouperInstallerPatchStatus == null || (grouperInstallerPatchStatus != GrouperInstallerPatchStatus.applied
8435               && grouperInstallerPatchStatus != GrouperInstallerPatchStatus.error)) {
8436             patchesOverallOk = false;
8437             editPropertiesFile(patchExistingPropertiesFile, patchName + ".date", 
8438                 GrouperInstallerUtils.dateMinutesSecondsFormat.format(new Date()), true);
8439             editPropertiesFile(patchExistingPropertiesFile, patchName + ".state", 
8440                 GrouperInstallerPatchStatus.applied.name(), true);
8441             System.out.println("Patch " + patchName + " was listed as " + grouperInstallerPatchStatus + " but was changed to applied (even though there are files missing)");
8442             
8443           }
8444           continue;          
8445         }
8446         
8447         if (grouperInstallerPatchStatus == null || grouperInstallerPatchStatus != GrouperInstallerPatchStatus.applied) {
8448           patchesOverallOk = false;
8449           editPropertiesFile(patchExistingPropertiesFile, patchName + ".date", 
8450               GrouperInstallerUtils.dateMinutesSecondsFormat.format(new Date()), true);
8451           editPropertiesFile(patchExistingPropertiesFile, patchName + ".state", 
8452               GrouperInstallerPatchStatus.applied.name(), true);
8453           System.out.println("Patch " + patchName + " was listed as " + grouperInstallerPatchStatus + " but was changed to applied");
8454           
8455         }
8456         
8457       } else {
8458         if (grouperInstallerPatchStatus == null || grouperInstallerPatchStatus != GrouperInstallerPatchStatus.applied) {
8459           continue;
8460         }
8461         
8462         patchesOverallOk = false;
8463         editPropertiesFile(patchExistingPropertiesFile, patchName + ".date", 
8464             GrouperInstallerUtils.dateMinutesSecondsFormat.format(new Date()), true);
8465         editPropertiesFile(patchExistingPropertiesFile, patchName + ".state", 
8466             GrouperInstallerPatchStatus.skippedTemporarily.name(), true);
8467         System.out.println("Patch " + patchName + " was listed as applied but was changed to skippedTemporarily");
8468         continue;
8469       }
8470 
8471     }
8472 
8473     //tell the properties file that we have fixed the index file now
8474     editPropertiesFile(patchExistingPropertiesFile, "grouperInstallerLastFixedIndexFile.date", 
8475         GrouperInstallerUtils.dateMinutesSecondsFormat.format(new Date()), true);
8476   
8477     if (patchesOverallOk) {
8478       System.out.println("Patches for " + thisAppToUpgrade + " for version " + grouperVersion + " were in the index file correctly");
8479     }
8480   }
8481   
8482   /**
8483    * get all patches
8484    * @param thisAppToUpgrade app to upgrade to check
8485    * @param grouperVersion
8486    * @return next patch index
8487    */
8488   private int downloadPatches(AppToUpgrade thisAppToUpgrade, String grouperVersion) {
8489 
8490     if (thisAppToUpgrade == AppToUpgrade.CLIENT) {
8491       throw new RuntimeException("Cant install patches for " + thisAppToUpgrade);
8492     }
8493     
8494     Map<Integer, String> patchNumberToNameBase = new LinkedHashMap<Integer, String>();
8495     
8496     int nextPatchIndex = 0;
8497 
8498     OUTER: for (int i=0;i<1000;i++) {
8499 
8500       //grouper_v2_2_1_api_patch_0.state
8501       String keyBase = "grouper_v" + grouperVersion + "_" + thisAppToUpgrade.name().toLowerCase() + "_patch_" + i;
8502 
8503       patchNumberToNameBase.put(i, keyBase);
8504 
8505       //lets see if it exists on the server
8506       File patchUntarredDir = downloadAndUnzipPatch(keyBase);
8507 
8508       //if no more patches
8509       if (patchUntarredDir == null) {
8510         System.out.println("");
8511         break OUTER;
8512       }
8513 
8514       nextPatchIndex = i+1;
8515     }
8516 
8517     return nextPatchIndex;
8518 
8519   }
8520   
8521   /**
8522    * 
8523    * @param patchName e.g. grouper_v2_2_1_api_patch_0.tar.gz
8524    * @return the directory of the unzipped patch
8525    */
8526   public File downloadAndUnzipPatch(String patchName) {
8527     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
8528     
8529     if (!urlToDownload.endsWith("/")) {
8530       urlToDownload += "/";
8531     }
8532     urlToDownload += "release/";
8533     
8534     //e.g. 2.2.2
8535     Matcher patchNameMatcher = patchNamePattern.matcher(patchName);
8536     if (!patchNameMatcher.matches()) {
8537       throw new RuntimeException("Invalid patch name: " + patchName);
8538     }
8539     
8540     //String grouperVersion = GrouperInstallerUtils.propertiesValue("grouper.version", true);
8541     String grouperVersion = patchNameMatcher.group(1) + "." + patchNameMatcher.group(2) + "." + patchNameMatcher.group(3);
8542     
8543     urlToDownload +=  grouperVersion + "/patches/" + patchName + ".tar.gz";
8544 
8545     File patchFile = new File(this.grouperTarballDirectoryString + "patches" + File.separator + patchName + ".tar.gz");
8546     
8547     if (GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.downloadPatches", true, false)) {
8548 
8549       boolean foundFile = downloadFile(urlToDownload, patchFile.getAbsolutePath(), true, "Patch doesnt exist yet (not an error): ", 
8550           "grouperInstaller.autorun.useLocalPatchIfExists");
8551       
8552       if (!foundFile) {
8553 
8554         //if we are doing test patches
8555         if (GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.useTestPatches", false, false)) {
8556           String testUrlToDownload = GrouperInstallerUtils.replace(urlToDownload, ".tar.gz", "_test.tar.gz");
8557           
8558           //its a test url, but download to the same file name
8559           foundFile = downloadFile(testUrlToDownload, patchFile.getAbsolutePath(), true, "Patch doesnt exist yet (not an error): ", 
8560               "grouperInstaller.autorun.useLocalPatchIfExists");
8561         }
8562 
8563         if (!foundFile) {
8564           return null;
8565         }
8566       }
8567     } else {
8568       if (!patchFile.exists()) {
8569         return null;
8570       }
8571     }
8572     
8573     //####################################
8574     //unzip/untar the patch file
8575     
8576     File unzippedFile = unzip(patchFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalPatchIfExists");
8577     File untarredDir = untar(unzippedFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalPatchIfExists", null);
8578     return untarredDir;
8579   }
8580 
8581   /**
8582    * 
8583    * @param branchName
8584    * @return the directory of the unzipped source repo
8585    */
8586   public File downloadAndUnzipGrouperSource(String branchName) {
8587     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.source.url", false);
8588     
8589     if (GrouperInstallerUtils.isBlank(urlToDownload)) {
8590       urlToDownload = "https://github.com/Internet2/grouper/archive/$BRANCH_NAME$.zip";
8591     }
8592     
8593     urlToDownload = GrouperInstallerUtils.replace(urlToDownload, "$BRANCH_NAME$", branchName);
8594 
8595     String fileToDownload = GrouperInstallerUtils.substringAfterLast(urlToDownload, "/");
8596     
8597     File sourceFile = new File(this.grouperTarballDirectoryString + fileToDownload);
8598     
8599     if (GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.downloadSource", true, false)) {
8600 
8601       downloadFile(urlToDownload, sourceFile.getAbsolutePath(), "grouperInstaller.autorun.createPatchDownloadSourceUseLocalIfExist");
8602       
8603     } else {
8604       if (!sourceFile.exists()) {
8605         throw new RuntimeException("Cant find grouper source");
8606       }
8607     }
8608     
8609     //####################################
8610     //unzip/untar the source file
8611     File unzippedDir = unzipFromZip(sourceFile.getAbsolutePath(), "grouperInstaller.autorun.createPatchDownloadSourceUseLocalIfExist");
8612     return unzippedDir;
8613   }
8614 
8615   /**
8616    * 
8617    * @param branchName
8618    * @return the directory of the unzipped source repo
8619    */
8620   public File downloadAndUnzipPspSource(String branchName) {
8621     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.pspSource.url", false);
8622     
8623     if (GrouperInstallerUtils.isBlank(urlToDownload)) {
8624       urlToDownload = "https://github.com/Internet2/grouper-psp/archive/$BRANCH_NAME$.zip";
8625     }
8626     
8627     urlToDownload = GrouperInstallerUtils.replace(urlToDownload, "$BRANCH_NAME$", branchName);
8628 
8629     String fileToDownload = GrouperInstallerUtils.substringAfterLast(urlToDownload, "/");
8630     
8631     File sourceFile = new File(this.grouperTarballDirectoryString + fileToDownload);
8632     
8633     if (GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.downloadSource", true, false)) {
8634 
8635       downloadFile(urlToDownload, sourceFile.getAbsolutePath(), "grouperInstaller.autorun.createPatchDownloadSourceUseLocalIfExist");
8636       
8637     } else {
8638       if (!sourceFile.exists()) {
8639         throw new RuntimeException("Cant find grouper psp source");
8640       }
8641     }
8642     
8643     //####################################
8644     //unzip/untar the source file
8645     File unzippedDir = unzipFromZip(sourceFile.getAbsolutePath(), "grouperInstaller.autorun.createPatchDownloadSourceUseLocalIfExist");
8646     return unzippedDir;
8647   }
8648 
8649   /**
8650    * 
8651    */
8652   public static enum GrouperInstallerPatchStatus {
8653 
8654     /**
8655      * patch was applied
8656      */
8657     applied, 
8658     
8659     /**
8660      * patch was removed
8661      */
8662     reverted, 
8663     
8664     /**
8665      * patch was skipped temporarily, prompt again
8666      */
8667     skippedTemporarily, 
8668 
8669     /**
8670      * patch had an error applying
8671      */
8672     error, 
8673 
8674     /**
8675      * patch was skipped permanently, dont prompt again
8676      */
8677     skippedPermanently;
8678 
8679     /**
8680      * 
8681      * @param string
8682      * @param exceptionIfNotFound
8683      * @param exceptionIfInvalid
8684      * @return the patch status
8685      */
8686     public static GrouperInstallerPatchStatus valueOfIgnoreCase(String string, boolean exceptionIfNotFound, boolean exceptionIfInvalid) {
8687       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerPatchStatus.class, string, exceptionIfNotFound, exceptionIfInvalid);
8688     }
8689     
8690   }
8691 
8692   /**
8693    * patch status api
8694    */
8695   private void patchStatusApi() {
8696     this.patchStatus(AppToUpgrade.API);
8697   }
8698 
8699 
8700   /**
8701    * patch the api
8702    */
8703   private void patchApi() {
8704     this.downloadAndInstallPatches(AppToUpgrade.API, AppToUpgrade.API);
8705   }
8706 
8707   /**
8708    * fix index file api
8709    */
8710   private void fixIndexFileApi() {
8711     this.fixIndexFile(AppToUpgrade.API);
8712   }
8713 
8714   /**
8715    * fix index file ui
8716    */
8717   private void fixIndexFileUi() {
8718     this.fixIndexFile(AppToUpgrade.UI);
8719     this.fixIndexFile(AppToUpgrade.API);
8720   }
8721 
8722   /**
8723    * fix index file ws
8724    */
8725   private void fixIndexFileWs() {
8726     this.fixIndexFile(AppToUpgrade.WS);
8727     this.fixIndexFile(AppToUpgrade.API);
8728   }
8729 
8730   /**
8731    * fix index file psp
8732    */
8733   private void fixIndexFilePsp() {
8734     this.fixIndexFile(AppToUpgrade.PSP);
8735     this.fixIndexFile(AppToUpgrade.API);
8736   }
8737 
8738   /**
8739    * fix index file psp
8740    */
8741   private void fixIndexFilePspng() {
8742     this.fixIndexFile(AppToUpgrade.PSPNG);
8743     this.fixIndexFile(AppToUpgrade.API);
8744   }
8745 
8746   /**
8747    * patch status ui
8748    */
8749   private void patchStatusUi() {
8750     this.patchStatus(AppToUpgrade.API);
8751     this.patchStatus(AppToUpgrade.UI);
8752   }
8753 
8754   /**
8755    * patch status ws
8756    */
8757   private void patchStatusWs() {
8758     this.patchStatus(AppToUpgrade.API);
8759     this.patchStatus(AppToUpgrade.WS);
8760   }
8761 
8762   /**
8763    * patch status psp
8764    */
8765   private void patchStatusPsp() {
8766     this.patchStatus(AppToUpgrade.API);
8767     this.patchStatus(AppToUpgrade.PSP);
8768   }
8769 
8770   /**
8771    * patch status pspng
8772    */
8773   private void patchStatusPspng() {
8774     this.patchStatus(AppToUpgrade.API);
8775     this.patchStatus(AppToUpgrade.PSPNG);
8776   }
8777 
8778 
8779   /**
8780    * patch the client
8781    */
8782   private void patchUi() {
8783     this.downloadAndInstallPatches(AppToUpgrade.API, AppToUpgrade.UI);
8784     boolean patchesApplied = this.downloadAndInstallPatches(AppToUpgrade.UI, AppToUpgrade.UI);
8785     if (patchesApplied && (this.grouperInstallerMainFunction == GrouperInstallerMainFunction.patch 
8786         || this.grouperInstallerMainFunction == GrouperInstallerMainFunction.upgrade)) {
8787       System.out.print("Since patches were applied, you should delete files in your app server work directory,"
8788           + "\n  in tomcat it is named 'work'.  Hit <enter> to continue: ");
8789       readFromStdIn("grouperInstaller.autorun.continueAfterDeleteUiWorkDirectory");
8790     }
8791   }
8792   
8793   /**
8794    * patch the client
8795    */
8796   private void patchWs() {
8797     this.downloadAndInstallPatches(AppToUpgrade.API, AppToUpgrade.WS);
8798     boolean patchesApplied = this.downloadAndInstallPatches(AppToUpgrade.WS, AppToUpgrade.WS);
8799     if (patchesApplied && (this.grouperInstallerMainFunction == GrouperInstallerMainFunction.patch 
8800         || this.grouperInstallerMainFunction == GrouperInstallerMainFunction.upgrade)) {
8801       System.out.print("Since patches were applied, you should delete files in your app server work directory,"
8802           + "\n  in tomcat it is named 'work'.  Hit <enter> to continue: ");
8803       readFromStdIn("grouperInstaller.autorun.continueAfterDeleteWsWorkDirectory");
8804     }
8805   }
8806   
8807   /**
8808    * patch the psp
8809    */
8810   private void patchPsp() {
8811     this.downloadAndInstallPatches(AppToUpgrade.API, AppToUpgrade.PSP);
8812     this.downloadAndInstallPatches(AppToUpgrade.PSP, AppToUpgrade.PSP);
8813   }
8814 
8815   /**
8816    * patch the pspng
8817    */
8818   private void patchPspng() {
8819     this.downloadAndInstallPatches(AppToUpgrade.API, AppToUpgrade.PSPNG);
8820     this.downloadAndInstallPatches(AppToUpgrade.PSPNG, AppToUpgrade.PSPNG);
8821   }
8822 
8823   /**
8824    * revert patch the client
8825    */
8826   private void patchRevertApi() {
8827     this.revertPatches(AppToUpgrade.API, AppToUpgrade.API);
8828   }
8829 
8830   /**
8831    * revert patch the client
8832    */
8833   private void patchRevertUi() {
8834     this.revertPatches(AppToUpgrade.UI, AppToUpgrade.UI);
8835     boolean patchesReverted = this.revertPatches(AppToUpgrade.API, AppToUpgrade.UI);
8836     if (patchesReverted && (this.grouperInstallerMainFunction == GrouperInstallerMainFunction.patch 
8837         || this.grouperInstallerMainFunction == GrouperInstallerMainFunction.upgrade)) {
8838       System.out.print("Since patches were reverted, you should delete files in your app server work directory,"
8839           + "\n  in tomcat it is named 'work'.  Hit <enter> to continue: ");
8840       readFromStdIn("grouperInstaller.autorun.continueAfterDeleteUiWorkDirectory");
8841     }
8842   }
8843   
8844   /**
8845    * revert patch the client
8846    */
8847   private void patchRevertWs() {
8848     this.revertPatches(AppToUpgrade.WS,AppToUpgrade.WS);
8849     boolean patchesReverted = this.revertPatches(AppToUpgrade.API, AppToUpgrade.WS);
8850     if (patchesReverted && (this.grouperInstallerMainFunction == GrouperInstallerMainFunction.patch 
8851         || this.grouperInstallerMainFunction == GrouperInstallerMainFunction.upgrade)) {
8852       System.out.print("Since patches were reverted, you should delete files in your app server work directory,"
8853           + "\n  in tomcat it is named 'work'.  Hit <enter> to continue: ");
8854       readFromStdIn("grouperInstaller.autorun.continueAfterDeleteWsWorkDirectory");
8855     }
8856   }
8857   
8858   /**
8859    * revert patch the psp
8860    */
8861   private void patchRevertPsp() {
8862     this.revertPatches(AppToUpgrade.PSP, AppToUpgrade.PSP);
8863     this.revertPatches(AppToUpgrade.API, AppToUpgrade.PSP);
8864   }
8865 
8866   /**
8867    * revert patch the pspng
8868    */
8869   private void patchRevertPspng() {
8870     this.revertPatches(AppToUpgrade.PSPNG, AppToUpgrade.PSPNG);
8871     this.revertPatches(AppToUpgrade.API, AppToUpgrade.PSPNG);
8872   }
8873 
8874   /**
8875    * owasp csrf guard file
8876    */
8877   private File owaspCsrfGuardFile;
8878   
8879   /**
8880    * owasp csrf guard base file
8881    */
8882   private File owaspCsrfGuardBaseFile;
8883   
8884   /**
8885    * on an upgrade, compare a new jar and an existing jar and see if needs to be updated, and if so, update it
8886    * @param existingJarFile
8887    * @param newJarFile
8888    * @param printResultIfNotUpgrade
8889    * @param toDir if file not there, copy here.  If null, then go to upgrade existing lib directory string
8890    * @return true if upgraded, false if not
8891    */
8892   private boolean compareAndReplaceJar(File existingJarFile, File newJarFile, boolean printResultIfNotUpgrade, File toDir) {
8893     
8894     if (toDir == null) {
8895       toDir = new File(this.upgradeExistingLibDirectoryString);
8896     }
8897     
8898     if (existingJarFile == null || !existingJarFile.exists()) {
8899       System.out.println(newJarFile.getName() + " is a new file and is being copied to the application lib dir");
8900       existingJarFile = new File(toDir.getAbsoluteFile() + File.separator + newJarFile.getName());
8901       GrouperInstallerUtils.copyFile(newJarFile, existingJarFile, true);
8902       return true;
8903     }
8904 
8905     String existingJarFilePath = existingJarFile.getAbsolutePath();
8906     if (!GrouperInstallerUtils.filePathStartsWith(existingJarFilePath,this.upgradeExistingApplicationDirectoryString)) {
8907       throw new RuntimeException("Why does existing path not start with upgrade path??? " + existingJarFilePath + ", " + this.upgradeExistingApplicationDirectoryString);
8908     }
8909     
8910     String bakJarFileString = this.grouperBaseBakDir + existingJarFilePath.substring(this.upgradeExistingApplicationDirectoryString.length());
8911     File bakJarFile = new File(bakJarFileString);
8912     
8913     String existingVersion = GrouperInstallerUtils.jarVersion(existingJarFile);
8914     String newVersion = GrouperInstallerUtils.jarVersion(newJarFile);
8915     
8916     long existingSize = existingJarFile.length();
8917     long newSize = newJarFile.length();
8918     
8919     if (!GrouperInstallerUtils.equals(existingVersion, newVersion) || existingSize != newSize) {
8920 
8921       //make sure parents exist
8922       GrouperInstallerUtils.createParentDirectories(bakJarFile);
8923       
8924       System.out.println(existingJarFile.getName() + " had version " + existingVersion + " and size " + existingSize + " bytes and is being upgraded to version "
8925           + newVersion + " and size " + newSize + " bytes.\n  It is backed up to " + bakJarFile);
8926 
8927       GrouperInstallerUtils.fileMove(existingJarFile, bakJarFile);
8928       
8929       GrouperInstallerUtils.copyFile(newJarFile, existingJarFile, true);
8930       
8931       return true;
8932     }
8933     
8934     if (printResultIfNotUpgrade) {
8935       System.out.println(existingJarFile.getName() + " is up to date");
8936     }
8937     return false;
8938   }
8939 
8940   /**
8941    * on an upgrade, compare a new file and an existing file and see if needs to be updated, and if so, update it
8942    * @param existingFile
8943    * @param newFile
8944    * @param printResultIfNotUpgrade
8945    * @param toDir if file not there, copy here.  If null, then go to upgrade existing lib directory string
8946    * @return true if upgraded, false if not
8947    */
8948   private boolean compareAndCopyFile(File existingFile, File newFile, boolean printResultIfNotUpgrade, File toDir) {
8949     
8950     if (toDir == null) {
8951       throw new RuntimeException("Which dir to copy to??? " + newFile + ", " + existingFile);
8952     }
8953     
8954     if (existingFile == null || !existingFile.exists()) {
8955       System.out.println(newFile.getName() + " is a new file and is being copied to the application dir: " + toDir.getAbsolutePath());
8956       existingFile = new File(toDir.getAbsoluteFile() + File.separator + newFile.getName());
8957       GrouperInstallerUtils.copyFile(newFile, existingFile, true);
8958       return true;
8959     }
8960 
8961     String existingFilePath = existingFile.getAbsolutePath();
8962     if (!GrouperInstallerUtils.filePathStartsWith(existingFilePath,this.upgradeExistingApplicationDirectoryString)) {
8963       throw new RuntimeException("Why does existing path not start with upgrade path??? " + existingFilePath + ", " + this.upgradeExistingApplicationDirectoryString);
8964     }
8965     
8966     String bakFileString = this.grouperBaseBakDir + existingFilePath.substring(this.upgradeExistingApplicationDirectoryString.length());
8967     File bakFile = new File(bakFileString);
8968     
8969     String existingChecksum = GrouperInstallerUtils.fileSha1(existingFile);
8970     String newChecksum = GrouperInstallerUtils.fileSha1(newFile);
8971     
8972     long existingSize = existingFile.length();
8973     long newSize = newFile.length();
8974     
8975     if (!GrouperInstallerUtils.equals(existingChecksum, newChecksum) || existingSize != newSize) {
8976 
8977       //make sure parents exist
8978       GrouperInstallerUtils.createParentDirectories(bakFile);
8979       
8980       System.out.println(existingFile.getName() + " had checksum " + existingChecksum + " and size " + existingSize + " bytes and is being upgraded to checksum "
8981           + newChecksum + " and size " + newSize + " bytes.\n  It is backed up to " + bakFile);
8982 
8983       GrouperInstallerUtils.fileMove(existingFile, bakFile);
8984       
8985       GrouperInstallerUtils.copyFile(newFile, existingFile, true);
8986       
8987       return true;
8988     }
8989     
8990     if (printResultIfNotUpgrade) {
8991       System.out.println(existingFile.getName() + " is up to date");
8992     }
8993     return false;
8994   }
8995 
8996   /**
8997    * grouper.client.properties
8998    */
8999   private File grouperClientPropertiesFile;
9000   
9001   /**
9002    * grouper.client.base.properties
9003    */
9004   private File grouperClientBasePropertiesFile;
9005   
9006   /**
9007    * grouper.client.example.properties
9008    */
9009   private File grouperClientExamplePropertiesFile;
9010 
9011   /**
9012    * grouperClient.jar
9013    */
9014   private File grouperClientJar;
9015 
9016   /**
9017    * grouper.properties
9018    */
9019   private File grouperPropertiesFile;
9020   
9021   /**
9022    * grouper.base.properties
9023    */
9024   private File grouperBasePropertiesFile;
9025   
9026   /**
9027    * subject.properties
9028    */
9029   private File subjectPropertiesFile;
9030   
9031   /**
9032    * subject.base.properties
9033    */
9034   private File subjectBasePropertiesFile;
9035   
9036   /**
9037    * grouperUtf8.txt
9038    */
9039   private File grouperUtf8File;
9040   
9041   /**
9042    * GSHFileLoad.properties
9043    */
9044   private File gshFileLoadPropertiesFile;
9045   
9046   /**
9047    * groovysh.profile
9048    */
9049   private File groovyshProfileFile;
9050   
9051   /**
9052    * grouper.client.usage.example.txt
9053    */
9054   private File grouperClientUsageExampleFile;
9055   
9056   /**
9057    * grouper.example.properties
9058    */
9059   private File grouperExamplePropertiesFile;
9060 
9061   /**
9062    * grouper.hibernate.properties
9063    */
9064   private File grouperHibernatePropertiesFile;
9065   
9066   /**
9067    * grouper.hibernate.base.properties
9068    */
9069   private File grouperHibernateBasePropertiesFile;
9070   
9071   /**
9072    * grouper.hibernate.example.properties
9073    */
9074   private File grouperHibernateExamplePropertiesFile;
9075 
9076   /**
9077    * grouper-ws.properties
9078    */
9079   private File grouperWsPropertiesFile;
9080   
9081   /**
9082    * grouper-ws.example.properties
9083    */
9084   private File grouperWsBasePropertiesFile;
9085 
9086   /**
9087    * grouper-ws.base.properties
9088    */
9089   private File grouperWsExamplePropertiesFile;
9090   
9091   /**
9092    * ehcache.xml
9093    */
9094   private File ehcacheFile;
9095 
9096   /**
9097    * ehcache.example.xml
9098    */
9099   private File ehcacheExampleFile;
9100   
9101   /**
9102    * grouper-loader.properties
9103    */
9104   private File grouperLoaderPropertiesFile;
9105   
9106   /**
9107    * grouper-loader.base.properties
9108    */
9109   private File grouperLoaderBasePropertiesFile;
9110   
9111   /**
9112    * grouper.cache.properties
9113    */
9114   private File grouperCachePropertiesFile;
9115   
9116   /**
9117    * grouper.cache.base.properties
9118    */
9119   private File grouperCacheBasePropertiesFile;
9120   
9121   /**
9122    * grouper-loader.example.properties
9123    */
9124   private File grouperLoaderExamplePropertiesFile;
9125 
9126   /**
9127    * grouper.jar
9128    */
9129   private File grouperJar;
9130 
9131   
9132   /**
9133    * find a classpath file on classpath by resourceName
9134    * @param resourceName resource name of file
9135    * @param exceptionIfNotFound 
9136    * @return the file or null if not exception if not found
9137    */
9138   private File findClasspathFile(String resourceName, boolean exceptionIfNotFound) {
9139     
9140     Set<String> fileNamesTried = new LinkedHashSet<String>();
9141     
9142     File file = new File(this.upgradeExistingApplicationDirectoryString + "classes" + File.separator + resourceName);
9143     if (file.exists()) {
9144       return file;
9145     }
9146     
9147     fileNamesTried.add(file.getAbsolutePath());
9148     
9149     file = new File(this.upgradeExistingApplicationDirectoryString + "conf" + File.separator + resourceName);
9150     if (file.exists()) {
9151       return file;
9152     }
9153 
9154     fileNamesTried.add(file.getAbsolutePath());
9155 
9156     //these could be in this location
9157     if (GrouperInstallerUtils.equals("nav.properties", resourceName) 
9158         || GrouperInstallerUtils.equals("media.properties", resourceName)) {
9159       file = new File(this.upgradeExistingApplicationDirectoryString + "WEB-INF" + File.separator 
9160           + "classes" + File.separator + "resources" + File.separator + "grouper" + File.separator + resourceName);
9161       if (file.exists()) {
9162         return file;
9163       }
9164       
9165       fileNamesTried.add(file.getAbsolutePath());
9166     }
9167     
9168     file = new File(this.upgradeExistingApplicationDirectoryString + "WEB-INF" + File.separator + "classes" 
9169         + File.separator + resourceName);
9170     if (file.exists()) {
9171       return file;
9172     }
9173 
9174     fileNamesTried.add(file.getAbsolutePath());
9175     
9176     file = new File(this.upgradeExistingApplicationDirectoryString + resourceName);
9177     if (file.exists()) {
9178       return file;
9179     }
9180 
9181     fileNamesTried.add(file.getAbsolutePath());
9182     
9183     if (exceptionIfNotFound) {
9184       throw new RuntimeException("Cant find file, looked in: " + GrouperInstallerUtils.join(fileNamesTried.iterator(), ", "));
9185     }
9186     
9187     return null;
9188   }
9189 
9190   /**
9191    * lib dirs where libs might be in this.upgradeExistingApplicationDirectoryString
9192    */
9193   private static List<String> libDirs = GrouperInstallerUtils.toList(
9194       "lib" + File.separator, 
9195       "WEB-INF" + File.separator + "lib" + File.separator,
9196       "lib" + File.separator + "grouper" + File.separator,
9197       "lib" + File.separator + "custom" + File.separator,
9198       "lib" + File.separator + "jdbcSamples" + File.separator,
9199       "dist" + File.separator + "lib" + File.separator,
9200       "");
9201 
9202   /**
9203    * get all library files
9204    * @param appDir 
9205    * @return the list of files
9206    */
9207   private List<File> findAllLibraryFiles(String appDir) {
9208     
9209     if (!appDir.endsWith("/") && !appDir.endsWith("\\")) {
9210       appDir = appDir + File.separator;
9211     }
9212     
9213     List<File> result = new ArrayList<File>();
9214     for (String libDir : libDirs) {
9215 
9216       File dir = new File(appDir + libDir);
9217       if (dir.exists() && dir.isDirectory()) {
9218         for (File file : dir.listFiles()) {
9219           if (file.getName().endsWith(".jar")) {
9220             result.add(file);
9221           }
9222         }
9223       }
9224       
9225     }
9226     return result;
9227   }
9228 
9229   /**
9230    * if file is there, return it.  if not a jar, return original
9231    * find a library file on lib dir by lib name, if there return it
9232    * otherwise just return the original file name.  fix the name if not correct
9233    * @param originalThoughtLocation 
9234    * @param originalAppToUpgrade
9235    * @return the file or null if not exception if not found
9236    */
9237   private File fixLibraryFileIfFoundAndDifferent(File originalThoughtLocation, AppToUpgrade originalAppToUpgrade) {
9238     
9239     if (originalThoughtLocation == null || (originalThoughtLocation.exists() && originalThoughtLocation.isFile())) {
9240       return originalThoughtLocation;
9241     }
9242     
9243     if (!originalThoughtLocation.getAbsolutePath().endsWith(".jar")) {
9244       return originalThoughtLocation;
9245     }
9246     
9247     File foundLibraryFile = findLibraryFile(originalThoughtLocation.getName(), false);
9248     if (foundLibraryFile != null && foundLibraryFile.exists() && foundLibraryFile.isFile()) {
9249       return foundLibraryFile;
9250     }
9251     
9252     if (!originalAppToUpgrade.isApiOrganized()) {
9253       
9254       // is in WEB-INF/lib/something.jar
9255       if (GrouperInstallerUtils.equals("lib", originalThoughtLocation.getParentFile().getName())) {
9256         return originalThoughtLocation;
9257       }
9258       
9259       if (GrouperInstallerUtils.equals("lib", originalThoughtLocation.getParentFile().getParentFile().getName())) {
9260         return new File(originalThoughtLocation.getParentFile().getParentFile().getAbsoluteFile() + File.separator + originalThoughtLocation.getName());
9261       }
9262       
9263       return originalThoughtLocation;
9264     }
9265     
9266     //is api
9267     if (!GrouperInstallerUtils.equals("lib", originalThoughtLocation.getParentFile().getName())
9268         && GrouperInstallerUtils.equals("lib", originalThoughtLocation.getParentFile().getParentFile().getName())) {
9269       return originalThoughtLocation;
9270     }
9271       
9272     if (GrouperInstallerUtils.equals("lib", originalThoughtLocation.getParentFile().getName())) {
9273       return new File(originalThoughtLocation.getParentFile().getAbsoluteFile() + File.separator + "grouper" + File.separator + originalThoughtLocation.getName());
9274     }
9275     
9276     // not sure what to do here
9277     return originalThoughtLocation;
9278   }
9279 
9280   /**
9281    * find a library file on lib dir by libName
9282    * @param libName lib name of file
9283    * @param exceptionIfNotFound 
9284    * @return the file or null if not exception if not found
9285    */
9286   private File findLibraryFile(String libName, boolean exceptionIfNotFound) {
9287     
9288     Set<String> fileNamesTried = new LinkedHashSet<String>();
9289 
9290     for (String libDir : libDirs) {
9291 
9292       File file = new File(this.upgradeExistingApplicationDirectoryString + libDir + libName);
9293       if (file.exists()) {
9294         return file;
9295       }
9296       
9297       fileNamesTried.add(file.getAbsolutePath());
9298       
9299     }
9300     
9301     if (exceptionIfNotFound) {
9302       throw new RuntimeException("Cant find file, looked in: " + GrouperInstallerUtils.join(fileNamesTried.iterator(), ", "));
9303     }
9304     
9305     return null;
9306   }
9307   
9308   /**
9309    * 
9310    */
9311   private void mainInstallLogic() {
9312     
9313     //####################################
9314     //Find out what directory to install to.  This ends in a file separator
9315     this.grouperInstallDirectoryString = grouperInstallDirectory();
9316 
9317     //Find out what directory to upgrade to.  This ends in a file separator
9318     this.grouperTarballDirectoryString = grouperUpgradeTempDirectory();
9319 
9320     //####################################
9321     //get default ip address
9322     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]: ");
9323     this.defaultIpAddress = readFromStdIn("grouperInstaller.autorun.defaultIpAddressForPorts");
9324     
9325     if (GrouperInstallerUtils.isBlank(this.defaultIpAddress)) {
9326       this.defaultIpAddress = "0.0.0.0";
9327     }
9328 
9329     if (!GrouperInstallerUtils.equals("0.0.0.0", this.defaultIpAddress)) {
9330       System.out.println("Note, you will probably need to change the hsql IP address, and tomcat server.xml IP addresses...");
9331     }
9332     
9333     //####################################
9334     //System.out.println("Grouper install directory is: " + grouperInstallDirectoryFile.getAbsolutePath());
9335 
9336     this.version = GrouperInstallerUtils.propertiesValue("grouper.version", true);
9337     System.out.println("Installing grouper version: " + this.version);
9338     //see if it is already downloaded
9339     
9340     //download api and set executable and dos2unix etc
9341     downloadAndConfigureApi();
9342 
9343     //####################################
9344     //ask about database
9345 
9346     File localGrouperHibernatePropertiesFile = new File(this.untarredApiDir.getAbsoluteFile() + File.separator + "conf" 
9347         + File.separator + "grouper.hibernate.properties");
9348 
9349     Properties grouperHibernateProperties = GrouperInstallerUtils.propertiesFromFile(localGrouperHibernatePropertiesFile);
9350 
9351     this.dbUrl = GrouperInstallerUtils.defaultString(grouperHibernateProperties.getProperty("hibernate.connection.url"), "jdbc:hsqldb:hsql://localhost:9001/grouper");
9352     this.dbUser = GrouperInstallerUtils.defaultString(grouperHibernateProperties.getProperty("hibernate.connection.username"));
9353     this.dbPass = GrouperInstallerUtils.defaultString(grouperHibernateProperties.getProperty("hibernate.connection.password"));
9354 
9355     boolean useHsqldb = false;
9356 
9357     if (this.dbUrl.contains(":hsqldb:")) {
9358       System.out.print("Do you want to use the default and included hsqldb database (t|f)? [t]: ");
9359       useHsqldb = readFromStdInBoolean(true, "grouperInstaller.autorun.useBuiltInHsql");
9360     }
9361 
9362     if (!useHsqldb) {
9363 
9364       System.out.println("\n##################################\n");
9365       System.out.println("Example mysql URL: jdbc:mysql://localhost:3306/grouper");
9366       System.out.println("Example oracle URL: jdbc:oracle:thin:@server.school.edu:1521:sid");
9367       System.out.println("Example hsqldb URL: jdbc:hsqldb:hsql://localhost:9001/grouper");
9368       System.out.println("Example postgres URL: jdbc:postgresql://localhost:5432/database");
9369       System.out.println("Example mssql URL: jdbc:sqlserver://localhost:3280;databaseName=grouper");
9370       System.out.print("\nEnter the database URL [" + this.dbUrl + "]: ");
9371       String newDbUrl = readFromStdIn("grouperInstaller.autorun.dbUrl");
9372       if (!GrouperInstallerUtils.isBlank(newDbUrl)) {
9373         this.dbUrl = newDbUrl;
9374         if (newDbUrl.contains("postgresql") || newDbUrl.contains("sqlserver")) {
9375           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");
9376           for (int i=0;i<3;i++) {
9377             System.out.print("Ready to continue? (t|f)? [t] ");
9378             boolean shouldContinue = readFromStdInBoolean(true, "grouperInstaller.autorun.dbContinueAfterChangeSourcesXmlForPostgresSqlServer");
9379             if (shouldContinue) {
9380               break;
9381             }
9382           }
9383         }
9384       }
9385       System.out.print("Database user [" + this.dbUser + "]: ");
9386       String newDbUser = readFromStdIn("grouperInstaller.autorun.dbUser");
9387       if (!GrouperInstallerUtils.isBlank(newDbUser)) {
9388         this.dbUser = newDbUser;
9389       }
9390       System.out.print("Database password (note, you aren't setting the pass here, you are using an existing pass, this will be echoed back) [" 
9391           + GrouperInstallerUtils.defaultIfEmpty(this.dbPass, "<blank>") + "]: ");
9392       String newDbPass = readFromStdIn("grouperInstaller.autorun.dbPass");
9393       if (!GrouperInstallerUtils.isBlank(newDbPass)) {
9394         this.dbPass = newDbPass;
9395       }
9396     }
9397 
9398     this.giDbUtils = new GiDbUtils(this.dbUrl, this.dbUser, this.dbPass);
9399     this.giDbUtils.registerDriverOnce(this.grouperInstallDirectoryString);
9400 
9401     //####################################
9402     //change the config file
9403     //get the config file
9404 
9405     //lets edit the three properties:
9406     System.out.println("Editing " + localGrouperHibernatePropertiesFile.getAbsolutePath() + ": ");
9407     editPropertiesFile(localGrouperHibernatePropertiesFile, "hibernate.connection.url", this.dbUrl, false);
9408     editPropertiesFile(localGrouperHibernatePropertiesFile, "hibernate.connection.username", this.dbUser, false);
9409     editPropertiesFile(localGrouperHibernatePropertiesFile, "hibernate.connection.password", this.dbPass, false);
9410 
9411     //####################################
9412     //check to see if listening on port?
9413 
9414     //####################################
9415     //lets get the java command
9416     validJavaVersion();
9417 
9418     //#####################################
9419     //add driver to classpath
9420     //note, we are note really doing this now, we are using drivers already on classpath since this doesnt work
9421     //this.addDriverJarToClasspath();
9422 
9423     //####################################
9424     //start database if needed (check on port?  ask to change port?)
9425     if (this.dbUrl.contains("hsqldb")) {
9426       //C:\mchyzer\grouper\trunk\grouper-installer\grouper.apiBinary-2.1.0
9427       startHsqlDb(true);
9428     }
9429     
9430     //####################################
9431     //check connection to database
9432     checkDatabaseConnection();
9433     
9434     //####################################
9435     // patch the API
9436     this.upgradeExistingApplicationDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath());
9437     this.upgradeExistingClassesDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath())
9438         + "conf" + File.separator;
9439     this.upgradeExistingLibDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath())
9440         + "lib" + File.separator;
9441     this.upgradeExistingBinDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath())
9442             + "bin" + File.separator;
9443     patchApi();
9444 
9445     //make sure log4j is debugging sql statements
9446     log4jDebugSql(this.upgradeExistingClassesDirectoryString + "log4j.properties");
9447     
9448     //####################################
9449     //ask then init the DB
9450     initDb();
9451     addQuickstartSubjects();
9452     addQuickstartData();
9453     
9454     //####################################
9455     //download and configure ui
9456     System.out.print("Do you want to install the user interface (t|f)? [t]: ");
9457     boolean installUi = readFromStdInBoolean(true, "grouperInstaller.autorun.installUi");
9458     if (installUi) {
9459       downloadAndConfigureUi();
9460     }
9461     
9462     //####################################
9463     //get ant
9464     downloadAndUnzipAnt();
9465     
9466     //####################################
9467     //look for or ask or download tomcat
9468     File tomcatDir = downloadTomcat();
9469     File unzippedTomcatFile = unzip(tomcatDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
9470     this.untarredTomcatDir = untar(unzippedTomcatFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc", 
9471         new File(this.grouperInstallDirectoryString));
9472 
9473     //####################################
9474     //ask for tomcat port
9475     configureTomcat();
9476 
9477     File apiPatchStatusFile = new File(GrouperInstallerUtils.fileAddLastSlashIfNotExists(
9478         this.untarredApiDir.getAbsolutePath()) + "grouperPatchStatus.properties");
9479 
9480     //####################################
9481     //build UI
9482     if (installUi) {
9483 
9484       buildUi(true);
9485 
9486       //####################################
9487       //configureTomcatUiWebapp
9488       configureTomcatUiWebapp();
9489   
9490       //####################################
9491       //copy api patch level to ui
9492       File uiPatchStatusFile = new File(GrouperInstallerUtils.fileAddLastSlashIfNotExists(
9493           this.grouperUiBuildToDirName()) + "WEB-INF" + File.separator + "grouperPatchStatus.properties");
9494       System.out.println("Copying applied API patch status to UI:");
9495       System.out.println("  - from: "  + apiPatchStatusFile.getAbsolutePath());
9496       System.out.println("  - to: "  + uiPatchStatusFile.getAbsolutePath());
9497       GrouperInstallerMergePatchFiles.mergePatchFiles(
9498           apiPatchStatusFile, uiPatchStatusFile, true);
9499 
9500 
9501       //####################################
9502       // patch the ui
9503       this.upgradeExistingApplicationDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperUiBuildToDirName());
9504       this.upgradeExistingClassesDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperUiBuildToDirName())
9505           + "WEB-INF" + File.separator + "classes" + File.separator ;
9506       this.upgradeExistingLibDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperUiBuildToDirName())
9507           + "WEB-INF" + File.separator + "lib" + File.separator;
9508       this.upgradeExistingBinDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperUiBuildToDirName())
9509           + "WEB-INF" + File.separator + "bin" + File.separator ;
9510 
9511       //####################################
9512       // temp fix for 2.4 full install - the api and ui /bin dirs differ in 2.4; this step
9513       // will sync by renaming ui files that differ and then copy the api versions
9514       String apiBinSource = GrouperInstallerUtils.fileAddLastSlashIfNotExists(
9515               this.untarredApiDir.getAbsolutePath()) + "bin" + File.separator;
9516       String targetBinSouce = GrouperInstallerUtils.fileAddLastSlashIfNotExists(
9517               this.grouperUiBuildToDirName()) + "WEB-INF" + File.separator + "bin" + File.separator;
9518       String[] filesToCopyFromApiBin = new String[]{"gsh.sh", "gsh.bat", "gsh", "README.txt", "setenv.example.bat", "setenv.example.sh"};
9519       this.grouperBaseBakDir = this.grouperTarballDirectoryString + "bak_UI_"
9520               + new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS").format(new Date()) + File.separator;
9521 
9522       System.out.println("Reconciling differences between API and UI /bin directories...");
9523       syncFilesInDirWithBackup(apiBinSource, targetBinSouce, filesToCopyFromApiBin);
9524       this.grouperBaseBakDir = null;
9525 
9526       this.patchUi();
9527     }
9528     
9529     //####################################
9530     //set the GrouperSystem password
9531     tomcatConfigureGrouperSystem();
9532 
9533     if (installUi) {
9534       //####################################
9535       //bounce tomcat
9536       tomcatBounce("restart");
9537       
9538       //####################################
9539       //tell user to go to url
9540       System.out.println("##################################\n");
9541       System.out.println("Go here for the Grouper UI (change hostname if on different host): http://localhost:" + this.tomcatHttpPort + "/" + this.tomcatUiPath + "/");
9542       System.out.println("\n##################################\n");
9543     }
9544     
9545     System.out.print("Do you want to install web services (t|f)? [t]: ");
9546     boolean installWs = readFromStdInBoolean(true, "grouperInstaller.autorun.installWs");
9547     
9548     if (installWs) {
9549       this.downloadAndUntarWs();
9550       
9551       //#################################### 
9552       //configure Ws
9553       this.configureWs();
9554       
9555       //####################################
9556       //build WS
9557       buildWs(true);
9558       
9559       //####################################
9560       //copy to tomcat
9561       configureTomcatWsWebapp();
9562   
9563       //####################################
9564       //copy api patch level to ui
9565       File wsPatchStatusFile = new File(GrouperInstallerUtils.fileAddLastSlashIfNotExists(
9566           this.grouperWsBuildToDirName()) + "WEB-INF" + File.separator + "grouperPatchStatus.properties");
9567       System.out.println("Copying applied API patch status to WS:");
9568       System.out.println("  - from: "  + apiPatchStatusFile.getAbsolutePath());
9569       System.out.println("  - to: "  + wsPatchStatusFile.getAbsolutePath());
9570       GrouperInstallerMergePatchFiles.mergePatchFiles(
9571           apiPatchStatusFile, wsPatchStatusFile, true);
9572 
9573       //####################################
9574       // patch the ws
9575       this.upgradeExistingApplicationDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperWsBuildToDirName());
9576       this.upgradeExistingClassesDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperWsBuildToDirName())
9577           + "WEB-INF" + File.separator + "classes" + File.separator ;
9578       this.upgradeExistingLibDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperWsBuildToDirName())
9579           + "WEB-INF" + File.separator + "lib" + File.separator;
9580       this.upgradeExistingBinDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.grouperWsBuildToDirName())
9581               + "WEB-INF" + File.separator + "bin" + File.separator ;
9582 
9583       //####################################
9584       // temp fix for 2.4 full install - the api and ws /bin dirs differ in 2.4; this step
9585       // will sync by renaming ws files that differ and then copy the api versions
9586       String apiBinSource = GrouperInstallerUtils.fileAddLastSlashIfNotExists(
9587               this.untarredApiDir.getAbsolutePath()) + "bin" + File.separator;
9588       String targetBinSouce = GrouperInstallerUtils.fileAddLastSlashIfNotExists(
9589               this.grouperWsBuildToDirName()) + "WEB-INF" + File.separator + "bin" + File.separator;
9590       String[] filesToCopyFromApiBin = new String[]{"gsh.sh", "gsh.bat", "gsh", "README.txt", "setenv.example.bat", "setenv.example.sh"};
9591       this.grouperBaseBakDir = this.grouperTarballDirectoryString + "bak_WS_"
9592               + new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS").format(new Date()) + File.separator;
9593 
9594       System.out.println("Reconciling differences between API and WS /bin directories...");
9595       syncFilesInDirWithBackup(apiBinSource, targetBinSouce, filesToCopyFromApiBin);
9596       this.grouperBaseBakDir = null;
9597 
9598       this.patchWs();
9599   
9600       //####################################
9601       //bounce tomcat
9602       tomcatBounce("restart");
9603   
9604       //####################################
9605       //tell user to go to url
9606       System.out.println("This is the Grouper WS URL (change hostname if on different host): http://localhost:" + this.tomcatHttpPort + "/" + this.tomcatWsPath + "/");
9607     }
9608     
9609     System.out.print("Do you want to install the web services client (t|f)? [t]: ");
9610     boolean installClient = readFromStdInBoolean(true, "grouperInstaller.autorun.installClient");
9611     
9612     if (installClient) {
9613       //download and build client
9614       this.downloadAndBuildClient();
9615   
9616       //####################################
9617       //configure where WS is
9618       this.configureClient();
9619       
9620       if (installWs) {
9621         //####################################
9622         //add grouper system to WS group
9623         this.addGrouperSystemWsGroup();
9624         
9625         //####################################
9626         //run a client command
9627         this.runClientCommand();
9628       }
9629     }
9630 
9631     //####################################
9632     //install pspng
9633     System.out.print("Do you want to install the provisioning service provider next generation (t|f)? [t]: ");
9634     boolean installPspng = readFromStdInBoolean(true, "grouperInstaller.autorun.installPspng");
9635     if (installPspng) {
9636       downloadAndBuildPspng();  
9637       
9638       //copy jars
9639       GrouperInstallerUtils.copyDirectory(new File(this.untarredPspngDir.getAbsolutePath() + File.separator + "lib" + File.separator + "custom"), 
9640           new File(this.untarredApiDir.getAbsolutePath() + File.separator + "lib" + File.separator + "custom"));
9641       GrouperInstallerUtils.copyDirectory(new File(this.untarredPspngDir.getAbsolutePath() + File.separator + "dist"), 
9642           new File(this.untarredApiDir.getAbsolutePath() + File.separator + "lib" + File.separator + "custom"));
9643 
9644       //####################################
9645       // patch the PSP
9646       this.upgradeExistingApplicationDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath());
9647       this.upgradeExistingClassesDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath())
9648           + "conf" + File.separator;
9649       this.upgradeExistingLibDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath())
9650           + "lib" + File.separator;
9651       patchPspng();
9652             
9653     }
9654 
9655 
9656     if (!installPspng) {
9657       //####################################
9658       //install psp
9659       System.out.print("Do you want to install the provisioning service provider (t|f)? [t]: ");
9660       if (readFromStdInBoolean(true, "grouperInstaller.autorun.installPsp")) {
9661         downloadAndBuildPsp();              
9662         GrouperInstallerUtils.copyDirectory(this.untarredPspDir, this.untarredApiDir);
9663   
9664         //####################################
9665         // patch the PSP
9666         this.upgradeExistingApplicationDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath());
9667         this.upgradeExistingClassesDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath())
9668             + "conf" + File.separator;
9669         this.upgradeExistingLibDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.untarredApiDir.getAbsolutePath())
9670             + "lib" + File.separator;
9671         patchPsp();
9672               
9673       }
9674     }
9675     
9676     reportOnConflictingJars(this.upgradeExistingApplicationDirectoryString);
9677 
9678     //#####################################
9679     //start the loader
9680     startLoader(true);
9681     
9682     //prompt and install ws scim
9683     installWsScim();
9684     
9685     //prompt and install rabbitmq messaging
9686     installMessagingRabbitMq();
9687     
9688     //prompt and install aws sqs messaging
9689     installMessagingAwsSqs();
9690     
9691     //prompt and install activemq messaging
9692     installMessagingActiveMq();
9693     
9694     //prompt and install activeMq messaging
9695     //installMessagingRabbitMq();
9696     
9697 
9698     //#####################################
9699     //success
9700     System.out.println("\n##################################\n");
9701 
9702     System.out.println("\nInstallation success!");
9703 
9704 
9705     System.out.println("\nRun the installer's 'admin' function to get information and manage about your installation (db, tomcat, logs, etc)");
9706     
9707     if (installUi) {
9708       System.out.println("\nGo here for the Grouper UI (change hostname if on different host): http://localhost:" + this.tomcatHttpPort + "/" + this.tomcatUiPath + "/");
9709       
9710     }
9711     if (installWs) {
9712       System.out.println("\nThis is the Grouper WS URL (change hostname if on different host): http://localhost:" + this.tomcatHttpPort + "/" + this.tomcatWsPath + "/");
9713     }
9714     System.out.println("\n##################################\n");
9715 
9716   }
9717 
9718   /**
9719    * 
9720    */
9721   private void installWsScim() {
9722     //#####################################
9723     // Install Grouper WS Scim Tier API
9724     //####################################
9725     System.out.print("Do you want to install the grouper ws scim (t|f)? [t]: ");
9726     boolean installWsScim = readFromStdInBoolean(true, "grouperInstaller.autorun.installGrouperWsScim");
9727     if (installWsScim) {
9728       downloadAndUntarWs();
9729       
9730       //####################################
9731       //get maven
9732       // NOTE: we dont need maven, ship the binary
9733       //downloadAndUnzipMaven();
9734       
9735       //####################################
9736       //look for or ask or download apache tomee
9737       File tomeeDir = downloadTomee();
9738       File unzippedTomeeFile = unzip(tomeeDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
9739       this.untarredTomeeDir = untar(unzippedTomeeFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc", 
9740           new File(this.grouperInstallDirectoryString));
9741 
9742       //####################################
9743       //ask for tomee port
9744       configureTomee();
9745 
9746       //####################################
9747       //build grouper ws scim
9748       // NOTE: we dont need to build it, ship the binary
9749       //buildWsScim();
9750 
9751       //####################################
9752       //configureTomeeGrouperWsScimWebapp
9753       configureTomeeGrouperWsScimWebapp();
9754       
9755       //####################################
9756       //set the GrouperSystem password
9757       tomeeConfigureGrouperSystem();
9758 
9759       //####################################
9760       //bounce tomcat
9761       tomeeBounce("restart");
9762       
9763       //####################################
9764       //tell user to go to url
9765       System.out.println("##################################\n");
9766       System.out.println("Go here for the Grouper WS Scim (change hostname if on different host): http://localhost:" + this.tomeeHttpPort + "/" + "grouper-ws-scim" + "/");
9767       System.out.println("\n##################################\n");
9768     }
9769   }
9770 
9771   /**
9772    * 
9773    */
9774   private void installMessagingRabbitMq() {
9775     //#####################################
9776     // Install Grouper Messaging RabbitMQ
9777     //####################################
9778     System.out.print("Do you want to install grouper rabbitMQ messaging (t|f)? [f]: ");
9779     boolean installRabbitMqMessaging = readFromStdInBoolean(false, "grouperInstaller.autorun.installGrouperRabbitMqMessaging");
9780     if (installRabbitMqMessaging) {
9781       
9782       String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
9783       
9784       if (!urlToDownload.endsWith("/")) {
9785         urlToDownload += "/";
9786       }
9787 
9788       urlToDownload += "release/";
9789       String rabbitMqFileName = "grouper.rabbitMq-" + this.version + ".tar.gz";
9790       urlToDownload += this.version + "/" + rabbitMqFileName;
9791 
9792       File rabbitMqFile = new File(this.grouperTarballDirectoryString + rabbitMqFileName);
9793       
9794       downloadFile(urlToDownload, rabbitMqFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalRabbitMqDownloadTarEtc");
9795 
9796       File unzippedRabbitMqFile = unzip(rabbitMqFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalRabbitMqDownloadTarEtc");
9797       File unzippedRabbitMqDir = untar(unzippedRabbitMqFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalRabbitMqDownloadTarEtc", 
9798           new File(this.grouperInstallDirectoryString));
9799 
9800       File rabbitMqInstallDirectoryFile = null;
9801       boolean success = false;
9802       for (int i=0;i<10;i++) {
9803 
9804         System.out.print("Where do you want the Grouper RabbitMQ messaging connector installed? ");
9805         String rabbitMqInstallDirectoryFileString = readFromStdIn("grouperInstaller.autorun.rabbitMqWhereInstalled");
9806         rabbitMqInstallDirectoryFile = new File(rabbitMqInstallDirectoryFileString);
9807         if (!rabbitMqInstallDirectoryFile.exists() || !rabbitMqInstallDirectoryFile.isDirectory()) {
9808           System.out.println("Error: cant find directory: '" + rabbitMqInstallDirectoryFile.getAbsolutePath() + "'");
9809           continue;
9810         }
9811 
9812         //make sure directory is where the app is
9813         
9814         List<File> grouperClientFiles = GrouperInstallerUtils.jarFindJar(rabbitMqInstallDirectoryFile, "grouperClient.jar");
9815         
9816         if (GrouperInstallerUtils.length(grouperClientFiles) == 0) {
9817           System.out.println("Cant find grouperClient.jar in a subdir of the install dir, please try again!");
9818           continue;
9819         }
9820         
9821         
9822         if (GrouperInstallerUtils.length(grouperClientFiles) > 1) {
9823           System.out.println("Found more than one grouperClient.jar in a subdir of the install dir, must only be one, please try again!");
9824           continue;
9825         }
9826 
9827         //ok, we know where the jars go
9828         File dirWhereFilesGo = grouperClientFiles.get(0).getParentFile();
9829         
9830         List<File> jarFiles = GrouperInstallerUtils.fileListRecursive(new File(unzippedRabbitMqDir.getAbsolutePath() + File.separatorChar 
9831             + "lib" + File.separatorChar));
9832       
9833         for (File jarFile : jarFiles) {
9834           
9835           String fileName = jarFile.getName();
9836           
9837           if (!fileName.endsWith(".jar")) {
9838             continue;
9839           }
9840           
9841           String sourceFileName = unzippedRabbitMqDir.getAbsolutePath() + File.separatorChar 
9842               + "lib" + File.separatorChar + fileName;
9843           
9844           File sourceFile = new File(sourceFileName);
9845           
9846           String destFileName = dirWhereFilesGo.getAbsolutePath() + File.separatorChar + fileName;
9847           
9848           File destFile = new File(destFileName);
9849           
9850           copyJarFileIfNotExists(sourceFile, destFile, false, false);
9851 
9852         }
9853 
9854         success = true;
9855         break;
9856       }        
9857       
9858       if (!success) {
9859         System.exit(1);
9860       }
9861       
9862       //####################################
9863       //tell user to configure
9864       System.out.println("##################################\n");
9865       
9866       System.out.println("Configure your grouper.client.properties based on this file " 
9867           + unzippedRabbitMqDir.getAbsoluteFile() + File.separator 
9868           + "grouper.client.rabbitMq.example.properties");
9869       System.out.println("\n##################################\n");
9870     }
9871   }
9872   
9873   /**
9874    * 
9875    */
9876   private void installMessagingAwsSqs() {
9877 
9878     //#####################################
9879     // Install Grouper Messaging AWS SQS
9880     //####################################
9881     System.out.print("Do you want to install grouper AWS SQS messaging (t|f)? [f]: ");
9882     boolean installAwsMessaging = readFromStdInBoolean(false, "grouperInstaller.autorun.installGrouperAwsSqsMessaging");
9883     if (installAwsMessaging) {
9884       
9885       String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
9886       
9887       if (!urlToDownload.endsWith("/")) {
9888         urlToDownload += "/";
9889       }
9890 
9891       urlToDownload += "release/";
9892       String awsFileName = "grouper.aws-" + this.version + ".tar.gz";
9893       urlToDownload += this.version + "/" + awsFileName;
9894 
9895       File awsFile = new File(this.grouperTarballDirectoryString + awsFileName);
9896       
9897       downloadFile(urlToDownload, awsFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalAwsSqsDownloadTarEtc");
9898 
9899       File unzippedAwsFile = unzip(awsFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalAwsSqsDownloadTarEtc");
9900       File unzippedAwsDir = untar(unzippedAwsFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalAwsSqsDownloadTarEtc", 
9901           new File(this.grouperInstallDirectoryString));
9902 
9903       File awsInstallDirectoryFile = null;
9904       boolean success = false;
9905       for (int i=0;i<10;i++) {
9906 
9907         System.out.print("Where do you want the Grouper AWS SQS messaging connector installed? ");
9908         String awsInstallDirectoryFileString = readFromStdIn("grouperInstaller.autorun.AwsSqsWhereInstalled");
9909         awsInstallDirectoryFile = new File(awsInstallDirectoryFileString);
9910         if (!awsInstallDirectoryFile.exists() || !awsInstallDirectoryFile.isDirectory()) {
9911           System.out.println("Error: cant find directory: '" + awsInstallDirectoryFile.getAbsolutePath() + "'");
9912           continue;
9913         }
9914 
9915         //make sure directory is where the app is
9916         
9917         List<File> grouperClientFiles = GrouperInstallerUtils.jarFindJar(awsInstallDirectoryFile, "grouperClient.jar");
9918         
9919         if (GrouperInstallerUtils.length(grouperClientFiles) == 0) {
9920           System.out.println("Cant find grouperClient.jar in a subdir of the install dir, please try again!");
9921           continue;
9922         }
9923         
9924         
9925         if (GrouperInstallerUtils.length(grouperClientFiles) > 1) {
9926           System.out.println("Found more than one grouperClient.jar in a subdir of the install dir, must only be one, please try again!");
9927           continue;
9928         }
9929 
9930         //ok, we know where the jars go
9931         File dirWhereFilesGo = grouperClientFiles.get(0).getParentFile();
9932         
9933         List<File> jarFiles = GrouperInstallerUtils.fileListRecursive(new File(unzippedAwsDir.getAbsolutePath() + File.separatorChar 
9934               + "lib" + File.separatorChar));
9935         
9936         for (File jarFile : jarFiles) {
9937           
9938           String fileName = jarFile.getName();
9939           
9940           if (!fileName.endsWith(".jar")) {
9941             continue;
9942           }
9943           
9944           String sourceFileName = unzippedAwsDir.getAbsolutePath() + File.separatorChar 
9945               + "lib" + File.separatorChar + fileName;
9946           
9947           File sourceFile = new File(sourceFileName);
9948           
9949           String destFileName = dirWhereFilesGo.getAbsolutePath() + File.separatorChar + fileName;
9950           
9951           File destFile = new File(destFileName);
9952           
9953           copyJarFileIfNotExists(sourceFile, destFile, false, false);
9954 
9955         }
9956 
9957         success = true;
9958         break;
9959       }        
9960       
9961       if (!success) {
9962         System.exit(1);
9963       }
9964       
9965       //####################################
9966       //tell user to configure
9967       System.out.println("##################################\n");
9968       
9969       System.out.println("Configure your grouper.client.properties based on this file " 
9970           + unzippedAwsDir.getAbsoluteFile() + File.separator 
9971           + "grouper.client.aws.example.properties");
9972       System.out.println("\n##################################\n");
9973     }
9974   
9975   }
9976   
9977   /**
9978    * 
9979    */
9980   private void installMessagingActiveMq() {
9981 
9982     //#####################################
9983     // Install Grouper Messaging ActiveMq
9984     //####################################
9985     System.out.print("Do you want to install grouper activeMq messaging (t|f)? [f]: ");
9986     boolean installActiveMqMessaging = readFromStdInBoolean(false, "grouperInstaller.autorun.installGrouperActiveMqMessaging");
9987     if (installActiveMqMessaging) {
9988       
9989       String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
9990       
9991       if (!urlToDownload.endsWith("/")) {
9992         urlToDownload += "/";
9993       }
9994 
9995       urlToDownload += "release/";
9996       String activeMqFileName = "grouper.activeMq-" + this.version + ".tar.gz";
9997       urlToDownload += this.version + "/" + activeMqFileName;
9998 
9999       File activeMqFile = new File(this.grouperTarballDirectoryString + activeMqFileName);
10000       
10001       downloadFile(urlToDownload, activeMqFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalActiveMqDownloadTarEtc");
10002 
10003       File unzippedActiveMqFile = unzip(activeMqFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalActiveMqDownloadTarEtc");
10004       File unzippedActiveMqDir = untar(unzippedActiveMqFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalActiveMqDownloadTarEtc", 
10005           new File(this.grouperInstallDirectoryString));
10006 
10007       File activeMqInstallDirectoryFile = null;
10008       boolean success = false;
10009       for (int i=0;i<10;i++) {
10010 
10011         System.out.print("Where do you want the Grouper ActiveMq messaging connector installed? ");
10012         String activeMqInstallDirectoryFileString = readFromStdIn("grouperInstaller.autorun.activeMqWhereInstalled");
10013         activeMqInstallDirectoryFile = new File(activeMqInstallDirectoryFileString);
10014         if (!activeMqInstallDirectoryFile.exists() || !activeMqInstallDirectoryFile.isDirectory()) {
10015           System.out.println("Error: cant find directory: '" + activeMqInstallDirectoryFile.getAbsolutePath() + "'");
10016           continue;
10017         }
10018 
10019         //make sure directory is where the app is
10020         
10021         List<File> grouperClientFiles = GrouperInstallerUtils.jarFindJar(activeMqInstallDirectoryFile, "grouperClient.jar");
10022         
10023         if (GrouperInstallerUtils.length(grouperClientFiles) == 0) {
10024           System.out.println("Cant find grouperClient.jar in a subdir of the install dir, please try again!");
10025           continue;
10026         }
10027         
10028         
10029         if (GrouperInstallerUtils.length(grouperClientFiles) > 1) {
10030           System.out.println("Found more than one grouperClient.jar in a subdir of the install dir, must only be one, please try again!");
10031           continue;
10032         }
10033 
10034         //ok, we know where the jars go
10035         File dirWhereFilesGo = grouperClientFiles.get(0).getParentFile();
10036         
10037         List<File> jarFiles = GrouperInstallerUtils.fileListRecursive(new File(unzippedActiveMqDir.getAbsolutePath() + File.separatorChar 
10038             + "lib" + File.separatorChar));
10039       
10040         for (File jarFile : jarFiles) {
10041           
10042           String fileName = jarFile.getName();
10043           
10044           if (!fileName.endsWith(".jar")) {
10045             continue;
10046           }
10047           
10048           String sourceFileName = unzippedActiveMqDir.getAbsolutePath() + File.separatorChar 
10049               + "lib" + File.separatorChar + fileName;
10050           
10051           File sourceFile = new File(sourceFileName);
10052           
10053           String destFileName = dirWhereFilesGo.getAbsolutePath() + File.separatorChar + fileName;
10054           
10055           File destFile = new File(destFileName);
10056           
10057           copyJarFileIfNotExists(sourceFile, destFile, false, false);
10058 
10059         }
10060 
10061         success = true;
10062         break;
10063       }        
10064       
10065       if (!success) {
10066         System.exit(1);
10067       }
10068       
10069       //####################################
10070       //tell user to configure
10071       System.out.println("##################################\n");
10072       
10073       System.out.println("Configure your grouper.client.properties based on this file " 
10074           + unzippedActiveMqDir.getAbsoluteFile() + File.separator 
10075           + "grouper.client.activeMq.example.properties");
10076       System.out.println("\n##################################\n");
10077     }
10078   
10079   }
10080 
10081   /**
10082    * 
10083    */
10084   private void downloadAndBuildPsp() {
10085     File pspDir = downloadPsp();
10086     File unzippedPspFile = unzip(pspDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalPspDownloadTarEtc");
10087     this.untarredPspDir = untar(unzippedPspFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalPspDownloadTarEtc", 
10088         null);
10089 
10090   }
10091   
10092   /**
10093    * 
10094    */
10095   private void downloadAndBuildPspng() {
10096     File pspngDir = downloadPspng();
10097     File unzippedPspngFile = unzip(pspngDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalPspngDownloadTarEtc");
10098     this.untarredPspngDir = untar(unzippedPspngFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalPspngDownloadTarEtc", 
10099         null);
10100 
10101   }
10102   
10103   /** untarred psp dir file */
10104   private File untarredPspDir;
10105   
10106   /** untarred pspng dir file */
10107   private File untarredPspngDir;
10108   
10109   /**
10110    * 
10111    */
10112   public void downloadAndUnzipAnt() {
10113     File antDir = downloadAnt();
10114     File unzippedAntFile = unzip(antDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
10115     this.untarredAntDir = untar(unzippedAntFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc", null);
10116   }
10117 
10118   /**
10119    * 
10120    */
10121   public void downloadAndUnzipMaven() {
10122     File mavenDir = downloadMaven();
10123     File unzippedMavenFile = unzip(mavenDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
10124     this.untarredMavenDir = untar(unzippedMavenFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc", null);
10125   }
10126 
10127   /**
10128    * 
10129    */
10130   public void downloadAndUntarWs() {
10131 
10132     //####################################
10133     //download the ws
10134     File wsDir = downloadWs();
10135 
10136     //####################################
10137     //unzip/untar the ws file
10138     File unzippedWsFile = unzip(wsDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalWsDownloadTarEtc");
10139     System.out.println("Unzipped Ws file is "+unzippedWsFile);
10140     this.untarredWsDir = untar(unzippedWsFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalWsDownloadTarEtc", 
10141         new File(this.grouperInstallDirectoryString));
10142 
10143   }
10144 
10145   /**
10146    * 
10147    */
10148   public void downloadAndConfigureUi() {
10149     //####################################
10150     //get UI
10151     File uiDir = downloadUi();
10152     
10153     //####################################
10154     //unzip/untar the ui file
10155     File unzippedUiFile = unzip(uiDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalUiDownloadTarEtc");
10156     this.untarredUiDir = untar(unzippedUiFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalUiDownloadTarEtc", 
10157         new File(this.grouperInstallDirectoryString));
10158 
10159     //####################################
10160     //configure UI
10161     configureUi();
10162   }
10163 
10164   /**
10165    * 
10166    */
10167   public void downloadAndConfigureApi() {
10168     File apiFile = downloadApi();
10169     
10170     //####################################
10171     //unzip/untar the api file
10172     
10173     File unzippedApiFile = unzip(apiFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalApiDownloadTarEtc");
10174     File theUntarredApiDir = untar(unzippedApiFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalApiDownloadTarEtc", 
10175         new File(this.grouperInstallDirectoryString));
10176     
10177     File theGrouperJar = new File(GrouperInstallerUtils.fileAddLastSlashIfNotExists(theUntarredApiDir.getAbsolutePath())
10178         + "dist" + File.separator + "lib" + File.separator + "grouper.jar");
10179     
10180     gshExcutableAndDos2Unix(theUntarredApiDir.getAbsolutePath() + File.separator + "bin" + File.separator);
10181 
10182     //these might be set from UI or WS
10183     if (this.untarredApiDir == null) {
10184       this.untarredApiDir = theUntarredApiDir;
10185     }
10186     
10187     if (this.grouperJar == null) {
10188       this.grouperJar = theGrouperJar;
10189     }
10190   }
10191 
10192   /**
10193    * @param binDirLocation which includes trailing slash
10194    */
10195   public void gshExcutableAndDos2Unix(String binDirLocation) {
10196     gshExcutableAndDos2Unix(binDirLocation, null);
10197   }
10198 
10199   /**
10200    * run dos2unix on a file
10201    * @param file
10202    * @param fileNameInPrompt 
10203    * @param configSuffixAutorun 
10204    */
10205   public static void dos2unix(File file, String fileNameInPrompt, String configSuffixAutorun) {
10206     dos2unix(GrouperInstallerUtils.toSet(file), fileNameInPrompt, configSuffixAutorun);
10207   }
10208 
10209   /**
10210    * run dos2unix on a file
10211    * @param files
10212    * @param fileNameInPrompt e.g. gsh.sh
10213    * @param configSuffixAutorun suffix after grouperInstaller.autorun.dos2unix in properties file
10214    */
10215   public static void dos2unix(Collection<File> files, String fileNameInPrompt, String configSuffixAutorun) {
10216 
10217     if (!GrouperInstallerUtils.isWindows()) {
10218 
10219       System.out.print("Do you want to run dos2unix on " + fileNameInPrompt + " (t|f)? [t]: ");
10220       boolean dos2unixRunOnFile = readFromStdInBoolean(true, "grouperInstaller.autorun.dos2unix" + configSuffixAutorun);
10221       
10222       if (dos2unixRunOnFile) {
10223 
10224         for (File file : files) {
10225           
10226           if (!file.exists()) {
10227             continue;
10228           }
10229           
10230           List<String> commands = GrouperInstallerUtils.toList("dos2unix", 
10231               file.getAbsolutePath());
10232     
10233           System.out.println("Making sure " + file.getName() + " is in unix format: " + convertCommandsIntoCommand(commands) + "\n");
10234           String error = null;
10235           CommandResult commandResult = null;
10236           boolean didntWork = false;
10237           Throwable throwable = null;
10238           try {
10239             commandResult = GrouperInstallerUtils.execCommand(
10240                 GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10241                 file.getParentFile(), null, false, true, false);
10242 
10243             if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
10244               System.out.println("stderr: " + commandResult.getErrorText());
10245             }
10246             if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
10247               System.out.println("stdout: " + commandResult.getOutputText());
10248             }
10249             continue;
10250           } catch (Throwable t) {
10251             didntWork = true;
10252             error = t.getMessage();
10253             throwable = t;
10254           }
10255           
10256           if (didntWork) {
10257             try {
10258               //lets try the java way?
10259               String fileContents = GrouperInstallerUtils.readFileIntoString(file);
10260               if (fileContents.contains("\r\n")) {
10261                 System.out.println("Problem with command 'dos2unix'.   Is it installed?  Converting to unix via java replacing \\r\\n with \\n: " + file.getAbsolutePath());
10262                 fileContents = fileContents.replaceAll("\r\n", "\n");
10263                 GrouperInstallerUtils.saveStringIntoFile(file, fileContents);
10264               }
10265               continue;
10266             } catch (Throwable t) {
10267               t.printStackTrace();
10268             }
10269           }
10270           
10271           if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
10272             System.out.println("stderr: " + commandResult.getErrorText());
10273           }
10274           if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
10275             System.out.println("stdout: " + commandResult.getOutputText());
10276           }
10277           if (!GrouperInstallerUtils.isBlank(error)) {
10278             if (throwable != null) {
10279               throwable.printStackTrace();
10280             }
10281             System.out.println("Error: " + error);
10282             System.out.println("NOTE: you might need to run this to convert newline characters to mac/unix:\n\n" +
10283                 "cat " + file.getAbsolutePath()
10284                 + " | col -b > " + file.getAbsolutePath() + "\n");
10285           }
10286         }
10287       }
10288 
10289     }
10290   }
10291   
10292   /**
10293    * @param binDirLocation which includes trailing slash
10294    * @param specify if specifying location
10295    */
10296   public void gshExcutableAndDos2Unix(String binDirLocation, String specify) {
10297     //lts make sure gsh is executable and in unix format
10298 
10299     if (!GrouperInstallerUtils.isWindows()) {
10300 
10301       specify = GrouperInstallerUtils.trimToEmpty(specify);
10302       
10303       if (specify.length() > 0) {
10304         specify += " ";
10305       }
10306       
10307       System.out.print("Do you want to set " + specify + "gsh script to executable (t|f)? [t]: ");
10308       boolean setGshFile = readFromStdInBoolean(true, "grouperInstaller.autorun.setGshScriptsToExecutable");
10309       
10310       if (setGshFile) {
10311       
10312         binDirLocation = GrouperInstallerUtils.fileAddLastSlashIfNotExists(binDirLocation);
10313         
10314         List<String> commands = GrouperInstallerUtils.toList("chmod", "+x", 
10315             binDirLocation + "gsh.sh");
10316   
10317         System.out.println("Making sure gsh.sh is executable with command: " + convertCommandsIntoCommand(commands) + "\n");
10318   
10319         CommandResult commandResult = GrouperInstallerUtils.execCommand(
10320             GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10321             new File(binDirLocation), null, true);
10322         
10323         if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
10324           System.out.println("stderr: " + commandResult.getErrorText());
10325         }
10326         if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
10327           System.out.println("stdout: " + commandResult.getOutputText());
10328         }
10329 
10330         if (new File(binDirLocation + "gsh").exists()) {
10331           commands = GrouperInstallerUtils.toList("chmod", "+x", 
10332               binDirLocation + "gsh");
10333     
10334           System.out.println("Making sure gsh is executable with command: " + convertCommandsIntoCommand(commands) + "\n");
10335     
10336           commandResult = GrouperInstallerUtils.execCommand(
10337               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10338               new File(binDirLocation), null, true);
10339           
10340           if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
10341             System.out.println("stderr: " + commandResult.getErrorText());
10342           }
10343           if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
10344             System.out.println("stdout: " + commandResult.getOutputText());
10345           }
10346         }
10347         
10348         dos2unix(GrouperInstallerUtils.toSet(new File(binDirLocation + "gsh.sh"), new File(binDirLocation + "gsh")), "gsh.sh", "OnGsh");
10349 
10350       }
10351       
10352     }
10353   }
10354   
10355   /**
10356    * if we are debugging sql in log4j
10357    */
10358   private Boolean log4jDebugSql = null;
10359   
10360   /**
10361    * if this file has been taken care of for a while
10362    */
10363   private Set<File> log4jDebugDone = new HashSet<File>();
10364   
10365   /**
10366    * if this file has been taken care of for a while
10367    */
10368   private Set<File> removeLegacyHibernatePropertiesDone = new HashSet<File>();
10369   
10370   /**
10371    * @param hibernateFileLocation
10372    */
10373   public void removeLegacyHibernateProperties(String hibernateFileLocation) {
10374 
10375     //if not a file dont worry about it
10376     File hibernateFile = new File(hibernateFileLocation);
10377 
10378     if (this.removeLegacyHibernatePropertiesDone.contains(hibernateFile)) {
10379       return;
10380     }
10381 
10382     this.removeLegacyHibernatePropertiesDone.add(hibernateFile);
10383 
10384     if (!hibernateFile.exists()) {
10385       System.out.println("Cant find grouper.hibernate.properties: " + hibernateFileLocation);
10386       return;
10387     }
10388     
10389     //see if its there
10390     Properties hibernateProperties = GrouperInstallerUtils.propertiesFromFile(hibernateFile);
10391     String current = GrouperInstallerUtils.propertiesValue(hibernateProperties, "hibernate.cache.region.factory_class");
10392     
10393     if (current == null) {
10394       //not there, we're good
10395       return;
10396     }
10397 
10398 
10399     removeRedundantProperties(hibernateFile, GrouperInstallerUtils.toSet("hibernate.cache.region.factory_class"));
10400     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.");
10401   }
10402   
10403   /**
10404    * @param log4jLocation
10405    */
10406   public void log4jDebugSql(String log4jLocation) {
10407 
10408     //if not a file dont worry about it
10409     File log4jFile = new File(log4jLocation);
10410 
10411     if (this.log4jDebugDone.contains(log4jFile)) {
10412       return;
10413     }
10414 
10415     this.log4jDebugDone.add(log4jFile);
10416 
10417     if (!log4jFile.exists()) {
10418       System.out.println("Cant find log4j.properties: " + log4jLocation);
10419       return;
10420     }
10421     
10422     //see if its already there
10423     Properties log4jProperties = GrouperInstallerUtils.propertiesFromFile(log4jFile);
10424     String currentAntEntry = GrouperInstallerUtils.propertiesValue(log4jProperties, "log4j.logger.org.apache.tools.ant");
10425     
10426     if (GrouperInstallerUtils.equalsIgnoreCase(GrouperInstallerUtils.trimToEmpty(currentAntEntry), "DEBUG")
10427         || GrouperInstallerUtils.equalsIgnoreCase(GrouperInstallerUtils.trimToEmpty(currentAntEntry), "INFO")
10428         || GrouperInstallerUtils.equalsIgnoreCase(GrouperInstallerUtils.trimToEmpty(currentAntEntry), "WARN")) {
10429       //we are already there
10430       return;
10431     }
10432 
10433     if (this.log4jDebugSql == null) {
10434       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]: ");
10435       this.log4jDebugSql = readFromStdInBoolean(true, "grouperInstaller.autorun.log4jDebugSql");
10436     }
10437 
10438     if (this.log4jDebugSql) {
10439 
10440       editPropertiesFile(log4jFile, "log4j.logger.org.apache.tools.ant", "WARN", false);
10441 
10442     }
10443   }
10444 
10445   /**
10446    * 
10447    */
10448   public void downloadAndBuildClient() {
10449     //####################################
10450     //download the client
10451     File clientDir = downloadClient();
10452 
10453     //####################################
10454     //unzip/untar the client file
10455     File unzippedClientFile = unzip(clientDir.getAbsolutePath(), "grouperInstaller.autorun.useLocalClientDownloadTarEtc");
10456     this.untarredClientDir = untar(unzippedClientFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalClientDownloadTarEtc", 
10457         new File(this.grouperInstallDirectoryString));
10458     
10459   }
10460 
10461   /**
10462    * 
10463    */
10464   private int tomcatHttpPort = -1;
10465   
10466   /**
10467    * 
10468    */
10469   private int tomeeHttpPort = -1;
10470   
10471   
10472   /**
10473    * 
10474    */
10475   private void configureTomcat() {
10476     
10477     System.out.print("Do you want to set the tomcat memory limit (t|f)? [t]: ");
10478     boolean setTomcatMemory = readFromStdInBoolean(true, "grouperInstaller.autorun.setTomcatMemoryLimit");
10479     
10480     if (setTomcatMemory) {
10481       
10482       {
10483         File catalinaBatFile = new File(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin" + File.separator + "catalina.bat");
10484         
10485         System.out.println("Editing file: " + catalinaBatFile.getAbsolutePath());
10486         
10487         Boolean edited = editFile(catalinaBatFile, "^\\s*set\\s+\"JAVA_OPTS\\s*=.*-Xmx([0-9mMgG]+)", null, null, "512M", "max memory");
10488         if (edited == null) {
10489           addToFile(catalinaBatFile, "\nset \"JAVA_OPTS=-server -Xmx512M -XX:MaxPermSize=256M\"\n", 65, "max memory");
10490         }
10491         if (null == editFile(catalinaBatFile, "^\\s*set\\s+\"JAVA_OPTS\\s*=.*-XX:MaxPermSize=([0-9mMgG]+)", null, null, "256M", "permgen memory")) {
10492           throw new RuntimeException("Why not edit permgen in file " + catalinaBatFile);
10493         }
10494       }
10495       
10496       {
10497         File catalinaShFile = new File(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin" + File.separator + "catalina.sh");
10498         
10499         System.out.println("Editing file: " + catalinaShFile.getAbsolutePath());
10500 
10501         Boolean edited = editFile(catalinaShFile, "^\\s*JAVA_OPTS\\s*=\".*-Xmx([0-9mMgG]+)", null, null, "512M", "max memory");
10502         if (edited == null) {
10503           addToFile(catalinaShFile, "\nJAVA_OPTS=\"-server -Xmx512M -XX:MaxPermSize=256M\"\n", 65, "max memory");
10504         }
10505         if (null == editFile(catalinaShFile, "^\\s*JAVA_OPTS\\s*=\".*-XX:MaxPermSize=([0-9mMgG]+)", null, null, "256M", "permgen memory")) {
10506           throw new RuntimeException("Why not edit permgen in file " + catalinaShFile);
10507         }
10508       }
10509     }      
10510     
10511     
10512     if (!GrouperInstallerUtils.isWindows()) {
10513 
10514       System.out.print("Do you want to set tomcat scripts to executable (t|f)? [t]: ");
10515       boolean setTomcatFiles = readFromStdInBoolean(true, "grouperInstaller.autorun.setTomcatScriptsToExecutable");
10516       
10517       //GrouperInstallerUtils.toSet("catalina.sh", "startup.sh", "shutdown.sh");
10518       Set<String> shFileNames = new HashSet<String>();
10519 
10520       File binDir = new File(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin");
10521 
10522       //get all sh files, doing wildcards doesnt work
10523       for (File file : binDir.listFiles()) {
10524         String fileName = GrouperInstallerUtils.defaultString(file.getName());
10525         if (file.isFile() && fileName.endsWith(".sh")) {
10526           shFileNames.add(fileName);
10527         }
10528       }
10529 
10530       if (setTomcatFiles) {
10531       
10532         for (String command : shFileNames) {
10533           List<String> commands = new ArrayList<String>();
10534           
10535           commands.add("chmod");
10536           commands.add("+x");
10537           //have to do * since all the  sh files need chmod
10538           commands.add(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin" + File.separator + command);
10539     
10540           System.out.println("Making tomcat file executable with command: " + convertCommandsIntoCommand(commands) + "\n");
10541     
10542           CommandResult commandResult = GrouperInstallerUtils.execCommand(
10543               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10544               new File(this.untarredTomcatDir.getAbsolutePath() + File.separator + "bin"), null, true);
10545           
10546           if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
10547             System.out.println("stderr: " + commandResult.getErrorText());
10548           }
10549           if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
10550             System.out.println("stdout: " + commandResult.getOutputText());
10551           }
10552         }
10553       }
10554       
10555       Set<File> shFiles = new LinkedHashSet<File>();
10556       for (String shFileName : shFileNames) {
10557         shFiles.add(new File(shFileName));
10558       }
10559       
10560       dos2unix(shFiles, "tomcat sh files", "OnTomcatFiles");
10561 
10562     }
10563       
10564     //see what the current ports are
10565     this.tomcatHttpPort = -1;
10566     
10567     File serverXmlFile = new File(this.untarredTomcatDir.getAbsolutePath() + File.separator + "conf" + File.separator + "server.xml");
10568     
10569     int shutdownPort = GrouperInstallerUtils.xpathEvaluateAttributeInt(serverXmlFile, "/Server", "port", -1);
10570     
10571     int originalShutdownPort = shutdownPort;
10572     
10573     //  /Server/Service/Connector <Connector port="8080" protocol="HTTP/1.1" 
10574     this.tomcatHttpPort = GrouperInstallerUtils.xpathEvaluateAttributeInt(serverXmlFile, "/Server/Service/Connector[@protocol='HTTP/1.1']", "port", -1);
10575 
10576     int originalTomcatHttpPort = this.tomcatHttpPort;
10577 
10578     // <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
10579     int jkPort = GrouperInstallerUtils.xpathEvaluateAttributeInt(serverXmlFile, "/Server/Service/Connector[@protocol='AJP/1.3']", "port", -1);
10580 
10581     int originalJkPort = jkPort;
10582     
10583     String portsCommaSeparated = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.tomcatPorts", false);
10584     if (!GrouperInstallerUtils.isBlank(portsCommaSeparated)) {
10585       
10586       String[] portsStrings = GrouperInstallerUtils.splitTrim(portsCommaSeparated, ",");
10587       
10588       if (portsStrings.length != 3) {
10589         throw new RuntimeException("Why is grouperInstaller.default.tomcatPorts from grouper.installer.properties not 3 ints comma separated? " + portsCommaSeparated);
10590       }
10591       
10592       this.tomcatHttpPort = GrouperInstallerUtils.intValue(portsStrings[0]);
10593       jkPort = GrouperInstallerUtils.intValue(portsStrings[1]);
10594       shutdownPort = GrouperInstallerUtils.intValue(portsStrings[2]);
10595       
10596     }
10597     
10598     while(true) {
10599       System.out.print("What ports do you want tomcat to run on (HTTP, JK, shutdown): [" + this.tomcatHttpPort + ", " + jkPort + ", " + shutdownPort + "]: ");
10600       
10601       String ports = readFromStdIn("grouperInstaller.autorun.tomcatPorts");
10602       
10603       if (GrouperInstallerUtils.isBlank(ports)) {
10604         if (this.tomcatHttpPort == originalTomcatHttpPort && jkPort == originalJkPort && shutdownPort == originalShutdownPort) {
10605           break;
10606         }
10607       } else {
10608         String[] portsArray = GrouperInstallerUtils.splitTrim(ports, ",");
10609         if (GrouperInstallerUtils.length(portsArray) == 3) {
10610           for (String portString : portsArray) {
10611             try {
10612               GrouperInstallerUtils.intValue(portString);
10613             } catch (Exception e) {
10614               continue;
10615             }
10616           }
10617         } else {
10618           continue;
10619         }
10620         //ok, we have three integer entries
10621         this.tomcatHttpPort = GrouperInstallerUtils.intValue(portsArray[0]);
10622         jkPort = GrouperInstallerUtils.intValue(portsArray[1]);
10623         shutdownPort = GrouperInstallerUtils.intValue(portsArray[2]);
10624       }
10625       
10626       if (!GrouperInstallerUtils.portAvailable(this.tomcatHttpPort, this.defaultIpAddress)) {
10627         System.out.print("The tomcat HTTP port is in use or unavailable: " + this.tomcatHttpPort + ", do you want to pick different ports? (t|f): ");
10628         boolean pickDifferentPorts = readFromStdInBoolean(null, "grouperInstaller.autorun.pickDifferentPortIfInUse");
10629         if (pickDifferentPorts) {
10630           continue;
10631         }
10632       }
10633       if (!GrouperInstallerUtils.portAvailable(jkPort, this.defaultIpAddress)) {
10634         System.out.print("The tomcat JK port is in use or unavailable: " + this.tomcatHttpPort + ", do you want to pick different ports? (t|f): ");
10635         boolean pickDifferentPorts = readFromStdInBoolean(null, "grouperInstaller.autorun.pickDifferentPortIfInUse");
10636         if (pickDifferentPorts) {
10637           continue;
10638         }
10639       }
10640       
10641       System.out.println("Editing tomcat config file: " + serverXmlFile.getAbsolutePath());
10642       //lets edit the file
10643       //<Connector port="8080" protocol="HTTP/1.1" 
10644       editFile(serverXmlFile, "port=\"([\\d]+)\"", new String[]{"<Connector", "protocol=\"HTTP/1.1\""}, 
10645           new String[]{"SSLEnabled=\"true\""}, Integer.toString(this.tomcatHttpPort), "tomcat HTTP port");
10646       //<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
10647       editFile(serverXmlFile, "port=\"([\\d]+)\"", new String[]{"<Connector", "protocol=\"AJP/1.3\""}, null, Integer.toString(jkPort), "tomcat JK port");
10648       //<Server port="8005" shutdown="SHUTDOWN">
10649       editFile(serverXmlFile, "port=\"([\\d]+)\"", new String[]{"<Server", "shutdown=\"SHUTDOWN\""}, null, Integer.toString(shutdownPort), "tomcat shutdown port");
10650       break;
10651     }
10652 
10653     configureTomcatUriEncoding(serverXmlFile);
10654     
10655   }
10656   
10657   /**
10658    * 
10659    */
10660   private void configureTomee() {
10661     
10662     System.out.print("Do you want to set the tomee memory limit (t|f)? [t]: ");
10663     boolean setTomeeMemory = readFromStdInBoolean(true, "grouperInstaller.autorun.setTomeeMemoryLimit");
10664     
10665     if (setTomeeMemory) {
10666       
10667       {
10668         File catalinaBatFile = new File(this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin" + File.separator + "catalina.bat");
10669         
10670         System.out.println("Editing file: " + catalinaBatFile.getAbsolutePath());
10671         
10672         Boolean edited = editFile(catalinaBatFile, "^\\s*set\\s+\"JAVA_OPTS\\s*=.*-Xmx([0-9mMgG]+)", null, null, "512M", "max memory");
10673         if (edited == null) {
10674           addToFile(catalinaBatFile, "\nset \"JAVA_OPTS=-server -Xmx512M -XX:MaxPermSize=256M\"\n", 65, "max memory");
10675         }
10676         if (null == editFile(catalinaBatFile, "^\\s*set\\s+\"JAVA_OPTS\\s*=.*-XX:MaxPermSize=([0-9mMgG]+)", null, null, "256M", "permgen memory")) {
10677           throw new RuntimeException("Why not edit permgen in file " + catalinaBatFile);
10678         }
10679       }
10680       
10681       {
10682         File catalinaShFile = new File(this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin" + File.separator + "catalina.sh");
10683         
10684         System.out.println("Editing file: " + catalinaShFile.getAbsolutePath());
10685 
10686         Boolean edited = editFile(catalinaShFile, "^\\s*JAVA_OPTS\\s*=\".*-Xmx([0-9mMgG]+)", null, null, "512M", "max memory");
10687         if (edited == null) {
10688           addToFile(catalinaShFile, "\nJAVA_OPTS=\"-server -Xmx512M -XX:MaxPermSize=256M\"\n", 65, "max memory");
10689         }
10690         if (null == editFile(catalinaShFile, "^\\s*JAVA_OPTS\\s*=\".*-XX:MaxPermSize=([0-9mMgG]+)", null, null, "256M", "permgen memory")) {
10691           throw new RuntimeException("Why not edit permgen in file " + catalinaShFile);
10692         }
10693       }
10694     }      
10695     
10696     
10697     if (!GrouperInstallerUtils.isWindows()) {
10698 
10699       System.out.print("Do you want to set tomee scripts to executable (t|f)? [t]: ");
10700       boolean setTomeeFiles = readFromStdInBoolean(true, "grouperInstaller.autorun.setTomeeScriptsToExecutable");
10701       
10702       //GrouperInstallerUtils.toSet("catalina.sh", "startup.sh", "shutdown.sh");
10703       Set<String> shFileNames = new HashSet<String>();
10704 
10705       File binDir = new File(this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin");
10706 
10707       //get all sh files, doing wildcards doesnt work
10708       for (File file : binDir.listFiles()) {
10709         String fileName = GrouperInstallerUtils.defaultString(file.getName());
10710         if (file.isFile() && fileName.endsWith(".sh")) {
10711           shFileNames.add(fileName);
10712         }
10713       }
10714 
10715       if (setTomeeFiles) {
10716       
10717         for (String command : shFileNames) {
10718           List<String> commands = new ArrayList<String>();
10719           
10720           commands.add("chmod");
10721           commands.add("+x");
10722           //have to do * since all the  sh files need chmod
10723           commands.add(this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin" + File.separator + command);
10724     
10725           System.out.println("Making tomee file executable with command: " + convertCommandsIntoCommand(commands) + "\n");
10726     
10727           CommandResult commandResult = GrouperInstallerUtils.execCommand(
10728               GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
10729               new File(this.untarredTomeeDir.getAbsolutePath() + File.separator + "bin"), null, true);
10730           
10731           if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
10732             System.out.println("stderr: " + commandResult.getErrorText());
10733           }
10734           if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
10735             System.out.println("stdout: " + commandResult.getOutputText());
10736           }
10737         }
10738       }
10739       
10740       Set<File> shFiles = new LinkedHashSet<File>();
10741       for (String shFileName : shFileNames) {
10742         shFiles.add(new File(shFileName));
10743       }
10744       
10745       dos2unix(shFiles, "tomee sh files", "OnTomeeFiles");
10746 
10747     }
10748       
10749     //see what the current ports are
10750     this.tomeeHttpPort = -1;
10751     
10752     File serverXmlFile = new File(this.untarredTomeeDir.getAbsolutePath() + File.separator + "conf" + File.separator + "server.xml");
10753     
10754     int shutdownPort = GrouperInstallerUtils.xpathEvaluateAttributeInt(serverXmlFile, "/Server", "port", -1);
10755     
10756     int originalShutdownPort = shutdownPort;
10757     
10758     //  /Server/Service/Connector <Connector port="8080" protocol="HTTP/1.1" 
10759     this.tomeeHttpPort = GrouperInstallerUtils.xpathEvaluateAttributeInt(serverXmlFile, "/Server/Service/Connector[@protocol='HTTP/1.1']", "port", -1);
10760 
10761     int originalTomeeHttpPort = this.tomeeHttpPort;
10762 
10763     // <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
10764     int jkPort = GrouperInstallerUtils.xpathEvaluateAttributeInt(serverXmlFile, "/Server/Service/Connector[@protocol='AJP/1.3']", "port", -1);
10765 
10766     int originalJkPort = jkPort;
10767     
10768     String portsCommaSeparated = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.tomeePorts", false);
10769     if (!GrouperInstallerUtils.isBlank(portsCommaSeparated)) {
10770       
10771       String[] portsStrings = GrouperInstallerUtils.splitTrim(portsCommaSeparated, ",");
10772       
10773       if (portsStrings.length != 3) {
10774         throw new RuntimeException("Why is grouperInstaller.default.tomeePorts from grouper.installer.properties not 3 ints comma separated? " + portsCommaSeparated);
10775       }
10776       
10777       this.tomeeHttpPort = GrouperInstallerUtils.intValue(portsStrings[0]);
10778       jkPort = GrouperInstallerUtils.intValue(portsStrings[1]);
10779       shutdownPort = GrouperInstallerUtils.intValue(portsStrings[2]);
10780       
10781     }
10782     
10783     while(true) {
10784       System.out.print("What ports do you want tomee to run on (HTTP, JK, shutdown): [" + this.tomeeHttpPort + ", " + jkPort + ", " + shutdownPort + "]: ");
10785       
10786       String ports = readFromStdIn("grouperInstaller.autorun.tomeePorts");
10787       
10788       if (GrouperInstallerUtils.isBlank(ports)) {
10789         if (this.tomeeHttpPort == originalTomeeHttpPort && jkPort == originalJkPort && shutdownPort == originalShutdownPort) {
10790           break;
10791         }
10792       } else {
10793         String[] portsArray = GrouperInstallerUtils.splitTrim(ports, ",");
10794         if (GrouperInstallerUtils.length(portsArray) == 3) {
10795           for (String portString : portsArray) {
10796             try {
10797               GrouperInstallerUtils.intValue(portString);
10798             } catch (Exception e) {
10799               continue;
10800             }
10801           }
10802         } else {
10803           continue;
10804         }
10805         //ok, we have three integer entries
10806         this.tomeeHttpPort = GrouperInstallerUtils.intValue(portsArray[0]);
10807         jkPort = GrouperInstallerUtils.intValue(portsArray[1]);
10808         shutdownPort = GrouperInstallerUtils.intValue(portsArray[2]);
10809       }
10810       
10811       if (!GrouperInstallerUtils.portAvailable(this.tomeeHttpPort, this.defaultIpAddress)) {
10812         System.out.print("The tomee HTTP port is in use or unavailable: " + this.tomeeHttpPort + ", do you want to pick different ports? (t|f): ");
10813         boolean pickDifferentPorts = readFromStdInBoolean(null, "grouperInstaller.autorun.pickDifferentPortIfInUse");
10814         if (pickDifferentPorts) {
10815           continue;
10816         }
10817       }
10818       if (!GrouperInstallerUtils.portAvailable(jkPort, this.defaultIpAddress)) {
10819         System.out.print("The tomee JK port is in use or unavailable: " + this.tomeeHttpPort + ", do you want to pick different ports? (t|f): ");
10820         boolean pickDifferentPorts = readFromStdInBoolean(null, "grouperInstaller.autorun.pickDifferentPortIfInUse");
10821         if (pickDifferentPorts) {
10822           continue;
10823         }
10824       }
10825       
10826       System.out.println("Editing tomee config file: " + serverXmlFile.getAbsolutePath());
10827       //lets edit the file
10828       //<Connector port="8080" protocol="HTTP/1.1" 
10829       editFile(serverXmlFile, "port=\"([\\d]+)\"", new String[]{"<Connector", "protocol=\"HTTP/1.1\""}, 
10830           new String[]{"SSLEnabled=\"true\""}, Integer.toString(this.tomcatHttpPort), "tomee HTTP port");
10831       //<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
10832       editFile(serverXmlFile, "port=\"([\\d]+)\"", new String[]{"<Connector", "protocol=\"AJP/1.3\""}, null, Integer.toString(jkPort), "tomee JK port");
10833       //<Server port="8005" shutdown="SHUTDOWN">
10834       editFile(serverXmlFile, "port=\"([\\d]+)\"", new String[]{"<Server", "shutdown=\"SHUTDOWN\""}, null, Integer.toString(shutdownPort), "tomee shutdown port");
10835       break;
10836     }
10837 
10838     configureTomcatUriEncoding(serverXmlFile);
10839     
10840   }
10841   /**
10842    * @param serverXmlFile
10843    */
10844   public void configureTomcatUriEncoding(File serverXmlFile) {
10845     //set encoding for connectors
10846     //  /Server/Service/Connector <Connector port="8080" protocol="HTTP/1.1" 
10847     String uriEncodingHttp = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
10848         "/Server/Service/Connector[@protocol='HTTP/1.1']", "URIEncoding");
10849     
10850     // <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
10851     String uriEncodingAjp = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
10852         "/Server/Service/Connector[@protocol='AJP/1.3']", "URIEncoding");
10853 
10854     if (!GrouperInstallerUtils.equals(uriEncodingAjp, "UTF-8") || !GrouperInstallerUtils.equals(uriEncodingHttp, "UTF-8")) {
10855 
10856       boolean defaultSetUriEncoding = GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.ui.setTomcatUriEncoding", true, false);
10857       System.out.print("Do you want to set URIEncoding to UTF-8 in tomcat server.xml <Connector> elements (t|f)? [" 
10858           + (defaultSetUriEncoding ? "t" : "f") + "]: ");
10859       boolean assignUriEncoding = readFromStdInBoolean(defaultSetUriEncoding, "grouperInstaller.autorun.setUriEncodingToUtf8inServerXml");
10860 
10861       if (assignUriEncoding) {
10862         
10863         if (!GrouperInstallerUtils.equals(uriEncodingAjp, "UTF-8")) {
10864           editFile(serverXmlFile, "URIEncoding=\"([^\"]+)\"", new String[]{"<Connector", "protocol=\"AJP/1.3\""}, 
10865               new String[]{"SSLEnabled=\"true\""}, "UTF-8", "tomcat URIEncoding attribute for element <Connector AJP", true, "URIEncoding");
10866           
10867         }
10868         
10869         if (!GrouperInstallerUtils.equals(uriEncodingHttp, "UTF-8")) {
10870           editFile(serverXmlFile, "URIEncoding=\"([^\"]+)\"", new String[]{"<Connector", "protocol=\"HTTP/1.1\""}, 
10871               new String[]{"SSLEnabled=\"true\""}, "UTF-8", "tomcat URIEncoding attribute for element <Connector HTTP", true, "URIEncoding");
10872           
10873         }
10874       }
10875 
10876     }
10877   }
10878 
10879   /**
10880    * 
10881    * @param newEhcacheExampleFile
10882    * @param existingEhcacheExampleFile
10883    * @param existingEhcacheFile
10884    */
10885   public static void mergeEhcacheXmlFiles(File newEhcacheExampleFile, File existingEhcacheExampleFile, File existingEhcacheFile) {
10886     
10887     try {
10888       //lets get the differences of the existing ehcache file and the existing ehcache example file
10889       DocumentBuilderFactory domFactory = GrouperInstallerUtils.xmlDocumentBuilderFactory();
10890       DocumentBuilder builder = domFactory.newDocumentBuilder();
10891       Document existingEhcacheDoc = builder.parse(existingEhcacheFile);
10892       Document existingEhcacheExampleDoc = builder.parse(existingEhcacheExampleFile);
10893 
10894       Element existingDocumentElement = existingEhcacheDoc.getDocumentElement();
10895       Element existingExampleDocumentElement = existingEhcacheExampleDoc.getDocumentElement();
10896 
10897       Map<String, String> diskStoreDifferences = null;
10898       
10899       {
10900         Element existingDiskStoreElement = (Element)existingDocumentElement.getElementsByTagName("diskStore").item(0);
10901         Element existingExampleDiskStoreElement = (Element)existingExampleDocumentElement.getElementsByTagName("diskStore").item(0);
10902         diskStoreDifferences = xmlNodeAttributeDifferences(existingExampleDiskStoreElement, existingDiskStoreElement);
10903       }
10904 
10905       Map<String, String> defaultCacheDifferences = null;
10906 
10907       {
10908         Element existingDefaultCacheElement = (Element)existingDocumentElement.getElementsByTagName("defaultCache").item(0);
10909         Element existingExampleDefaultCacheElement = (Element)existingExampleDocumentElement.getElementsByTagName("defaultCache").item(0);
10910         defaultCacheDifferences = xmlNodeAttributeDifferences(existingExampleDefaultCacheElement, existingDefaultCacheElement);
10911         
10912       }
10913 
10914       XPath xpath = XPathFactory.newInstance().newXPath();
10915       
10916       //map of cache name to the differences in the attributes of the cache
10917       Map<String, Map<String, String>> cacheDifferencesByCacheName = new LinkedHashMap<String, Map<String, String>>();
10918       
10919       {
10920         NodeList existingCacheNodeList = existingDocumentElement.getElementsByTagName("cache");
10921         
10922         //loop through all the caches
10923         for (int i=0;i<existingCacheNodeList.getLength(); i++) {
10924           
10925           Element existingCacheElement = (Element)existingCacheNodeList.item(i);
10926 
10927           String cacheName = existingCacheElement.getAttribute("name");
10928 
10929           //find the example cache
10930           XPathExpression expr = xpath.compile("cache[@name='" + cacheName + "']");
10931           Element existingExampleCacheElement = (Element)expr.evaluate(existingExampleDocumentElement, XPathConstants.NODE);
10932 
10933           //see if they differ
10934           Map<String, String> differences = xmlNodeAttributeDifferences(existingExampleCacheElement, existingCacheElement);
10935           
10936           if (differences != null) {
10937             cacheDifferencesByCacheName.put(cacheName, differences);
10938           }
10939         }
10940         
10941         //note, dont worry if there were caches in the example that werent in the configured one
10942         
10943       }      
10944       
10945       //lets see if there are any other nodes
10946       Set<Element> otherNodes = new LinkedHashSet<Element>();
10947       {
10948         NodeList nodeList = existingDocumentElement.getChildNodes();
10949         
10950         for (int i=0;i<nodeList.getLength();i++) {
10951           Node node = nodeList.item(i);
10952           if (node instanceof Element) {
10953             Element nodeElement = (Element)node;
10954             String nodeName = nodeElement.getNodeName();
10955             if (!GrouperInstallerUtils.equals(nodeName, "cache")
10956                 && !GrouperInstallerUtils.equals(nodeName, "defaultCache")
10957                 && !GrouperInstallerUtils.equals(nodeName, "diskStore")) {
10958               otherNodes.add(nodeElement);
10959             }
10960           }
10961         }
10962       }
10963       
10964       //lets copy the new example to both the example and the configured ehcache file
10965       //assume this is already backed up
10966       GrouperInstallerUtils.copyFile(newEhcacheExampleFile, existingEhcacheExampleFile, true);
10967       GrouperInstallerUtils.copyFile(newEhcacheExampleFile, existingEhcacheFile, true);
10968 
10969       //now lets do our edits
10970       if (GrouperInstallerUtils.length(diskStoreDifferences) > 0) {
10971         
10972         for (String attributeName : diskStoreDifferences.keySet()) {
10973 
10974           String attributeValue = diskStoreDifferences.get(attributeName);
10975 
10976           editXmlFileAttribute(existingEhcacheFile, "diskStore", null, attributeName, attributeValue, 
10977               "ehcache diskStore attribute '" + attributeName + "'");
10978           
10979         }
10980       }
10981       
10982       if (GrouperInstallerUtils.length(defaultCacheDifferences) > 0) {
10983         
10984         for (String attributeName : defaultCacheDifferences.keySet()) {
10985           
10986           String attributeValue = defaultCacheDifferences.get(attributeName);
10987 
10988           editXmlFileAttribute(existingEhcacheFile, "defaultCache", null, attributeName, attributeValue, 
10989               "ehcache defaultCache attribute '" + attributeName + "'");
10990 
10991         }
10992       }
10993 
10994       if (GrouperInstallerUtils.length(cacheDifferencesByCacheName) > 0) {
10995 
10996         existingEhcacheDoc = builder.parse(existingEhcacheFile);
10997         existingDocumentElement = existingEhcacheDoc.getDocumentElement();
10998         
10999         for (String cacheName : cacheDifferencesByCacheName.keySet()) {
11000 
11001           //see if the name exists
11002           //find the example cache
11003           XPathExpression expr = xpath.compile("cache[@name='" + cacheName + "']");
11004 
11005           Element existingCacheElement = (Element)expr.evaluate(existingDocumentElement, XPathConstants.NODE);
11006 
11007           Map<String, String> attributeMap = cacheDifferencesByCacheName.get(cacheName);
11008 
11009           //it exists
11010           if (existingCacheElement != null) {
11011 
11012             Map<String, String> expectedAttribute = new HashMap<String, String>();
11013 
11014             expectedAttribute.put("name", cacheName);
11015             
11016             for (String attributeName : attributeMap.keySet()) {
11017 
11018               String attributeValue = attributeMap.get(attributeName);
11019 
11020               editXmlFileAttribute(existingEhcacheFile, "cache", expectedAttribute, attributeName, attributeValue, 
11021                   "ehcache cache name=" + cacheName + " attribute '" + attributeName + "'");
11022             }
11023           } else {
11024 
11025               String fileContents = GrouperInstallerUtils.readFileIntoString(existingEhcacheFile);
11026 
11027               String newline = GrouperInstallerUtils.newlineFromFile(fileContents);
11028 
11029               int lastTagStart = fileContents.lastIndexOf("</ehcache>");
11030               
11031               if (lastTagStart == -1) {
11032                 throw new RuntimeException("Why is </ehcache> not found???? " + fileContents);
11033               }
11034 
11035               String tag = GrouperInstallerUtils.xmlElementToXml("cache", null, attributeMap);
11036 //              sdf
11037               String newFileContents = fileContents.substring(0, lastTagStart) + tag + newline 
11038                   + fileContents.substring(lastTagStart, fileContents.length());
11039 
11040               System.out.println(" - adding ehcache cache " + cacheName);
11041 
11042               GrouperInstallerUtils.saveStringIntoFile(existingEhcacheFile, newFileContents);
11043 
11044           }
11045 
11046         }
11047       }
11048 
11049       if (GrouperInstallerUtils.length(otherNodes) > 0) {
11050         String fileContents = GrouperInstallerUtils.readFileIntoString(existingEhcacheFile);
11051         
11052         String newline = GrouperInstallerUtils.newlineFromFile(fileContents);
11053 
11054         StringBuilder otherNodesStringBuilder = new StringBuilder();
11055         for (Element element : otherNodes) {
11056           String elementString = GrouperInstallerUtils.xmlToString(element);
11057           // take out the xml header: <?xml version="1.0" encoding="UTF-8"?>
11058           
11059           int elementStart = elementString.indexOf("<" + element.getNodeName());
11060           
11061           elementString = elementString.substring(elementStart);
11062           
11063           otherNodesStringBuilder.append(elementString).append(newline);
11064           System.out.println(" - adding element " + element.getTagName());
11065         }
11066 
11067         int lastTagStart = fileContents.lastIndexOf("</ehcache>");
11068         
11069         if (lastTagStart == -1) {
11070           throw new RuntimeException("Why is </ehcache> not found???? " + fileContents);
11071         }
11072 
11073         String newFileContents = fileContents.substring(0, lastTagStart) + otherNodesStringBuilder.toString()
11074             + fileContents.substring(lastTagStart, fileContents.length());
11075 
11076         GrouperInstallerUtils.saveStringIntoFile(existingEhcacheFile, newFileContents);
11077 
11078       }
11079 
11080 
11081       // test the new file, look for things
11082       existingEhcacheDoc = builder.parse(existingEhcacheFile);
11083       existingDocumentElement = existingEhcacheDoc.getDocumentElement();
11084 
11085       if (GrouperInstallerUtils.length(diskStoreDifferences) > 0) {
11086         Element existingDiskStoreElement = (Element)existingDocumentElement.getElementsByTagName("diskStore").item(0);
11087         for (String attributeName : diskStoreDifferences.keySet()) {
11088           String attributeValue = diskStoreDifferences.get(attributeName);
11089           if (!GrouperInstallerUtils.equals(attributeValue, existingDiskStoreElement.getAttribute(attributeName))) {
11090             throw new RuntimeException("Why is diskStore attribute " + attributeName + " not '" + attributeValue + "'" 
11091                 + existingEhcacheFile.getAbsolutePath());
11092           }
11093         }
11094       }
11095       
11096       if (GrouperInstallerUtils.length(defaultCacheDifferences) > 0) {
11097         Element existingDefaultCacheElement = (Element)existingDocumentElement.getElementsByTagName("defaultCache").item(0);
11098         for (String attributeName : defaultCacheDifferences.keySet()) {
11099           String attributeValue = defaultCacheDifferences.get(attributeName);
11100           if (!GrouperInstallerUtils.equals(attributeValue, existingDefaultCacheElement.getAttribute(attributeName))) {
11101             throw new RuntimeException("Why is defaultCache attribute " + attributeName + " not '" + attributeValue + "'" 
11102                 + existingEhcacheFile.getAbsolutePath());
11103           }
11104         }
11105       }
11106 
11107       if (GrouperInstallerUtils.length(cacheDifferencesByCacheName) > 0) {
11108         for (String cacheName : cacheDifferencesByCacheName.keySet()) {
11109 
11110           //see if the name exists
11111           //find the example cache
11112           XPathExpression expr = xpath.compile("cache[@name='" + cacheName + "']");
11113           Element existingCacheElement = (Element)expr.evaluate(existingDocumentElement, XPathConstants.NODE);
11114 
11115           Map<String, String> attributeMap = cacheDifferencesByCacheName.get(cacheName);
11116           
11117           for (String attributeName : attributeMap.keySet()) {
11118             
11119             String attributeValue = attributeMap.get(attributeName);
11120 
11121             if (!GrouperInstallerUtils.equals(attributeValue, existingCacheElement.getAttribute(attributeName))) {
11122               throw new RuntimeException("Why is cache " + cacheName + " attribute " + attributeName + " not '" + attributeValue + "'" 
11123                   + existingEhcacheFile.getAbsolutePath());
11124             }
11125             
11126           }
11127         }
11128       }
11129 
11130       if (GrouperInstallerUtils.length(otherNodes) > 0) {
11131         for (Element element : otherNodes) {
11132           
11133           NodeList nodeList = existingDocumentElement.getElementsByTagName(element.getNodeName());
11134           if (nodeList == null || nodeList.getLength() == 0 ) {
11135             throw new RuntimeException("Why is new element not there? " + element.getTagName() + ", "
11136                 + existingEhcacheFile.getAbsolutePath());
11137           }
11138         }
11139       }
11140 
11141     } catch (Exception e) {
11142       throw new RuntimeException(e.getMessage(), e);
11143     }
11144   }
11145 
11146   /**
11147    * 
11148    * @param newEhcacheExampleFile
11149    * @param existingEhcacheExampleFile
11150    * @param existingEhcacheFile
11151    * @return hasMerging
11152    */
11153   @SuppressWarnings("unused")
11154   private static boolean mergeEhcacheXmlFiles_XML_NOT_USED(File newEhcacheExampleFile, File existingEhcacheExampleFile, 
11155       File existingEhcacheFile) {
11156     
11157     boolean hasMerging = false;
11158     
11159     try {
11160       //lets get the differences of the existing ehcache file and the existing ehcache example file
11161       DocumentBuilderFactory domFactory = GrouperInstallerUtils.xmlDocumentBuilderFactory();
11162       DocumentBuilder builder = domFactory.newDocumentBuilder();
11163       Document existingEhcacheDoc = builder.parse(existingEhcacheFile);
11164       Document existingEhcacheExampleDoc = builder.parse(existingEhcacheExampleFile);
11165 
11166       Element existingDocumentElement = existingEhcacheDoc.getDocumentElement();
11167       Element existingExampleDocumentElement = existingEhcacheExampleDoc.getDocumentElement();
11168 
11169       Map<String, String> diskStoreDifferences = null;
11170       
11171       {
11172         Element existingDiskStoreElement = (Element)existingDocumentElement.getElementsByTagName("diskStore").item(0);
11173         Element existingExampleDiskStoreElement = (Element)existingExampleDocumentElement.getElementsByTagName("diskStore").item(0);
11174         diskStoreDifferences = xmlNodeAttributeDifferences(existingExampleDiskStoreElement, existingDiskStoreElement);
11175       }
11176 
11177       Map<String, String> defaultCacheDifferences = null;
11178 
11179       {
11180         Element existingDefaultCacheElement = (Element)existingDocumentElement.getElementsByTagName("defaultCache").item(0);
11181         Element existingExampleDefaultCacheElement = (Element)existingExampleDocumentElement.getElementsByTagName("defaultCache").item(0);
11182         defaultCacheDifferences = xmlNodeAttributeDifferences(existingExampleDefaultCacheElement, existingDefaultCacheElement);
11183         
11184       }
11185 
11186       XPath xpath = XPathFactory.newInstance().newXPath();
11187       
11188       //map of cache name to the differences in the attributes of the cache
11189       Map<String, Map<String, String>> cacheDifferencesByCacheName = new LinkedHashMap<String, Map<String, String>>();
11190       
11191       {
11192         NodeList existingCacheNodeList = existingDocumentElement.getElementsByTagName("cache");
11193         
11194         //loop through all the caches
11195         for (int i=0;i<existingCacheNodeList.getLength(); i++) {
11196           
11197           Element existingCacheElement = (Element)existingCacheNodeList.item(i);
11198 
11199           String cacheName = existingCacheElement.getAttribute("name");
11200 
11201           //find the example cache
11202           XPathExpression expr = xpath.compile("cache[@name='" + cacheName + "']");
11203           Element existingExampleCacheElement = (Element)expr.evaluate(existingExampleDocumentElement, XPathConstants.NODE);
11204 
11205           //see if they differ
11206           Map<String, String> differences = xmlNodeAttributeDifferences(existingExampleCacheElement, existingCacheElement);
11207           
11208           if (differences != null) {
11209             cacheDifferencesByCacheName.put(cacheName, differences);
11210           }
11211         }
11212         
11213         //note, dont worry if there were caches in the example that werent in the configured one
11214         
11215       }      
11216       
11217       //lets see if there are any other nodes
11218       Set<Element> otherNodes = new LinkedHashSet<Element>();
11219       {
11220         NodeList nodeList = existingDocumentElement.getChildNodes();
11221         
11222         for (int i=0;i<nodeList.getLength();i++) {
11223           Node node = nodeList.item(i);
11224           if (node instanceof Element) {
11225             Element nodeElement = (Element)node;
11226             String nodeName = nodeElement.getNodeName();
11227             if (!GrouperInstallerUtils.equals(nodeName, "cache")
11228                 && !GrouperInstallerUtils.equals(nodeName, "defaultCache")
11229                 && !GrouperInstallerUtils.equals(nodeName, "diskStore")) {
11230               otherNodes.add(nodeElement);
11231             }
11232           }
11233         }
11234       }
11235       
11236       //lets copy the new example to both the example and the configured ehcache file
11237       //assume this is already backed up
11238       GrouperInstallerUtils.copyFile(newEhcacheExampleFile, existingEhcacheExampleFile, true);
11239 
11240       //this is the new existing ehcache file
11241       existingEhcacheExampleDoc = builder.parse(existingEhcacheExampleFile);
11242       existingExampleDocumentElement = existingEhcacheExampleDoc.getDocumentElement();
11243 
11244       //now lets do our edits
11245       if (GrouperInstallerUtils.length(diskStoreDifferences) > 0) {
11246         
11247         hasMerging = true;
11248 
11249         Element existingExampleDiskStoreElement = (Element)existingExampleDocumentElement.getElementsByTagName("diskStore").item(0);
11250 
11251         for (String attributeName : diskStoreDifferences.keySet()) {
11252 
11253           String attributeValue = diskStoreDifferences.get(attributeName);
11254 
11255           existingExampleDiskStoreElement.setAttribute(attributeName, attributeValue);
11256         }
11257       }
11258       
11259       if (GrouperInstallerUtils.length(defaultCacheDifferences) > 0) {
11260 
11261         hasMerging = true;
11262 
11263         Element existingExampleDefaultCacheElement = (Element)existingExampleDocumentElement.getElementsByTagName("defaultCache").item(0);
11264         
11265         for (String attributeName : defaultCacheDifferences.keySet()) {
11266           
11267           String attributeValue = defaultCacheDifferences.get(attributeName);
11268 
11269           existingExampleDefaultCacheElement.setAttribute(attributeName, attributeValue);
11270           
11271         }
11272       }
11273 
11274       if (GrouperInstallerUtils.length(cacheDifferencesByCacheName) > 0) {
11275         hasMerging = true;
11276         for (String cacheName : cacheDifferencesByCacheName.keySet()) {
11277 
11278           //see if the name exists
11279           //find the example cache
11280           XPathExpression expr = xpath.compile("cache[@name='" + cacheName + "']");
11281           Element existingExampleCacheElement = (Element)expr.evaluate(existingExampleDocumentElement, XPathConstants.NODE);
11282 
11283           Map<String, String> attributeMap = cacheDifferencesByCacheName.get(cacheName);
11284           
11285           //it exists
11286           if (existingExampleCacheElement != null) {
11287             
11288             for (String attributeName : attributeMap.keySet()) {
11289               
11290               String attributeValue = attributeMap.get(attributeName);
11291               existingExampleCacheElement.setAttribute(attributeName, attributeValue);
11292               
11293             }
11294           } else {
11295             
11296             Element existingCacheElement = (Element)expr.evaluate(existingDocumentElement, XPathConstants.NODE);
11297             //move a cache from one document to another
11298             existingExampleDocumentElement.appendChild(existingCacheElement.cloneNode(true));
11299             
11300           }
11301           
11302         }
11303       }
11304 
11305       if (GrouperInstallerUtils.length(otherNodes) > 0) {
11306         hasMerging = true;
11307         for (Element element : otherNodes) {
11308           
11309           //move a cache from one document to another
11310           existingExampleDocumentElement.appendChild(element.cloneNode(true));
11311         }
11312       }
11313 
11314 //      System.out.println("Compare you old ehcache.xml with your new ehcache.xml file: " 
11315 //          + "\n  Old file: "
11316 //          + backedUpEhcacheFile.getAbsolutePath()
11317 //          + "\n  New file: " + existingEhcacheFile.getAbsolutePath());
11318 
11319       // save to file
11320       String xml = GrouperInstallerUtils.xmlToString(existingEhcacheExampleDoc);
11321       GrouperInstallerUtils.saveStringIntoFile(existingEhcacheFile, xml);
11322       
11323       // test the new file, look for things
11324       existingEhcacheDoc = builder.parse(existingEhcacheFile);
11325       existingDocumentElement = existingEhcacheDoc.getDocumentElement();
11326 
11327       if (GrouperInstallerUtils.length(diskStoreDifferences) > 0) {
11328         Element existingDiskStoreElement = (Element)existingDocumentElement.getElementsByTagName("diskStore").item(0);
11329         for (String attributeName : diskStoreDifferences.keySet()) {
11330           String attributeValue = diskStoreDifferences.get(attributeName);
11331           if (!GrouperInstallerUtils.equals(attributeValue, existingDiskStoreElement.getAttribute(attributeName))) {
11332             throw new RuntimeException("Why is diskStore attribute " + attributeName + " not '" + attributeValue + "'" 
11333                 + existingEhcacheFile.getAbsolutePath());
11334           }
11335         }
11336       }
11337       
11338       if (GrouperInstallerUtils.length(defaultCacheDifferences) > 0) {
11339         Element existingDefaultCacheElement = (Element)existingDocumentElement.getElementsByTagName("defaultCache").item(0);
11340         for (String attributeName : defaultCacheDifferences.keySet()) {
11341           String attributeValue = defaultCacheDifferences.get(attributeName);
11342           if (!GrouperInstallerUtils.equals(attributeValue, existingDefaultCacheElement.getAttribute(attributeName))) {
11343             throw new RuntimeException("Why is defaultCache attribute " + attributeName + " not '" + attributeValue + "'" 
11344                 + existingEhcacheFile.getAbsolutePath());
11345           }
11346         }
11347       }
11348 
11349       if (GrouperInstallerUtils.length(cacheDifferencesByCacheName) > 0) {
11350         for (String cacheName : cacheDifferencesByCacheName.keySet()) {
11351 
11352           //see if the name exists
11353           //find the example cache
11354           XPathExpression expr = xpath.compile("cache[@name='" + cacheName + "']");
11355           Element existingCacheElement = (Element)expr.evaluate(existingDocumentElement, XPathConstants.NODE);
11356 
11357           Map<String, String> attributeMap = cacheDifferencesByCacheName.get(cacheName);
11358           
11359           for (String attributeName : attributeMap.keySet()) {
11360             
11361             String attributeValue = attributeMap.get(attributeName);
11362 
11363             if (!GrouperInstallerUtils.equals(attributeValue, existingCacheElement.getAttribute(attributeName))) {
11364               throw new RuntimeException("Why is cache " + cacheName + " attribute " + attributeName + " not '" + attributeValue + "'" 
11365                   + existingEhcacheFile.getAbsolutePath());
11366             }
11367             
11368           }
11369         }
11370       }
11371 
11372       if (GrouperInstallerUtils.length(otherNodes) > 0) {
11373         for (Element element : otherNodes) {
11374           
11375           NodeList nodeList = existingDocumentElement.getElementsByTagName(element.getNodeName());
11376           if (nodeList == null || nodeList.getLength() == 0 ) {
11377             throw new RuntimeException("Why is new element not there? " + element.getTagName() 
11378                 + existingEhcacheFile.getAbsolutePath());
11379           }
11380         }
11381       }
11382 
11383     } catch (Exception e) {
11384       throw new RuntimeException(e.getMessage(), e);
11385     }
11386     return hasMerging;
11387   }
11388   /**
11389    * 
11390    * @param baseElement
11391    * @param configuredElement
11392    * @return the map of differences
11393    */
11394   public static Map<String, String> xmlNodeAttributeDifferences(Element baseElement, Element configuredElement) {
11395     NamedNodeMap configuredNamedNodeMap = configuredElement.getAttributes();
11396     
11397     Map<String, String> result = null;
11398     
11399     //see which attributes are new or changed
11400     for (int i=0;i<configuredNamedNodeMap.getLength();i++) {
11401       Node configuredAttribute = configuredNamedNodeMap.item(i);
11402       Node baseAttribute = baseElement == null ? null : baseElement.getAttributeNode(configuredAttribute.getNodeName());
11403 
11404       String configuredValue = configuredAttribute.getNodeValue();
11405       String baseValue = baseAttribute == null ? null : baseAttribute.getNodeValue();
11406       
11407       if (!GrouperInstallerUtils.equals(configuredValue, baseValue)) {
11408         if (result == null) {
11409           result = new LinkedHashMap<String, String>();
11410         }
11411         result.put(configuredAttribute.getNodeName(), configuredValue);
11412       }
11413     }
11414     
11415     //see which ones are missing
11416     NamedNodeMap baseNamedNodeMap = baseElement == null ? null : baseElement.getAttributes();
11417     
11418     //see which attributes are new or changed
11419     for (int i=0;i<(baseNamedNodeMap == null ? 0 : baseNamedNodeMap.getLength());i++) {
11420       
11421       Node baseAttribute = configuredNamedNodeMap.item(0);
11422       Node configuredAttribute = configuredElement.getAttributeNode(baseAttribute.getNodeName());
11423 
11424       String baseValue = baseAttribute.getNodeValue();
11425       String configuredValue = configuredAttribute == null ? null : configuredAttribute.getNodeValue();
11426       
11427       if (configuredValue == null && !GrouperInstallerUtils.equals(configuredValue, baseValue)) {
11428         if (result == null) {
11429           result = new LinkedHashMap<String, String>();
11430         }
11431         result.put(baseAttribute.getNodeName(), configuredValue);
11432       }
11433     }
11434     
11435     return result;
11436   }
11437   
11438   /**
11439    * 
11440    * @return the file of the directory of API
11441    */
11442   private File downloadApi() {
11443     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
11444     
11445     if (!urlToDownload.endsWith("/")) {
11446       urlToDownload += "/";
11447     }
11448     urlToDownload += "release/";
11449     String apiFileName = "grouper.apiBinary-" + this.version + ".tar.gz";
11450     urlToDownload += this.version + "/" + apiFileName;
11451 
11452     File apiFile = new File(this.grouperTarballDirectoryString + apiFileName);
11453     
11454     downloadFile(urlToDownload, apiFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalApiDownloadTarEtc");
11455 
11456     return apiFile;
11457   }
11458 
11459   /**
11460    * 
11461    * @return the file of the directory of UI
11462    */
11463   private File downloadUi() {
11464     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
11465     
11466     if (!urlToDownload.endsWith("/")) {
11467       urlToDownload += "/";
11468     }
11469     urlToDownload += "release/";
11470 
11471     String uiFileName = "grouper.ui-" + this.version + ".tar.gz";
11472     urlToDownload += this.version + "/" + uiFileName;
11473 
11474     File uiFile = new File(this.grouperTarballDirectoryString + uiFileName);
11475     
11476     downloadFile(urlToDownload, uiFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalUiDownloadTarEtc");
11477 
11478     return uiFile;
11479   }
11480   
11481   /**
11482    * 
11483    * @return the file of the directory of WS
11484    */
11485   private File downloadWs() {
11486     
11487     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
11488     
11489     //String urlToDownload = "http://localhost:8085/grouper/grouperExternal/public/assets/dojo/dijit/themes/claro/images";
11490     
11491     if (!urlToDownload.endsWith("/")) {
11492       urlToDownload += "/";
11493     }
11494     urlToDownload += "release/";
11495 
11496     String wsFileName = "grouper.ws-" + this.version + ".tar.gz";
11497     urlToDownload += this.version + "/" + wsFileName;
11498 
11499     File wsFile = new File(this.grouperTarballDirectoryString + wsFileName);
11500     
11501     System.out.println("wsFile path is "+wsFile.getAbsolutePath());
11502     downloadFile(urlToDownload, wsFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalWsDownloadTarEtc");
11503 
11504     return wsFile;
11505   }
11506 
11507   /**
11508    * 
11509    * @return the file of the directory of ant
11510    */
11511   private File downloadAnt() {
11512     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
11513     
11514     if (!urlToDownload.endsWith("/")) {
11515       urlToDownload += "/";
11516     }
11517 
11518     urlToDownload += "downloads/tools/apache-ant-1.8.2-bin.tar.gz";
11519     
11520     File antFile = new File(this.grouperTarballDirectoryString + "apache-ant-1.8.2-bin.tar.gz");
11521     
11522     downloadFile(urlToDownload, antFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
11523 
11524     return antFile;
11525   }
11526 
11527   /**
11528    * 
11529    * @return the file of the directory of maven
11530    */
11531   private File downloadMaven() {
11532     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
11533     
11534     if (!urlToDownload.endsWith("/")) {
11535       urlToDownload += "/";
11536     }
11537 
11538     urlToDownload += "downloads/tools/apache-maven-3.2.5-bin.tar.gz";
11539     
11540     File mavenFile = new File(this.grouperTarballDirectoryString + "apache-maven-3.2.5-bin.tar.gz");
11541     
11542     downloadFile(urlToDownload, mavenFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
11543 
11544     return mavenFile;
11545   }
11546 
11547   /**
11548    * tomcat version
11549    */
11550   private String tomcatVersion = "8.5.42";
11551   
11552   /**
11553    * 
11554    * @return tomcat version
11555    */
11556   private String tomcatVersion() {
11557     
11558     // this is now hardcoded
11559     if (this.tomcatVersion == null) {
11560       
11561       String defaultTomcatVersion = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.tomcat.version", false);
11562       defaultTomcatVersion = GrouperInstallerUtils.defaultIfBlank(defaultTomcatVersion, "8.5.42");
11563       
11564       System.out.print("Enter the tomcat version (8.5.42 or 8.5.12 or 6.0.35) [" + defaultTomcatVersion + "]: ");
11565       this.tomcatVersion = readFromStdIn("grouperInstaller.autorun.tomcat.version");
11566       
11567       this.tomcatVersion = GrouperInstallerUtils.defaultIfBlank(this.tomcatVersion, defaultTomcatVersion);
11568       
11569       if (!GrouperInstallerUtils.equals(this.tomcatVersion, "8.5.42") && !GrouperInstallerUtils.equals(this.tomcatVersion, "6.0.35")) {
11570         System.out.print("Warning: this *should* be 8.5.42 or 8.5.12 or 6.0.35, hit <Enter> to continue: ");
11571         readFromStdIn("grouperInstaller.autorun.tomcat.version.mismatch");
11572       }
11573       
11574     }
11575     
11576     return this.tomcatVersion;
11577 
11578   }
11579   
11580   /**
11581    * Copy a jar file to another file.  this perserves the file date
11582    * 
11583    * @param sourceFile
11584    * @param destinationFile
11585    * @param onlyIfDifferentContents true if only saving due to different contents.  Note, this is only for non-binary files!
11586    * @param ignoreWhitespace true to ignore whitespace in comparisons
11587    * @return true if contents were saved (thus different if param set)
11588    */
11589   public static boolean copyJarFileIfNotExists(File sourceFile, File destinationFile, boolean onlyIfDifferentContents, boolean ignoreWhitespace) {
11590     
11591     if (!sourceFile.isFile() || !sourceFile.exists()) {
11592       throw new RuntimeException("Why does this not exist???? " + sourceFile.getAbsolutePath());
11593     }
11594     
11595     if (destinationFile.isFile() && destinationFile.exists() && 
11596         GrouperInstallerUtils.equals(GrouperInstallerUtils.fileSha1(destinationFile), GrouperInstallerUtils.fileSha1(sourceFile))) {
11597       System.out.println("Skipping file that exists in destination: " + destinationFile.getAbsolutePath());
11598       return false;
11599     }
11600 
11601     if (onlyIfDifferentContents) {
11602       String sourceContents = GrouperInstallerUtils.readFileIntoString(sourceFile);
11603       return GrouperInstallerUtils.saveStringIntoFile(destinationFile, sourceContents, 
11604           onlyIfDifferentContents, ignoreWhitespace);
11605     }
11606     
11607     File destinationFolder = destinationFile.getParentFile();
11608     
11609     Set<String> relatedBaseNames = GrouperInstallerUtils.jarFileBaseNames(destinationFile.getName());
11610 
11611     boolean hasConflict = false;
11612     for (File destinationCandidateFile : destinationFolder.listFiles()) {
11613       if (!destinationCandidateFile.getName().endsWith(".jar")) {
11614         continue;
11615       }
11616       Set<String> relatedCandidateBaseNames = GrouperInstallerUtils.jarFileBaseNames(destinationCandidateFile.getName());
11617       if (GrouperInstallerUtils.containsAny(relatedBaseNames, relatedCandidateBaseNames)) {
11618         
11619         hasConflict = true;
11620       }
11621     }
11622     
11623     if (hasConflict) {
11624       List<File> relatedFiles = GrouperInstallerUtils.jarFindJar(destinationFolder, sourceFile.getName());
11625       
11626       if (GrouperInstallerUtils.length(relatedFiles) == 1) {
11627         File relatedFile = relatedFiles.iterator().next();
11628         File newerVersion = GrouperInstallerUtils.jarNewerVersion(relatedFile, sourceFile);
11629         if (newerVersion != null) {
11630           
11631           if (newerVersion.equals(sourceFile)) {
11632             System.out.println("There is a conflicting jar: " + sourceFile.getAbsolutePath());
11633             System.out.println("Deleting older jar: " + relatedFile.getAbsolutePath());
11634             GrouperInstallerUtils.fileDelete(relatedFile);
11635             System.out.println("Copying " + sourceFile.getAbsolutePath() + " to " + destinationFile.getAbsolutePath());
11636             GrouperInstallerUtils.copyFile(sourceFile, destinationFile);
11637             return true;
11638           }
11639           System.out.println("There is a conflicting jar for source: " + sourceFile.getAbsolutePath());
11640           System.out.println("Not copying to dest due to this jar is newer: " + relatedFile.getAbsolutePath());
11641           return false;
11642         }
11643         System.out.println("There is a conflicting jar, source jar: " + sourceFile.getAbsolutePath());
11644         System.out.println("Destination jar: " + destinationFile.getAbsolutePath());
11645         System.out.print("Unable to resolve conflict, resolve manually, press <enter> to continue... ");
11646         readFromStdIn("grouperInstaller.autorun.conflictingJarContinue");
11647         return false;
11648       }
11649 
11650     }
11651     
11652     System.out.println("Copying " + sourceFile.getAbsolutePath() + " to " + destinationFile.getAbsolutePath());
11653     GrouperInstallerUtils.copyFile(sourceFile, destinationFile);
11654     return true;
11655   }
11656 
11657   /**
11658    * 
11659    * @return the file of the directory of tomcat
11660    */
11661   private File downloadTomcat() {
11662     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
11663     
11664     if (!urlToDownload.endsWith("/")) {
11665       urlToDownload += "/";
11666     }
11667 
11668     urlToDownload += "downloads/tools/apache-tomcat-" + this.tomcatVersion() + ".tar.gz";
11669     
11670     File tomcatFile = new File(this.grouperTarballDirectoryString + "apache-tomcat-" + this.tomcatVersion() + ".tar.gz");
11671     
11672     downloadFile(urlToDownload, tomcatFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
11673 
11674     return tomcatFile;
11675   }
11676   
11677   /**
11678    * 
11679    * @return the file of the directory of tomee
11680    */
11681   private File downloadTomee() {
11682     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
11683     
11684     //String urlToDownload = "http://localhost:8085/grouper/grouperExternal/public/assets/dojo/dijit/themes/claro/images";
11685     
11686     if (!urlToDownload.endsWith("/")) {
11687       urlToDownload += "/";
11688     }
11689 
11690     urlToDownload += "downloads/tools/apache-tomee-webprofile-7.0.0.tar.gz";
11691     
11692     File tomeeFile = new File(this.grouperTarballDirectoryString + "apache-tomee-webprofile-7.0.0.tar.gz");
11693     
11694     downloadFile(urlToDownload, tomeeFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalToolsDownloadTarEtc");
11695 
11696     return tomeeFile;
11697   }
11698 
11699   /**
11700    * add quick start subjects
11701    */
11702   private void addQuickstartSubjects() {
11703     
11704     System.out.print("Do you want to add quickstart subjects to DB (t|f)? [t]: ");
11705     boolean addQuickstartSubjects = readFromStdInBoolean(true, "grouperInstaller.autorun.addQuickstartSubjectsToDb");
11706     
11707     if (addQuickstartSubjects) {
11708 
11709       String url = GrouperInstallerUtils.propertiesValue("download.server.url", true);
11710       
11711       if (!url.endsWith("/")) {
11712         url += "/";
11713       }
11714       url += "release/" + this.version + "/subjects.sql";
11715 
11716       String subjectsSqlFileName = this.untarredApiDir.getParent() + File.separator + "subjects.sql";
11717       File subjectsSqlFile = new File(subjectsSqlFileName);
11718       downloadFile(url, subjectsSqlFileName, "grouperInstaller.autorun.useLocalApiDownloadTarEtc");
11719 
11720       List<String> commands = new ArrayList<String>();
11721       
11722       addGshCommands(commands);
11723       commands.add("-registry");
11724       commands.add("-runsqlfile");
11725       commands.add(subjectsSqlFile.getAbsolutePath());
11726       commands.add("-noprompt");
11727       
11728       System.out.println("\n##################################");
11729       System.out.println("Adding sample subjects with command: " + convertCommandsIntoCommand(commands) + "\n");
11730       
11731       CommandResult commandResult = GrouperInstallerUtils.execCommand(
11732           GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
11733           this.untarredApiDir, null, true);
11734       
11735       if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
11736         System.out.println("stderr: " + commandResult.getErrorText());
11737       }
11738       if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
11739         System.out.println("stdout: " + commandResult.getOutputText());
11740       }
11741       
11742       System.out.println("\nEnd adding sample subjects");
11743       System.out.println("##################################\n");
11744 
11745     }
11746   }
11747 
11748   /**
11749    * add quick start subjects
11750    */
11751   private void addQuickstartData() {
11752 
11753     System.out.print("Do you want to add quickstart data to registry (t|f)? [t] ");
11754     boolean addQuickstartData = readFromStdInBoolean(true, "grouperInstaller.autorun.addQuickstartData");
11755 
11756     if (addQuickstartData) {
11757       String url = GrouperInstallerUtils.propertiesValue("download.server.url", true);
11758       
11759       if (!url.endsWith("/")) {
11760         url += "/";
11761       }
11762       url += "release/" + this.version + "/quickstart.xml";
11763       String quickstartFileName = this.untarredApiDir.getParent() + File.separator + "quickstart.xml";
11764       
11765       File quickstartFile = new File(quickstartFileName);
11766       downloadFile(url, quickstartFileName, "grouperInstaller.autorun.useLocalApiDownloadTarEtc");
11767 
11768       List<String> commands = new ArrayList<String>();
11769       
11770       addGshCommands(commands);
11771       commands.add("-xmlimportold");
11772       commands.add("GrouperSystem");
11773       commands.add(quickstartFile.getAbsolutePath());
11774       commands.add("-noprompt");
11775       
11776       System.out.println("\n##################################");
11777       System.out.println("Adding quickstart data with command: " + convertCommandsIntoCommand(commands) + "\n");
11778       
11779       CommandResult commandResult = GrouperInstallerUtils.execCommand(
11780           GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
11781           this.untarredApiDir, null, true);
11782       
11783       if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
11784         System.out.println("stderr: " + commandResult.getErrorText());
11785       }
11786       if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
11787 
11788         System.out.println("stdout: " + commandResult.getOutputText());
11789       }
11790       System.out.println("\nEnd adding quickstart data");
11791       System.out.println("##################################\n");
11792 
11793     }
11794   }
11795   
11796   /**
11797    * if commands have spaces, put quotes around...
11798    * @param commands
11799    * @return the command
11800    */
11801   private static String convertCommandsIntoCommand(List<String> commands) {
11802     StringBuilder result = new StringBuilder();
11803     for (int i=0;i<GrouperInstallerUtils.length(commands); i++) {
11804       String command = GrouperInstallerUtils.defaultString(commands.get(i));
11805       
11806       //if there is a space, put quotes around command
11807       if (command.contains(" ")) {
11808         result.append("\"").append(command).append("\"");
11809       } else {
11810         result.append(command);
11811       }
11812       if (i != GrouperInstallerUtils.length(commands)-1) {
11813         result.append(" ");
11814       }
11815     }
11816     return result.toString();
11817   }
11818   
11819   /**
11820    * 
11821    */
11822   private void initDb() {
11823     System.out.print("Do you want to init the database (delete all existing grouper tables, add new ones) (t|f)? ");
11824     boolean initdb = readFromStdInBoolean(null, "grouperInstaller.autorun.deleteAndInitDatabase");
11825     
11826     if (initdb) {
11827       List<String> commands = new ArrayList<String>();
11828       
11829       addGshCommands(commands);
11830       commands.add("-registry");
11831       commands.add("-drop");
11832       commands.add("-runscript");
11833       commands.add("-noprompt");
11834       
11835       System.out.println("\n##################################");
11836       System.out.println("Initting DB with command: " + convertCommandsIntoCommand(commands) + "\n");
11837       
11838       CommandResult commandResult = GrouperInstallerUtils.execCommand(
11839           GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
11840           this.untarredApiDir, null, true);
11841       
11842       if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
11843         System.out.println("stderr: " + commandResult.getErrorText());
11844       }
11845       if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
11846 
11847         System.out.println("stdout: " + commandResult.getOutputText());
11848       }
11849       System.out.println("\nEnd Initting DB");
11850       System.out.println("##################################\n");
11851       
11852       
11853     }
11854 
11855   }
11856   
11857   /**
11858    * @param prompt
11859    */
11860   private void startLoader(boolean prompt) {
11861     
11862     boolean startLoader = true;
11863     
11864     if (prompt) {
11865       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 " 
11866           + (GrouperInstallerUtils.isWindows() ? "the task manager for java.exe" : "ps -ef | grep gsh | grep loader") + ") (t|f)? [f]: ");
11867       startLoader = readFromStdInBoolean(false, "grouperInstaller.autorun.startGrouperDaemons");
11868     }
11869     
11870     if (startLoader) {
11871       final List<String> commands = new ArrayList<String>();
11872       
11873       addGshCommands(commands);
11874       commands.add("-loader");
11875       
11876       if (!GrouperInstallerUtils.isWindows()) {
11877         
11878         //let this database run forever
11879         commands.add(0, "nohup");
11880         //run in new process
11881         commands.add("> /dev/null 2>&1 &");
11882         
11883         String fullCommand = GrouperInstallerUtils.join(commands.iterator(), ' ');
11884         commands.clear();
11885         commands.add(shCommand());
11886         commands.add("-c");
11887         commands.add(fullCommand);
11888         
11889       }
11890       System.out.println("\n##################################");
11891       System.out.println("Starting the Grouper loader (daemons): " + convertCommandsIntoCommand(commands) + "\n");
11892 
11893       //start in new thread
11894       Thread thread = new Thread(new Runnable() {
11895         
11896         @Override
11897         public void run() {
11898           GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
11899               true, true, null, GrouperInstaller.this.untarredApiDir, 
11900               GrouperInstaller.this.grouperInstallDirectoryString + "grouperLoader", false);
11901         }
11902       });
11903       thread.setDaemon(true);
11904       thread.start();
11905       
11906       System.out.println("\nEnd starting the Grouper loader (daemons)");
11907       System.out.println("##################################\n");
11908       
11909     }
11910 
11911   }
11912 
11913   /**
11914    * gsh command fully qualified
11915    */
11916   private String gshCommand;
11917   
11918   /**
11919    * 
11920    * @return the gsh command
11921    */
11922   private String gshCommand() {
11923 
11924     if (this.gshCommand == null) {
11925 
11926       String gshDir = GrouperInstallerUtils.defaultIfBlank(this.upgradeExistingApplicationDirectoryString, 
11927           this.untarredApiDir.getAbsolutePath() + File.separator);
11928       
11929       String gsh = gshDir + "bin" + File.separator 
11930           + (GrouperInstallerUtils.isWindows() ? "gsh.bat" : "gsh.sh");
11931       
11932       if (new File(gsh).exists()) {
11933         this.gshCommand = gsh;
11934         return gsh;
11935       }
11936 
11937       gsh = gshDir + "WEB-INF" + File.separator + "bin" + File.separator 
11938           + (GrouperInstallerUtils.isWindows() ? "gsh.bat" : "gsh.sh");
11939 
11940       if (new File(gsh).exists()) {
11941         this.gshCommand = gsh;
11942         return gsh;
11943       }
11944       
11945       throw new RuntimeException("Cant find gsh: " + gshDir);
11946     }
11947 
11948     return this.gshCommand;
11949   }
11950 
11951   /**
11952    * 
11953    */
11954   private void checkDatabaseConnection() {
11955     System.out.println("Checking database with query: " + this.giDbUtils.checkConnectionQuery());
11956     Exception exception = this.giDbUtils.checkConnection();
11957     if (exception == null) {
11958       System.out.println("Successfully tested database connection");
11959     } else {
11960       if (GrouperInstallerUtils.getFullStackTrace(exception).contains(ClassNotFoundException.class.getName())) {
11961         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");
11962       } else {
11963         System.out.println("Error: could not connect to the database: ");
11964         exception.printStackTrace();
11965       }
11966     }
11967   }
11968 
11969   /**
11970    * get hsql port
11971    * @return port
11972    */
11973   private int hsqlPort() {
11974     //get right port
11975     int port = 9001;
11976     
11977     //match this, get the port: jdbc:hsqldb:hsql://localhost:9001/grouper
11978     Pattern pattern = Pattern.compile("jdbc:hsqldb:.*:(\\d+)/.*");
11979     Matcher matcher = pattern.matcher(this.dbUrl);
11980     if (matcher.matches()) {
11981       port = GrouperInstallerUtils.intValue(matcher.group(1));
11982     }
11983     return port;
11984   }
11985   
11986   /**
11987    * @param prompt
11988    */
11989   private void startHsqlDb(boolean prompt) {
11990     boolean startdb = true;
11991     
11992     if (prompt) {
11993       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]: ");
11994       startdb = readFromStdInBoolean(true, "grouperInstaller.autorun.startHsqlDatabase");
11995     }
11996     if (startdb) {
11997       
11998       //get right port
11999       int port = hsqlPort();
12000 
12001       if (!GrouperInstallerUtils.portAvailable(port, this.defaultIpAddress)) {
12002         shutdownHsql();
12003       }
12004 
12005       if (!GrouperInstallerUtils.portAvailable(port, this.defaultIpAddress)) {
12006         System.out.println("This port does not seem available, even after trying to stop the DB! " + port + "...");
12007         if (!shouldContinue("grouperInstaller.autorun.continueAfterPortNotAvailable")) {
12008           throw new RuntimeException("This port is not available, even after trying to stop the DB! " + port);
12009         }
12010       }
12011       
12012       final List<String> command = new ArrayList<String>();
12013 
12014       command.add(GrouperInstallerUtils.javaCommand());
12015       command.add("-cp");
12016       command.add(this.untarredApiDir + File.separator + "lib" + File.separator + "jdbcSamples" + File.separator 
12017           + "*");
12018       //-cp lib\jdbcSamples\hsqldb.jar org.hsqldb.Server -database.0 file:grouper -dbname.0 grouper -port 9001
12019       command.addAll(GrouperInstallerUtils.splitTrimToList("org.hsqldb.Server -database.0 file:" 
12020           + this.untarredApiDir + File.separator + "grouper -dbname.0 grouper -port " + port , " "));
12021       
12022       if (!GrouperInstallerUtils.isWindows()) {
12023 
12024         //let this database run forever
12025         command.add(0, "nohup");
12026         //run in new process
12027         command.add("> /dev/null 2>&1 &");
12028       
12029       }
12030       
12031 //        System.out.println("Starting DB with command: java -cp grouper.apiBinary-" + this.version + File.separator 
12032 //            + "lib" + File.separator + "jdbcSamples" + File.separator 
12033 //            + "hsqldb.jar org.hsqldb.Server -database.0 file:grouper -dbname.0 grouper");
12034 
12035       System.out.println("Starting DB with command: " + GrouperInstallerUtils.join(command.iterator(), " "));
12036 
12037       //start in new thread
12038       Thread thread = new Thread(new Runnable() {
12039         
12040         @Override
12041         public void run() {
12042           GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(command, String.class),
12043               true, true, null, null, 
12044               GrouperInstaller.this.grouperInstallDirectoryString + "hsqlDb", false, false);
12045         }
12046       });
12047       thread.setDaemon(true);
12048       thread.start();
12049       
12050     }
12051     
12052     //lets sleep for a bit to let it start
12053     GrouperInstallerUtils.sleep(2000);
12054     
12055   }
12056 
12057   /** gi db utils */
12058   private GiDbUtils giDbUtils = null;
12059   
12060   /**
12061    * 
12062    */
12063   private void shutdownHsql() {
12064     
12065     try {
12066       this.giDbUtils.executeUpdate("SHUTDOWN", null, false);
12067       System.out.println("Shutting down HSQL before starting it by sending the SQL: SHUTDOWN");
12068     } catch (Exception e) {
12069       //e.printStackTrace();
12070       System.out.println("HSQL was not detected to be running (did not successfully stop it)");
12071     }
12072   }
12073   
12074   
12075   /**
12076    * 
12077    */
12078   private void configureTomeeGrouperWsScimWebapp() {
12079     
12080     File serverXmlFile = new File(this.untarredTomeeDir.getAbsolutePath() 
12081         + File.separator + "conf" + File.separator + "server.xml");
12082     
12083     //C:\mchyzer\grouper\trunk\grouper-installer\grouper.ui-2.0.2\dist\grouper
12084     //
12085     //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
12086     //Server
12087     //Service
12088     //Engine
12089     //Host
12090 
12091     System.out.print("Enter the URL path for the Grouper WS Scim [grouper-ws-scim]: ");
12092     this.tomeeWsScimPath = readFromStdIn("grouperInstaller.autorun.urlPathForGropuerWsScim");
12093     
12094     if (GrouperInstallerUtils.isBlank(this.tomeeWsScimPath)) {
12095       this.tomeeWsScimPath = "grouper-ws-scim";
12096     }
12097 
12098     if (this.tomeeWsScimPath.endsWith("/") || this.tomeeWsScimPath.endsWith("\\")) {
12099       this.tomeeWsScimPath = this.tomeeWsScimPath.substring(0, this.tomeeWsScimPath.length()-1);
12100     }
12101     if (this.tomeeWsScimPath.startsWith("/") || this.tomeeWsScimPath.startsWith("\\")) {
12102       this.tomeeWsScimPath = this.tomeeWsScimPath.substring(1, this.tomeeWsScimPath.length());
12103     }
12104     
12105     String currentDocBase = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
12106         "Server/Service/Engine/Host/Context[@path='/" + this.tomeeWsScimPath + "']", "docBase");
12107 
12108     String shouldBeDocBase = this.untarredWsDir.getAbsolutePath() + File.separator + "grouper-ws-scim" + File.separator + "targetBuiltin" + File.separator + "grouper-ws-scim";
12109 
12110     System.out.println("Editing tomee config file: " + serverXmlFile.getAbsolutePath());
12111     
12112     if (GrouperInstallerUtils.isBlank(currentDocBase)) {
12113 
12114       //need to add it
12115       //<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">
12116       //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
12117       addToXmlFile(serverXmlFile, ">", new String[]{"<Host "}, 
12118           "<Context docBase=\"" + shouldBeDocBase + "\" path=\"/" + this.tomeeWsScimPath + "\" reloadable=\"false\"/>", "tomee context for Grouper WS Scim");
12119 
12120     } else {
12121 
12122       if (!GrouperInstallerUtils.equals(currentDocBase, shouldBeDocBase)) {
12123         
12124         //lets edit the file
12125         //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
12126         editFile(serverXmlFile, "docBase=\"([^\"]+)\"", new String[]{"<Context", "path=\"/" + this.tomeeWsScimPath + "\""}, 
12127             null, shouldBeDocBase, "tomee context for Grouper WS Scim");
12128 
12129       } else {
12130         
12131         System.out.println("  - Context is already set for Grouper WS Scim");
12132         
12133       }
12134       
12135       
12136     }
12137     
12138     currentDocBase = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
12139         "Server/Service/Engine/Host/Context[@path='/" + this.tomeeWsScimPath + "']", "docBase");
12140     
12141     if (!GrouperInstallerUtils.equals(currentDocBase, shouldBeDocBase)) {
12142       System.out.println("Tried to edit server.xml but it didnt work, should have context of: '" 
12143           + shouldBeDocBase + "', but was: '" + currentDocBase + "'");
12144     }
12145     
12146     File[] allFiles = new File(this.untarredApiDir + File.separator + "conf").listFiles(new FilenameFilter() {
12147       
12148       @Override
12149       public boolean accept(File file, String name) {
12150         return name.endsWith(".properties") || name.endsWith(".xml") || name.endsWith(".txt");
12151       }
12152     });
12153     
12154     
12155     for (File fileToCopyFrom : allFiles) {
12156       if (fileToCopyFrom.isFile()) {
12157         File destFile = new File(shouldBeDocBase + File.separator + "WEB-INF" + File.separator + "classes" + File.separator + fileToCopyFrom.getName());
12158         if (!destFile.exists()) {
12159           GrouperInstallerUtils.fileCreate(destFile);
12160         }
12161         GrouperInstallerUtils.copyFile(fileToCopyFrom, destFile, false);
12162       }
12163     }
12164     
12165   }
12166   
12167 
12168   /**
12169    * 
12170    */
12171   private void configureTomcatUiWebapp() {
12172     
12173     File serverXmlFile = new File(this.untarredTomcatDir.getAbsolutePath() 
12174         + File.separator + "conf" + File.separator + "server.xml");
12175     
12176     //C:\mchyzer\grouper\trunk\grouper-installer\grouper.ui-2.0.2\dist\grouper
12177     //
12178     //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
12179     //Server
12180     //Service
12181     //Engine
12182     //Host
12183 
12184     System.out.print("Enter the URL path for the UI [grouper]: ");
12185     this.tomcatUiPath = readFromStdIn("grouperInstaller.autorun.urlPathForUi");
12186     
12187     if (GrouperInstallerUtils.isBlank(this.tomcatUiPath)) {
12188       this.tomcatUiPath = "grouper";
12189     }
12190 
12191     if (this.tomcatUiPath.endsWith("/") || this.tomcatUiPath.endsWith("\\")) {
12192       this.tomcatUiPath = this.tomcatUiPath.substring(0, this.tomcatUiPath.length()-1);
12193     }
12194     if (this.tomcatUiPath.startsWith("/") || this.tomcatUiPath.startsWith("\\")) {
12195       this.tomcatUiPath = this.tomcatUiPath.substring(1, this.tomcatUiPath.length());
12196     }
12197     
12198     String currentDocBase = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
12199         "Server/Service/Engine/Host/Context[@path='/" + this.tomcatUiPath + "']", "docBase");
12200 
12201     String shouldBeDocBase = grouperUiBuildToDirName();
12202 
12203     System.out.println("Editing tomcat config file: " + serverXmlFile.getAbsolutePath());
12204     
12205     if (GrouperInstallerUtils.isBlank(currentDocBase)) {
12206 
12207       //need to add it
12208       //<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">
12209       //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
12210       addToXmlFile(serverXmlFile, ">", new String[]{"<Host "}, 
12211           "<Context docBase=\"" + shouldBeDocBase + "\" path=\"/" + this.tomcatUiPath + "\" reloadable=\"false\"/>", "tomcat context for UI");
12212 
12213     } else {
12214 
12215       if (!GrouperInstallerUtils.equals(currentDocBase, shouldBeDocBase)) {
12216         
12217         //lets edit the file
12218         //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
12219         editFile(serverXmlFile, "docBase=\"([^\"]+)\"", new String[]{"<Context", "path=\"/" + this.tomcatUiPath + "\""}, 
12220             null, shouldBeDocBase, "tomcat context for UI");
12221 
12222       } else {
12223         
12224         System.out.println("  - Context is already set for Grouper UI");
12225         
12226       }
12227       
12228       
12229     }
12230     
12231     currentDocBase = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
12232         "Server/Service/Engine/Host/Context[@path='/" + this.tomcatUiPath + "']", "docBase");
12233     
12234     if (!GrouperInstallerUtils.equals(currentDocBase, shouldBeDocBase)) {
12235       System.out.println("Tried to edit server.xml but it didnt work, should have context of: '" 
12236           + shouldBeDocBase + "', but was: '" + currentDocBase + "'");
12237     }
12238     
12239   }
12240 
12241   /**
12242    * 
12243    * @return grouper ui build to name
12244    */
12245   private String grouperUiBuildToDirName() {
12246     return this.untarredUiDir.getAbsolutePath() + File.separator + "dist" + File.separator + "grouper";
12247   }
12248 
12249   /**
12250    * @param isInstallNotUpgrade
12251    */
12252   private void buildWs(boolean isInstallNotUpgrade) {
12253     
12254     File grouperWsBuildToDir = new File(this.grouperWsBuildToDirName());
12255     
12256     if (grouperWsBuildToDir.exists()) {
12257 
12258       boolean rebuildWs = true;
12259       
12260       boolean defaultRebuild = GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.ws.rebuildIfBuilt", true, false);
12261       System.out.print("The Grouper WS has been built in the past, do you want it rebuilt? (t|f) [" 
12262           + (defaultRebuild ? "t" : "f") + "]: ");
12263       rebuildWs = readFromStdInBoolean(defaultRebuild, "grouperInstaller.autorun.rebuildWsIfBuiltAlready");
12264   
12265       if (!rebuildWs) {
12266         return;
12267       }
12268     }
12269     
12270     if (isInstallNotUpgrade) {
12271       //stop tomcat
12272       try {
12273         tomcatBounce("stop");
12274       } catch (Throwable e) {
12275         System.out.println("Couldnt stop tomcat, ignoring...");
12276       }
12277     }
12278     
12279     List<String> commands = new ArrayList<String>();
12280     
12281     addAntCommands(commands);
12282     commands.add("dist");
12283     
12284     System.out.println("\n##################################");
12285     System.out.println("Building WS with command:\n" + this.untarredWsDir.getAbsolutePath() + File.separator + "grouper-ws" + "> " 
12286         + convertCommandsIntoCommand(commands) + "\n");
12287     
12288     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
12289         true, true, null, new File(this.untarredWsDir.getAbsolutePath() + File.separator + "grouper-ws"), null, true);
12290     
12291     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
12292       System.out.println("stderr: " + commandResult.getErrorText());
12293     }
12294     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
12295       System.out.println("stdout: " + commandResult.getOutputText());
12296     }
12297 
12298     if (isInstallNotUpgrade) {
12299       System.out.print("Do you want to set the log dir of WS (t|f)? [t]: ");
12300       boolean setLogDir = readFromStdInBoolean(true, "grouperInstaller.autorun.setWsLogDir");
12301       
12302       if (setLogDir) {
12303         
12304         ////set the log dir
12305         //C:\apps\grouperInstallerTest\grouper.ws-2.0.2\grouper-ws\build\dist\grouper-ws\WEB-INF\classes\log4j.properties
12306         //
12307         //${grouper.home}logs
12308   
12309         String defaultLogDir = this.untarredTomcatDir.getAbsolutePath() + File.separator + "logs" + File.separator + "grouperWs";
12310         System.out.print("Enter the WS log dir: [" + defaultLogDir + "]: ");
12311         String logDir = readFromStdIn("grouperInstaller.autorun.wsLogDir");
12312         logDir = GrouperInstallerUtils.defaultIfBlank(logDir, defaultLogDir);
12313         
12314         //lets replace \\ with /
12315         logDir = GrouperInstallerUtils.replace(logDir, "\\\\", "/");
12316         //lets replace \ with /
12317         logDir = GrouperInstallerUtils.replace(logDir, "\\", "/");
12318   
12319         File log4jFile = new File(grouperWsBuildToDirName() + File.separator + "WEB-INF" + File.separator + "classes"
12320             + File.separator + "log4j.properties");
12321         
12322         System.out.println("Editing file: " + log4jFile.getAbsolutePath());
12323         
12324         editFile(log4jFile, "log4j\\.\\S+\\.File\\s*=\\s*([^\\s]+logs)/grouper_[^\\s]+\\.log", null, 
12325             null, logDir, "WS log directory");
12326         
12327         File logDirFile = new File(defaultLogDir);
12328         if (!logDirFile.exists()) {
12329           System.out.println("Creating log directory: " + logDirFile.getAbsolutePath());
12330           GrouperInstallerUtils.mkdirs(logDirFile);
12331         }
12332         //test log dir
12333         File testLogDirFile = new File(logDirFile.getAbsolutePath() + File.separator + "testFile" + GrouperInstallerUtils.uniqueId() + ".txt");
12334         GrouperInstallerUtils.saveStringIntoFile(testLogDirFile, "test");
12335         if (!testLogDirFile.delete()) {
12336           throw new RuntimeException("Cant delete file: " +  testLogDirFile.getAbsolutePath());
12337         }
12338         System.out.println("Created and deleted a test file successfully in dir: " + logDirFile.getAbsolutePath());
12339       }
12340     }
12341     
12342     System.out.println("\nEnd building Ws");
12343     System.out.println("##################################\n");
12344   
12345     
12346   }
12347 
12348   /**
12349    * 
12350    */
12351   private void configureTomcatWsWebapp() {
12352     
12353     File serverXmlFile = new File(this.untarredTomcatDir.getAbsolutePath() 
12354         + File.separator + "conf" + File.separator + "server.xml");
12355     
12356     //C:\mchyzer\grouper\trunk\grouper-installer\grouper.ui-2.0.2\dist\grouper
12357     //
12358     //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
12359     //Server
12360     //Service
12361     //Engine
12362     //Host
12363   
12364     System.out.print("Enter the URL path for the WS [grouper-ws]: ");
12365     this.tomcatWsPath = readFromStdIn("grouperInstaller.autorun.wsUrlPath");
12366     
12367     if (GrouperInstallerUtils.isBlank(this.tomcatWsPath)) {
12368       this.tomcatWsPath = "grouper-ws";
12369     }
12370   
12371     if (this.tomcatWsPath.endsWith("/") || this.tomcatWsPath.endsWith("\\")) {
12372       this.tomcatWsPath = this.tomcatWsPath.substring(0, this.tomcatWsPath.length()-1);
12373     }
12374     if (this.tomcatWsPath.startsWith("/") || this.tomcatWsPath.startsWith("\\")) {
12375       this.tomcatWsPath = this.tomcatWsPath.substring(1);
12376     }
12377     
12378     String currentDocBase = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
12379         "Server/Service/Engine/Host/Context[@path='/" + this.tomcatWsPath + "']", "docBase");
12380   
12381     //grouper.ws-2.0.2\grouper-ws\build\dist\grouper-ws
12382     
12383     String shouldBeDocBase = grouperWsBuildToDirName();
12384   
12385     System.out.println("Editing tomcat config file: " + serverXmlFile.getAbsolutePath());
12386     
12387     if (GrouperInstallerUtils.isBlank(currentDocBase)) {
12388   
12389       //need to add it
12390       //<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">
12391       //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
12392       addToXmlFile(serverXmlFile, ">", new String[]{"<Host "}, 
12393           "<Context docBase=\"" + shouldBeDocBase + "\" path=\"/" + this.tomcatWsPath + "\" reloadable=\"false\"/>", "tomcat context for WS");
12394   
12395     } else {
12396   
12397       if (!GrouperInstallerUtils.equals(currentDocBase, shouldBeDocBase)) {
12398         
12399         //lets edit the file
12400         //<Context docBase="C:\mchyzer\grouper\trunk\grouper-ws_trunk\webapp" path="/grouper-ws" reloadable="false"/>
12401         editFile(serverXmlFile, "docBase=\"([^\"]+)\"", new String[]{"<Context", "path=\"/" + this.tomcatWsPath + "\""}, 
12402             null, shouldBeDocBase, "tomcat context for WS");
12403   
12404       } else {
12405         
12406         System.out.println("  - Context is already set for Grouper WS");
12407         
12408       }
12409       
12410       
12411     }
12412     
12413     currentDocBase = GrouperInstallerUtils.xpathEvaluateAttribute(serverXmlFile, 
12414         "Server/Service/Engine/Host/Context[@path='/" + this.tomcatWsPath + "']", "docBase");
12415     
12416     if (!GrouperInstallerUtils.equals(currentDocBase, shouldBeDocBase)) {
12417       System.out.println("Tried to edit server.xml but it didnt work, should have context of: '" 
12418           + shouldBeDocBase + "', but was: '" + currentDocBase + "'");
12419     }
12420     
12421   }
12422 
12423   /**
12424    * @return grouper ws build to dir name
12425    */
12426   private String grouperWsBuildToDirName() {
12427     return this.untarredWsDir.getAbsolutePath() + File.separator + "grouper-ws" + File.separator 
12428       + "build" + File.separator + "dist" + File.separator + "grouper-ws";
12429   }
12430   
12431   /**
12432      * 
12433      */
12434     private void configureClient() {
12435       //properties file
12436       File localGrouperClientPropertiesFile = new File(this.untarredClientDir.getAbsolutePath() + File.separator 
12437           + "grouper.client.properties");
12438       
12439       //set the grouper property
12440       System.out.println("Editing " + localGrouperClientPropertiesFile.getAbsolutePath() + ": ");
12441       editPropertiesFile(localGrouperClientPropertiesFile, "grouperClient.webService.url", "http://localhost:" 
12442           + this.tomcatHttpPort + "/" + this.tomcatWsPath + "/servicesRest", false);
12443       editPropertiesFile(localGrouperClientPropertiesFile, "grouperClient.webService.login", "GrouperSystem", false);
12444       editPropertiesFile(localGrouperClientPropertiesFile, "grouperClient.webService.password", this.grouperSystemPassword, false);
12445       
12446       
12447 //      grouperClient.webService.url = http://localhost:8200/grouper-ws/servicesRest
12448 //
12449 //      # kerberos principal used to connect to web service
12450 //      grouperClient.webService.login = GrouperSystem
12451 //
12452 //      # password for shared secret authentication to web service
12453 //      # or you can put a filename with an encrypted password
12454 //      grouperClient.webService.password = myNewPass
12455 
12456       
12457     }
12458 
12459   /**
12460    * 
12461    * @return the file of the directory of WS
12462    */
12463   private File downloadClient() {
12464     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
12465     
12466     if (!urlToDownload.endsWith("/")) {
12467       urlToDownload += "/";
12468     }
12469     urlToDownload += "release/";
12470   
12471     String clientFileName = "grouper.clientBinary-" + this.version + ".tar.gz";
12472     urlToDownload += this.version + "/" + clientFileName;
12473   
12474     File clientFile = new File(this.grouperTarballDirectoryString + clientFileName);
12475     
12476     downloadFile(urlToDownload, clientFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalClientDownloadTarEtc");
12477 
12478     return clientFile;
12479   }
12480 
12481   /**
12482    * 
12483    */
12484   private void addGrouperSystemWsGroup() {
12485 
12486     //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);"
12487 
12488     //running with command on command line doenst work on linux since the args with whitespace translate to 
12489     //save the commands to a file, and runt he file
12490     StringBuilder gshCommands = new StringBuilder();
12491     gshCommands.append("grouperSession = GrouperSession.startRootSession();\n");
12492     gshCommands.append("wsGroup = new GroupSave(grouperSession).assignName(\"etc:webServiceClientUsers\").assignCreateParentStemsIfNotExist(true).save();\n");
12493     gshCommands.append("wsGroup.addMember(SubjectFinder.findRootSubject(), false);\n");
12494     
12495     File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "gshAddGrouperSystemWsGroup.gsh");
12496     GrouperInstallerUtils.saveStringIntoFile(gshFile, gshCommands.toString());
12497     
12498     List<String> commands = new ArrayList<String>();
12499 
12500     addGshCommands(commands);
12501     commands.add(gshFile.getAbsolutePath());
12502 
12503     System.out.println("\n##################################");
12504     System.out.println("Adding user GrouperSystem to grouper-ws users group with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
12505 
12506     CommandResult commandResult = GrouperInstallerUtils.execCommand(
12507         GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
12508         this.untarredApiDir, null, true);
12509 
12510     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
12511       System.out.println("stderr: " + commandResult.getErrorText());
12512     }
12513     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
12514       System.out.println("stdout: " + commandResult.getOutputText());
12515     }
12516 
12517 
12518   }
12519 
12520   /**
12521    * 
12522    */
12523   private void runChangeLogTempToChangeLog() {
12524 
12525     boolean defaultBoolean = GrouperInstallerUtils.propertiesValueBoolean("grouperInstaller.default.api.runChangeLogToChangeLogTemp", true, false);
12526     System.out.print("Is it ok to run a script that copies change log temp records to the change log (recommended) (t|f)? [" 
12527         + (defaultBoolean ? "t" : "f") + "]: ");
12528     boolean runScript = readFromStdInBoolean(defaultBoolean, "grouperInstaller.autorun.runChangeLogTempToChangeLog");
12529 
12530     
12531     if (!runScript) {
12532       return;
12533     }
12534     
12535     //running with command on command line doenst work on linux since the args with whitespace translate to 
12536     //save the commands to a file, and runt he file
12537     StringBuilder gshCommands = new StringBuilder();
12538     gshCommands.append("grouperSession = GrouperSession.startRootSession();\n");
12539     gshCommands.append("loaderRunOneJob(\"CHANGE_LOG_changeLogTempToChangeLog\");\n");
12540     
12541     File gshFile = new File(this.untarredApiDir.getAbsolutePath() + File.separator + "gshChangeLogTempToChangeLog.gsh");
12542     GrouperInstallerUtils.saveStringIntoFile(gshFile, gshCommands.toString());
12543     
12544     List<String> commands = new ArrayList<String>();
12545 
12546     addGshCommands(commands);
12547     commands.add(gshFile.getAbsolutePath());
12548 
12549     System.out.println("\n##################################");
12550     System.out.println("Copying records from change log temp to change log with command:\n  " + convertCommandsIntoCommand(commands) + "\n");
12551 
12552     CommandResult commandResult = GrouperInstallerUtils.execCommand(
12553         GrouperInstallerUtils.toArray(commands, String.class), true, true, null, 
12554        new File(this.gshCommand()).getParentFile(), null, true);
12555 
12556     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
12557       System.out.println("stderr: " + commandResult.getErrorText());
12558     }
12559     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
12560       System.out.println("stdout: " + commandResult.getOutputText());
12561     }
12562 
12563 
12564   }
12565 
12566   /**
12567    * 
12568    */
12569   private void runClientCommand() {
12570     System.out.println("##################################");
12571     System.out.println("Running client command:");
12572     System.out.println(this.untarredClientDir.getAbsolutePath() + "> " + GrouperInstallerUtils.javaCommand() 
12573         + " -jar grouperClient.jar --operation=getMembersWs --groupNames=etc:webServiceClientUsers");
12574     
12575     try {
12576       final List<String> command = new ArrayList<String>();
12577   
12578       command.add(GrouperInstallerUtils.javaCommand());
12579       command.add("-jar");
12580       command.addAll(GrouperInstallerUtils.splitTrimToList(
12581           "grouperClient.jar --operation=getMembersWs --groupNames=etc:webServiceClientUsers", " "));
12582               
12583       CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(command, String.class), 
12584           true, true, null, this.untarredClientDir, null, true);
12585   
12586       if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
12587         System.out.println("stderr: " + commandResult.getErrorText());
12588       }
12589       if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
12590         System.out.println("stdout: " + commandResult.getOutputText());
12591       }
12592       System.out.println("Success running client command:");
12593     } catch (Exception e) {
12594       System.out.println("Exception running Grouper client");
12595       e.printStackTrace();
12596       System.out.print("Press <enter> to continue: ");
12597       readFromStdIn("grouperInstaller.autorun.grouperClientErrorContinue");
12598     }
12599     System.out.println("##################################");
12600 
12601   }
12602 
12603   /**
12604    * edit a property in a property file
12605    * @param file
12606    * @param valueRegex 
12607    * @param lineMustHaveRegexes 
12608    * @param lineCantHaveRegexes 
12609    * @param newValue 
12610    * @param description of change for sys out print
12611    * @return true if edited file, or false if not but didnt need to, null if not found
12612    */
12613   public static Boolean editFile(File file, String valueRegex, String[] lineMustHaveRegexes, 
12614       String[] lineCantHaveRegexes, String newValue, String description) {
12615     return editFile(file, valueRegex, lineMustHaveRegexes, lineCantHaveRegexes, newValue, description, false, null);
12616   }
12617 
12618   /**
12619    * edit a property in a property file
12620    * @param file
12621    * @param valueRegex 
12622    * @param lineMustHaveRegexes 
12623    * @param lineCantHaveRegexes 
12624    * @param newValue 
12625    * @param description of change for sys out print
12626    * @param addAttributeIfNotExists if attribute isnt there, then if true, then add the attribute
12627    * @param newAttributeName if adding new attribute, this is the name
12628    * @return true if edited file, or false if not but didnt need to, null if not found
12629    */
12630   public static Boolean editFile(File file, String valueRegex, String[] lineMustHaveRegexes, 
12631       String[] lineCantHaveRegexes, String newValue, String description, boolean addAttributeIfNotExists, String newAttributeName) {
12632     
12633     if (!GrouperInstallerUtils.isBlank(newAttributeName) != addAttributeIfNotExists) {
12634       throw new RuntimeException("newAttributeName cant be null if addAttributeIfNotExists, and must be null if not addAttributeIfNotExists");
12635     }
12636     
12637     if (!file.exists() || file.length() == 0) {
12638       throw new RuntimeException("Why does " + file.getName() + " not exist and have contents? " 
12639           + file.getAbsolutePath());
12640     }
12641     
12642     String fileContents = GrouperInstallerUtils.readFileIntoString(file);
12643     
12644     String newline = GrouperInstallerUtils.newlineFromFile(fileContents);
12645     
12646     String[] lines = GrouperInstallerUtils.splitLines(fileContents);
12647     
12648     Pattern pattern = Pattern.compile(valueRegex);
12649     
12650     Pattern[] lineMustHavePatterns = new Pattern[GrouperInstallerUtils.length(lineMustHaveRegexes)];
12651     
12652     {
12653       int index = 0;
12654       for (String lineMustHaveRegex : GrouperInstallerUtils.nonNull(lineMustHaveRegexes, String.class)) {
12655         Pattern lineMustHavePattern = Pattern.compile(lineMustHaveRegex);
12656         lineMustHavePatterns[index] = lineMustHavePattern;
12657         
12658         index++;
12659       }
12660     }    
12661   
12662     Pattern[] lineCantHavePatterns = new Pattern[GrouperInstallerUtils.length(lineCantHaveRegexes)];
12663     
12664     {
12665       int index = 0;
12666       for (String lineCantHaveRegex : GrouperInstallerUtils.nonNull(lineCantHaveRegexes, String.class)) {
12667         Pattern lineCantHavePattern = Pattern.compile(lineCantHaveRegex);
12668         lineCantHavePatterns[index] = lineCantHavePattern;
12669         
12670         index++;
12671       }
12672     }    
12673   
12674     StringBuilder newfile = new StringBuilder();
12675     
12676     boolean madeChange = false;
12677     boolean noChangeNeeded = false;
12678     
12679     OUTER: for (String line : lines) {
12680       line = GrouperInstallerUtils.defaultString(line);
12681       
12682       //lets see if it satisfies all
12683       for (Pattern lineMustHavePattern : lineMustHavePatterns) {
12684         if (!lineMustHavePattern.matcher(line).find()) {
12685           newfile.append(line).append(newline);
12686           continue OUTER;
12687         }
12688       }
12689       
12690       //lets see if it doesnt have these
12691       for (Pattern lineCantHavePattern : lineCantHavePatterns) {
12692         if (lineCantHavePattern.matcher(line).find()) {
12693           newfile.append(line).append(newline);
12694           continue OUTER;
12695         }
12696       }
12697       
12698       //see if satisfies current
12699       Matcher matcher = pattern.matcher(line);
12700       if (!matcher.find()) {
12701         
12702         if (addAttributeIfNotExists) {
12703           
12704           System.out.println(" - adding " + description + " with value: '" + newValue + "'");
12705           
12706           line = GrouperInstallerUtils.trimEnd(line);
12707           
12708           boolean endsWithCloseTag = false;
12709           boolean endElement = false;
12710           
12711           if (line.endsWith("/>")) {
12712             line = line.substring(0, line.length()-2);
12713             line = GrouperInstallerUtils.trimEnd(line);
12714             endsWithCloseTag = true;
12715           } else if (line.endsWith(">")) {
12716             line = line.substring(0, line.length()-1);
12717             line = GrouperInstallerUtils.trimEnd(line);
12718             endElement = true;
12719           }
12720           
12721           newfile.append(line).append(" ").append(newAttributeName).append("=\"").append(newValue).append("\"");
12722           
12723           if (endsWithCloseTag) {
12724             newfile.append(" />");
12725           } else if (endElement) {
12726             newfile.append(" >");
12727           }
12728           
12729           newfile.append(newline);
12730           madeChange = true;
12731           
12732         } else {
12733         
12734           newfile.append(line).append(newline);
12735         }
12736         continue;
12737       }
12738       
12739       String oldValue = matcher.group(1);
12740       if (GrouperInstallerUtils.equals(newValue, oldValue)) {
12741         System.out.println(" - old " + description + " value is same as new value: " + newValue);
12742         noChangeNeeded = true;
12743         newfile.append(line).append(newline);
12744         continue;
12745       }
12746       
12747       //we need to change the value
12748       System.out.println(" - changing " + description + " from: '" + oldValue + "' to: '" + newValue + "'");
12749       newfile.append(line.substring(0, matcher.start(1)));
12750       newfile.append(newValue);
12751       newfile.append(line.substring(matcher.end(1), line.length()));
12752       newfile.append(newline);
12753       madeChange = true;
12754       continue;
12755     }
12756     
12757     if (!madeChange) {
12758       //true if edited file, or false if not but didnt need to, null if not found
12759       if (noChangeNeeded) {
12760         return false;
12761       }
12762       return null;
12763     }
12764     
12765     GrouperInstallerUtils.writeStringToFile(file, newfile.toString());
12766     
12767     return true;
12768   }
12769 
12770   /**
12771    * add a line to a file.  will replace \n with whatever newline is
12772    * @param file
12773    * @param line (not ending in newline)
12774    * @param lineNumber 1 indexed.  If not exist, add to end of file
12775    * @param description is a description of what was just changed
12776    */
12777   public static void addToFile(File file, String line, int lineNumber, String description) {
12778     if (!file.exists() || file.length() == 0) {
12779       throw new RuntimeException("Why does " + file.getName() + " not exist and have contents? " 
12780           + file.getAbsolutePath());
12781     }
12782     
12783     String fileContents = GrouperInstallerUtils.readFileIntoString(file);
12784     
12785     String newline = GrouperInstallerUtils.newlineFromFile(fileContents);
12786     
12787     String[] lines = GrouperInstallerUtils.splitLines(fileContents);
12788 
12789     line = GrouperInstallerUtils.replace(line, "\n", newline);
12790     
12791     line += newline;
12792     
12793     StringBuilder newfile = new StringBuilder();
12794     
12795     boolean madeChange = false;
12796     
12797     int index = 0;
12798     
12799     for (String fileLine : lines) {
12800       fileLine = GrouperInstallerUtils.defaultString(fileLine);
12801       newfile.append(fileLine).append(newline);
12802       index++;
12803       
12804       if (index >= lineNumber  && !madeChange) {
12805 
12806         System.out.println("Adding " + description + " to file at line number: " + lineNumber);        
12807         
12808         newfile.append(line);
12809         madeChange = true;
12810       }
12811     }
12812     
12813     if (!madeChange) {
12814       System.out.println("Appending " + description + " to end of file");        
12815       newfile.append(line);
12816     }
12817     
12818     GrouperInstallerUtils.writeStringToFile(file, newfile.toString());
12819     
12820   }
12821 
12822   /** tomcat ui path */
12823   private String tomcatUiPath = null;
12824 
12825   /** tomcat ws path */
12826   private String tomcatWsPath = null;
12827   
12828   /** tomee grouper ws scim path */
12829   private String tomeeWsScimPath = null;
12830 
12831   /** untarred dir, this does NOT end in file.separator */
12832   private File untarredClientDir;
12833   
12834   /**
12835    * 
12836    */
12837   private static void validJavaVersion() {
12838     CommandResult commandResult = GrouperInstallerUtils.execCommand(
12839         GrouperInstallerUtils.javaCommand(), 
12840         new String[]{"-version"}, true);
12841     String javaResult = commandResult.getOutputText();
12842     if (GrouperInstallerUtils.isBlank(javaResult)) {
12843       javaResult = commandResult.getErrorText();
12844     }
12845     if (!validJava(javaResult)) {
12846       throw new RuntimeException("Expecting Java 6+, but received: " + javaResult + ", run the installer jar with a jdk v6");
12847     }
12848     
12849     //try javac
12850     try {
12851       commandResult = GrouperInstallerUtils.execCommand(
12852           GrouperInstallerUtils.javaCommand() + "c", 
12853           new String[]{"-version"}, true);
12854       javaResult = commandResult.getOutputText();
12855       if (GrouperInstallerUtils.isBlank(javaResult)) {
12856         javaResult = commandResult.getErrorText();
12857       }
12858       if (!validJava(javaResult)) {
12859         throw new RuntimeException("Expecting Java 6+, but received: " + javaResult + ", run the installer jar with a jdk v6");
12860       }
12861     } catch (Exception e) {
12862       throw new RuntimeException("This needs to be run from a jdk, but it is detected to be running from a JRE...  run the installed from a JDK!");
12863     }
12864   }
12865 
12866   /**
12867    * see if install, upgrade, patch, etc
12868    * @return true if install, or false if upgrade
12869    */
12870   private GrouperInstallerMainFunction grouperInstallerMainFunction() {
12871 
12872     GrouperInstallerMainFunction grouperInstallerMainFunctionLocal = 
12873         (GrouperInstallerMainFunction)promptForEnum(
12874             "Do you want to 'install' a new installation of grouper, 'upgrade' an existing installation,\n"
12875                 + "  'patch' an existing installation, 'admin' utilities, or 'createPatch' for Grouper developers\n" 
12876                 + "  (enter: 'install', 'upgrade', 'patch', 'admin', 'createPatch' or blank for the default) ",
12877             "grouperInstaller.autorun.actionEgInstallUpgradePatch", GrouperInstallerMainFunction.class, 
12878             GrouperInstallerMainFunction.install, "grouperInstaller.default.installOrUpgrade");
12879     return grouperInstallerMainFunctionLocal;
12880   }
12881 
12882 
12883   
12884   /**
12885    * 
12886    * @return install directory
12887    */
12888   private static String grouperInstallDirectory() {
12889     String grouperInstallDirectoryString;
12890     {
12891       File grouperInstallDirectoryFile = new File("");
12892       String defaultDirectory = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.installDirectory", false);
12893       System.out.print("Enter in the Grouper install directory (note: better if no spaces or special chars) [" 
12894           + (GrouperInstallerUtils.isBlank(defaultDirectory) ? grouperInstallDirectoryFile.getAbsolutePath() : defaultDirectory) + "]: ");
12895       String input = readFromStdIn("grouperInstaller.autorun.installDirectory");
12896       if (!GrouperInstallerUtils.isBlank(input)) {
12897         grouperInstallDirectoryFile = new File(input);
12898         if (!grouperInstallDirectoryFile.exists() || !grouperInstallDirectoryFile.isDirectory()) {
12899           System.out.println("Error: cant find directory: '" + input + "'");
12900           System.exit(1);
12901         }
12902       } else {
12903         if (!GrouperInstallerUtils.isBlank(defaultDirectory)) {
12904           grouperInstallDirectoryFile = new File(defaultDirectory);
12905           if (!grouperInstallDirectoryFile.exists() || !grouperInstallDirectoryFile.isDirectory()) {
12906             System.out.println("Error: cant find directory: '" + input + "'");
12907             System.exit(1);
12908           }
12909         }
12910       }
12911       grouperInstallDirectoryString = grouperInstallDirectoryFile.getAbsolutePath();
12912       if (!grouperInstallDirectoryString.endsWith(File.separator)) {
12913         grouperInstallDirectoryString += File.separator;
12914       }
12915     }
12916     return grouperInstallDirectoryString;
12917   }
12918 
12919   /**
12920    * 
12921    * @return upgrade directory
12922    */
12923   private String grouperUpgradeTempDirectory() {
12924     String localGrouperInstallDirectoryString = null;
12925     {
12926       File grouperInstallDirectoryFile = new File(new File("").getAbsolutePath() + File.separator + "tarballs");
12927       if (!GrouperInstallerUtils.isBlank(this.grouperInstallDirectoryString)) {
12928         grouperInstallDirectoryFile = new File(this.grouperInstallDirectoryString + "tarballs");
12929       }
12930       String defaultDirectory = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.tarballDirectory", false);
12931       if (GrouperInstallerUtils.isBlank(defaultDirectory)) {
12932         defaultDirectory = grouperInstallDirectoryFile.getAbsolutePath();
12933       }
12934       System.out.print("Enter in a Grouper temp directory to download tarballs (note: better if no spaces or special chars) [" 
12935           + defaultDirectory + "]: ");
12936       localGrouperInstallDirectoryString = readFromStdIn("grouperInstaller.autorun.tarballDirectory");
12937       if (!GrouperInstallerUtils.isBlank(localGrouperInstallDirectoryString)) {
12938         grouperInstallDirectoryFile = new File(localGrouperInstallDirectoryString);
12939         if (!grouperInstallDirectoryFile.exists() || !grouperInstallDirectoryFile.isDirectory()) {
12940           System.out.println("Error: cant find directory: '" + grouperInstallDirectoryFile.getAbsolutePath() + "'");
12941           System.exit(1);
12942         }
12943       } else {
12944         localGrouperInstallDirectoryString = defaultDirectory;
12945       }
12946       if (!localGrouperInstallDirectoryString.endsWith(File.separator)) {
12947         localGrouperInstallDirectoryString += File.separator;
12948       }
12949     }
12950     return localGrouperInstallDirectoryString;
12951   }
12952 
12953   /**
12954    * 
12955    * @return see if operating on a source directory or deployed directory
12956    */
12957   @SuppressWarnings("unused")
12958   private GrouperInstallType sourceOrDeployed() {
12959 
12960     if (this.grouperDirectories.getGrouperInstallType() == null) {
12961              
12962     }
12963     
12964     return this.grouperDirectories.getGrouperInstallType();
12965   }
12966 
12967 
12968   
12969   /**
12970    * 
12971    * @return directory where existing installation exists
12972    */
12973   private String upgradeExistingDirectory() {
12974 
12975     //get the directory where the existing installation is
12976     String tempUpgradeExistingApplicationDirectoryString = this.upgradeExistingApplicationDirectoryString;
12977 
12978     String errorMessage = "Cant find Grouper " + this.appToUpgrade.name() + " properties files or libs, looked in the directory, "
12979         + "/classes/ , /conf/ , /WEB-INF/classes/ , /lib/ , /WEB-INF/lib/ , /lib/grouper/ , /dist/lib/ ";
12980 
12981     try {
12982       String upgradeExistingDirectoryString = null;
12983       for (int i=0;i<10;i++) {
12984         File grouperInstallDirectoryFile = new File("");
12985         String defaultDirectory = GrouperInstallerUtils.propertiesValue("grouperInstaller.default.existingInstalledDirectory", false);
12986         System.out.print("Where is the grouper " + this.appToUpgrade.name() + " installed? " +
12987           (GrouperInstallerUtils.isBlank(defaultDirectory) ? "" : ("[" + defaultDirectory + "]: ")));
12988         upgradeExistingDirectoryString = readFromStdIn("grouperInstaller.autorun.grouperWhereInstalled");
12989         if (!GrouperInstallerUtils.isBlank(upgradeExistingDirectoryString)) {
12990           grouperInstallDirectoryFile = new File(upgradeExistingDirectoryString);
12991           if (!grouperInstallDirectoryFile.exists() || !grouperInstallDirectoryFile.isDirectory()) {
12992             System.out.println("Error: cant find directory: '" + grouperInstallDirectoryFile.getAbsolutePath() + "'");
12993             continue;
12994           }
12995         } else {
12996           upgradeExistingDirectoryString = defaultDirectory;
12997         }
12998         upgradeExistingDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(upgradeExistingDirectoryString);
12999 
13000         this.upgradeExistingApplicationDirectoryString = upgradeExistingDirectoryString;
13001 
13002         //make sure directory is where the app is
13003         if (!this.appToUpgrade.validateExistingDirectory(this)) {
13004           System.out.println(errorMessage);
13005           continue;
13006         }
13007         //find the resources dir
13008         {
13009           File resourcesDirFile = new File(this.upgradeExistingApplicationDirectoryString + "classes" + File.separator);
13010           if (resourcesDirFile.exists()) {
13011             this.upgradeExistingClassesDirectoryString = resourcesDirFile.getAbsolutePath();
13012           } else {
13013             resourcesDirFile = new File(this.upgradeExistingApplicationDirectoryString + "conf" + File.separator);
13014             if (resourcesDirFile.exists()) {
13015               this.upgradeExistingClassesDirectoryString = resourcesDirFile.getAbsolutePath();
13016             } else {
13017               resourcesDirFile = new File(this.upgradeExistingApplicationDirectoryString + "WEB-INF" + File.separator + "classes" + File.separator);
13018               if (resourcesDirFile.exists()) {
13019                 this.upgradeExistingClassesDirectoryString = resourcesDirFile.getAbsolutePath();
13020               } else {
13021                 this.upgradeExistingClassesDirectoryString = this.upgradeExistingApplicationDirectoryString;
13022               }            
13023             }
13024           }
13025           this.upgradeExistingClassesDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.upgradeExistingClassesDirectoryString);
13026         }
13027         
13028         //find the lib dir
13029         {
13030           File libDirFile = new File(this.upgradeExistingApplicationDirectoryString + "lib" + File.separator + "grouper" + File.separator);
13031           if (libDirFile.exists()) {
13032             this.upgradeExistingLibDirectoryString = libDirFile.getAbsolutePath();
13033           } else {
13034             libDirFile = new File(this.upgradeExistingApplicationDirectoryString + "WEB-INF" + File.separator + "lib" + File.separator);
13035             if (libDirFile.exists()) {
13036               this.upgradeExistingLibDirectoryString = libDirFile.getAbsolutePath();
13037             } else {
13038               libDirFile = new File(this.upgradeExistingApplicationDirectoryString + "lib" + File.separator);
13039               if (libDirFile.exists()) {
13040                 this.upgradeExistingLibDirectoryString = libDirFile.getAbsolutePath();
13041               } else {
13042                 this.upgradeExistingLibDirectoryString = this.upgradeExistingApplicationDirectoryString;
13043               }            
13044             }
13045           }
13046           this.upgradeExistingLibDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.upgradeExistingLibDirectoryString);
13047         }
13048 
13049         //find the bin dir
13050         {
13051           File binDirFile = new File(this.upgradeExistingApplicationDirectoryString + "bin" + File.separator);
13052           if (binDirFile.exists()) {
13053             this.upgradeExistingBinDirectoryString = binDirFile.getAbsolutePath();
13054           } else {
13055             binDirFile = new File(this.upgradeExistingApplicationDirectoryString + "WEB-INF" + File.separator + "bin" + File.separator);
13056             if (binDirFile.exists()) {
13057               this.upgradeExistingBinDirectoryString = binDirFile.getAbsolutePath();
13058             } else {
13059               this.upgradeExistingBinDirectoryString = this.upgradeExistingApplicationDirectoryString;
13060             }
13061           }
13062           this.upgradeExistingBinDirectoryString = GrouperInstallerUtils.fileAddLastSlashIfNotExists(this.upgradeExistingBinDirectoryString);
13063         }
13064 
13065 
13066         return upgradeExistingDirectoryString;
13067       }
13068       
13069       throw new RuntimeException(errorMessage);
13070       
13071     } finally {
13072       //set this back
13073       this.upgradeExistingApplicationDirectoryString = tempUpgradeExistingApplicationDirectoryString;
13074     }
13075   }
13076 
13077   /**
13078    * where classes are in the upgrade directory, ends in file separator
13079    */
13080   private String upgradeExistingClassesDirectoryString;
13081   
13082   /**
13083    * where jars are in the upgrade directory, ends in file separator
13084    */
13085   private String upgradeExistingLibDirectoryString;
13086 
13087   /**
13088    * where bin files (gsh) are in the upgrade directory, ends in file separator
13089    */
13090   private String upgradeExistingBinDirectoryString;
13091   /**
13092    * 
13093    * @param action upgrade or patch
13094    * @return what we are upgrading
13095    */
13096   private AppToUpgrade grouperAppToUpgradeOrPatch(String action) {
13097 
13098     AppToUpgrade appToUpgradeLocal = 
13099         (AppToUpgrade)promptForEnum(
13100             "What do you want to " + action + "?  api, ui, ws, pspng, or psp? ",
13101             "grouperInstaller.autorun.appToUpgrade", AppToUpgrade.class, AppToUpgrade.API, "grouperInstaller.default.appToUpgrade");
13102     return appToUpgradeLocal;
13103   }
13104 
13105   /**
13106    * 
13107    * @param javaResult
13108    * @return if valid
13109    */
13110   private static boolean validJava(String javaResult) {
13111     //C:\mchyzer\grouper\trunk\grouper\bin>java -version
13112     //java version "1.6.0_21"
13113     //Java(TM) SE Runtime Environment (build 1.6.0_21-b07)
13114     //Java HotSpot(TM) Client VM (build 17.0-b17, mixed mode, sharing)
13115     Pattern pattern = Pattern.compile("(\\d+)\\.(\\d+)\\.\\d+_?\\d*");
13116     Matcher matcher = pattern.matcher(javaResult);
13117     if (matcher.find()) {
13118       int majorVersion = GrouperInstallerUtils.intValue(matcher.group(1));
13119       int minorVersion = GrouperInstallerUtils.intValue(matcher.group(2));
13120       if (majorVersion == 1 && minorVersion >= 6) {
13121         return true;
13122       }
13123       return majorVersion >= 6;
13124     }
13125     return false;
13126   }
13127   
13128   /**
13129    * add something to an xml file
13130    * @param file
13131    * @param addAfterThisRegex 
13132    * @param mustPassTheseRegexes 
13133    * @param newValue 
13134    * @param description of change for sys out print
13135    */
13136   public static void addToXmlFile(File file, String addAfterThisRegex, String[] mustPassTheseRegexes, String newValue, String description) {
13137     if (!file.exists() || file.length() == 0) {
13138       throw new RuntimeException("Why does " + file.getName() + " not exist and have contents? " 
13139           + file.getAbsolutePath());
13140     }
13141     
13142     String fileContents = GrouperInstallerUtils.readFileIntoString(file);
13143     
13144     String newline = GrouperInstallerUtils.newlineFromFile(fileContents);
13145     
13146     Pattern pattern = Pattern.compile(addAfterThisRegex);
13147 
13148     String[] lines = GrouperInstallerUtils.splitLines(fileContents);
13149     
13150     Pattern[] lineMustPassThesePatterns = new Pattern[GrouperInstallerUtils.length(mustPassTheseRegexes)];
13151     
13152     boolean[] hasPassedTheseRegexes = new boolean[lineMustPassThesePatterns.length];
13153     
13154     for (int i=0;i<hasPassedTheseRegexes.length;i++) {
13155       hasPassedTheseRegexes[i] = false;
13156     }
13157     
13158     {
13159       int index = 0;
13160       for (String lineMustHaveRegex : GrouperInstallerUtils.nonNull(mustPassTheseRegexes, String.class)) {
13161         Pattern lineMustHavePattern = Pattern.compile(lineMustHaveRegex);
13162         lineMustPassThesePatterns[index] = lineMustHavePattern;
13163         
13164         index++;
13165       }
13166     }    
13167 
13168     StringBuilder newfile = new StringBuilder();
13169 
13170     boolean madeChange = false;
13171 
13172     OUTER: for (String line : lines) {
13173       line = GrouperInstallerUtils.defaultString(line);
13174       
13175       //lets see if it satisfies all
13176       for (int i=0;i<lineMustPassThesePatterns.length;i++) {
13177         Pattern lineMustHavePattern = lineMustPassThesePatterns[i];
13178         if (lineMustHavePattern.matcher(line).find()) {
13179           hasPassedTheseRegexes[i] = true;
13180         }
13181       }
13182       
13183       //see if we have passed all the prefixes
13184       for (int i=0;i<hasPassedTheseRegexes.length;i++) {
13185         if (!hasPassedTheseRegexes[i]) {
13186           newfile.append(line).append(newline);
13187           continue OUTER;
13188         }
13189       }
13190       
13191       //see if satisfies current, and only add once
13192       Matcher matcher = pattern.matcher(line);
13193       if (!matcher.find() || madeChange) {
13194         newfile.append(line).append(newline);
13195         continue;
13196       }
13197 
13198       //we need to change the value
13199       System.out.println(" - adding " + description + " line: '" + newValue + "'");
13200       newfile.append(line);
13201       newfile.append(newline);
13202       newfile.append(newValue);
13203       newfile.append(newline);
13204       madeChange = true;
13205     }
13206     if (!madeChange) {
13207       throw new RuntimeException("Couldnt find place to add to server.xml!  Are there newlines that werent there before or something?");
13208     }
13209     
13210     GrouperInstallerUtils.writeStringToFile(file, newfile.toString());
13211     
13212   }
13213 
13214   
13215   /**
13216    * edit a property in a property file
13217    * @param file
13218    * @param propertyName
13219    * @param propertyValue
13220    * @param createFileIfNotExist 
13221    */
13222   public static void editPropertiesFile(File file, String propertyName, String propertyValue, boolean createFileIfNotExist) {
13223     if (!file.exists()) {
13224       if (createFileIfNotExist) {
13225         System.out.println("Creating file: " + (file == null ? null : file.getAbsolutePath()));
13226         GrouperInstallerUtils.fileCreate(file);
13227       } else {
13228         throw new RuntimeException("Why does " + file.getName() + " not exist and have contents? " 
13229             + file.getAbsolutePath());
13230       }
13231     }
13232     
13233     propertyValue = GrouperInstallerUtils.defaultString(propertyValue);
13234     
13235     String fileContents = GrouperInstallerUtils.readFileIntoString(file);
13236     
13237     String newline = GrouperInstallerUtils.newlineFromFile(fileContents);
13238     
13239     //if it starts with it, add a newline to start so the regexes work
13240     if (!fileContents.startsWith(newline)) {
13241       fileContents = newline + fileContents;
13242     }
13243     
13244     //lets look for property in file
13245     //this is a newline or form feed then some optional whitespace, and the property name
13246     //then some optional whitespace then an equals, then optional whitespace, then some text
13247     String propertyNameRegex = GrouperInstallerUtils.replace(propertyName, ".", "\\.");
13248     String regex = "[\\n\\r][ \\t]*" + propertyNameRegex + "[ \\t]*=[ \\t]*([^\\n\\r]*)";
13249     Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
13250     Matcher matcher = pattern.matcher(fileContents);
13251 
13252     if (matcher.find()) {
13253       String previousValue = matcher.group(1);
13254       
13255       if (GrouperInstallerUtils.trimToEmpty(previousValue).equals(GrouperInstallerUtils.trim(propertyValue))) {
13256         System.out.println(" - property " + propertyName + " already was set to: " + propertyValue + ", not changing file");
13257         return;
13258       }
13259       
13260       int startIndex = matcher.start(1);
13261       
13262       int endIndex = matcher.end(1);
13263       
13264       String newContents = fileContents.substring(0, startIndex) + propertyValue;
13265       
13266       //if not the last char
13267       if (endIndex < fileContents.length()-1) {
13268         newContents += fileContents.substring(endIndex, fileContents.length());
13269       }
13270 
13271       //if there is another match, there is a problem
13272       if (matcher.find()) {
13273         throw new RuntimeException("Why are there multiple matches for " + propertyName + " in propertyFile: " + file.getAbsolutePath() + "??????");
13274       }
13275 
13276       System.out.println(" - set property: " 
13277           + propertyName + " from: " + previousValue + " to: " + propertyValue);
13278       GrouperInstallerUtils.writeStringToFile(file, newContents);
13279       return;
13280     }
13281     
13282     //lets see if it is in a comment
13283     //this is a newline or form feed then some optional whitespace, hash, optional whitespace, and the property name
13284     //then some optional whitespace then an equals, then optional whitespace, then some text
13285     regex = ".*[\\n\\r]([ \\t]*#[ \\t]*)" + propertyNameRegex + "[ \\t]*=[ \\t]*([^\\n\\r]*).*";
13286     pattern = Pattern.compile(regex, Pattern.DOTALL);
13287     matcher = pattern.matcher(fileContents);
13288 
13289     if (matcher.matches()) {
13290       String previousValue = matcher.group(2);
13291       
13292       int startIndexHash = matcher.start(1);
13293       
13294       int endIndexHash = matcher.end(1);
13295 
13296       int startIndex = matcher.start(2);
13297       
13298       int endIndex = matcher.end(2);
13299       
13300       String newContents = fileContents.substring(0, startIndexHash) + fileContents.substring(endIndexHash, startIndex)
13301         + propertyValue;
13302       
13303       //if not the last char
13304       if (endIndex < fileContents.length()-1) {
13305         newContents += fileContents.substring(endIndex, fileContents.length());
13306       }
13307       System.out.println(" - uncommented property: " 
13308           + propertyName + " from: " + previousValue + " to: " + propertyValue);
13309       GrouperInstallerUtils.writeStringToFile(file, newContents);
13310       
13311       return;
13312     }
13313     
13314     //it must have not existed
13315     //add a newline..
13316     //add to end in case it was already there, now it will be overwritten
13317     String newContents = fileContents + newline + "# added by grouper-installer" + newline + propertyName + " = " + propertyValue + newline;
13318     GrouperInstallerUtils.writeStringToFile(file, newContents);
13319 
13320     System.out.println(" - added to end of property file: " + propertyName + " = " + propertyValue);
13321     
13322   }
13323 
13324   /**
13325    * untar a file to a dir
13326    * @param fileName
13327    * @param autorunPropertiesKeyIfFileExistsUseLocal key in properties file to automatically fill in a value
13328    * @param dirToUntarTo or null to keep in same dir as tarfile
13329    * @return the directory where the files are (assuming has a single dir the same name as the archive)
13330    */
13331   private File untar(final String fileName, final String autorunPropertiesKeyIfFileExistsUseLocal,
13332       File dirToUntarTo) {
13333 
13334     if (!fileName.endsWith(".tar")) {
13335       throw new RuntimeException("File doesnt end in .tar: " + fileName);
13336     }
13337     String untarredFileName = fileName.substring(0, fileName.length() - ".tar".length());
13338     
13339     //ant has -bin which is annoying
13340     if (untarredFileName.endsWith("-bin")) {
13341       untarredFileName = untarredFileName.substring(0, untarredFileName.length() - "-bin".length());
13342     }
13343 
13344     if (dirToUntarTo == null) {
13345       dirToUntarTo = new File(untarredFileName).getParentFile();
13346     }
13347     
13348     File untarredFile = new File(dirToUntarTo.getAbsoluteFile() + File.separator + new File(untarredFileName).getName());
13349     untarredFileName = untarredFile.getAbsolutePath();
13350     
13351     if (untarredFile.exists()) {
13352       
13353       if (this.useAllUntarredDirectories != null && this.useAllUntarredDirectories == true) {
13354         return untarredFile;
13355       }
13356       
13357       System.out.print("Untarred dir exists: " + untarredFileName + ", use untarred dir (t|f)? [t]: ");
13358       boolean useUnzippedFile = readFromStdInBoolean(true, autorunPropertiesKeyIfFileExistsUseLocal);
13359       if (useUnzippedFile) {
13360         
13361         if (this.useAllUntarredDirectories == null) {
13362           System.out.print("Would you like to use all existing untarred directories (t|f)? [t]: ");
13363           this.useAllUntarredDirectories = readFromStdInBoolean(true, "grouperInstaller.autorun.useAllUntarredDirectories");
13364         }
13365         
13366         return untarredFile;
13367       }
13368       
13369       System.out.println("Deleting: " + untarredFileName);
13370       GrouperInstallerUtils.deleteRecursiveDirectory(untarredFileName);
13371     }
13372     
13373     System.out.println("Expanding: " + fileName + " to " + untarredFile.getAbsolutePath());
13374     
13375     final File[] result = new File[1];
13376     
13377     final File DIR_TO_UNTAR_TO = dirToUntarTo;
13378     
13379     Runnable runnable = new Runnable() {
13380 
13381       public void run() {
13382         result[0] = untarHelper(fileName, autorunPropertiesKeyIfFileExistsUseLocal, DIR_TO_UNTAR_TO);
13383       }
13384     };
13385 
13386     GrouperInstallerUtils.threadRunWithStatusDots(runnable, true);
13387 
13388     return result[0];
13389 
13390   }
13391 
13392   /**
13393    * untar a file to a dir
13394    * @param fileName
13395    * @param autorunPropertiesKeyIfFileExistsUseLocal key in properties file to automatically fill in a value
13396    * @param dirToUntarTo or null to keep in same dir as tarfile
13397    * @return the directory where the files are (assuming has a single dir the same name as the archive)
13398    */
13399   @SuppressWarnings("resource")
13400   private static File untarHelper(String fileName, String autorunPropertiesKeyIfFileExistsUseLocal, File dirToUntarTo) {
13401     TarArchiveInputStream tarArchiveInputStream = null;
13402     
13403     String untarredFileName = fileName.substring(0, fileName.length() - ".tar".length());
13404     
13405     //ant has -bin which is annoying
13406     if (untarredFileName.endsWith("-bin")) {
13407       untarredFileName = untarredFileName.substring(0, untarredFileName.length() - "-bin".length());
13408     }
13409     
13410     if (dirToUntarTo == null) {
13411       dirToUntarTo = new File(untarredFileName).getParentFile();
13412     }
13413 
13414     File untarredFile = new File(dirToUntarTo.getAbsoluteFile() + File.separator + new File(untarredFileName).getName());
13415 
13416     try {
13417 
13418       tarArchiveInputStream = new TarArchiveInputStream(new FileInputStream(fileName));
13419       
13420       while (true) {
13421   
13422         TarArchiveEntry tarArchiveEntry = tarArchiveInputStream.getNextTarEntry();
13423         if (tarArchiveEntry == null) {
13424           break;
13425         }
13426         
13427         //System.out.println("Entry: " + tarArchiveEntry.getName()
13428         //    + ", isDirectory: " + tarArchiveEntry.isDirectory()
13429         //    + ", isFile: " + tarArchiveEntry.isFile());
13430         String fileEntryName = dirToUntarTo.getAbsolutePath() + File.separator + tarArchiveEntry.getName();
13431         File tarEntryFile = new File(fileEntryName);
13432         
13433         if (tarArchiveEntry.isDirectory()) {
13434           if (!tarEntryFile.exists() && !tarEntryFile.mkdirs()) {
13435             throw new RuntimeException("Cant create dirs: " + tarEntryFile.getAbsolutePath());
13436           }
13437           continue;
13438         }
13439         
13440         byte[] content = new byte[(int)tarArchiveEntry.getSize()];
13441 
13442         int size = tarArchiveInputStream.read(content, 0, content.length);
13443         
13444         //for some reason we get an error when 0 bytes...
13445         if (size != content.length && (!(size == -1 && content.length == 0))) {
13446           throw new RuntimeException("Didnt read the right amount of bytes: " + size 
13447               + ", should have been: " + content.length + " on entry: " + tarArchiveEntry.getName());
13448         }
13449         
13450         ByteArrayInputStream byteArrayInputStream = null;
13451         FileOutputStream fileOutputStream = null;
13452         
13453         try {
13454           
13455           //create parent directories
13456           if (!tarEntryFile.getParentFile().exists() && !tarEntryFile.getParentFile().mkdirs()) {
13457             throw new RuntimeException("Cant create dirs: " + tarEntryFile.getParentFile().getAbsolutePath());
13458           }
13459 
13460           fileOutputStream = new FileOutputStream(tarEntryFile);
13461           byteArrayInputStream = new ByteArrayInputStream(content);
13462           GrouperInstallerUtils.copy(byteArrayInputStream, fileOutputStream);
13463           
13464         } catch (Exception e) {
13465           throw new RuntimeException("Problem with entry: " + tarArchiveEntry.getName(), e);
13466         } finally {
13467           GrouperInstallerUtils.closeQuietly(byteArrayInputStream);
13468           GrouperInstallerUtils.closeQuietly(fileOutputStream);
13469         }
13470         
13471       }
13472     } catch (Exception e) {
13473       throw new RuntimeException("Error untarring: " + fileName, e);
13474     } finally {
13475       GrouperInstallerUtils.closeQuietly(tarArchiveInputStream);
13476     }
13477     return untarredFile;
13478   }
13479 
13480   /**
13481    * unzip a file, this is for .zip files
13482    * @param fileName
13483    * @param autorunPropertiesKeyIfFileExistsUseLocal key in properties file to automatically fill in a value
13484    * @return the unzipped file
13485    */
13486   private static File unzipFromZip(final String fileName, final String autorunPropertiesKeyIfFileExistsUseLocal) {
13487 
13488     if (!fileName.endsWith(".zip")) {
13489       throw new RuntimeException("File doesnt end in .zip: " + fileName);
13490     }
13491     String unzippedFileName = fileName.substring(0, fileName.length() - ".zip".length());
13492 
13493     File unzippedDir = new File(unzippedFileName);
13494 
13495     if (unzippedDir.exists()) {
13496       System.out.print("Unzipped dir exists: " + unzippedFileName + ", use unzipped dir (t|f)? [t]: ");
13497       boolean useUnzippedFile = readFromStdInBoolean(true, autorunPropertiesKeyIfFileExistsUseLocal);
13498       if (useUnzippedFile) {
13499         return unzippedDir;
13500       }
13501       System.out.println("Deleting: " + unzippedFileName);
13502       GrouperInstallerUtils.deleteRecursiveDirectory(unzippedFileName);
13503     } else {
13504       if (!unzippedDir.mkdir()) {
13505         throw new RuntimeException("Cant make dir: " + unzippedDir.getAbsolutePath());
13506       }
13507     }
13508 
13509     System.out.println("Unzipping: " + fileName);
13510 
13511     final File[] result = new File[1];
13512     
13513     Runnable runnable = new Runnable() {
13514 
13515       public void run() {
13516         result[0] = unzipFromZipHelper(fileName, autorunPropertiesKeyIfFileExistsUseLocal);
13517       }
13518     };
13519 
13520     GrouperInstallerUtils.threadRunWithStatusDots(runnable, true);
13521 
13522     return result[0];
13523 
13524   }
13525 
13526   /**
13527    * unzip a file, this is for .zip files
13528    * @param fileName
13529    * @param autorunPropertiesKeyIfFileExistsUseLocal key in properties file to automatically fill in a value
13530    * @return the unzipped file
13531    */
13532   private static File unzipFromZipHelper(String fileName, String autorunPropertiesKeyIfFileExistsUseLocal) {
13533 
13534     String unzippedFileName = fileName.substring(0, fileName.length() - ".zip".length());
13535 
13536     File unzippedDir = new File(unzippedFileName);
13537 
13538     ZipFile zipFile = null;
13539     try {
13540       zipFile = new ZipFile(fileName);
13541       Enumeration<? extends ZipEntry> entries = zipFile.entries();
13542       while (entries.hasMoreElements()) {
13543         ZipEntry entry = entries.nextElement();
13544         File entryDestination = new File(unzippedDir, entry.getName());
13545         if (entry.isDirectory()) {
13546           entryDestination.mkdirs();
13547         } else {
13548           entryDestination.getParentFile().mkdirs();
13549           InputStream in = zipFile.getInputStream(entry);
13550           OutputStream out = new FileOutputStream(entryDestination);
13551           try {
13552             IOUtils.copy(in, out);
13553           } finally {
13554             GrouperInstallerUtils.closeQuietly(in);
13555             GrouperInstallerUtils.closeQuietly(out);
13556           }
13557         }
13558       }
13559     } catch (IOException ioe) {
13560       throw new RuntimeException(ioe);
13561     } finally {
13562       GrouperInstallerUtils.closeQuietly(zipFile);
13563     }
13564 
13565     return unzippedDir;
13566 
13567   }
13568 
13569   /**
13570    * unzip a file to another file, this is for .gz files
13571    * @param fileName
13572    * @param autorunPropertiesKeyIfFileExistsUseLocal key in properties file to automatically fill in a value
13573    * @return the unzipped file
13574    */
13575   private File unzip(final String fileName, final String autorunPropertiesKeyIfFileExistsUseLocal) {
13576 
13577     if (!fileName.endsWith(".gz")) {
13578       throw new RuntimeException("File doesnt end in .gz: " + fileName);
13579     }
13580     String unzippedFileName = fileName.substring(0, fileName.length() - ".gz".length());
13581     
13582     File unzippedFile = new File(unzippedFileName);
13583     if (unzippedFile.exists()) {
13584       
13585       if (this.useAllUnzippedFiles != null && this.useAllUnzippedFiles == true) {
13586         return unzippedFile;
13587       }
13588       
13589       System.out.print("Unzipped file exists: " + unzippedFileName + ", use unzipped file (t|f)? [t]: ");
13590       boolean useUnzippedFile = readFromStdInBoolean(true, autorunPropertiesKeyIfFileExistsUseLocal);
13591       if (useUnzippedFile) {
13592         if (this.useAllUnzippedFiles == null) {
13593           System.out.print("Would you like to use all existing unzipped files (t|f)? [t]: ");
13594           this.useAllUnzippedFiles = readFromStdInBoolean(true, "grouperInstaller.autorun.useAllUnzippedFiles");
13595         }
13596         
13597         return unzippedFile;
13598       }
13599       System.out.println("Deleting: " + unzippedFileName);
13600       if (!unzippedFile.delete()) {
13601         throw new RuntimeException("Cant delete file: " + unzippedFileName);
13602       }
13603     }
13604 
13605     System.out.println("Unzipping: " + fileName);
13606     
13607     final File[] result = new File[1];
13608     
13609     Runnable runnable = new Runnable() {
13610 
13611       public void run() {
13612         result[0] = unzipHelper(fileName, autorunPropertiesKeyIfFileExistsUseLocal);
13613       }
13614     };
13615 
13616     GrouperInstallerUtils.threadRunWithStatusDots(runnable, true);
13617 
13618     return result[0];
13619 
13620   }
13621 
13622   /**
13623    * unzip a file to another file, this is for .gz files
13624    * @param fileName
13625    * @param autorunPropertiesKeyIfFileExistsUseLocal key in properties file to automatically fill in a value
13626    * @return the unzipped file
13627    */
13628   private static File unzipHelper(String fileName, String autorunPropertiesKeyIfFileExistsUseLocal) {
13629 
13630     String unzippedFileName = fileName.substring(0, fileName.length() - ".gz".length());
13631     File unzippedFile = new File(unzippedFileName);
13632 
13633     GzipCompressorInputStream gzipCompressorInputStream = null;
13634     FileOutputStream fileOutputStream = null;
13635     try {
13636       gzipCompressorInputStream = new GzipCompressorInputStream(new BufferedInputStream(new FileInputStream(new File(fileName))));
13637       fileOutputStream = new FileOutputStream(unzippedFile);
13638       GrouperInstallerUtils.copy(gzipCompressorInputStream, fileOutputStream);
13639     } catch (Exception e) {
13640       throw new RuntimeException("Cant unzip file: " + fileName, e);
13641     } finally {
13642       GrouperInstallerUtils.closeQuietly(gzipCompressorInputStream);
13643       GrouperInstallerUtils.closeQuietly(fileOutputStream);
13644     }
13645     return unzippedFile;
13646   }
13647 
13648   /**
13649    * 
13650    * @return the file of the directory of the psp
13651    */
13652   private File downloadPsp() {
13653     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
13654     
13655     if (!urlToDownload.endsWith("/")) {
13656       urlToDownload += "/";
13657     }
13658     urlToDownload += "release/";
13659 
13660     String pspFileName = "grouper.psp-" + this.version + ".tar.gz";
13661     urlToDownload += this.version + "/" + pspFileName;
13662 
13663     File pspFile = new File(this.grouperTarballDirectoryString + pspFileName);
13664     
13665     downloadFile(urlToDownload, pspFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalPspDownloadTarEtc");
13666 
13667     return pspFile;
13668   }
13669   
13670   /**
13671    * 
13672    * @return the file of the directory of the psp
13673    */
13674   private File downloadPspng() {
13675     String urlToDownload = GrouperInstallerUtils.propertiesValue("download.server.url", true);
13676     
13677     if (!urlToDownload.endsWith("/")) {
13678       urlToDownload += "/";
13679     }
13680     urlToDownload += "release/";
13681 
13682     String pspFileName = "grouper.pspng-" + this.version + ".tar.gz";
13683     urlToDownload += this.version + "/" + pspFileName;
13684 
13685     File pspFile = new File(this.grouperTarballDirectoryString + pspFileName);
13686     
13687     downloadFile(urlToDownload, pspFile.getAbsolutePath(), "grouperInstaller.autorun.useLocalPspngDownloadTarEtc");
13688 
13689     return pspFile;
13690   }
13691   
13692   /**
13693    * upgrade the ws
13694    */
13695   private void upgradeWs() {
13696   
13697     this.upgradeApiPreRevertPatch();
13698 
13699     System.out.println("You need to revert all patches to upgrade");
13700     this.patchRevertWs();
13701     
13702     System.out.println("\n##################################");
13703     System.out.println("Upgrading WS\n");
13704     
13705     //copy the jars there
13706     System.out.println("\n##################################");
13707     System.out.println("Upgrading WS jars\n");
13708   
13709     this.upgradeJars(new File(this.untarredWsDir + File.separator + 
13710         "grouper-ws" + File.separator + "build" + File.separator + "dist" + File.separator + "grouper-ws"
13711         + File.separator + "WEB-INF" + File.separator + "lib" + File.separator));
13712   
13713     System.out.println("\n##################################");
13714     System.out.println("Upgrading WS files\n");
13715   
13716     //copy files there
13717     this.copyFiles(this.untarredWsDir + File.separator + 
13718         "grouper-ws" + File.separator + "build" + File.separator + "dist" + File.separator + "grouper-ws"
13719         + File.separator,
13720         this.upgradeExistingApplicationDirectoryString,
13721         GrouperInstallerUtils.toSet("WEB-INF/lib", "WEB-INF/web.xml", "WEB-INF/classes",
13722             "WEB-INF/bin/gsh", "WEB-INF/bin/gsh.bat", "WEB-INF/bin/gsh.sh"));
13723 
13724     {
13725       boolean hadChange = false;
13726       for (String gshName : new String[]{"gsh", "gsh.bat", "gsh.sh"}) {
13727         File newGshFile = new File(this.untarredWsDir + File.separator + "grouper-ws" + File.separator 
13728             + "build" + File.separator + "dist" + File.separator + "grouper-ws"
13729             + File.separator + "WEB-INF" + File.separator + "bin" 
13730             + File.separator + gshName);
13731   
13732         File existingGshFile = new File(this.upgradeExistingApplicationDirectoryString 
13733             + File.separator + "WEB-INF" + File.separator + "bin" + File.separator + gshName);
13734   
13735         if (!GrouperInstallerUtils.contentEquals(newGshFile, existingGshFile)) {
13736           this.backupAndCopyFile(newGshFile, existingGshFile, true);
13737           if (!GrouperInstallerUtils.equals("gsh.bat", gshName)) {
13738             hadChange = true;
13739           }
13740         }
13741         
13742       }
13743       if (hadChange) {
13744         //set executable and dos2unix
13745         gshExcutableAndDos2Unix(this.upgradeExistingApplicationDirectoryString + "WEB-INF" 
13746             + File.separator + "bin" 
13747             + File.separator);
13748       }
13749     }
13750     
13751     upgradeWebXml(new File(this.untarredWsDir + File.separator + "grouper-ws" 
13752         + File.separator + "build" + File.separator + "dist" + File.separator + "grouper-ws"
13753         + File.separator + "WEB-INF" + File.separator + "web.xml"),
13754             new File(this.upgradeExistingApplicationDirectoryString 
13755                 + File.separator + "WEB-INF" + File.separator + "web.xml"));
13756     
13757     System.out.println("\n##################################");
13758     System.out.println("Upgrading WS config files\n");
13759 
13760     this.compareUpgradePropertiesFile(this.grouperWsBasePropertiesFile, 
13761         new File(this.untarredWsDir + File.separator + 
13762             "grouper-ws" + File.separator + "build" + File.separator + "dist" + File.separator + "grouper-ws"
13763             + File.separator + "WEB-INF" + File.separator + "classes" + File.separator + "grouper-ws.base.properties"),
13764         this.grouperWsPropertiesFile,
13765         this.grouperWsExamplePropertiesFile, null, "grouperInstaller.autorun.removeRedundantPropetiesFromGrouperWsProperties"
13766       );
13767 
13768     this.upgradeApiPostRevertPatch();
13769     
13770     //patch it
13771     this.patchWs();
13772 
13773   }
13774   /**
13775    * get the patches available to apply that are not already applied
13776    * @param thisAppToUpgrade app to upgrade to check
13777    * @return true if up to date, false if not
13778    */
13779   private boolean patchStatus(AppToUpgrade thisAppToUpgrade) {
13780   
13781     if (thisAppToUpgrade == AppToUpgrade.CLIENT) {
13782       throw new RuntimeException("Cant get status on " + thisAppToUpgrade);
13783     }
13784     
13785     Properties patchesExistingProperties = patchExistingProperties();
13786     
13787     String grouperVersion = this.grouperVersionOfJar().toString();
13788     
13789     grouperVersion = GrouperInstallerUtils.replace(grouperVersion, ".", "_");
13790     
13791     boolean foundNewPatch = false;
13792     
13793     OUTER: for (int i=0;i<1000;i++) {
13794       
13795       //grouper_v2_2_1_api_patch_0.state
13796       String keyBase = "grouper_v" + grouperVersion + "_" + thisAppToUpgrade.name().toLowerCase() + "_patch_" + i;
13797       System.out.println("\n################ Checking patch " + keyBase);
13798       String key = keyBase + ".state";
13799   
13800       String value = patchesExistingProperties.getProperty(key);
13801 
13802       if (!GrouperInstallerUtils.isBlank(value)) {
13803 
13804         GrouperInstallerPatchStatus grouperInstallerPatchStatus = GrouperInstallerPatchStatus.valueOfIgnoreCase(value, true, true);
13805 
13806         switch (grouperInstallerPatchStatus) {
13807           case applied:
13808             
13809             System.out.println("Patch: " + keyBase + ": was applied on: " + patchesExistingProperties.getProperty(keyBase + ".date"));
13810             break;
13811             
13812           case skippedPermanently:
13813             
13814             foundNewPatch = true;
13815             System.out.println("Patch: " + keyBase + ": was skipped permanently on: " + patchesExistingProperties.getProperty(keyBase + ".date"));
13816             break;
13817             
13818           case skippedTemporarily:
13819   
13820             foundNewPatch = true;
13821             System.out.println("Patch: " + keyBase + ": was skipped termporarily on: " + patchesExistingProperties.getProperty(keyBase + ".date"));
13822             break;
13823   
13824           case reverted:
13825   
13826             foundNewPatch = true;
13827             System.out.println("Patch: " + keyBase + ": was reverted on: " + patchesExistingProperties.getProperty(keyBase + ".date"));
13828             break;
13829   
13830           case error:
13831   
13832             foundNewPatch = true;
13833             System.out.println("Patch: " + keyBase + ": had an error installing on: " + patchesExistingProperties.getProperty(keyBase + ".date"));
13834             break;
13835   
13836           default:
13837             throw new RuntimeException("Not expecting: " + grouperInstallerPatchStatus);
13838         }
13839         
13840       }
13841   
13842       //lets see if it exists on the server
13843       File patchUntarredDir = downloadAndUnzipPatch(keyBase);
13844       
13845       //if no more patches
13846       if (patchUntarredDir == null) {
13847         System.out.println("");
13848         break OUTER;
13849       }
13850       
13851       //lets get the description:
13852       //  # will show up on screen so user knows what it is
13853       //  description = This patch fixes GRP-1080: browse folders refresh button only works in chrome, not other browsers
13854       //
13855       //  # patches that this patch is dependant on (comma separated)
13856       //  dependencies = 
13857       //
13858       //  # low, medium, or high risk to applying the patch
13859       //  risk = low
13860       //
13861       //  # is this is a security patch (true or false)
13862       //  security = false
13863       //
13864       //  # if this patch requires restart of processes (true or false)
13865       //  requiresRestart = false
13866       Properties patchProperties = GrouperInstallerUtils.propertiesFromFile(new File(patchUntarredDir.getAbsoluteFile() + File.separator + keyBase + ".properties"));
13867   
13868       foundNewPatch = true;
13869   
13870       // check dependencies
13871       {
13872         String[] dependencies = GrouperInstallerUtils.splitTrim(patchProperties.getProperty("dependencies"), ",");
13873   
13874         for (String dependency : GrouperInstallerUtils.nonNull(dependencies, String.class)) {
13875           if (!this.patchesInstalled.contains(dependency)) {
13876             System.out.println("Cannot install patch " + keyBase + " since it is dependent on a patch which is not installed: " + dependency);
13877           }
13878         }
13879       }
13880       
13881       boolean securityRelated = GrouperInstallerUtils.booleanValue(patchProperties.getProperty("security"), false);
13882       boolean requiresRestart = GrouperInstallerUtils.booleanValue(patchProperties.getProperty("requiresRestart"), true);
13883       
13884       //print description
13885       System.out.println("Patch " + keyBase + " is " + patchProperties.getProperty("risk") + " risk, "
13886           + (securityRelated ? "is a security patch" : "is not a security patch"));
13887       System.out.println("Patch " + keyBase + (requiresRestart ? " requires" : " does not require") + " a restart");
13888       System.out.println(patchProperties.getProperty("description") + "\n");      
13889     }
13890   
13891     if (!foundNewPatch) {
13892       System.out.println("There are no new " + thisAppToUpgrade + " patches to install");
13893       return true;
13894     }
13895     
13896     return false;
13897   }
13898   /**
13899    * build PSPNG
13900    * @param pspngDir
13901    */
13902   private void buildPspng(File pspngDir) {
13903 
13904     if (!pspngDir.exists() || pspngDir.isFile()) {
13905       throw new RuntimeException("Cant find psp: " + pspngDir.getAbsolutePath());
13906     }
13907     
13908     File pspngBuildToDir = new File(pspngDir.getAbsolutePath() 
13909         + File.separator + "target" + File.separator + "classes");
13910     
13911     boolean rebuildPspng = true;
13912     
13913     if (pspngBuildToDir.exists()) {
13914       System.out.print("The PSPNG has been built in the past, do you want it rebuilt? (t|f) [t]: ");
13915       rebuildPspng = readFromStdInBoolean(true, "grouperInstaller.autorun.rebuildPspngAfterHavingBeenBuilt");
13916     }
13917     
13918     if (!rebuildPspng) {
13919       return;
13920     }
13921     
13922     List<String> commands = new ArrayList<String>();
13923     
13924 //    \bin\mvn compile -DskipTests
13925     addMavenCommands(commands);
13926 
13927     //put 'compile -DskipTests' in there so it wont run tests which we dont want to do
13928     // dependency:copy-dependencies package -DskipTests
13929     //not compile
13930     commands.add("dependency:copy-dependencies");
13931     commands.add("package");
13932     commands.add("-DskipTests");
13933     commands.add("-Drat.ignoreErrors=true");
13934     commands.add("-Dlicense.skip=true");
13935         
13936     System.out.println("\n##################################");
13937     System.out.println("Building PSPNG with command:\n" + pspngDir.getAbsolutePath() + "> " 
13938         + convertCommandsIntoCommand(commands) + "\n");
13939 
13940     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
13941         true, true, null, new File(pspngDir.getAbsolutePath()), null, true);
13942     
13943     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
13944       System.out.println("stderr: " + commandResult.getErrorText());
13945     }
13946     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
13947       System.out.println("stdout: " + commandResult.getOutputText());
13948     }
13949 
13950     System.out.println("\nEnd building PSPNG");
13951     System.out.println("##################################\n");
13952     
13953   }
13954 
13955   /**
13956    * 
13957    */
13958   private void upgradeSourcesXmlToProperties() {
13959   
13960     //dont do this if less than 2.3.1
13961     if (new GiGrouperVersionersion(this.version).lessThanArg(new GiGrouperVersion("2.3.1"))) {
13962       return;
13963     }
13964     
13965     //this file is done
13966     File sourcesXmlFile = new File(this.grouperPropertiesFile.getParentFile().getAbsolutePath() + File.separator + "sources.xml");
13967 
13968     if (!sourcesXmlFile.exists()) {
13969       return;
13970     }
13971       
13972     
13973     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]: ");
13974     boolean convert = readFromStdInBoolean(true, "grouperInstaller.autorun.convertSourcesXmlToProperties");
13975   
13976     if (!convert) {
13977       System.out.println("Note: grouper will not run, but whatever you want to do!!!!");
13978     }
13979     File bakFile = null;
13980     if (this.subjectPropertiesFile.exists()) {
13981       //see if there is anything in it
13982       Properties grouperCacheProperties = GrouperInstallerUtils.propertiesFromFile(this.subjectPropertiesFile);
13983       if (grouperCacheProperties.size() > 0) {
13984         bakFile = this.backupAndDeleteFile(this.grouperCachePropertiesFile, true);
13985       }
13986     }
13987     
13988     URL sourcesXmlUrl = null;
13989     
13990     try {
13991       sourcesXmlUrl = sourcesXmlFile.toURI().toURL();
13992     } catch (Exception e) {
13993       throw new RuntimeException("Problem with sources.xml: " + (sourcesXmlFile == null ? null : sourcesXmlFile.getAbsoluteFile()), e);
13994     }
13995     
13996     //convert
13997     convertSourcesXmlToProperties(this.subjectPropertiesFile, sourcesXmlUrl);
13998     
13999     File subjectBakFile = bakFile(this.subjectPropertiesFile);
14000     GrouperInstallerUtils.copyFile(this.subjectPropertiesFile, subjectBakFile, true);
14001     this.backupAndDeleteFile(sourcesXmlFile, true);
14002     
14003     {
14004       File sourcesExampleXmlFile = new File(this.grouperPropertiesFile.getParentFile().getAbsolutePath() + File.separator + "sources.example.xml");
14005       if (sourcesExampleXmlFile.exists()) {
14006         this.backupAndDeleteFile(sourcesExampleXmlFile, true);
14007       }
14008     }
14009     
14010     if (bakFile != null) {
14011       System.out.println("Note, you had settings in your subject.properties (not common), this file has been moved to: " + bakFile.getAbsolutePath());
14012       System.out.println("Merge your settings from that file to " + this.subjectPropertiesFile.getAbsolutePath());
14013       System.out.print("Press <enter> to continue: ");
14014       readFromStdIn("grouperInstaller.autorun.convertSourcesXmlToPropertiesHadPropertiesInFile");
14015     }
14016   }
14017 
14018   /**
14019    * build client API
14020    * @param messagingActiveMqDir
14021    */
14022   private void buildMessagingActivemq(File messagingActiveMqDir) {
14023     if (!messagingActiveMqDir.exists() || messagingActiveMqDir.isFile()) {
14024       throw new RuntimeException("Cant find messaging activemq: " + messagingActiveMqDir.getAbsolutePath());
14025     }
14026     
14027     File messagingActivemqBuildToDir = new File(messagingActiveMqDir.getAbsoluteFile() + File.separator + "dist" + File.separator + "bin");
14028     
14029     boolean rebuildMessagingActivemq = true;
14030     
14031     if (messagingActivemqBuildToDir.exists()) {
14032       System.out.print("Grouper messaging activemq has been built in the past, do you want it rebuilt? (t|f) [t]: ");
14033       rebuildMessagingActivemq = readFromStdInBoolean(true, "grouperInstaller.autorun.rebuildMessagingActivemqAfterHavingBeenBuilt");
14034     }
14035     
14036     if (!rebuildMessagingActivemq) {
14037       return;
14038     }
14039   
14040     List<String> commands = new ArrayList<String>();
14041     
14042     addAntCommands(commands);
14043     
14044     System.out.println("\n##################################");
14045     System.out.println("Building messaging activemq with command:\n" + messagingActiveMqDir.getAbsolutePath() + "> " 
14046         + convertCommandsIntoCommand(commands) + "\n");
14047     
14048     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
14049         true, true, null, messagingActiveMqDir, null, true);
14050     
14051     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
14052       System.out.println("stderr: " + commandResult.getErrorText());
14053     }
14054     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
14055       System.out.println("stdout: " + commandResult.getOutputText());
14056     }
14057   
14058     System.out.println("\nEnd building messaging activemq");
14059     System.out.println("##################################\n");
14060     
14061   }
14062 
14063   /**
14064    * build client API
14065    * @param messagingAwsDir
14066    */
14067   private void buildMessagingAws(File messagingAwsDir) {
14068     if (!messagingAwsDir.exists() || messagingAwsDir.isFile()) {
14069       throw new RuntimeException("Cant find messaging aws: " + messagingAwsDir.getAbsolutePath());
14070     }
14071     
14072     File messagingAwsBuildToDir = new File(messagingAwsDir.getAbsoluteFile() + File.separator + "dist" + File.separator + "bin");
14073     
14074     boolean rebuildMessagingAws = true;
14075     
14076     if (messagingAwsBuildToDir.exists()) {
14077       System.out.print("Grouper messaging aws has been built in the past, do you want it rebuilt? (t|f) [t]: ");
14078       rebuildMessagingAws = readFromStdInBoolean(true, "grouperInstaller.autorun.rebuildMessagingAwsAfterHavingBeenBuilt");
14079     }
14080     
14081     if (!rebuildMessagingAws) {
14082       return;
14083     }
14084   
14085     List<String> commands = new ArrayList<String>();
14086     
14087     addAntCommands(commands);
14088     
14089     System.out.println("\n##################################");
14090     System.out.println("Building messaging aws with command:\n" + messagingAwsDir.getAbsolutePath() + "> " 
14091         + convertCommandsIntoCommand(commands) + "\n");
14092     
14093     CommandResult commandResult = GrouperInstallerUtils.execCommand(GrouperInstallerUtils.toArray(commands, String.class),
14094         true, true, null, messagingAwsDir, null, true);
14095     
14096     if (!GrouperInstallerUtils.isBlank(commandResult.getErrorText())) {
14097       System.out.println("stderr: " + commandResult.getErrorText());
14098     }
14099     if (!GrouperInstallerUtils.isBlank(commandResult.getOutputText())) {
14100       System.out.println("stdout: " + commandResult.getOutputText());
14101     }
14102   
14103     System.out.println("\nEnd building aws rabbitmq");
14104     System.out.println("##################################\n");
14105     
14106   }
14107 
14108   /**
14109    * 
14110    * @param grouperCacheBasePropertiesFile 
14111    * @param grouperCachePropertiesFile
14112    * @param ehcacheXmlUrl
14113    */
14114   public static void convertEhcacheXmlToProperties(File grouperCacheBasePropertiesFile, File grouperCachePropertiesFile, URL ehcacheXmlUrl) {
14115   
14116     //look at base properties
14117     Properties grouperCacheProperties = grouperCachePropertiesFile.exists() ? 
14118         GrouperInstallerUtils.propertiesFromFile(grouperCachePropertiesFile) : new Properties();
14119   
14120     if (!grouperCacheBasePropertiesFile.exists()) {
14121       throw new RuntimeException(grouperCacheBasePropertiesFile.getAbsolutePath() + " must exist and does not!");
14122     }
14123     
14124     if (grouperCacheProperties.size() > 0) {
14125       throw new RuntimeException(grouperCachePropertiesFile.getAbsolutePath() + " exists and must not.  Delete the file and run this again!");
14126     }
14127   
14128     if (!grouperCachePropertiesFile.getParentFile().exists() || !grouperCachePropertiesFile.getParentFile().isDirectory()) {
14129       throw new RuntimeException(grouperCachePropertiesFile.getParentFile().getAbsolutePath() + " must exist and must be a directory");
14130     }
14131     
14132     //look at base properties
14133     Properties grouperCacheBaseProperties = GrouperInstallerUtils.propertiesFromFile(grouperCacheBasePropertiesFile);
14134     
14135     StringBuilder grouperEhcachePropertiesContents = new StringBuilder();
14136     
14137     grouperEhcachePropertiesContents.append(
14138               "# Copyright 2016 Internet2\n"
14139             + "#\n"
14140             + "# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
14141             + "# you may not use this file except in compliance with the License.\n"
14142             + "# You may obtain a copy of the License at\n"
14143             + "#\n"
14144             + "#   http://www.apache.org/licenses/LICENSE-2.0\n"
14145             + "#\n"
14146             + "# Unless required by applicable law or agreed to in writing, software\n"
14147             + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n"
14148             + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
14149             + "# See the License for the specific language governing permissions and\n"
14150             + "# limitations under the License.\n"
14151             + "\n"
14152             + "#\n"
14153             + "# Grouper Cache Configuration\n"
14154             + "#\n"
14155             + "\n"
14156             + "# The grouper cache config uses Grouper Configuration Overlays (documented on wiki)\n"
14157             + "# By default the configuration is read from grouper.cache.base.properties\n"
14158             + "# (which should not be edited), and the grouper.cache.properties overlays\n"
14159             + "# the base settings.  See the grouper.cache.base.properties for the possible\n"
14160             + "# settings that can be applied to the grouper.cache.properties\n\n"
14161         );
14162   
14163     {
14164       // <diskStore path="java.io.tmpdir"/>
14165       NodeList diskStoreNodeList = GrouperInstallerUtils.xpathEvaluate(ehcacheXmlUrl, "/ehcache/diskStore");
14166       if (diskStoreNodeList.getLength() != 1) {
14167         throw new RuntimeException("Expecting one diskStore element");
14168       }
14169   
14170       Element element = (Element)diskStoreNodeList.item(0);
14171   
14172       NamedNodeMap configuredNamedNodeMap = element.getAttributes();
14173       if (configuredNamedNodeMap.getLength() != 1 || !"path".equals(configuredNamedNodeMap.item(0).getNodeName())) {
14174         throw new RuntimeException("Expecting one diskStore attribute: path");
14175       }
14176       
14177       String path = element.getAttribute("path");
14178       
14179       if (!"java.io.tmpdir".equals(path)) {
14180         grouperEhcachePropertiesContents.append("grouper.cache.diskStorePath = " + path + "\n\n");
14181       }
14182       
14183     }    
14184   
14185     {
14186       //  <defaultCache
14187       //    maxElementsInMemory="1000"
14188       //    eternal="false"
14189       //    timeToIdleSeconds="10"
14190       //    timeToLiveSeconds="10"
14191       //    overflowToDisk="false"
14192       //    statistics="false"
14193       //  />
14194       
14195       NodeList diskStoreNodeList = GrouperInstallerUtils.xpathEvaluate(ehcacheXmlUrl, "/ehcache/defaultCache");
14196       if (diskStoreNodeList.getLength() != 1) {
14197         throw new RuntimeException("Expecting one defaultCache element");
14198       }
14199   
14200       Element element = (Element)diskStoreNodeList.item(0);
14201   
14202       NamedNodeMap configuredNamedNodeMap = element.getAttributes();
14203       
14204       if (configuredNamedNodeMap.getLength() != 6) {
14205         throw new RuntimeException("Expecting defaultCache with these attributes: maxElementsInMemory, "
14206             + "eternal, timeToIdleSeconds, timeToLiveSeconds, overflowToDisk, statistics");
14207       }
14208   
14209       boolean madeChanges = false;
14210       
14211       for (int i=0;i<configuredNamedNodeMap.getLength(); i++) {
14212         
14213         String attributeName = configuredNamedNodeMap.item(i).getNodeName();
14214         String value = element.getAttribute(attributeName);
14215   
14216         if ("maxElementsInMemory".equals(attributeName)) {
14217           if (!"1000".equals(value)) {
14218             grouperEhcachePropertiesContents.append("cache.defaultCache.maxElementsInMemory = " + value + "\n");
14219             madeChanges = true;
14220           }
14221         } else if ("eternal".equals(attributeName)) {
14222           if (!"false".equals(value)) {
14223             grouperEhcachePropertiesContents.append("cache.defaultCache.eternal = " + value + "\n");
14224             madeChanges = true;
14225           }
14226         } else if ("timeToIdleSeconds".equals(attributeName)) {
14227           if (!"10".equals(value)) {
14228             grouperEhcachePropertiesContents.append("cache.defaultCache.timeToIdleSeconds = " + value + "\n");
14229             madeChanges = true;
14230           }
14231           
14232         } else if ("timeToLiveSeconds".equals(attributeName)) {
14233           if (!"10".equals(value)) {
14234             grouperEhcachePropertiesContents.append("cache.defaultCache.timeToLiveSeconds = " + value + "\n");
14235             madeChanges = true;
14236           }
14237           
14238         } else if ("overflowToDisk".equals(attributeName)) {
14239           if (!"false".equals(value)) {
14240             grouperEhcachePropertiesContents.append("cache.defaultCache.overflowToDisk = " + value + "\n");
14241             madeChanges = true;
14242           }
14243           
14244         } else if ("statistics".equals(attributeName)) {
14245           if (!"false".equals(value)) {
14246             grouperEhcachePropertiesContents.append("cache.defaultCache.statistics = " + value + "\n");
14247             madeChanges = true;
14248           }
14249           
14250         } else {
14251           throw new RuntimeException("Not expecting attribuet defaultCache " + attributeName);
14252         }
14253       }
14254   
14255       if (madeChanges) {
14256         grouperEhcachePropertiesContents.append("\n");
14257       }
14258       
14259     }
14260     
14261     NodeList nodeList = GrouperInstallerUtils.xpathEvaluate(ehcacheXmlUrl, "/ehcache/cache");
14262     
14263     Set<String> usedKeys = new HashSet<String>();
14264     
14265     for (int i=0;i<nodeList.getLength();i++) {
14266       
14267       Element element = (Element)nodeList.item(i);
14268   
14269       //  <cache  name="edu.internet2.middleware.grouper.internal.dao.hib3.Hib3MemberDAO.FindBySubject"
14270       //      maxElementsInMemory="5000"
14271       //      eternal="false"
14272       //      timeToIdleSeconds="5"
14273       //      timeToLiveSeconds="10"
14274       //      overflowToDisk="false"  
14275       //      statistics="false"
14276       //  />
14277       
14278       String name = element.getAttribute("name");
14279       Integer maxElementsInMemory = GrouperInstallerUtils.intObjectValue(element.getAttribute("maxElementsInMemory"), true);
14280       Boolean eternal = GrouperInstallerUtils.booleanObjectValue(element.getAttribute("eternal"));
14281       Integer timeToIdleSeconds = GrouperInstallerUtils.intObjectValue(element.getAttribute("timeToIdleSeconds"), true);
14282       Integer timeToLiveSeconds = GrouperInstallerUtils.intObjectValue(element.getAttribute("timeToLiveSeconds"), true);
14283       Boolean overflowToDisk = GrouperInstallerUtils.booleanObjectValue(element.getAttribute("overflowToDisk"));
14284       Boolean statistics = GrouperInstallerUtils.booleanObjectValue(element.getAttribute("statistics"));
14285   
14286       //any attributes we dont expect?
14287       NamedNodeMap configuredNamedNodeMap = element.getAttributes();
14288       //see which attributes are new or changed
14289       for (int j=0;j<configuredNamedNodeMap.getLength();j++) {
14290         Node configuredAttribute = configuredNamedNodeMap.item(j);
14291         if (!configuredAttribute.getNodeName().equals("name")
14292             && !configuredAttribute.getNodeName().equals("maxElementsInMemory")
14293             && !configuredAttribute.getNodeName().equals("eternal")
14294             && !configuredAttribute.getNodeName().equals("timeToIdleSeconds")
14295             && !configuredAttribute.getNodeName().equals("timeToLiveSeconds")
14296             && !configuredAttribute.getNodeName().equals("overflowToDisk")
14297             && !configuredAttribute.getNodeName().equals("statistics")) {
14298           throw new RuntimeException("Cant process attribute: '" + configuredAttribute.getNodeName() + "'");
14299         }
14300       }
14301       
14302       String key = convertEhcacheNameToPropertiesKey(name, usedKeys);
14303       
14304       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.name = edu.internet2.middleware.grouper.internal.dao.hib3.Hib3GroupDAO
14305       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.maxElementsInMemory = 500
14306       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.eternal = false
14307       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.timeToIdleSeconds = 1
14308       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.timeToLiveSeconds = 1
14309       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.overflowToDisk = false
14310       //  cache.name.edu_internet2_middleware_grouper_internal_dao_hib3_Hib3GroupDAO.statistics = false
14311   
14312       boolean madeChanges = false;
14313       
14314       if (maxElementsInMemory != null && !GrouperInstallerUtils.defaultString((String)grouperCacheBaseProperties.get("cache.name." + key + ".maxElementsInMemory")).equals(maxElementsInMemory.toString())) {
14315         grouperEhcachePropertiesContents.append("cache.name." + key + ".maxElementsInMemory = " + maxElementsInMemory + "\n");
14316         madeChanges = true;
14317       }
14318       if (eternal != null && !GrouperInstallerUtils.defaultString((String)grouperCacheBaseProperties.get("cache.name." + key + ".eternal")).equals(eternal.toString())) {
14319         grouperEhcachePropertiesContents.append("cache.name." + key + ".eternal = " + eternal + "\n");
14320         madeChanges = true;
14321       }
14322       if (timeToIdleSeconds != null && !GrouperInstallerUtils.defaultString((String)grouperCacheBaseProperties.get("cache.name." + key + ".timeToIdleSeconds")).equals(timeToIdleSeconds.toString())) {
14323         grouperEhcachePropertiesContents.append("cache.name." + key + ".timeToIdleSeconds = " + timeToIdleSeconds + "\n");
14324         madeChanges = true;
14325       }
14326       if (timeToLiveSeconds != null && !GrouperInstallerUtils.defaultString((String)grouperCacheBaseProperties.get("cache.name." + key + ".timeToLiveSeconds")).equals(timeToLiveSeconds.toString())) {
14327         grouperEhcachePropertiesContents.append("cache.name." + key + ".timeToLiveSeconds = " + timeToLiveSeconds + "\n");
14328         madeChanges = true;
14329       }
14330       if (overflowToDisk != null && !GrouperInstallerUtils.defaultString((String)grouperCacheBaseProperties.get("cache.name." + key + ".overflowToDisk")).equals(overflowToDisk.toString())) {
14331         grouperEhcachePropertiesContents.append("cache.name." + key + ".overflowToDisk = " + overflowToDisk + "\n");
14332         madeChanges = true;
14333       }
14334       if (statistics != null && !GrouperInstallerUtils.defaultString((String)grouperCacheBaseProperties.get("cache.name." + key + ".statistics")).equals(statistics.toString())) {
14335         grouperEhcachePropertiesContents.append("cache.name." + key + ".statistics = " + statistics + "\n");
14336         madeChanges = true;
14337       }
14338       if (madeChanges) {
14339         grouperEhcachePropertiesContents.append("\n");
14340       }
14341     }
14342   
14343     GrouperInstallerUtils.saveStringIntoFile(grouperCachePropertiesFile, grouperEhcachePropertiesContents.toString());
14344   }
14345 
14346   /**
14347    * get a subelement value.
14348    * e.g. if the node is &lt;source&gt;
14349    * and the sub element is &lt;id&gt;someId&lt;/id&gt;
14350    * It will return "someId" for subElementName "id"
14351    * @param parent
14352    * @param subElementName
14353    * @param required
14354    * @param descriptionForError 
14355    * @return the string or null if not there
14356    */
14357   public static String xmlElementValue(Element parent, String subElementName, boolean required, String descriptionForError) {
14358     
14359     NodeList nodeList = parent.getElementsByTagName(subElementName);
14360     
14361     if (nodeList.getLength() < 1) {
14362       if (required) {
14363         throw new RuntimeException("Cant find subElement <" + subElementName 
14364             + "> in parent element " + parent.getNodeName() + ", " + descriptionForError);
14365       }
14366       return null;
14367     }    
14368     
14369     if (nodeList.getLength() > 1) {
14370       throw new RuntimeException("Too many subElements <" + subElementName 
14371           + "> in parent element " + parent.getNodeName() + ", " 
14372           + nodeList.getLength() + ", " + descriptionForError);
14373     }
14374     return GrouperInstallerUtils.trimToEmpty(nodeList.item(0).getTextContent());
14375   }
14376   
14377   /**
14378    * put in a good comment about this param name
14379    * @param paramName
14380    * @param paramValue
14381    * @param subjectPropertiesContents
14382    */
14383   private static void convertSourcesXmlParamComment(String paramName, StringBuilder subjectPropertiesContents, String paramValue) {
14384     
14385     if (paramName == null) {
14386       throw new NullPointerException("param-name is null");
14387     }
14388     
14389     if (paramName.startsWith("subjectVirtualAttributeVariable_")) {
14390       subjectPropertiesContents.append("\n# when evaluating the virtual attribute EL expression, this variable can be used from this java class.\n"
14391           + "# " + paramName + " variable is the " + paramValue + " class.  Call static methods\n");
14392     } else if (paramName.startsWith("subjectVirtualAttribute_")) {
14393       
14394       Pattern pattern = Pattern.compile("^subjectVirtualAttribute_([\\d]+)_(.*)$");
14395       Matcher matcher = pattern.matcher(paramName);
14396       if (!matcher.matches()) {
14397         throw new RuntimeException(paramName + " is invalid, should be of form: subjectVirtualAttribute_<intIndex>_paramName");
14398       }
14399       
14400       String index = matcher.group(1);
14401       String attributeName = matcher.group(2);
14402       
14403       subjectPropertiesContents.append("\n# This virtual attribute index " + index + " is accessible via: subject.getAttributeValue(\"" + attributeName + "\");\n");
14404       
14405     } else if (GrouperInstallerUtils.equals(paramName, "findSubjectByIdOnCheckConfig")) {
14406       
14407       subjectPropertiesContents.append("\n# if a system check should try to resolve a subject by id on this source\n");
14408 
14409     } else if (GrouperInstallerUtils.equals(paramName, "subjectIdToFindOnCheckConfig")) {
14410       
14411       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");
14412 
14413     } else if (GrouperInstallerUtils.equals(paramName, "findSubjectByIdentifiedOnCheckConfig")) {
14414       
14415       subjectPropertiesContents.append("\n# by default it will do a search by subject identifier\n");
14416 
14417     } else if (GrouperInstallerUtils.equals(paramName, "subjectIdentifierToFindOnCheckConfig")) {
14418       
14419       subjectPropertiesContents.append("\n# by default it will use a random value for subject identifier to lookup, you can specify a value here\n");
14420 
14421     } else if (GrouperInstallerUtils.equals(paramName, "findSubjectByStringOnCheckConfig")) {
14422       
14423       subjectPropertiesContents.append("\n# by default it will search for a subject by string\n");
14424 
14425     } else if (GrouperInstallerUtils.equals(paramName, "stringToFindOnCheckConfig")) {
14426       
14427       subjectPropertiesContents.append("\n# you can specify the search string here or it will be a random value\n");
14428 
14429     } else if (GrouperInstallerUtils.equals(paramName, "sortAttribute0")) {
14430       
14431       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"
14432           + "# you can have up to 5 sort attributes \n");
14433 
14434     } else if (GrouperInstallerUtils.equals(paramName, "sortAttribute1")) {
14435       
14436       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"
14437           + "# you can have up to 5 sort attributes \n");
14438 
14439     } else if (GrouperInstallerUtils.equals(paramName, "sortAttribute2")) {
14440       
14441       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"
14442           + "# you can have up to 5 sort attributes \n");
14443 
14444     } else if (GrouperInstallerUtils.equals(paramName, "sortAttribute3")) {
14445       
14446       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"
14447           + "# you can have up to 5 sort attributes \n");
14448 
14449     } else if (GrouperInstallerUtils.equals(paramName, "sortAttribute4")) {
14450       
14451       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"
14452           + "# you can have up to 5 sort attributes \n");
14453 
14454     } else if (GrouperInstallerUtils.equals(paramName, "searchAttribute0")) {
14455       
14456       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"
14457           + "# you can have up to 5 search attributes \n");
14458 
14459     } else if (GrouperInstallerUtils.equals(paramName, "searchAttribute1")) {
14460       
14461       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"
14462           + "# you can have up to 5 search attributes \n");
14463 
14464     } else if (GrouperInstallerUtils.equals(paramName, "searchAttribute2")) {
14465       
14466       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"
14467           + "# you can have up to 5 search attributes \n");
14468 
14469     } else if (GrouperInstallerUtils.equals(paramName, "searchAttribute3")) {
14470       
14471       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"
14472           + "# you can have up to 5 search attributes \n");
14473 
14474     } else if (GrouperInstallerUtils.equals(paramName, "searchAttribute4")) {
14475       
14476       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"
14477           + "# you can have up to 5 search attributes\n");
14478 
14479     } else if (GrouperInstallerUtils.equals(paramName, "subjectIdentifierAttribute0")) {
14480       
14481       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"
14482           + "# you can have up to max 1 subject identifier\n");
14483 
14484     } else if (GrouperInstallerUtils.equals(paramName, "maxConnectionAge")) {
14485       
14486       subjectPropertiesContents.append("\n# seconds of max connection age\n");
14487 
14488     } else if (GrouperInstallerUtils.equals(paramName, "testConnectionOnCheckout")) {
14489       
14490       subjectPropertiesContents.append("\n# if connections from pool should be tested when checked out from pool\n");
14491 
14492     } else if (GrouperInstallerUtils.equals(paramName, "preferredTestQuery")) {
14493       
14494       subjectPropertiesContents.append("\n# query to use to test the connection when checking out from pool\n");
14495 
14496     } else if (GrouperInstallerUtils.equals(paramName, "idleConnectionTestPeriod")) {
14497       
14498       subjectPropertiesContents.append("\n# seconds between tests of idle connections in pool\n");
14499 
14500     } else if (GrouperInstallerUtils.equals(paramName, "dbDriver")) {
14501       
14502       subjectPropertiesContents.append("\n#       e.g. mysql:           com.mysql.jdbc.Driver\n"
14503           + "#       e.g. p6spy (log sql): com.p6spy.engine.spy.P6SpyDriver\n"
14504           + "#         for p6spy, put the underlying driver in spy.properties\n"
14505           + "#       e.g. oracle:          oracle.jdbc.driver.OracleDriver\n"
14506           + "#       e.g. hsqldb:          org.hsqldb.jdbcDriver\n"
14507           + "#       e.g. postgres:        org.postgresql.Driver\n");
14508 
14509     } else if (GrouperInstallerUtils.equals(paramName, "dbUrl")) {
14510       
14511       subjectPropertiesContents.append("\n#       e.g. mysql:           jdbc:mysql://localhost:3306/grouper\n"
14512           + "#       e.g. p6spy (log sql): [use the URL that your DB requires]\n"
14513           + "#       e.g. oracle:          jdbc:oracle:thin:@server.school.edu:1521:sid\n"
14514           + "#       e.g. hsqldb (a):      jdbc:hsqldb:dist/run/grouper;create=true\n"
14515           + "#       e.g. hsqldb (b):      jdbc:hsqldb:hsql://localhost:9001\n"
14516           + "#       e.g. postgres:        jdbc:postgresql:grouper\n");
14517 
14518     } else if (GrouperInstallerUtils.equals(paramName, "dbUser")) {
14519       
14520       subjectPropertiesContents.append("\n# username when connecting to the database\n");
14521 
14522     } else if (GrouperInstallerUtils.equals(paramName, "dbPwd")) {
14523       
14524       subjectPropertiesContents.append("\n# password when connecting to the database (or file with encrypted password inside)\n");
14525 
14526     } else if (GrouperInstallerUtils.equals(paramName, "maxResults")) {
14527       
14528       subjectPropertiesContents.append("\n# maximum number of results from a search, generally no need to get more than 1000\n");
14529 
14530     } else if (GrouperInstallerUtils.equals(paramName, "dbTableOrView")) {
14531       
14532       subjectPropertiesContents.append("\n# the table or view to query results from.  Note, could prefix with a schema name\n");
14533 
14534     } else if (GrouperInstallerUtils.equals(paramName, "subjectIdCol")) {
14535       
14536       subjectPropertiesContents.append("\n# the column name to get the subjectId from\n");
14537 
14538     } else if (GrouperInstallerUtils.equals(paramName, "nameCol")) {
14539       
14540       subjectPropertiesContents.append("\n# the column name to get the name from\n");
14541 
14542     } else if (GrouperInstallerUtils.equals(paramName, "lowerSearchCol")) {
14543       
14544       subjectPropertiesContents.append("\n# search col where general searches take place, lower case\n");
14545 
14546     } else if (GrouperInstallerUtils.equals(paramName, "defaultSortCol")) {
14547       
14548       subjectPropertiesContents.append("\n# optional col if you want the search results sorted in the API (note, UI might override)\n");
14549 
14550     } else if (paramName.startsWith("subjectIdentifierCol")) {
14551       
14552       subjectPropertiesContents.append("\n# you can count up from 0 to N of columns to search by identifier (which might also include by id)\n");
14553 
14554     } else if (paramName.startsWith("subjectAttributeName")) {
14555       
14556       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");
14557 
14558     } else if (paramName.startsWith("subjectAttributeCol")) {
14559       
14560       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");
14561 
14562     } else if (GrouperInstallerUtils.equals(paramName, "statusDatastoreFieldName")) {
14563       
14564       subjectPropertiesContents.append("\n# STATUS SECTION for searches to filter out inactives and allow\n"
14565           + "# the user to filter by status with e.g. status=all\n"
14566           + "# this is optional, and advanced\n"
14567           + "#\n"
14568           + "# field in database or ldap or endpoint that is the status field\n");
14569 
14570     } else if (GrouperInstallerUtils.equals(paramName, "statusLabel")) {
14571       
14572       subjectPropertiesContents.append("\n# search string from user which represents the status.  e.g. status=active\n");
14573 
14574     } else if (GrouperInstallerUtils.equals(paramName, "statusesFromUser")) {
14575       
14576       subjectPropertiesContents.append("\n# available statuses from screen (if not specified, any will be allowed). comma separated list.\n"
14577           + "# Note, this is optional and you probably dont want to configure it, it is mostly necessary\n"
14578           + "# when you have multiple sources with statuses...  if someone types an invalid status\n"
14579           + "# and you have this configured, it will not filter by it\n");
14580 
14581     } else if (GrouperInstallerUtils.equals(paramName, "statusAllFromUser")) {
14582       
14583       subjectPropertiesContents.append("\n# all label from the user\n");
14584 
14585     } else if (GrouperInstallerUtils.equals(paramName, "statusSearchDefault")) {
14586       
14587       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"
14588           + "# form the user would type in\n");
14589 
14590     } else if (paramName.startsWith("statusTranslateUser")) {
14591       
14592       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"
14593           + "# so the user could enter: status=active, and that could translate to status_col=A.  The 'user' is what the user types in,\n"
14594           + "# the 'datastore' is what is in the datastore.  The user part is not case-sensitive.  Note, this could be a many to one\n");
14595 
14596     } else if (paramName.startsWith("statusTranslateDatastore")) {
14597       
14598       //hmmm, nothing to do here
14599     } else if (GrouperInstallerUtils.equals(paramName, "INITIAL_CONTEXT_FACTORY")) {
14600       
14601       subjectPropertiesContents.append("\n# e.g. com.sun.jndi.ldap.LdapCtxFactory\n");
14602 
14603     } else if (GrouperInstallerUtils.equals(paramName, "PROVIDER_URL")) {
14604       
14605       subjectPropertiesContents.append("\n# e.g. ldap://localhost:389\n");
14606 
14607     } else if (GrouperInstallerUtils.equals(paramName, "SECURITY_AUTHENTICATION")) {
14608       
14609       subjectPropertiesContents.append("\n# e.g. simple, none, sasl_mech\n");
14610 
14611     } else if (GrouperInstallerUtils.equals(paramName, "SECURITY_PRINCIPAL")) {
14612       
14613       subjectPropertiesContents.append("\n# e.g. cn=Manager,dc=example,dc=edu\n");
14614 
14615     } else if (GrouperInstallerUtils.equals(paramName, "SECURITY_CREDENTIALS")) {
14616       
14617       subjectPropertiesContents.append("\n# can be a password or a filename of the encrypted password\n");
14618 
14619     } else if (GrouperInstallerUtils.equals(paramName, "SubjectID_AttributeType")) {
14620       
14621       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");
14622 
14623     } else if (GrouperInstallerUtils.equals(paramName, "SubjectID_formatToLowerCase")) {
14624       
14625       subjectPropertiesContents.append("\n# if the subject id should be changed to lower case after reading from datastore.  true or false\n");
14626 
14627     } else if (GrouperInstallerUtils.equals(paramName, "Name_AttributeType")) {
14628       
14629       subjectPropertiesContents.append("\n# attribute which is the subject name\n");
14630 
14631     } else if (GrouperInstallerUtils.equals(paramName, "Description_AttributeType")) {
14632       
14633       subjectPropertiesContents.append("\n# attribute which is the subject description\n");
14634 
14635     } else if (GrouperInstallerUtils.equals(paramName, "VTLDAP_VALIDATOR")) {
14636       
14637       subjectPropertiesContents.append("\n# LdapValidator provides an interface for validating ldap objects when they are in the pool.\n"
14638           + "# ConnectLdapValidator validates an ldap connection is healthy by testing it is connected.\n"
14639           + "# CompareLdapValidator validates an ldap connection is healthy by performing a compare operation.\n");
14640 
14641     } else if (GrouperInstallerUtils.equals(paramName, "VTLDAP_VALIDATOR_COMPARE_DN")) {
14642       
14643       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");
14644 
14645     } else if (GrouperInstallerUtils.equals(paramName, "VTLDAP_VALIDATOR_COMPARE_SEARCH_FILTER_STRING")) {
14646       
14647       subjectPropertiesContents.append("\n# if VTLDAP_VALIDATOR is CompareLdapValidator, this is the filter string, e.g. ou=People\n");
14648 
14649     } else {
14650       
14651       //hmmm, not sure what to do here, no comment
14652       subjectPropertiesContents.append("\n");
14653 
14654     }
14655 
14656   }
14657   
14658   /**
14659    * valid source param pattern
14660    */
14661   private static Pattern sourcesValidParamPattern = Pattern.compile("^[A-Za-z0-9_]+$");
14662   
14663   /**
14664    * 
14665    * @param subjectPropertiesFile 
14666    * @param sourcesXmlUrl
14667    */
14668   public static void convertSourcesXmlToProperties(File subjectPropertiesFile, URL sourcesXmlUrl) {
14669 
14670     //look at base properties
14671     Properties subjectProperties = subjectPropertiesFile.exists() ? 
14672         GrouperInstallerUtils.propertiesFromFile(subjectPropertiesFile) : new Properties();
14673 
14674     if (subjectPropertiesFile.exists()) {
14675       
14676       //lets see if it just has the default.  the default has no properties
14677       if (subjectProperties.size() > 0) {
14678         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");
14679       }
14680     }
14681     
14682     if (!subjectPropertiesFile.getParentFile().exists() || !subjectPropertiesFile.getParentFile().isDirectory()) {
14683       throw new RuntimeException(subjectPropertiesFile.getParentFile().getAbsolutePath() + " must exist and must be a directory");
14684     }
14685     
14686     StringBuilder subjectPropertiesContents = new StringBuilder();
14687     
14688     subjectPropertiesContents.append(
14689               "# Copyright 2016 Internet2\n"
14690             + "#\n"
14691             + "# Licensed under the Apache License, Version 2.0 (the \"License\");\n"
14692             + "# you may not use this file except in compliance with the License.\n"
14693             + "# You may obtain a copy of the License at\n"
14694             + "#\n"
14695             + "#   http://www.apache.org/licenses/LICENSE-2.0\n"
14696             + "#\n"
14697             + "# Unless required by applicable law or agreed to in writing, software\n"
14698             + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n"
14699             + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
14700             + "# See the License for the specific language governing permissions and\n"
14701             + "# limitations under the License.\n"
14702             + "\n"
14703             + "#\n"
14704             + "# Subject configuration\n"
14705             + "#\n"
14706             + "\n"
14707             + "# The subject properties uses Grouper Configuration Overlays (documented on wiki)\n"
14708             + "# By default the configuration is read from subject.base.properties\n"
14709             + "# (which should not be edited), and the subject.properties overlays\n"
14710             + "# the base settings.  See the subject.base.properties for the possible\n"
14711             + "# settings that can be applied to the subject.properties\n\n"
14712         );
14713 
14714     subjectPropertiesContents.append(
14715         "# enter the location of the sources.xml.  Must start with classpath: or file:\n"
14716         + "# blank means dont use sources.xml, use subject.properties\n"
14717         + "# default is: classpath:sources.xml\n"
14718         + "# e.g. file:/dir1/dir2/sources.xml\n"
14719         + "subject.sources.xml.location = \n\n");
14720       
14721     //   <source adapterClass="edu.internet2.middleware.grouper.GrouperSourceAdapter">
14722     NodeList sourcesNodeList = GrouperInstallerUtils.xpathEvaluate(sourcesXmlUrl, "/sources/source");
14723     
14724     Set<String> usedConfigNames = new HashSet<String>();
14725     
14726     for (int i=0;i<sourcesNodeList.getLength();i++) {
14727 
14728       Element sourceElement = (Element)sourcesNodeList.item(i);
14729       
14730       String configName = null;
14731       String id = null;
14732       {
14733         //  #########################################
14734         //  ## Configuration for source: whateverId
14735         //  #########################################
14736         //  # generally the <configName> is the same as or similar to the source id.  This cannot have special characters
14737         //  # this links together all the configs for this source
14738         //  # subjectApi.source.<configName>.id = sourceId
14739         id = xmlElementValue(sourceElement, "id", true, "source index " + i);
14740         
14741         //these are configured in subject.base.properties
14742         if (GrouperInstallerUtils.equals(id, "g:gsa")
14743             || GrouperInstallerUtils.equals(id, "grouperEntities")) {
14744           continue;
14745         }
14746         configName = convertEhcacheNameToPropertiesKey(id, usedConfigNames);
14747         usedConfigNames.add(configName);
14748         
14749         subjectPropertiesContents.append(
14750             "\n#########################################\n"
14751             + "## Configuration for source id: " + id + "\n"
14752             + "## Source configName: " + configName + "\n"
14753             + "#########################################\n"
14754             + "subjectApi.source." + configName + ".id = " + id + "\n"
14755             );
14756       }
14757 
14758       {
14759         // <name>Grouper: Group Source Adapter</name>
14760         String name = xmlElementValue(sourceElement, "name", true, "source: " + id);
14761         subjectPropertiesContents.append("\n# this is a friendly name for the source\n"
14762             + "subjectApi.source." + configName + ".name = " + name + "\n");
14763       }
14764       
14765       {
14766         // <type>group</type>
14767         NodeList typeNodeList = sourceElement.getElementsByTagName("type");
14768         Set<String> typeSet = new LinkedHashSet<String>();
14769         
14770         for (int typeIndex=0; typeIndex<typeNodeList.getLength(); typeIndex++) {
14771           
14772           typeSet.add(GrouperInstallerUtils.trimToEmpty(typeNodeList.item(typeIndex).getTextContent()));
14773           
14774         }
14775         if (typeNodeList.getLength() > 0) {
14776           
14777           subjectPropertiesContents.append("\n# type is not used all that much.  Can have multiple types, comma separate.  Can be person, group, application\n"
14778               + "subjectApi.source." + configName + ".types = " + GrouperInstallerUtils.join(typeSet.iterator(), ", ") + "\n"
14779               );
14780         }
14781 
14782       }
14783 
14784       {
14785         NamedNodeMap configuredNamedNodeMap = sourceElement.getAttributes();
14786         if (configuredNamedNodeMap.getLength() != 1 || !"adapterClass".equals(configuredNamedNodeMap.item(0).getNodeName())) {
14787           throw new RuntimeException("Expecting one source attribute: adapterClass for source: " + id);
14788         }
14789         
14790         String adapterClass = sourceElement.getAttribute("adapterClass");
14791 
14792         subjectPropertiesContents.append("\n# the adapter class implements the interface: edu.internet2.middleware.subject.Source\n");
14793         subjectPropertiesContents.append("# adapter class must extend: edu.internet2.middleware.subject.provider.BaseSourceAdapter\n");
14794         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");
14795         subjectPropertiesContents.append("# edu.internet2.middleware.grouper.subj.GrouperJdbcSourceAdapter   :  oldest JDBC source.  Put freeform queries in here\n");
14796         subjectPropertiesContents.append("# edu.internet2.middleware.grouper.subj.GrouperJndiSourceAdapter   :  used for LDAP\n");
14797         subjectPropertiesContents.append("subjectApi.source." + configName + ".adapterClass = " + adapterClass + "\n");
14798       }      
14799 
14800       //  # You can flag a source as not throwing exception on a findAll (general search) i.e. if it is
14801       //  # ok if it is down.  Generally you probably won't want to do this.  It defaults to true if omitted.
14802       //  # subjectApi.source.<configName>.param.throwErrorOnFindAllFailure.value = true
14803 
14804       {
14805         //  <init-param>
14806         //    <param-name>subjectVirtualAttribute_0_searchAttribute0</param-name>
14807         //    <param-value>${subject.getAttributeValue('name')},${subject.getAttributeValue('displayName')},${subject.getAttributeValue('alternateName')}</param-value>
14808         //  </init-param>
14809         NodeList initParamNodeList = sourceElement.getElementsByTagName("init-param");
14810         
14811         Set<String> usedParamNames = new HashSet<String>();
14812 
14813         for (int initParamIndex=0; initParamIndex<initParamNodeList.getLength(); initParamIndex++) {
14814           
14815           Element initParamElement = (Element)initParamNodeList.item(initParamIndex);
14816           String paramName = xmlElementValue(initParamElement, "param-name", true, "param-name index " + initParamIndex + " in source " + id);
14817           String paramValue = xmlElementValue(initParamElement, "param-value", true, "param-value " + paramName + " in source " + id);
14818           
14819           String paramConfigKey = convertEhcacheNameToPropertiesKey(paramName, usedParamNames);
14820           convertSourcesXmlParamComment(paramName, subjectPropertiesContents, paramValue);
14821 
14822           //if the param name is invalid, then have a name config
14823           if (!GrouperInstallerUtils.equals(paramName, paramConfigKey)) {
14824             subjectPropertiesContents.append("subjectApi.source." + configName + ".param." + paramConfigKey + ".name = " + paramName + "\n");
14825           }
14826           
14827           //cant have newlines in there, convert to spaces
14828           paramValue = GrouperInstallerUtils.replaceNewlinesWithSpace(paramValue);
14829           
14830           //  # subjectApi.source.<configName>.param.throwErrorOnFindAllFailure.value = true
14831           subjectPropertiesContents.append("subjectApi.source." + configName + ".param." + paramConfigKey + ".value = " + paramValue + "\n");
14832 
14833         }
14834 
14835       }
14836 
14837       {
14838         //  <search>
14839         //    <searchType>searchSubject</searchType>
14840         //    <param>
14841         //      <param-name>sql</param-name>
14842         //                <param-value>
14843         //                  select
14844         //                    s.subjectid as id, s.name as name,
14845         //      </param-value>
14846         //    </param>
14847         //  </search>
14848         NodeList searchNodeList = sourceElement.getElementsByTagName("search");
14849         
14850         for (int searchIndex=0; searchIndex<searchNodeList.getLength(); searchIndex++) {
14851           
14852           Element searchElement = (Element)searchNodeList.item(searchIndex);
14853           
14854           String searchType = xmlElementValue(searchElement, "searchType", true, "search element in the source: " + id);
14855 
14856           NodeList searchParamNodeList = searchElement.getElementsByTagName("param");
14857 
14858           if (GrouperInstallerUtils.equals(searchType, "searchSubject")) {
14859             subjectPropertiesContents.append("\n#searchSubject: find a subject by ID.  ID is generally an opaque and permanent identifier, e.g. 12345678.\n"
14860                 + "#  Each subject has one and only on ID.  Returns one result when searching for one ID.\n");
14861           } else if (GrouperInstallerUtils.equals(searchType, "searchSubjectByIdentifier")) {
14862             subjectPropertiesContents.append("\n#searchSubjectByIdentifier: find a subject by identifier.  Identifier is anything that uniquely\n"
14863                 + "#  identifies the user, e.g. jsmith or jsmith@institution.edu.\n"
14864                 + "#  Subjects can have multiple identifiers.  Note: it is nice to have if identifiers are unique\n"
14865                 + "#  even across sources.  Returns one result when searching for one identifier.\n");
14866           } else if (GrouperInstallerUtils.equals(searchType, "search")) {
14867             subjectPropertiesContents.append("\n#   search: find subjects by free form search.  Returns multiple results.\n");
14868           } else {
14869             System.out.println("Not expecting searchType: '" + searchType + "'");
14870           }
14871 
14872           for (int searchParamIndex=0; searchParamIndex<searchParamNodeList.getLength(); searchParamIndex++) {
14873             
14874             Element searchParamElement = (Element)searchParamNodeList.item(searchParamIndex);
14875             
14876             String paramName = xmlElementValue(searchParamElement, "param-name", true, 
14877                 "search param name element index " + searchParamIndex + " in the source: " + id);
14878           
14879             String paramValue = xmlElementValue(searchParamElement, "param-value", true, 
14880                 "search param value element index " + searchParamIndex + " in the source: " + id);
14881 
14882             // cant have newlines in a properties file
14883             paramValue = GrouperInstallerUtils.replaceNewlinesWithSpace(paramValue);
14884 
14885             //  #
14886             //  # This is how search params are specified.  Note, each source can have different params for each search type
14887             //  # subjectApi.source.<configName>.search.<searchType>.param.<paramName>.value = something
14888             //  #
14889             //  ##############################################
14890             //  #
14891             //  # Searches for edu.internet2.middleware.grouper.subj.GrouperJdbcConnectionProvider
14892             //  #
14893             //  # searchSubject:
14894             //  #
14895             //  # 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}
14896             //  #    inclause allows searching by subject id for multiple ids in one query
14897             //  # subjectApi.source.<configName>.search.searchSubject.param.inclause.value = s.subjectid = ?
14898 
14899             if (!sourcesValidParamPattern.matcher(paramName).matches()) {
14900               throw new RuntimeException("Source " + id + " search " + searchType + " param name is not valid: '" + paramName + "'");
14901             }
14902             if (GrouperInstallerUtils.equals(searchType, "searchSubject")) {
14903               if (GrouperInstallerUtils.equals("sql", paramName)) {
14904                 subjectPropertiesContents.append("\n# sql is the sql to search for the subject by id should use an {inclause}\n");
14905               } else if (GrouperInstallerUtils.equals("inclause", paramName)) {
14906                 subjectPropertiesContents.append("\n# inclause allows searching by subject for multiple ids or identifiers in one query, must have {inclause} in the sql query,\n"
14907                     + "#    this will be subsituted to in clause with the following.  Should use a question mark ? for bind variable\n");
14908               } else if (GrouperInstallerUtils.equals("filter", paramName)) {
14909                   subjectPropertiesContents.append("\n# sql is the sql to search for the subject by id.  %TERM% will be subsituted by the id searched for\n");
14910               }
14911             } else if (GrouperInstallerUtils.equals(searchType, "searchSubjectByIdentifier")) {
14912               if (GrouperInstallerUtils.equals("sql", paramName)) {
14913                 subjectPropertiesContents.append("\n# sql is the sql to search for the subject by identifier should use an {inclause}\n");
14914               } else if (GrouperInstallerUtils.equals("inclause", paramName)) {
14915                 subjectPropertiesContents.append("\n# inclause allows searching by subject for multiple ids or identifiers in one query, must have {inclause} in the sql query,\n"
14916                     + "#    this will be subsituted to in clause with the following.  Should use a question mark ? for bind variable\n");
14917               } else if (GrouperInstallerUtils.equals("filter", paramName)) {
14918                 subjectPropertiesContents.append("\n# sql is the sql to search for the subject by identifier.  %TERM% will be subsituted by the identifier searched for\n");
14919               }
14920             } else if (GrouperInstallerUtils.equals(searchType, "search")) {
14921               if (GrouperInstallerUtils.equals("sql", paramName)) {
14922                 subjectPropertiesContents.append("\n# sql is the sql to search for the subject free-form search.  user question marks for bind variables\n");
14923               } else if (GrouperInstallerUtils.equals("inclause", paramName)) {
14924                 throw new RuntimeException("Should not have incluse for search of type search in source: " + id);
14925               } else if (GrouperInstallerUtils.equals("filter", paramName)) {
14926                 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");
14927               }
14928             }
14929             if (GrouperInstallerUtils.equals("scope", paramName)) {
14930               subjectPropertiesContents.append("\n# Scope Values can be: OBJECT_SCOPE, ONELEVEL_SCOPE, SUBTREE_SCOPE\n");
14931             } else if (GrouperInstallerUtils.equals("base", paramName)) {
14932               subjectPropertiesContents.append("\n# base dn to search in\n");
14933             }
14934             
14935             //  # subjectApi.source.<configName>.search.<searchType>.param.<paramName>.value = something
14936             subjectPropertiesContents.append("subjectApi.source." + configName + ".search." + searchType + ".param." + paramName + ".value = " + paramValue + "\n");
14937             
14938           }
14939         }
14940       }
14941       
14942       {
14943         // # attributes from ldap object to become subject attributes.  comma separated
14944         // <attribute>cn</attribute>
14945         // <attribute>sn</attribute>
14946         NodeList attributeNodeList = sourceElement.getElementsByTagName("attribute");
14947         Set<String> attributeSet = new LinkedHashSet<String>();
14948 
14949         for (int attributeIndex=0; attributeIndex<attributeNodeList.getLength(); attributeIndex++) {
14950 
14951           attributeSet.add(GrouperInstallerUtils.trimToEmpty(attributeNodeList.item(attributeIndex).getTextContent()));
14952         }
14953         if (attributeNodeList.getLength() > 0) {
14954 
14955           subjectPropertiesContents.append("\n# attributes from ldap object to become subject attributes.  comma separated\n"
14956               + "subjectApi.source." + configName + ".attributes = " + GrouperInstallerUtils.join(attributeSet.iterator(), ", ") + "\n");
14957 
14958         }
14959 
14960       }
14961       
14962       {
14963         // # internal attributes are used by grouper only not exposed to code that uses subjects.  comma separated
14964         // <internal-attributes>cn</internal-attributes>
14965         // <internal-attributes>sn</internal-attributes>
14966         NodeList internalAttributeNodeList = sourceElement.getElementsByTagName("internal-attribute");
14967         Set<String> internalAttributeSet = new LinkedHashSet<String>();
14968 
14969         for (int internalAttributeIndex=0; internalAttributeIndex<internalAttributeNodeList.getLength(); internalAttributeIndex++) {
14970 
14971           internalAttributeSet.add(GrouperInstallerUtils.trimToEmpty(internalAttributeNodeList.item(internalAttributeIndex).getTextContent()));
14972 
14973         }
14974         if (internalAttributeNodeList.getLength() > 0) {
14975 
14976           subjectPropertiesContents.append("\n# internal attributes are used by grouper only not exposed to code that uses subjects.  comma separated\n"
14977               + "subjectApi.source." + configName + ".internalAttributes = " + GrouperInstallerUtils.join(internalAttributeSet.iterator(), ", ") + "\n");
14978 
14979         }
14980 
14981       }
14982       //space between sources
14983       subjectPropertiesContents.append("\n");
14984     }
14985 
14986     GrouperInstallerUtils.saveStringIntoFile(subjectPropertiesFile, subjectPropertiesContents.toString());
14987 
14988   }
14989 
14990   /**
14991    * edit an xml file attribute in a xml file
14992    * @param file
14993    * @param elementName
14994    * @param elementMustHaveAttributeAndValue
14995    * @param newValue
14996    * @param description of change for sys out print
14997    * @param newAttributeName if adding new attribute, this is the name
14998    * @return true if edited file, or false if not but didnt need to, null if not found
14999    */
15000   public static Boolean editXmlFileAttribute(File file, String elementName, Map<String, String> elementMustHaveAttributeAndValue, 
15001       String newAttributeName, String newValue, String description) {
15002 
15003     if (!file.exists() || file.length() == 0) {
15004       throw new RuntimeException("Why does " + file.getName() + " not exist and have contents? " 
15005           + file.getAbsolutePath());
15006     }
15007     
15008     String fileContents = GrouperInstallerUtils.readFileIntoString(file);
15009     
15010     boolean inComment = false;
15011     
15012     //lets parse the file and get to the element
15013     OUTER: for (int i=0;i<fileContents.length();i++) {
15014       
15015       //look for start element
15016       char curChar = fileContents.charAt(i);
15017 
15018       Character nextChar = (i+1) < fileContents.length() ? fileContents.charAt(i+1) : null;
15019       Character nextNextChar = (i+2) < fileContents.length() ? fileContents.charAt(i+2) : null;
15020       Character nextNextNextChar = (i+3) < fileContents.length() ? fileContents.charAt(i+3) : null;
15021       
15022       //if we are in comment, see when we are out of comment
15023       if (inComment) {
15024         if (curChar == '-' && nextChar != null && nextChar == '-' && nextNextChar != null && nextNextChar == '>') {
15025           inComment = false;
15026         }
15027         continue;
15028         
15029       }
15030 
15031       //look for a tag or comment
15032       if (curChar != '<') {
15033         continue;
15034       }
15035       
15036       //see if this is a comment
15037       if (nextChar != null && nextChar == '!' && nextNextChar != null && nextNextChar == '-' && nextNextNextChar != null && nextNextNextChar == '-') {
15038         inComment = true;
15039         continue;
15040       }
15041 
15042       //get tagName
15043       String currentElementName = _internalXmlTagName(fileContents, i+1);
15044       
15045       //not the right tag
15046       if (!GrouperInstallerUtils.equals(currentElementName, elementName)) {
15047         continue;
15048       }
15049       
15050       int tagNameStart = fileContents.indexOf(currentElementName, i+1);
15051       
15052       //get the attributes
15053       int tagAttributesStart = tagNameStart + currentElementName.length();
15054       XmlParseAttributesResult xmlParseAttributesResult = _internalXmlParseAttributes(fileContents, tagAttributesStart);
15055       Map<String, String> currentAttributes = xmlParseAttributesResult.getAttributes();
15056       
15057       if (GrouperInstallerUtils.length(elementMustHaveAttributeAndValue) > 0) {
15058         for (String attributeName : elementMustHaveAttributeAndValue.keySet()) {
15059           String expectedValue = elementMustHaveAttributeAndValue.get(attributeName);
15060           String hasValue = currentAttributes.get(attributeName);
15061 
15062           //if we dont have that value, then keep going
15063           if (!GrouperInstallerUtils.equals(expectedValue, hasValue)) {
15064             continue OUTER;
15065           }
15066         }
15067       }
15068       
15069       //we have the tag and it has the expected attributes
15070 
15071       //see if the attribute is even there...
15072       if (!currentAttributes.containsKey(newAttributeName)) {
15073         System.out.println(" - adding " + description + " with value: '" + newValue + "'");
15074         String newFileContents = fileContents.substring(0, tagAttributesStart) + " " + newAttributeName + "=\"" + newValue + 
15075             "\" " + fileContents.substring(tagAttributesStart, fileContents.length());
15076         GrouperInstallerUtils.writeStringToFile(file, newFileContents);
15077         return true;
15078       }
15079 
15080       //does it already have the value?
15081       String currentValue = currentAttributes.get(newAttributeName);
15082       
15083       //value is already there
15084       if (GrouperInstallerUtils.equals(currentValue, newValue)) {
15085         return false;
15086       }
15087 
15088       //it has the wrong value
15089       int startQuote = xmlParseAttributesResult.getAttributeStartIndex().get(newAttributeName);
15090       int endQuote = xmlParseAttributesResult.getAttributeEndIndex().get(newAttributeName);
15091 
15092       System.out.println(" - changing " + description + " from old value: '" + currentValue 
15093           + "' to new value: '" + newValue + "'");
15094 
15095       String newFileContents = fileContents.substring(0, startQuote+1)  + newValue + 
15096           fileContents.substring(endQuote, fileContents.length());
15097       GrouperInstallerUtils.writeStringToFile(file, newFileContents);
15098       return true;
15099 
15100     }
15101 
15102     return null;
15103 
15104   }
15105 
15106   /**
15107    * 
15108    * @param fileContents
15109    * @param tagIndexStart
15110    * @return the tag name
15111    */
15112   private static String _internalXmlTagName(String fileContents, int tagIndexStart) {
15113     StringBuilder tagName = new StringBuilder();
15114     for (int i=tagIndexStart; i<fileContents.length(); i++) {
15115       char curChar = fileContents.charAt(i);
15116       if (tagName.length() == 0 && Character.isWhitespace(curChar)) {
15117         continue;
15118       }
15119       if (Character.isWhitespace(curChar) || '/' == curChar || '>' == curChar) {
15120         return tagName.toString();
15121       }
15122       tagName.append(curChar);
15123     }
15124     throw new RuntimeException("How did I get here???? '" + tagName.toString() + "'");
15125   }
15126   
15127   /**
15128    * xml parse attribute result
15129    */
15130   private static class XmlParseAttributesResult {
15131 
15132     /**
15133      * attributes name to value
15134      */
15135     private Map<String, String> attributes;
15136     
15137     /**
15138      * attribute name to startIndex (of quote)
15139      */
15140     private Map<String, Integer> attributeStartIndex;
15141 
15142     /**
15143      * attribute name to endIndex (of quote)
15144      */
15145     private Map<String, Integer> attributeEndIndex;
15146 
15147     
15148     /**
15149      * attributes name to value
15150      * @return the attributes
15151      */
15152     public Map<String, String> getAttributes() {
15153       return this.attributes;
15154     }
15155 
15156     
15157     /**
15158      * attributes name to value
15159      * @param attributes1 the attributes to set
15160      */
15161     public void setAttributes(Map<String, String> attributes1) {
15162       this.attributes = attributes1;
15163     }
15164 
15165     
15166     /**
15167      * attribute name to startIndex (of quote)
15168      * @return the attributeStartIndex
15169      */
15170     public Map<String, Integer> getAttributeStartIndex() {
15171       return this.attributeStartIndex;
15172     }
15173     
15174     /**
15175      * attribute name to startIndex (of quote)
15176      * @param attributeStartIndex1 the attributeStartIndex to set
15177      */
15178     public void setAttributeStartIndex(Map<String, Integer> attributeStartIndex1) {
15179       this.attributeStartIndex = attributeStartIndex1;
15180     }
15181     
15182     /**
15183      * attribute name to endIndex (of quote)
15184      * @return the attributeEndIndex
15185      */
15186     public Map<String, Integer> getAttributeEndIndex() {
15187       return this.attributeEndIndex;
15188     }
15189     
15190     /**
15191      * attribute name to endIndex (of quote)
15192      * @param attributeEndIndex1 the attributeEndIndex to set
15193      */
15194     public void setAttributeEndIndex(Map<String, Integer> attributeEndIndex1) {
15195       this.attributeEndIndex = attributeEndIndex1;
15196     }
15197     
15198   }
15199 
15200   /**
15201    * 
15202    */
15203   public static enum GrouperInstallerAdminManageServiceAction {
15204   
15205     /** start */
15206     start,
15207     
15208     /** stop */
15209     stop,
15210     
15211     /** restart */
15212     restart,
15213     
15214     /** status */
15215     status;
15216     
15217     /**
15218      * 
15219      * @param string
15220      * @param exceptionIfInvalid
15221      * @param exceptionIfBlank
15222      * @return the action
15223      */
15224     public static GrouperInstallerAdminManageServiceAction valueOfIgnoreCase(String string, boolean exceptionIfBlank, boolean exceptionIfInvalid) {
15225       return GrouperInstallerUtils.enumValueOfIgnoreCase(GrouperInstallerAdminManageServiceAction.class, string, exceptionIfBlank, exceptionIfInvalid);
15226     }
15227     
15228   }
15229 
15230   /**
15231    * parse attributes
15232    * @param fileContents
15233    * @param tagAttributesStart is the index where the attributes start
15234    * @return the map of attribute names and values
15235    */
15236   private static XmlParseAttributesResult _internalXmlParseAttributes(String fileContents, int tagAttributesStart) {
15237 
15238     XmlParseAttributesResult xmlParseAttributesResult = new XmlParseAttributesResult();
15239 
15240     Map<String, String> attributes = new LinkedHashMap<String, String>();
15241     Map<String, Integer> attributeStartIndex = new LinkedHashMap<String, Integer>();
15242     Map<String, Integer> attributeEndIndex = new LinkedHashMap<String, Integer>();
15243 
15244     xmlParseAttributesResult.setAttributes(attributes);
15245     xmlParseAttributesResult.setAttributeStartIndex(attributeStartIndex);
15246     xmlParseAttributesResult.setAttributeEndIndex(attributeEndIndex);
15247     
15248     boolean inAttributeStartValue = false;
15249     boolean inAttributeStartName = true;
15250     boolean inAttributeName = false;
15251     boolean inAttributeValue = false;
15252     
15253     StringBuilder attributeName = null;
15254     StringBuilder attributeValue = null;
15255     
15256     for (int i=tagAttributesStart; i<fileContents.length(); i++) {
15257       char curChar = fileContents.charAt(i);
15258       boolean isWhitespace = Character.isWhitespace(curChar);
15259 
15260       //waiting for the attribute
15261       if ((inAttributeStartValue || inAttributeStartName) && isWhitespace) {
15262         continue;
15263       }
15264 
15265       //if waiting for value and equals, keep looking
15266       if (inAttributeStartValue && curChar == '=') {
15267         continue;
15268       }
15269 
15270       //waiting to start an attribute name, its not whitespace so do it
15271       if (inAttributeStartName) {
15272         
15273         //we done if we got to this character
15274         if (curChar == '/' || curChar == '>') {
15275           return xmlParseAttributesResult;
15276         }
15277         
15278         inAttributeStartName = false;
15279         inAttributeName = true;
15280         attributeName = new StringBuilder();
15281       }
15282 
15283       //if in an attribute name and whitespace or equals, then we are done
15284       if (inAttributeName && (isWhitespace || curChar == '=' )) {
15285         inAttributeName = false;
15286         inAttributeStartValue = true;
15287         continue;
15288       }
15289 
15290       //getting the attribute name
15291       if (inAttributeName) {
15292         attributeName.append(curChar);
15293         continue;
15294       }
15295 
15296       //if waiting for start value and found quote
15297       if (inAttributeStartValue && curChar == '"') {
15298         inAttributeStartValue = false;
15299         inAttributeValue = true;
15300         attributeValue = new StringBuilder();
15301         attributeStartIndex.put(attributeName.toString(), i);
15302         continue;
15303       }
15304 
15305       //if in attribute value and not quote, append the char
15306       if (inAttributeValue && curChar != '"') {
15307         attributeValue.append(curChar);
15308         continue;
15309       }
15310 
15311       //done with attribute value
15312       if (inAttributeValue && curChar == '"') {
15313         inAttributeValue = false;
15314         inAttributeStartName = true;
15315         if (attributes.containsKey(attributeName.toString())) {
15316           throw new RuntimeException("Duplicate attribute: " + attributeName.toString());
15317         }
15318         attributes.put(attributeName.toString(), attributeValue.toString());
15319         attributeEndIndex.put(attributeName.toString(), i);
15320         continue;
15321       }
15322 
15323       throw new RuntimeException("Why are we here? " + i + ", " + fileContents);
15324     }
15325     return xmlParseAttributesResult;
15326   }
15327   
15328   /** revert patch excludes */
15329   private static Set<String> revertPatchExcludes = new HashSet<String>();
15330   
15331   static {
15332     revertPatchExcludes.add("grouper.cache.properties");
15333     revertPatchExcludes.add("ehcache.xml");
15334     revertPatchExcludes.add("ehcache.example.xml");
15335   }
15336 }