View Javadoc
1   /**
2    * Copyright 2005-2015 The Kuali Foundation
3    *
4    * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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 org.kuali.rice.krad.devtools.maintainablexml;
17  
18  import java.io.BufferedReader;
19  import java.io.File;
20  import java.io.IOException;
21  import java.io.InputStreamReader;
22  import java.util.Arrays;
23  import java.util.HashMap;
24  import javax.xml.parsers.DocumentBuilder;
25  import javax.xml.parsers.DocumentBuilderFactory;
26  
27  import org.apache.commons.lang.StringUtils;
28  import org.w3c.dom.Document;
29  import org.w3c.dom.Element;
30  import org.w3c.dom.Node;
31  import org.w3c.dom.NodeList;
32  
33  /**
34   * This is a command line utility class which upgrades the maintenance document xml stored in krns_maint_doc_t.doc_cntnt
35   * to be able to still open and use any maintenance documents that were enroute at the time of an upgrade to Rice 2.0.
36   *
37   * <p>Instructions:</p>
38   * <ol>
39   *   <li>Backup database.</li>
40   *   <li>Add the conversion rules to the rules xml file -
41   *       ..\rice\development-tools\src\main\resources\org\kuali\rice\devtools\krad\maintainablexml\MaintainableXMLUpgradeRules.xml
42   *       See comments in the xml file to setup the rules.</li>
43   *   <li>Run this class.  Note that any classes being converted must be on the classpath.</li>
44   *   <li>Enter the rice config file location that has the database connection properties. Only enter the location relative
45   *       to user.dir.  NOTE -- If it is uncommented in ConvertMaintainableXML.java, MaintainableXMLConversionConfig.xml
46   *       in the user.dir will be used as the conversion config file.  runmode, fromRange, and toRange can be
47   *       specified in this file to avoid prompting the user for this information.  An example file can be found in
48   *        ..\rice\development-tools\src\main\resources\org\kuali\rice\devtools\krad\maintainablexml\MaintainableXMLConversionConfig.xml
49   *   </li>
50   *   <li>If it is not specified in the config file, the user will be prompted for a run mode. Mode 1 will do the xml
51   *   upgrade and update the krns_maint_doc_t table with the new xml. CANNOT ROLL BACK AFTER THIS HAS BEEN DONE.
52   *   Mode 2 will only print out the old xml and the new xml - this can be used to test the rules xml setup.</li>
53   *   <li>If it is not specified in the config file, the user will be prompted for the document number to upgrade from.
54   *   Enter "all" in the from range prompt to upgrade all documents.  Otherwise this should be an starting document ID
55   *   such as 1000</li>
56   *   <li>If it is not specified in the config file and the fromRange is not "all", the user will be prompted for the document
57   *   number to upgrade to.  If "all" was entered for the fromRange, toRange will be ignored.  toRange should be a
58   *   document ID such as 9999</li>
59   * </ol>
60   *
61   * @author Kuali Rice Team (rice.collab@kuali.org)
62   */
63  public class ConvertMaintainableXML {
64  
65      private static final String MAINTAINABLE_CONVERSION_CONFIG_FILE = "maintainable.conversion.rule.file";
66  
67      public static void main(String[] args) {
68  
69          // NOTE : If you want the user to be prompted for the config settings file name, Leave this set to ""
70          // Otherwise uncomment out the second line and it will look for the file MaintainableXMLConversionConfig.xml
71  
72          String settingsFile = "";
73          //settingsFile =  "MaintainableXMLConversionConfig.xml";
74  
75          if (StringUtils.isEmpty(settingsFile)) {
76              settingsFile = readInput("Enter config file location relative to Kuali main user directory "
77                  + "(ie. dev/sample-app-config.xml OR MaintainableXMLConversionConfig.xml) : ", null, false);
78          }
79  
80          String filePath = System.getProperty("user.home") + "/" + settingsFile;
81  
82          File file = new File(filePath);
83          if (!file.exists()) {
84              System.out.println("The settings file location is not valid : \n" + filePath);
85              System.exit(1);
86          }
87  
88          try {
89              HashMap settingsMap = getSettings(filePath);
90              System.out.println("Using the following settings : " + settingsMap + "\n");
91  
92              //  RUN MODE
93              String runMode = (String)settingsMap.get("runMode");
94              if (StringUtils.isEmpty(runMode) || !(StringUtils.equals(runMode, "1") || StringUtils.equals(runMode, "2"))) {
95                  runMode = readInput("Run mode :\n1. Update xml in DB\n2. Print old and new xml\nEnter 1 or 2\n",
96                      new String[]{"1", "2"}, false);
97              }
98  
99              //  FROM RANGE
100             String fromRange = (String)settingsMap.get("fromRange");
101             if (StringUtils.isEmpty(fromRange) || (!StringUtils.equals(fromRange, "all") && !(isNumber(fromRange)))) {
102                 fromRange = readInput("Please enter document start range value ('all', empty line for all, or the starting document number such as 1000) :\n",
103                     new String[]{"all", ""}, true);
104             }
105 
106             String toRange = null;
107             boolean hasRangeParameters = false;
108 
109             if (!"".equals(fromRange) && !"all".equals(fromRange)) {
110                 //  TO RANGE, if needed
111                 toRange = (String)settingsMap.get("toRange");
112                 if (StringUtils.isEmpty(toRange) || !(isNumber(toRange))) {
113                     toRange = readInput("Please enter end range value :\n", null, true);
114                 }
115                 System.out.println("Upgrading document numbers from " + fromRange + " to " + toRange + "\n");
116                 hasRangeParameters = true;
117             }
118 
119             FileConverter fileConverter = new FileConverter();
120 
121             // RUN THE CONVERSION!
122             fileConverter.runFileConversion(settingsMap, runMode, fromRange, toRange, hasRangeParameters);
123 
124         } catch (Exception e) {
125             System.out.println("Error executing conversion : " + e.getMessage());
126             e.printStackTrace();
127         }
128     }
129 
130     public static boolean isNumber(String string) {
131         try {
132             Integer.parseInt(string);
133         } catch (Exception e) {
134             return false;
135         }
136         return true;
137     }
138 
139     /**
140      * Displays message in command line and read user input. Checks that entered values are within valid input.
141      *
142      * @param message - the message string to print out
143      * @param validOptions - the allowed user input
144      * @return the string input from the user
145      */
146     private static String readInput(String message, String[] validOptions, boolean numeric) {
147         System.out.print(message);
148         BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
149 
150         String inputString = null;
151 
152         try {
153             inputString = br.readLine();
154             if (numeric && (validOptions == null || !Arrays.asList(validOptions).contains(inputString))) {
155                 Integer.parseInt(inputString);
156                 return inputString;
157             }
158         } catch (IOException ioe) {
159             System.out.println("IO error trying to read input!");
160             System.exit(1);
161         } catch (NumberFormatException nfe) {
162             System.out.println("Invalid Option! Must be numeric.");
163             readInput(message, validOptions, numeric);
164         }
165         if (validOptions != null && !Arrays.asList(validOptions).contains(inputString)) {
166             System.out.println("Invalid Option!");
167             readInput(message, validOptions, numeric);
168         }
169         return inputString;
170     }
171 
172     /**
173      * Parses settings file and put the properties in a map.
174      *
175      * @param filePath - the location of the settings file
176      * @return a HashMap populated with the settings
177      * @throws Exception
178      */
179     public static HashMap getSettings(String filePath) throws Exception {
180         File file = new File(filePath);
181         HashMap params = new HashMap();
182         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
183         DocumentBuilder db = dbf.newDocumentBuilder();
184         Document doc = db.parse(file);
185         doc.getDocumentElement().normalize();
186         NodeList nodeLst = doc.getElementsByTagName("param");
187 
188         for (int s = 0; s < nodeLst.getLength(); s++) {
189             Node fstNode = nodeLst.item(s);
190             if (fstNode.getNodeType() == Node.ELEMENT_NODE) {
191                 Element fstElmnt = (Element) fstNode;
192                 String paramName = fstElmnt.getAttribute("name");
193                 NodeList textFNList = fstElmnt.getChildNodes();
194                 if (textFNList.item(0) != null) {
195                     String paramValue = ((Node) textFNList.item(0)).getNodeValue().trim();
196                     params.put(paramName, paramValue);
197                 } else {
198                     params.put(paramName, null);
199                 }
200             }
201         }
202         return params;
203     }
204 
205 }