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: package org.apache.jmeter.assertions;
019:
020: import java.io.IOException;
021: import java.io.Serializable;
022: import java.io.StringReader;
023:
024: import javax.xml.parsers.DocumentBuilder;
025: import javax.xml.parsers.DocumentBuilderFactory;
026: import javax.xml.parsers.ParserConfigurationException;
027:
028: import org.apache.jmeter.samplers.SampleResult;
029: import org.apache.jmeter.testelement.AbstractTestElement;
030: import org.apache.jorphan.logging.LoggingManager;
031: import org.apache.jorphan.util.JOrphanUtils;
032: import org.apache.log.Logger; // import org.w3c.dom.Document;
033: import org.xml.sax.ErrorHandler;
034: import org.xml.sax.InputSource;
035: import org.xml.sax.SAXException;
036: import org.xml.sax.SAXParseException;
037:
038: // See Bug 34383
039:
040: /**
041: * XMLSchemaAssertion.java Validate response against an XML Schema author <a
042: * href="mailto:d.maung@mdl.com">Dave Maung</a>
043: *
044: */
045: public class XMLSchemaAssertion extends AbstractTestElement implements
046: Serializable, Assertion {
047: public static final String FILE_NAME_IS_REQUIRED = "FileName is required";
048:
049: public static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
050:
051: public static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";
052:
053: public static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";
054:
055: private static final Logger log = LoggingManager
056: .getLoggerForClass();
057:
058: public static final String XSD_FILENAME_KEY = "xmlschema_assertion_filename";
059:
060: // private StringBuffer failureMessage = new StringBuffer();
061:
062: /**
063: * getResult
064: *
065: */
066: public AssertionResult getResult(SampleResult response) {
067: AssertionResult result = new AssertionResult(getName());
068: // Note: initialised with error = failure = false
069:
070: byte data[] = response.getResponseData();
071: if (data.length == 0) {
072: return result.setResultForNull();
073: }
074: String resultData = new String(getResultBody(data));
075:
076: String xsdFileName = getXsdFileName();
077: if (log.isDebugEnabled()) {
078: log.debug("xmlString: " + resultData);
079: log.debug("xsdFileName: " + xsdFileName);
080: }
081: if (xsdFileName == null || xsdFileName.length() == 0) {
082: result.setResultForFailure(FILE_NAME_IS_REQUIRED);
083: } else {
084: setSchemaResult(result, resultData, xsdFileName);
085: }
086: return result;
087: }
088:
089: /*
090: * TODO move to SampleResult class? Return the body of the http return.
091: */
092: private byte[] getResultBody(byte[] resultData) {
093: for (int i = 0; i < (resultData.length - 1); i++) {
094: if (resultData[i] == '\n' && resultData[i + 1] == '\n') {
095: return JOrphanUtils.getByteArraySlice(resultData,
096: (i + 2), resultData.length - 1);
097: }
098: }
099: return resultData;
100: }
101:
102: public void setXsdFileName(String xmlSchemaFileName)
103: throws IllegalArgumentException {
104: setProperty(XSD_FILENAME_KEY, xmlSchemaFileName);
105: }
106:
107: public String getXsdFileName() {
108: return getPropertyAsString(XSD_FILENAME_KEY);
109: }
110:
111: /**
112: * set Schema result
113: *
114: * @param result
115: * @param xmlStr
116: * @param xsdFileName
117: */
118: private void setSchemaResult(AssertionResult result, String xmlStr,
119: String xsdFileName) {
120: try {
121: // boolean toReturn = true;
122:
123: // Document doc = null;
124: DocumentBuilderFactory parserFactory = DocumentBuilderFactory
125: .newInstance();
126: parserFactory.setValidating(true);
127: parserFactory.setNamespaceAware(true);
128: parserFactory.setAttribute(JAXP_SCHEMA_LANGUAGE,
129: W3C_XML_SCHEMA);
130: parserFactory.setAttribute(JAXP_SCHEMA_SOURCE, xsdFileName);
131:
132: // create a parser:
133: DocumentBuilder parser = parserFactory.newDocumentBuilder();
134: parser.setErrorHandler(new SAXErrorHandler(result));
135:
136: // doc =
137: parser.parse(new InputSource(new StringReader(xmlStr)));
138: // if everything went fine then xml schema validation is valid
139: } catch (SAXParseException e) {
140:
141: // Only set message if error not yet flagged
142: if (!result.isError() && !result.isFailure()) {
143: result.setError(true);
144: result.setFailureMessage(errorDetails(e));
145: }
146:
147: } catch (SAXException e) {
148:
149: log.warn(e.toString());
150: result.setResultForFailure(e.getMessage());
151:
152: } catch (IOException e) {
153:
154: log.warn("IO error", e);
155: result.setResultForFailure(e.getMessage());
156:
157: } catch (ParserConfigurationException e) {
158:
159: log.warn("Problem with Parser Config", e);
160: result.setResultForFailure(e.getMessage());
161:
162: }
163:
164: }
165:
166: // Helper method to construct SAX error details
167: private static String errorDetails(SAXParseException spe) {
168: StringBuffer str = new StringBuffer(80);
169: int i;
170: i = spe.getLineNumber();
171: if (i != -1) {
172: str.append("line=");
173: str.append(i);
174: str.append(" col=");
175: str.append(spe.getColumnNumber());
176: str.append(" ");
177: }
178: str.append(spe.getLocalizedMessage());
179: return str.toString();
180: }
181:
182: /**
183: * SAXErrorHandler class
184: */
185: private static class SAXErrorHandler implements ErrorHandler {
186: private AssertionResult result;
187:
188: public SAXErrorHandler(AssertionResult result) {
189: this .result = result;
190: }
191:
192: /*
193: * Can be caused by: - failure to read XSD file - xml does not match XSD
194: */
195: public void error(SAXParseException exception)
196: throws SAXParseException {
197:
198: String msg = "error: " + errorDetails(exception);
199: log.debug(msg);
200: result.setFailureMessage(msg);
201: result.setError(true);
202: throw exception;
203: }
204:
205: /*
206: * Can be caused by: - premature end of file - non-whitespace content
207: * after trailer
208: */
209: public void fatalError(SAXParseException exception)
210: throws SAXParseException {
211:
212: String msg = "fatal: " + errorDetails(exception);
213: log.debug(msg);
214: result.setFailureMessage(msg);
215: result.setError(true);
216: throw exception;
217: }
218:
219: /*
220: * Not clear what can cause this ? conflicting versions perhaps
221: */
222: public void warning(SAXParseException exception)
223: throws SAXParseException {
224:
225: String msg = "warning: " + errorDetails(exception);
226: log.debug(msg);
227: result.setFailureMessage(msg);
228: // result.setError(true); // TODO is this the correct strategy?
229: // throw exception; // allow assertion to pass
230:
231: }
232: }
233: }
|