Since no one replied to my questions, probably because there are other threads with similar questions answering that this is not possible with the current version of JAXP and that it really should be a JAXP / JAXB2 bug, I figured I would try to contribute something and show how I created a workaround so I can still validate the payload against the schema, but skip the MTOM elements:
Basically,
The idea is to take advantage of the setErrorHandler method in the javax.xml.validation.Validator class. By implementing my own ErrorHandler class, I can check for exceptions and skip the ones for the MTOM elements.
Here is what I did:
1 - Implement the ErrorHandler interface:
Code:
package com.oracle.validation.handler;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
public class ValidationErrorHandler implements ErrorHandler {
private List errors = new ArrayList();
private String [] elements;
public SAXParseException[] getErrors() {
return (SAXParseException[]) errors.toArray(new SAXParseException[errors.size()]);
}
public void skipElements(String[] elements){
this.elements = elements;
}
public void warning(SAXParseException ex) throws SAXException {
}
public void error(SAXParseException ex) throws SAXException {
String text = ex.getMessage();
for (int i=0; elements != null && i < elements.length; i++){
if (text.contains(elements[i])){
System.out.println("Skipping validation for XSD element <" + elements[i] + ">.");
return;
}
}
System.out.println("*****************Error**************************");
ex.printStackTrace();
System.out.println("************************************************");
errors.add(ex);
}
public void fatalError(SAXParseException ex) throws SAXException {
System.out.println("******************Fatal Error***************************");
ex.printStackTrace();
System.out.println("********************************************************");
errors.add(ex);
}
}
2 - Modify the AbstractValidatingInterceptor class for the server side by adding the following instance variables and methods:
Code:
private String errorHandler;
private String skipElements;
public void setErrorHandler(String errorHandler){
this.errorHandler = errorHandler;
}
public String getErrorHandler(){
return errorHandler;
}
public void setSkipElements(String skipElements){
this.skipElements = skipElements;
}
public String getSkipElements(){
return skipElements;
}
private ErrorHandler getErrorHandlerFor(String errorHandler){
ErrorHandler handler = null;
if (errorHandler != null && errorHandler.trim().length() > 0){
System.out.println("*****Setting custom error handler <" + errorHandler + ">.");
try{
Class _class = Class.forName(errorHandler);
handler = (ErrorHandler)_class.newInstance();
}catch(Exception e){
e.printStackTrace();
}
}
return handler;
}
3 - Modify the following methods in the same class (AbstractValidatingInterceptor):
Code:
public void setXsdSchema(XsdSchema schema) throws IOException {
this.validator = schema.createValidator();
ErrorHandler handler = getErrorHandlerFor(errorHandler);
if (handler != null){
this.validator.setErrorHandler(handler);
}
if (skipElements != null && skipElements.trim().length() > 0){
this.validator.setSkipElements(skipElements);
}
}
public void setXsdSchemaCollection(XsdSchemaCollection schemaCollection) throws IOException {
this.validator = schemaCollection.createValidator();
ErrorHandler handler = getErrorHandlerFor(errorHandler);
if (handler != null){
this.validator.setErrorHandler(handler);
}
if (skipElements != null && skipElements.trim().length() > 0){
this.validator.setSkipElements(skipElements);
}
}
public void afterPropertiesSet() throws Exception {
if (validator == null && !ObjectUtils.isEmpty(schemas)) {
Assert.hasLength(schemaLanguage, "schemaLanguage is required");
for (int i = 0; i < schemas.length; i++) {
Assert.isTrue(schemas[i].exists(), "schema [" + schemas[i] + "] does not exist");
}
if (logger.isInfoEnabled()) {
logger.info("Validating using " + StringUtils.arrayToCommaDelimitedString(schemas));
}
validator = XmlValidatorFactory.createValidator(schemas, schemaLanguage);
ErrorHandler handler = getErrorHandlerFor(errorHandler);
if (handler != null){
this.validator.setErrorHandler(handler);
}
if (skipElements != null && skipElements.trim().length() > 0){
this.validator.setSkipElements(skipElements);
}
}
Assert.notNull(validator, "Setting 'schema', 'schemas', 'xsdSchema', or 'xsdSchemaCollection' is required");
}
4 - Modify the Jaxp13ValidatorFactory class as follows:
Code:
4.1 Inside the Jaxp13Validator static class, add the following instance variables and methods:
private ErrorHandler handler;
private String [] skipElements;
private Method _getErrorsMethod;
private Method _skipElementsMethod;
public void setErrorHandler(ErrorHandler handler){
System.out.println("*****Setting Jaxp 1.3 error handler.");
this.handler = handler;
try{
_getErrorsMethod = handler.getClass().getMethod("getErrors", //Method name
null); //Arguments
_skipElementsMethod = handler.getClass().getMethod("skipElements",
new Class[] { String[].class });
}catch(Exception e){
e.printStackTrace();
}
}
public void setSkipElements(String skipElements){
this.skipElements = skipElements == null? null : skipElements.split(",");
}
4.2 - Inside the same class, modify the validate method as follows:
Code:
public SAXParseException[] validate(Source source) throws IOException {
validator = schema.newValidator();
try {
if (handler == null){
handler = new ValidationErrorHandler();
_getErrorsMethod = handler.getClass().getMethod("getErrors", //Method name
null); //Arguments
_skipElementsMethod = handler.getClass().getMethod("skipElements",
new Class[] { String[].class });
}
if (skipElements != null){
if (_skipElementsMethod != null){
_skipElementsMethod.invoke(handler,
new Object[] { skipElements });
}
}
validator.setErrorHandler(handler);
validator.validate(source);
return _getErrorsMethod == null? (SAXParseException[])new ArrayList().toArray(new SAXParseException[0]): (SAXParseException[])_getErrorsMethod.invoke(handler, null);
}catch (Exception ex) {
throw new XmlValidationException("Could not validate source: " + ex.getMessage(), ex);
}
}
4.3 - Inside the same class, modify the ValidationErrorHandler class as follows:
Code:
private static class ValidationErrorHandler implements ErrorHandler {
private List errors = new ArrayList();
public ValidationErrorHandler(){
System.out.println("*****Creating default Jaxp1.3 Validation Error Handler.");
}
public SAXParseException[] getErrors() {
return (SAXParseException[]) errors.toArray(new SAXParseException[errors.size()]);
}
public void skipElements(String[] elements){
//Do nothing
}
public void warning(SAXParseException ex) throws SAXException {
}
public void error(SAXParseException ex) throws SAXException {
errors.add(ex);
}
public void fatalError(SAXParseException ex) throws SAXException {
errors.add(ex);
}
}
continues on the next post.....