001 /**
002 * Copyright 2005-2013 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.kuali.rice.krad.devtools.maintainablexml;
017
018 import java.io.BufferedReader;
019 import java.io.File;
020 import java.io.IOException;
021 import java.io.InputStreamReader;
022 import java.util.Arrays;
023 import java.util.HashMap;
024 import javax.xml.parsers.DocumentBuilder;
025 import javax.xml.parsers.DocumentBuilderFactory;
026
027 import org.apache.commons.lang.StringUtils;
028 import org.w3c.dom.Document;
029 import org.w3c.dom.Element;
030 import org.w3c.dom.Node;
031 import org.w3c.dom.NodeList;
032
033 /**
034 * This is a command line utility class which upgrades the maintenance document xml stored in krns_maint_doc_t.doc_cntnt
035 * to be able to still open and use any maintenance documents that were enroute at the time of an upgrade to Rice 2.0.
036 *
037 * <p>Instructions:</p>
038 * <ol>
039 * <li>Backup database.</li>
040 * <li>Add the conversion rules to the rules xml file -
041 * ..\rice\development-tools\src\main\resources\org\kuali\rice\devtools\krad\maintainablexml\MaintainableXMLUpgradeRules.xml
042 * See comments in the xml file to setup the rules.</li>
043 * <li>Run this class. Note that any classes being converted must be on the classpath.</li>
044 * <li>Enter the rice config file location that has the database connection properties. Only enter the location relative
045 * to user.dir. NOTE -- If it is uncommented in ConvertMaintainableXML.java, MaintainableXMLConversionConfig.xml
046 * in the user.dir will be used as the conversion config file. runmode, fromRange, and toRange can be
047 * specified in this file to avoid prompting the user for this information. An example file can be found in
048 * ..\rice\development-tools\src\main\resources\org\kuali\rice\devtools\krad\maintainablexml\MaintainableXMLConversionConfig.xml
049 * </li>
050 * <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
051 * upgrade and update the krns_maint_doc_t table with the new xml. CANNOT ROLL BACK AFTER THIS HAS BEEN DONE.
052 * Mode 2 will only print out the old xml and the new xml - this can be used to test the rules xml setup.</li>
053 * <li>If it is not specified in the config file, the user will be prompted for the document number to upgrade from.
054 * Enter "all" in the from range prompt to upgrade all documents. Otherwise this should be an starting document ID
055 * such as 1000</li>
056 * <li>If it is not specified in the config file and the fromRange is not "all", the user will be prompted for the document
057 * number to upgrade to. If "all" was entered for the fromRange, toRange will be ignored. toRange should be a
058 * document ID such as 9999</li>
059 * </ol>
060 *
061 * @author Kuali Rice Team (rice.collab@kuali.org)
062 */
063 public class ConvertMaintainableXML {
064
065 private static final String MAINTAINABLE_CONVERSION_CONFIG_FILE = "maintainable.conversion.rule.file";
066
067 public static void main(String[] args) {
068
069 // NOTE : If you want the user to be prompted for the config settings file name, Leave this set to ""
070 // Otherwise uncomment out the second line and it will look for the file MaintainableXMLConversionConfig.xml
071
072 String settingsFile = "";
073 //settingsFile = "MaintainableXMLConversionConfig.xml";
074
075 if (StringUtils.isEmpty(settingsFile)) {
076 settingsFile = readInput("Enter config file location relative to Kuali main user directory "
077 + "(ie. dev/sample-app-config.xml OR MaintainableXMLConversionConfig.xml) : ", null, false);
078 }
079
080 String filePath = System.getProperty("user.home") + "/" + settingsFile;
081
082 File file = new File(filePath);
083 if (!file.exists()) {
084 System.out.println("The settings file location is not valid : \n" + filePath);
085 System.exit(1);
086 }
087
088 try {
089 HashMap settingsMap = getSettings(filePath);
090 System.out.println("Using the following settings : " + settingsMap + "\n");
091
092 // RUN MODE
093 String runMode = (String)settingsMap.get("runMode");
094 if (StringUtils.isEmpty(runMode) || !(StringUtils.equals(runMode, "1") || StringUtils.equals(runMode, "2"))) {
095 runMode = readInput("Run mode :\n1. Update xml in DB\n2. Print old and new xml\nEnter 1 or 2\n",
096 new String[]{"1", "2"}, false);
097 }
098
099 // 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 }