View Javadoc

1   /**
2    * Copyright 2005-2012 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  /*
17   * To change this template, choose Tools | Templates
18   * and open the template in the editor.
19   */
20  package org.kuali.rice.krad.maintainablexml;
21  
22  import java.io.StringReader;
23  import java.io.StringWriter;
24  import java.sql.PreparedStatement;
25  import java.sql.ResultSet;
26  import java.sql.SQLException;
27  import java.util.ArrayList;
28  import java.util.HashMap;
29  import java.util.logging.Level;
30  import java.util.logging.Logger;
31  import javax.sql.DataSource;
32  import javax.xml.parsers.DocumentBuilder;
33  import javax.xml.parsers.DocumentBuilderFactory;
34  import javax.xml.transform.OutputKeys;
35  import javax.xml.transform.Transformer;
36  import javax.xml.transform.TransformerFactory;
37  import javax.xml.transform.dom.DOMSource;
38  import javax.xml.transform.stream.StreamResult;
39  import javax.xml.xpath.XPath;
40  import javax.xml.xpath.XPathConstants;
41  import javax.xml.xpath.XPathExpression;
42  import javax.xml.xpath.XPathFactory;
43  
44  import org.apache.commons.lang.StringUtils;
45  import org.springframework.jdbc.core.BatchPreparedStatementSetter;
46  import org.springframework.jdbc.core.JdbcTemplate;
47  import org.springframework.jdbc.core.RowCallbackHandler;
48  import org.springframework.jdbc.datasource.DriverManagerDataSource;
49  import org.w3c.dom.Document;
50  import org.w3c.dom.Element;
51  import org.w3c.dom.NodeList;
52  import org.xml.sax.InputSource;
53  
54  /**
55   * Used to upgrade the maintenance document xml stored in krns_maint_doc_t.doc_cntnt
56   * to be able to still open and use any maintenance documents that were enroute at the time of an upgrade to Rice 2.0.
57   *
58   * @author Kuali Rice Team (rice.collab@kuali.org)
59   */
60  
61  public class FileConverter {
62  
63      private HashMap<String, String> classNameRuleMap;
64      private HashMap<String, String> packageNameRuleMap;
65      private HashMap<String, String> maintImplRuleMap;
66      private HashMap<String, HashMap<String, String>> classPropertyRuleMap;
67      private JdbcTemplate jdbcTemplate;
68      private int totalDocs = 0;
69  
70      /**
71       * Selects all the encrypted xml documents from krns_maint_doc_t, decrypts them, runs the rules to upgrade them,
72       * encrypt them and update krns_maint_doc_t with the upgraded xml.
73       *
74       * @param settingsMap - the settings
75       * @throws Exception
76       */
77      public void runFileConversion(HashMap settingsMap, final String runMode, final String fromRange,
78              final String toRange, final boolean hasRangeParameters) throws Exception {
79  
80          final EncryptionService encryptService = new EncryptionService((String) settingsMap.get("encryption.key"));
81  
82          if (classNameRuleMap == null) {
83              setRuleMaps();
84          }
85  
86          String docSQL = "SELECT DOC_HDR_ID, DOC_CNTNT FROM krns_maint_doc_t ";
87  
88          // If user entered range add the sql parameters and filter results because DOC_HDR_ID is a varchar field.
89          if (hasRangeParameters) {
90              docSQL = docSQL.concat(" WHERE DOC_HDR_ID >= '" + fromRange + "' AND DOC_HDR_ID <= '" + toRange + "'");
91          }
92  
93          jdbcTemplate = new JdbcTemplate(getDataSource(settingsMap));
94          jdbcTemplate.query(docSQL, new RowCallbackHandler() {
95  
96              public void processRow(ResultSet rs) throws SQLException {
97                  // Check that all docId's is in range
98                  if (hasRangeParameters) {
99                      int docId = Integer.parseInt(rs.getString(1));
100                     if (docId >= Integer.parseInt(fromRange) && docId <= Integer.parseInt(toRange)) {
101                         processDocumentRow(rs.getString(1), rs.getString(2), encryptService, runMode);
102                     }
103                 } else {
104                     processDocumentRow(rs.getString(1), rs.getString(2), encryptService, runMode);
105                 }
106             }
107         });
108 
109         System.out.println(totalDocs + " maintenance documents upgraded.");
110 
111     }
112 
113     /**
114      * Creates the data source from the settings map
115      *
116      * @param settingsMap - the settingMap containing the db connection settings
117      * @return the DataSource object
118      */
119     public static DataSource getDataSource(HashMap settingsMap) {
120         String driver = "";
121         if ("MySQL".equals(settingsMap.get("datasource.ojb.platform"))) {
122             driver = "com.mysql.jdbc.Driver";
123         } else if ("Oracle9i".equals(settingsMap.get("datasource.ojb.platform"))) {
124             driver = "oracle.jdbc.driver.OracleDriver";
125         } else {
126             driver = (String) settingsMap.get("datasource.driver.name");
127         }
128 
129         DriverManagerDataSource dataSource = new DriverManagerDataSource();
130         dataSource.setDriverClassName(driver);
131         dataSource.setUrl((String) settingsMap.get("datasource.url"));
132         dataSource.setUsername((String) settingsMap.get("datasource.username"));
133         dataSource.setPassword((String) settingsMap.get("datasource.password"));
134         return dataSource;
135     }
136 
137     /**
138      * Called for each row in the processRow method of the spring query. Upgrades the xml and update the
139      * krns_maint_doc_t table.
140      *
141      * @param docId - the document id string
142      * @param docCntnt - the old xml string
143      * @param encryptServ - the encryption service used to encrypt/decrypt the xml
144      */
145     public void processDocumentRow(String docId, String docCntnt, EncryptionService encryptServ, String runMode) {
146         System.out.println(docId);
147         try {
148             String oldXml = encryptServ.decrypt(docCntnt);
149             if ("2".equals(runMode)) {
150                 System.out.println("------ ORIGINAL DOC XML --------");
151                 System.out.println(oldXml);
152                 System.out.println("--------------------------------");
153             }
154             String newXML = upgradeXML(oldXml);
155             if ("2".equals(runMode)) {
156                 System.out.println("******* UPGRADED DOC XML ********");
157                 System.out.println(newXML);
158                 System.out.println("*********************************\n");
159             }
160             if ("1".equals(runMode)) {
161                 jdbcTemplate.update("update krns_maint_doc_t set DOC_CNTNT = ? where DOC_HDR_ID = ?",
162                         new Object[]{encryptServ.encrypt(newXML), docId});
163             }
164             totalDocs++;
165         } catch (Exception ex) {
166             Logger.getLogger(FileConverter.class.getName()).log(Level.SEVERE, null, ex);
167             System.exit(1);
168         }
169     }
170 
171     /**
172      * Upgrades the xml using the rule maps executing the following actions :
173      * 1. Replace class names from rules. 2. Upgrade BO notes 3. Update class property names from rules
174      * 4. Set MaintainableImplClass name from rules.
175      *
176      * @param oldXML - the old xml that must be upgraded
177      * @return the upgraded xml string
178      * @throws Exception
179      */
180     public String upgradeXML(String oldXML) throws Exception {
181 
182         // Replace class names
183         for (String key : classNameRuleMap.keySet()) {
184             oldXML = oldXML.replaceAll(key, classNameRuleMap.get(key));
185         }
186 
187         // Replace package names
188         for (String key : packageNameRuleMap.keySet()) {
189             oldXML = oldXML.replaceAll(key, packageNameRuleMap.get(key));
190         }
191 
192         // Upgrade Bo notes
193         oldXML = upgradeBONotes(oldXML);
194 
195         // Replace or remove the property names of the classes
196         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
197         DocumentBuilder db = dbf.newDocumentBuilder();
198         InputSource is = new InputSource(new StringReader(oldXML));
199         Document doc = db.parse(is);
200         doc.getDocumentElement().normalize();
201         XPath xpath = XPathFactory.newInstance().newXPath();
202         for (String key : classPropertyRuleMap.keySet()) {
203             HashMap<String, String> properties = classPropertyRuleMap.get(key);
204             for (String keyProperties : properties.keySet()) {
205                 XPathExpression exprMaintainableObject = xpath.compile("//" + key + "/" + keyProperties);
206                 NodeList propertyNodeList = (NodeList) exprMaintainableObject.evaluate(doc, XPathConstants.NODESET);
207                 for (int s = 0; s < propertyNodeList.getLength(); s++) {
208                     if (properties.get(keyProperties).equals("")) {
209                         propertyNodeList.item(s).getParentNode().removeChild(propertyNodeList.item(s));
210                     } else {
211                         doc.renameNode(propertyNodeList.item(s), null, properties.get(keyProperties));
212                     }
213                 }
214             }
215         }
216 
217         // Replace MaintainableImplClass names
218         for (String key : maintImplRuleMap.keySet()) {
219             // Only do replace for files containing the maintainable object class
220             if (oldXML.contains(key)) {
221                 String maintImpl = maintImplRuleMap.get(key);
222                 XPathExpression exprMaintainableTest = xpath.compile(
223                         "//maintainableDocumentContents/newMaintainableObject/" + key);
224                 NodeList exprMaintainableTestList = (NodeList) exprMaintainableTest.evaluate(doc,
225                         XPathConstants.NODESET);
226                 if (exprMaintainableTestList.getLength() > 0) {
227                     XPathExpression exprMaintainableObject = xpath.compile("//maintainableDocumentContents");
228                     NodeList exprMaintainableNodeList = (NodeList) exprMaintainableObject.evaluate(doc,
229                             XPathConstants.NODESET);
230                     if (exprMaintainableNodeList.getLength() > 0) {
231                         ((Element) exprMaintainableNodeList.item(0)).setAttribute("maintainableImplClass", maintImpl);
232                     }
233                 }
234             }
235 
236         }
237 
238         // Dom to string
239 
240         doc.getDocumentElement().normalize();
241 
242         TransformerFactory transFactory = TransformerFactory.newInstance();
243         Transformer trans = transFactory.newTransformer();
244         trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
245         trans.setOutputProperty(OutputKeys.INDENT, "yes");
246 
247         StringWriter sw = new StringWriter();
248         StreamResult result = new StreamResult(sw);
249         DOMSource source = new DOMSource(doc);
250         trans.transform(source, result);
251         // Remove empty lines where properties has been removed
252         oldXML = sw.toString().replaceAll("(?m)^\\s+\\n", "");
253 
254         return oldXML;
255     }
256 
257     /**
258      * Upgrades the old Bo notes tag that was part of the maintainable to the new notes tag.
259      *
260      * @param oldXML - the xml to upgrade
261      * @throws Exception
262      */
263     private String upgradeBONotes(String oldXML) throws Exception {
264         // Get the old bo note xml
265         String notesXml = StringUtils.substringBetween(oldXML, "<boNotes>", "</boNotes>");
266         if (notesXml != null) {
267             notesXml = notesXml.replace("org.kuali.rice.kns.bo.Note", "org.kuali.rice.krad.bo.Note");
268             notesXml = "<org.apache.ojb.broker.core.proxy.ListProxyDefaultImpl>\n"
269                     + notesXml
270                     + "\n</org.apache.ojb.broker.core.proxy.ListProxyDefaultImpl>";
271             oldXML = oldXML.replaceFirst(">", ">\n<notes>\n" + notesXml + "\n</notes>");
272         }
273         return oldXML;
274     }
275 
276     /**
277      * Reads the rule xml and sets up the rule maps that will be used to transform the xml
278      */
279     public void setRuleMaps() {
280         classNameRuleMap = new HashMap();
281         packageNameRuleMap = new HashMap();
282         classPropertyRuleMap = new HashMap();
283         maintImplRuleMap = new HashMap();
284         try {
285             DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
286             DocumentBuilder db = dbf.newDocumentBuilder();
287 
288             Document doc = db.parse(getClass().getResourceAsStream(
289                     "/org/kuali/rice/devtools/krad/maintainablexml/MaintainableXMLUpgradeRules.xml"));
290             doc.getDocumentElement().normalize();
291             XPath xpath = XPathFactory.newInstance().newXPath();
292 
293             // Get the moved classes rules
294 
295             XPathExpression exprClassNames = xpath.compile("//*[@name='maint_doc_classname_changes']/pattern");
296             NodeList classNamesList = (NodeList) exprClassNames.evaluate(doc, XPathConstants.NODESET);
297             for (int s = 0; s < classNamesList.getLength(); s++) {
298                 String matchText = xpath.evaluate("match/text()", classNamesList.item(s));
299                 String replaceText = xpath.evaluate("replacement/text()", classNamesList.item(s));
300                 classNameRuleMap.put(matchText, replaceText);
301             }
302 
303             // Get the package change rules
304 
305             XPathExpression exprPackageNames = xpath.compile("//*[@name='maint_doc_moved_packages']/pattern");
306             NodeList packageNamesList = (NodeList) exprClassNames.evaluate(doc, XPathConstants.NODESET);
307             for (int s = 0; s < classNamesList.getLength(); s++) {
308                 String matchText = xpath.evaluate("match/text()", classNamesList.item(s));
309                 String replaceText = xpath.evaluate("replacement/text()", classNamesList.item(s));
310                 packageNameRuleMap.put(matchText, replaceText);
311             }
312 
313             // Get the property changed rules
314 
315             XPathExpression exprClassProperties = xpath.compile(
316                     "//*[@name='maint_doc_changed_class_properties']/pattern");
317             XPathExpression exprClassPropertiesPatterns = xpath.compile("pattern");
318             NodeList propertyClassList = (NodeList) exprClassProperties.evaluate(doc, XPathConstants.NODESET);
319             for (int s = 0; s < propertyClassList.getLength(); s++) {
320                 String classText = xpath.evaluate("class/text()", propertyClassList.item(s));
321                 HashMap propertyRuleMap = new HashMap();
322                 NodeList classPropertiesPatterns = (NodeList) exprClassPropertiesPatterns.evaluate(
323                         propertyClassList.item(s), XPathConstants.NODESET);
324                 for (int c = 0; c < classPropertiesPatterns.getLength(); c++) {
325                     String matchText = xpath.evaluate("match/text()", classPropertiesPatterns.item(c));
326                     String replaceText = xpath.evaluate("replacement/text()", classPropertiesPatterns.item(c));
327                     propertyRuleMap.put(matchText, replaceText);
328                 }
329                 classPropertyRuleMap.put(classText, propertyRuleMap);
330             }
331 
332             // Get the maint impl class rule
333 
334             XPathExpression exprMaintImpl = xpath.compile("//*[@name='maint_doc_impl_classes']/pattern");
335             NodeList maintImplList = (NodeList) exprMaintImpl.evaluate(doc, XPathConstants.NODESET);
336             for (int s = 0; s < maintImplList.getLength(); s++) {
337                 String maintainableText = xpath.evaluate("maintainable/text()", maintImplList.item(s));
338                 String maintainableImplText = xpath.evaluate("maintainableImpl/text()", maintImplList.item(s));
339                 maintImplRuleMap.put(maintainableText, maintainableImplText);
340             }
341 
342         } catch (Exception e) {
343             System.out.println("Error parsing rule xml file. Please check file. : " + e.getMessage());
344             System.exit(1);
345         }
346     }
347 }