View Javadoc
1   /**
2    * Copyright 2005-2014 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.ken.xpath;
17  
18  import org.apache.commons.io.IOUtils;
19  import org.apache.xerces.jaxp.JAXPConstants;
20  import org.junit.Test;
21  import org.kuali.rice.ken.test.KENTestCase;
22  import org.kuali.rice.ken.util.DocumentNamespaceContext;
23  import org.kuali.rice.ken.util.SimpleErrorHandler;
24  import org.kuali.rice.ken.util.Util;
25  import org.kuali.rice.test.BaselineTestCase.BaselineMode;
26  import org.kuali.rice.test.BaselineTestCase.Mode;
27  import org.w3c.dom.Document;
28  import org.xml.sax.InputSource;
29  
30  import javax.xml.parsers.DocumentBuilder;
31  import javax.xml.parsers.DocumentBuilderFactory;
32  import javax.xml.xpath.XPath;
33  import javax.xml.xpath.XPathConstants;
34  import javax.xml.xpath.XPathFactory;
35  import java.io.IOException;
36  import java.io.InputStream;
37  
38  import static org.junit.Assert.assertEquals;
39  
40  
41  /**
42   * Unit test that tests the affects of various document parsing (DocumentBuilderFactory)
43   * and XPath (XPath) flags, such as validation, namespace awareness, and namespace context.
44   * Lessons learned:
45   * <ul>
46   *   <li>DocumentBuilder namespace awareness needs to be turned on for validation to work</li>
47   *   <li>XPath absolutely requires a working NamespaceContext and qualified node names in expressions
48   *       if operating against a DOM which is the result of a validating parse</li>
49   *   <li>There is no apparent way to set the "default" namespace for XPath...so even when NamespaceContext
50   *       is set, nodes must be qualified.  The only way to obtain the "default" namespace is to explicitly
51   *       qualify with an empty namespace, e.g. /:notification/:channel (which is ugly and potentially confusing)</li>
52   *   <li>When deriving NamespaceContext from validated DOM, the "default" namespace must therefore be explicitly
53   *       registered with a prefix (which is redundant) so the NamespaceContext lookup can succeed.  The alternative
54   *       is to predefine the prefix in the NamespaceContent (which can be done by using a CompositeNamespaceContenxt
55   *       consisting of a ConfiguredNamespaceContext and a DocumentNamespaceContext).</li>
56   * </ul>
57   * @author Kuali Rice Team (rice.collab@kuali.org)
58   */
59  @BaselineMode(Mode.CLEAR_DB)
60  public class XPathTest extends KENTestCase {
61      private static final String TEST_XML = "sample_message_event_type.xml";
62  
63      protected InputSource getTestXMLInputSource() {
64          InputStream is = XPathTest.class.getResourceAsStream(TEST_XML);
65          if (is != null) {
66              try {
67                  LOG.info(IOUtils.toString(is));
68              } catch (IOException e) {
69                  throw new RuntimeException(e);
70              } finally {
71                  IOUtils.closeQuietly(is);
72              }
73  
74              is = XPathTest.class.getResourceAsStream(TEST_XML);
75          }
76          return new InputSource(is);
77      }
78  
79      protected XPath getXPath(Document doc) {
80          XPath xpath = XPathFactory.newInstance().newXPath();
81          if (doc != null) {
82              xpath.setNamespaceContext(new DocumentNamespaceContext(doc));
83          } else {
84              xpath.setNamespaceContext(Util.NOTIFICATION_NAMESPACE_CONTEXT);
85          }
86          return xpath;
87      }
88  
89      protected Document getDocument(boolean namespaceAware, boolean validate) throws Exception {
90          // TODO: optimize this
91          final InputSource source = getTestXMLInputSource();
92          DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
93          dbf.setValidating(validate);
94          dbf.setNamespaceAware(namespaceAware);
95          dbf.setAttribute(JAXPConstants.JAXP_SCHEMA_LANGUAGE, JAXPConstants.W3C_XML_SCHEMA);
96          DocumentBuilder db = dbf.newDocumentBuilder();
97          LOG.info("Setting entityresolver");
98          db.setEntityResolver(Util.getNotificationEntityResolver(services.getNotificationContentTypeService()));
99          db.setErrorHandler(new SimpleErrorHandler(LOG));
100         return db.parse(source);
101     }
102 
103     @Test
104     public void testXPathWithPlainDOM() throws Exception {
105         Document doc = getDocument(false, false);
106         XPath xpath = getXPath(null);
107         String channelName = (String) xpath.evaluate("/notification/channel", doc.getDocumentElement(), XPathConstants.STRING);
108         assertEquals("Test Channel #1", channelName);
109     }
110     @Test
111     public void testXPathWithNamespaceAwareDOM() throws Exception {
112         Document doc = getDocument(true, false);
113         XPath xpath = getXPath(null);
114         String channelName = (String) xpath.evaluate("/nreq:notification/nreq:channel", doc.getDocumentElement(), XPathConstants.STRING);
115         assertEquals("Test Channel #1", channelName);
116     }
117     @Test
118     public void testXPathWithValidatedDOMFixedNamespace() throws Exception {
119         LOG.debug("TEST");
120         Document doc = getDocument(true, true);
121         LOG.info("Default namespace: " + doc.lookupNamespaceURI(null));
122         XPath xpath = getXPath(null);
123         String channelName = (String) xpath.evaluate("/nreq:notification/nreq:channel", doc.getDocumentElement(), XPathConstants.STRING);
124         assertEquals("Test Channel #1", channelName);
125     }
126     @Test
127     public void testXPathWithValidatedDOMDocNamespace() throws Exception {
128         LOG.debug("TEST");
129         Document doc = getDocument(true, true);
130         LOG.info("Default namespace: " + doc.lookupNamespaceURI(null));
131         LOG.info("default prefix: " + doc.lookupPrefix(doc.lookupNamespaceURI(null)));
132         XPath xpath = XPathFactory.newInstance().newXPath();
133         xpath.setNamespaceContext(Util.getNotificationNamespaceContext(doc));
134         String channelName = (String) xpath.evaluate("/nreq:notification/nreq:channel", doc.getDocumentElement(), XPathConstants.STRING);
135         assertEquals("Test Channel #1", channelName);
136     }
137 }