001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.jmeter.assertions;
020:
021: import java.io.ByteArrayInputStream;
022: import java.io.IOException;
023: import java.io.Serializable;
024:
025: import javax.xml.parsers.ParserConfigurationException;
026: import javax.xml.transform.TransformerException;
027:
028: import org.apache.jmeter.samplers.SampleResult;
029: import org.apache.jmeter.testelement.AbstractTestElement;
030: import org.apache.jmeter.testelement.property.BooleanProperty;
031: import org.apache.jmeter.testelement.property.StringProperty;
032: import org.apache.jmeter.util.XPathUtil;
033: import org.apache.jorphan.logging.LoggingManager;
034: import org.apache.log.Logger;
035: import org.apache.xpath.XPathAPI;
036: import org.apache.xpath.objects.XObject;
037: import org.w3c.dom.Document;
038: import org.w3c.dom.NodeList;
039: import org.xml.sax.SAXException;
040:
041: /**
042: * Checks if the result is a well-formed XML content and whether it matches an
043: * XPath
044: *
045: * author <a href="mailto:jspears@astrology.com">Justin Spears </a>
046: */
047: public class XPathAssertion extends AbstractTestElement implements
048: Serializable, Assertion {
049: private static final Logger log = LoggingManager
050: .getLoggerForClass();
051:
052: // private static XPathAPI xpath = null;
053:
054: private static final String XPATH_KEY = "XPath.xpath";
055:
056: private static final String WHITESPACE_KEY = "XPath.whitespace";
057:
058: private static final String VALIDATE_KEY = "XPath.validate";
059:
060: private static final String TOLERANT_KEY = "XPath.tolerant";
061:
062: private static final String NEGATE_KEY = "XPath.negate";
063:
064: private static final String NAMESPACE_KEY = "XPath.namespace";
065:
066: public static final String DEFAULT_XPATH = "/";
067:
068: /**
069: * Returns the result of the Assertion. Checks if the result is well-formed
070: * XML, and that the XPath expression is matched (or not, as the case may
071: * be)
072: */
073: public AssertionResult getResult(SampleResult response) {
074: // no error as default
075: AssertionResult result = new AssertionResult(getName());
076: byte[] responseData = response.getResponseData();
077: if (responseData.length == 0) {
078: return result.setResultForNull();
079: }
080: result.setFailure(false);
081: result.setFailureMessage("");
082:
083: if (log.isDebugEnabled()) {
084: log.debug(new StringBuffer("Validation is set to ").append(
085: isValidating()).toString());
086: log.debug(new StringBuffer("Whitespace is set to ").append(
087: isWhitespace()).toString());
088: log.debug(new StringBuffer("Tolerant is set to ").append(
089: isTolerant()).toString());
090: }
091:
092: Document doc = null;
093:
094: try {
095: doc = XPathUtil.makeDocument(new ByteArrayInputStream(
096: responseData), isValidating(), isWhitespace(),
097: isNamespace(), isTolerant());
098: } catch (SAXException e) {
099: log.debug("Caught sax exception: " + e);
100: result.setError(true);
101: result.setFailureMessage(new StringBuffer("SAXException: ")
102: .append(e.getMessage()).toString());
103: return result;
104: } catch (IOException e) {
105: log.warn("Cannot parse result content", e);
106: result.setError(true);
107: result.setFailureMessage(new StringBuffer("IOException: ")
108: .append(e.getMessage()).toString());
109: return result;
110: } catch (ParserConfigurationException e) {
111: log.warn("Cannot parse result content", e);
112: result.setError(true);
113: result.setFailureMessage(new StringBuffer(
114: "ParserConfigurationException: ").append(
115: e.getMessage()).toString());
116: return result;
117: }
118:
119: if (doc == null || doc.getDocumentElement() == null) {
120: result.setError(true);
121: result
122: .setFailureMessage("Document is null, probably not parsable");
123: return result;
124: }
125:
126: NodeList nodeList = null;
127:
128: final String pathString = getXPathString();
129: try {
130: XObject xObject = XPathAPI.eval(doc, pathString);
131: switch (xObject.getType()) {
132: case XObject.CLASS_NODESET:
133: nodeList = xObject.nodelist();
134: break;
135: case XObject.CLASS_BOOLEAN:
136: if (!xObject.bool()) {
137: result.setFailure(!isNegated());
138: result.setFailureMessage("No Nodes Matched "
139: + pathString);
140: }
141: return result;
142: default:
143: result.setFailure(true);
144: result.setFailureMessage("Cannot understand: "
145: + pathString);
146: return result;
147: }
148: } catch (TransformerException e) {
149: result.setError(true);
150: result.setFailureMessage(new StringBuffer(
151: "TransformerException: ").append(e.getMessage())
152: .append(" for:").append(pathString).toString());
153: return result;
154: }
155:
156: if (nodeList == null || nodeList.getLength() == 0) {
157: log.debug(new StringBuffer("nodeList null no match ")
158: .append(pathString).toString());
159: result.setFailure(!isNegated());
160: result.setFailureMessage("No Nodes Matched " + pathString);
161: return result;
162: }
163: log.debug("nodeList length " + nodeList.getLength());
164: if (log.isDebugEnabled() & !isNegated()) {
165: for (int i = 0; i < nodeList.getLength(); i++)
166: log.debug(new StringBuffer("nodeList[").append(i)
167: .append("] ").append(nodeList.item(i))
168: .toString());
169: }
170: result.setFailure(isNegated());
171: if (isNegated())
172: result
173: .setFailureMessage("Specified XPath was found... Turn off negate if this is not desired");
174: return result;
175: }
176:
177: /**
178: * Get The XPath String that will be used in matching the document
179: *
180: * @return String xpath String
181: */
182: public String getXPathString() {
183: return getPropertyAsString(XPATH_KEY, DEFAULT_XPATH);
184: }
185:
186: /**
187: * Set the XPath String this will be used as an xpath
188: *
189: * @param xpath
190: * String
191: */
192: public void setXPathString(String xpath) {
193: setProperty(new StringProperty(XPATH_KEY, xpath));
194: }
195:
196: /**
197: * Set whether to ignore element whitespace
198: *
199: * @param whitespace
200: */
201: public void setWhitespace(boolean whitespace) {
202: setProperty(new BooleanProperty(WHITESPACE_KEY, whitespace));
203: }
204:
205: /**
206: * Set use validation
207: *
208: * @param validate
209: */
210: public void setValidating(boolean validate) {
211: setProperty(new BooleanProperty(VALIDATE_KEY, validate));
212: }
213:
214: /**
215: * Set whether this is namespace aware
216: *
217: * @param namespace
218: */
219: public void setNamespace(boolean namespace) {
220: setProperty(new BooleanProperty(NAMESPACE_KEY, namespace));
221: }
222:
223: /**
224: * Set tolerant mode if required
225: *
226: * @param tolerant
227: * true/false
228: */
229: public void setTolerant(boolean tolerant) {
230: setProperty(new BooleanProperty(TOLERANT_KEY, tolerant));
231: }
232:
233: public void setNegated(boolean negate) {
234: setProperty(new BooleanProperty(NEGATE_KEY, negate));
235: }
236:
237: /**
238: * Is this whitepsace ignored.
239: *
240: * @return boolean
241: */
242: public boolean isWhitespace() {
243: return getPropertyAsBoolean(WHITESPACE_KEY, false);
244: }
245:
246: /**
247: * Is this validating
248: *
249: * @return boolean
250: */
251: public boolean isValidating() {
252: return getPropertyAsBoolean(VALIDATE_KEY, false);
253: }
254:
255: /**
256: * Is this namespace aware?
257: *
258: * @return boolean
259: */
260: public boolean isNamespace() {
261: return getPropertyAsBoolean(NAMESPACE_KEY, false);
262: }
263:
264: /**
265: * Is this using tolerant mode?
266: *
267: * @return boolean
268: */
269: public boolean isTolerant() {
270: return getPropertyAsBoolean(TOLERANT_KEY, false);
271: }
272:
273: /**
274: * Negate the XPath test, that is return true if something is not found.
275: *
276: * @return boolean negated
277: */
278: public boolean isNegated() {
279: return getPropertyAsBoolean(NEGATE_KEY, false);
280: }
281:
282: }
|