001: /*
002: * Copyright 2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.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/ecl1.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.workflow.attribute;
017:
018: import java.io.StringReader;
019: import java.io.StringWriter;
020: import java.sql.Connection;
021: import java.sql.ResultSet;
022: import java.sql.SQLException;
023: import java.sql.Statement;
024: import java.util.HashSet;
025: import java.util.Set;
026:
027: import javax.sql.DataSource;
028: import javax.xml.transform.Result;
029: import javax.xml.transform.Source;
030: import javax.xml.transform.TransformerException;
031: import javax.xml.transform.TransformerFactory;
032: import javax.xml.transform.dom.DOMSource;
033: import javax.xml.transform.stream.StreamResult;
034: import javax.xml.xpath.XPath;
035: import javax.xml.xpath.XPathConstants;
036:
037: import junit.framework.AssertionFailedError;
038:
039: import org.apache.commons.lang.StringUtils;
040: import org.apache.commons.logging.Log;
041: import org.apache.commons.logging.LogFactory;
042: import org.kuali.core.datadictionary.AttributeDefinition;
043: import org.kuali.core.datadictionary.BusinessObjectEntry;
044: import org.kuali.core.datadictionary.DocumentEntry;
045: import org.kuali.core.service.DataDictionaryService;
046: import org.kuali.kfs.context.KualiTestBase;
047: import org.kuali.kfs.context.SpringContext;
048: import org.kuali.rice.definition.ObjectDefinition;
049: import org.kuali.rice.resourceloader.GlobalResourceLoader;
050: import org.kuali.test.ConfigureContext;
051: import org.kuali.workflow.KualiWorkflowUtils;
052: import org.w3c.dom.NamedNodeMap;
053: import org.w3c.dom.Node;
054: import org.w3c.dom.NodeList;
055: import org.xml.sax.InputSource;
056:
057: import edu.iu.uis.eden.EdenConstants;
058: import edu.iu.uis.eden.routetemplate.RuleAttribute;
059: import edu.iu.uis.eden.routetemplate.xmlrouting.XPathHelper;
060: import edu.iu.uis.eden.xml.XmlConstants;
061:
062: /**
063: * This class test that the labels used by workflow in the UI are coming from the KFS data dictionary. It also indirectly test if
064: * routing is working, since the xpath is parsed to find the label in the data dictionary. If the label isn't found, it is likely
065: * that the xpath will also fail to find the element in the document xml. The fact that the name and title are unique for each field
066: * within the attribute is also verfied.
067: */
068: @ConfigureContext
069: public class KualiXmlAttributeImplTest extends KualiTestBase {
070:
071: private static Log LOG = LogFactory
072: .getLog(KualiXmlRuleAttributeImpl.class);
073:
074: private static final String RULE_ATTRIBUTE_CONFIG_NODE_NAME = XmlConstants.ROUTING_CONFIG;
075: private static final String SEARCH_ATTRIBUTE_CONFIG_NODE_NAME = XmlConstants.SEARCHING_CONFIG;
076:
077: XPath myXPath = XPathHelper.newXPath();
078: String ruleAttributeXml = "";
079: String searchAttributeXml = "";
080: private boolean testFailed = false;
081:
082: @Override
083: public void setUp() throws Exception {
084: super .setUp();
085: if ((StringUtils.isNotBlank(ruleAttributeXml))
086: && (StringUtils.isNotBlank(searchAttributeXml))) {
087: return;
088: }
089:
090: DataSource mySource = SpringContext.getBean(DataSource.class);
091: ruleAttributeXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<data >\n<ruleAttributes>\n";
092: searchAttributeXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<data >\n<ruleAttributes>\n";
093: Connection dbCon = null;
094: try {
095:
096: dbCon = mySource.getConnection();
097: Statement dbAsk = dbCon.createStatement();
098: ResultSet dbAnswer = dbAsk
099: .executeQuery("select * from EN_RULE_ATTRIB_T");
100: // ResultSet dbAnswer = dbAsk.executeQuery("select * from EN_RULE_ATTRIB_T where RULE_ATTRIB_NM =
101: // 'SystemParameterRoutingAttribute'");
102:
103: while (dbAnswer.next()) {
104: String className = dbAnswer
105: .getString("RULE_ATTRIB_CLS_NM");
106: if (StringUtils.isNotBlank(className)) {
107: try {
108: if (KualiXmlAttribute.class
109: .isAssignableFrom(Class
110: .forName(className))) {
111: LOG
112: .debug("Adding attribute to test with class name "
113: + className);
114: String attributeType = dbAnswer
115: .getString("RULE_ATTRIB_TYP");
116: if (EdenConstants.RULE_XML_ATTRIBUTE_TYPE
117: .equals(attributeType)) {
118: ruleAttributeXml = ruleAttributeXml
119: + "<ruleAttribute>\n\t<name>";
120: ruleAttributeXml = ruleAttributeXml
121: + dbAnswer
122: .getString("RULE_ATTRIB_NM");
123: ruleAttributeXml = ruleAttributeXml
124: + "</name>\n\t<className>";
125: ruleAttributeXml = ruleAttributeXml
126: + className;
127: ruleAttributeXml = ruleAttributeXml
128: + "</className>\n\t<label>";
129: ruleAttributeXml = ruleAttributeXml
130: + dbAnswer
131: .getString("RULE_ATTRIB_LBL_TXT");
132: ruleAttributeXml = ruleAttributeXml
133: + "</label>\n\t<description>";
134: ruleAttributeXml = ruleAttributeXml
135: + dbAnswer
136: .getString("RULE_ATTRIB_DESC");
137: ruleAttributeXml = ruleAttributeXml
138: + "</description>\n\t<type>";
139: ruleAttributeXml = ruleAttributeXml
140: + attributeType;
141: ruleAttributeXml = ruleAttributeXml
142: + "</type>\n\t"
143: + dbAnswer
144: .getString("RULE_ATTRIB_XML_RTE_TXT")
145: + "\n</ruleAttribute>\n";
146:
147: } else if (EdenConstants.SEARCHABLE_XML_ATTRIBUTE_TYPE
148: .equals(attributeType)) {
149: searchAttributeXml = searchAttributeXml
150: + "<ruleAttribute>\n\t<name>";
151: searchAttributeXml = searchAttributeXml
152: + dbAnswer
153: .getString("RULE_ATTRIB_NM");
154: searchAttributeXml = searchAttributeXml
155: + "</name>\n\t<className>";
156: searchAttributeXml = searchAttributeXml
157: + className;
158: searchAttributeXml = searchAttributeXml
159: + "</className>\n\t<label>";
160: searchAttributeXml = searchAttributeXml
161: + dbAnswer
162: .getString("RULE_ATTRIB_LBL_TXT");
163: searchAttributeXml = searchAttributeXml
164: + "</label>\n\t<description>";
165: searchAttributeXml = searchAttributeXml
166: + dbAnswer
167: .getString("RULE_ATTRIB_DESC");
168: searchAttributeXml = searchAttributeXml
169: + "</description>\n\t<type>";
170: searchAttributeXml = searchAttributeXml
171: + attributeType;
172: searchAttributeXml = searchAttributeXml
173: + "</type>\n\t"
174: + dbAnswer
175: .getString("RULE_ATTRIB_XML_RTE_TXT")
176: + "\n</ruleAttribute>\n";
177:
178: }
179: } else {
180: LOG
181: .debug("Skipping attribute with class name "
182: + className);
183: }
184: } catch (ClassNotFoundException cnfe) {
185: LOG.debug("Could not find class for name '"
186: + className + "'");
187: }
188: }
189: }
190: ruleAttributeXml = ruleAttributeXml
191: + "</ruleAttributes>\n</data>\n";
192: searchAttributeXml = searchAttributeXml
193: + "</ruleAttributes>\n</data>\n";
194:
195: ruleAttributeXml = ruleAttributeXml.replaceAll(" & ",
196: " & ");
197: searchAttributeXml = searchAttributeXml.replaceAll(" & ",
198: " & ");
199:
200: loadDataDictionaryEntries();
201: } catch (Exception e) {
202: e.printStackTrace();
203: } finally {
204: try {
205: dbCon.close();
206: } catch (SQLException sqle2) {
207: sqle2.printStackTrace();
208: }
209: }
210: }
211:
212: /**
213: * This method goes through all of the ruleAttributes in the inputSource and tries to get a label out of the data dictionary.
214: */
215: public void testConfirmLabels() {
216: testFailed = false;
217:
218: // test rule xml attributes
219: confirmLabels(KualiXmlAttributeHelper.notFound,
220: ruleAttributeXml, RULE_ATTRIBUTE_CONFIG_NODE_NAME);
221:
222: // test search xml attributes
223: confirmLabels(KualiXmlAttributeHelper.notFound,
224: searchAttributeXml, SEARCH_ATTRIBUTE_CONFIG_NODE_NAME);
225:
226: assertFalse("testConfirmLabels failed", testFailed);
227: }
228:
229: /**
230: * This method accepts a Node, and if all goes well, returns the exact same Node, with the name and title attributes added to
231: * the fieldDef element. This exercises the getConfigXML method on the class under test.
232: *
233: * @param xmlNode
234: * @return
235: * @throws TransformerException
236: */
237: private Node configureRuleAttribute(Node xmlNode,
238: KualiXmlAttribute myAttribute) throws TransformerException {
239: RuleAttribute ruleAttribute = new RuleAttribute();
240:
241: StringWriter xmlBuffer = new StringWriter();
242: Source source = new DOMSource(xmlNode);
243: Result result = new StreamResult(xmlBuffer);
244: TransformerFactory.newInstance().newTransformer().transform(
245: source, result);
246:
247: ruleAttribute
248: .setXmlConfigData(new String(xmlBuffer.getBuffer()));
249: myAttribute.setRuleAttribute(ruleAttribute);
250:
251: if (LOG.isDebugEnabled()) {
252: LOG
253: .debug("This is the XML that was added to the attribute");
254: LOG.debug(new String(xmlBuffer.getBuffer()));
255: StringWriter xmlBuffer2 = new StringWriter();
256: Source source2 = new DOMSource(xmlNode);
257: Result result2 = new StreamResult(xmlBuffer2);
258: TransformerFactory.newInstance().newTransformer()
259: .transform(source2, result2);
260: LOG
261: .debug("This is the XML that was returned from the ruleAttribute");
262: LOG.debug(new String(xmlBuffer2.getBuffer()));
263: }
264: return myAttribute.getConfigXML();
265: }
266:
267: /**
268: * This method compares the label from the test to the expected, or not expected, value for all of the rule attributes in the
269: * file. The inputSource file should be as close to the production version as possible, as described by the class comments. It
270: * accepts the string to test against as a parameter.
271: *
272: * @param testString
273: */
274: private void confirmLabels(String testString, String attributeXml,
275: String configNodeName) {
276: String theTitle = "";
277: String theName = "";
278: String attributeName = "";
279: try {
280: NodeList tempList = (NodeList) myXPath.evaluate(
281: "//ruleAttribute", new InputSource(
282: new StringReader(attributeXml)),
283: XPathConstants.NODESET);
284: for (int i = 0; i < tempList.getLength(); i++) { // loop over ruleattributes
285: Node originalNode = tempList.item(i);
286: Set ruleAttributeFieldDefNames = new HashSet();
287: Set ruleAttributeFieldDefTitles = new HashSet();
288: attributeName = (String) myXPath
289: .evaluate(
290: KualiWorkflowUtils.XSTREAM_MATCH_RELATIVE_PREFIX
291: + "name", originalNode,
292: XPathConstants.STRING);
293: Node classNameNode = (Node) myXPath
294: .evaluate(
295: KualiWorkflowUtils.XSTREAM_MATCH_RELATIVE_PREFIX
296: + "className", originalNode,
297: XPathConstants.NODE);
298: if ((classNameNode != null)
299: && (classNameNode.getFirstChild() != null)) {
300: KualiXmlAttribute myAttribute = (KualiXmlAttribute) GlobalResourceLoader
301: .getObject(new ObjectDefinition(
302: classNameNode.getFirstChild()
303: .getNodeValue()));
304: Node xmlNode = configureRuleAttribute(originalNode,
305: myAttribute);
306: NamedNodeMap fieldDefAttributes = null;
307: String potentialFailMessage = "";
308:
309: try {
310: NodeList xmlNodeList = (NodeList) myXPath
311: .evaluate("//fieldDef", xmlNode,
312: XPathConstants.NODESET);
313:
314: for (int j = 0; j < xmlNodeList.getLength(); j++) {
315: Node fieldDefXmlNode = xmlNodeList.item(j);
316: fieldDefAttributes = fieldDefXmlNode
317: .getAttributes();
318:
319: theTitle = fieldDefAttributes.getNamedItem(
320: "title").getNodeValue();// Making sure they are clean
321: theName = fieldDefAttributes.getNamedItem(
322: "name").getNodeValue();
323: if (LOG.isDebugEnabled()) {
324: LOG.debug(attributeName);
325: LOG.debug("name=" + theName
326: + " title=" + theTitle);
327: }
328: if (ruleAttributeFieldDefNames
329: .contains(theName)) {
330: // names of fieldDefs inside a single attribute must be unique
331: potentialFailMessage = "Each fieldDef name on a single attribute must be unique and the fieldDef name '"
332: + theName
333: + "' already exists on the attribute '"
334: + attributeName + "'";
335: fail(potentialFailMessage);
336: } else {
337: ruleAttributeFieldDefNames.add(theName);
338: }
339: if (testString
340: .equals(KualiXmlAttributeHelper.notFound)) {
341: potentialFailMessage = "Each fieldDef title should be a valid value and currently the title for attribute '"
342: + attributeName
343: + "' is '"
344: + theTitle + "'";
345: assertFalse(potentialFailMessage,
346: theTitle.equals(testString));
347: if (ruleAttributeFieldDefTitles
348: .contains(theTitle)) {
349: /*
350: * Titles of fieldDefs inside a single attribute should be unique in the normal case. Having two
351: * fields with the same label would certainly confuse the user. However, due to the way the
352: * confirmSource test works, all the titles/labels must be the same. So only run this check when
353: * not in the confirmSource test.
354: */
355: potentialFailMessage = "Each fieldDef title on a single attribute must be unique and the fieldDef title '"
356: + theTitle
357: + "' already exists on the attribute '"
358: + attributeName + "'";
359: fail(potentialFailMessage);
360: } else {
361: ruleAttributeFieldDefTitles
362: .add(theTitle);
363: }
364: } else {
365: potentialFailMessage = "For attribute '"
366: + attributeName
367: + "' the title should have been '"
368: + testString
369: + "' but was actually '"
370: + theTitle + "'";
371: assertEquals(potentialFailMessage,
372: testString, theTitle);
373: }
374: }
375: } catch (AssertionFailedError afe) {
376: LOG.warn("Assertion Failed for attribute '"
377: + attributeName + "' with error "
378: + potentialFailMessage, afe);
379: testFailed = true;
380: } finally {
381: attributeName = "";
382: }
383: } else {
384: throw new RuntimeException(
385: "Could not find class for attribute named '"
386: + attributeName + "'");
387: }
388: }
389: } catch (Exception e) {
390: LOG.error("General Exception thrown for attribute '"
391: + attributeName + "'", e);
392: testFailed = true;
393: }
394: }
395:
396: /**
397: * This method confirms that the labels are coming from the data dictionary by modifing all the dictionary values
398: * programatically to a nonsense value. It then rebuilds the Hash Table and runs confirmLabels() to make sure the labels have
399: * changed.
400: */
401: public void testLabelSource() {
402: DataDictionaryService myDDService = SpringContext
403: .getBean(DataDictionaryService.class);
404: XPath xpath = XPathHelper.newXPath();
405: String nonsenseString = "BananaRama";
406: for (Object tempEntity : myDDService.getDataDictionary()
407: .getBusinessObjectEntries().values()) {
408:
409: Object[] tempArray = (((BusinessObjectEntry) tempEntity)
410: .getAttributes().keySet().toArray());
411: for (Object tempVal : tempArray) {
412:
413: ((AttributeDefinition) ((BusinessObjectEntry) tempEntity)
414: .getAttributes().get(tempVal))
415: .setLabel(nonsenseString);
416: ((AttributeDefinition) ((BusinessObjectEntry) tempEntity)
417: .getAttributes().get(tempVal))
418: .setShortLabel(nonsenseString);
419: }
420:
421: }
422: for (Object tempEntity : myDDService.getDataDictionary()
423: .getDocumentEntries().values()) {
424:
425: Object[] tempArray = (((DocumentEntry) tempEntity)
426: .getAttributes().keySet().toArray());
427: for (Object tempVal : tempArray) {
428: ((AttributeDefinition) ((DocumentEntry) tempEntity)
429: .getAttributes().get(tempVal))
430: .setLabel(nonsenseString);
431: ((AttributeDefinition) ((DocumentEntry) tempEntity)
432: .getAttributes().get(tempVal))
433: .setShortLabel(nonsenseString);
434: }
435:
436: }
437: // KualiXmlAttributeHelper.buildDictionaryHash();
438:
439: // test rule xml attributes
440: KualiXmlAttribute ruleAttribute = new KualiXmlRuleAttributeImpl();
441: confirmLabels(nonsenseString, ruleAttributeXml,
442: RULE_ATTRIBUTE_CONFIG_NODE_NAME);
443:
444: // test search xml attributes
445: KualiXmlAttribute searchAttribute = new KualiXmlSearchableAttributeImpl();
446: confirmLabels(nonsenseString, searchAttributeXml,
447: SEARCH_ATTRIBUTE_CONFIG_NODE_NAME);
448:
449: if (testFailed)
450: throw new AssertionFailedError("testLabelSource failed");
451: }
452:
453: private void loadDataDictionaryEntries() throws Exception {
454: KualiXmlRuleAttributeImpl myAttribute = new KualiXmlRuleAttributeImpl();
455: NamedNodeMap fieldDefAttributes = null;
456: NodeList tempList = (NodeList) myXPath.evaluate(
457: "//ruleAttribute", new InputSource(new StringReader(
458: ruleAttributeXml)), XPathConstants.NODESET);
459: for (int i = 0; i < tempList.getLength(); i++) {
460: Node xmlNode = configureRuleAttribute(tempList.item(i),
461: myAttribute);
462: }
463: KualiXmlSearchableAttributeImpl mySearchAttribute = new KualiXmlSearchableAttributeImpl();
464: fieldDefAttributes = null;
465: tempList = (NodeList) myXPath.evaluate("//ruleAttribute",
466: new InputSource(new StringReader(searchAttributeXml)),
467: XPathConstants.NODESET);
468: for (int i = 0; i < tempList.getLength(); i++) {
469: Node xmlNode = configureRuleAttribute(tempList.item(i),
470: mySearchAttribute);
471: }
472: }
473: }
|