Author: davsclaus
Date: Wed Oct 17 17:13:46 2012
New Revision: 1399351

URL: http://svn.apache.org/viewvc?rev=1399351&view=rev
Log:
CAMEL-5608: Ensure validator component closes input stream after usage. Align 
code with the xlst/xpath is doing. Added option to fail if no body or not.

Modified:
    camel/branches/camel-2.9.x/   (props changed)
    
camel/branches/camel-2.9.x/camel-core/src/main/java/org/apache/camel/component/validator/ValidatorComponent.java
    
camel/branches/camel-2.9.x/camel-core/src/main/java/org/apache/camel/processor/validation/ValidatingProcessor.java
    
camel/branches/camel-2.9.x/camel-core/src/test/java/org/apache/camel/component/validator/FileValidatorRouteTest.java

Propchange: camel/branches/camel-2.9.x/
------------------------------------------------------------------------------
  Merged /camel/trunk:r1399333
  Merged /camel/branches/camel-2.10.x:r1399335

Propchange: camel/branches/camel-2.9.x/
------------------------------------------------------------------------------
Binary property 'svnmerge-integrated' - no diff available.

Modified: 
camel/branches/camel-2.9.x/camel-core/src/main/java/org/apache/camel/component/validator/ValidatorComponent.java
URL: 
http://svn.apache.org/viewvc/camel/branches/camel-2.9.x/camel-core/src/main/java/org/apache/camel/component/validator/ValidatorComponent.java?rev=1399351&r1=1399350&r2=1399351&view=diff
==============================================================================
--- 
camel/branches/camel-2.9.x/camel-core/src/main/java/org/apache/camel/component/validator/ValidatorComponent.java
 (original)
+++ 
camel/branches/camel-2.9.x/camel-core/src/main/java/org/apache/camel/component/validator/ValidatorComponent.java
 Wed Oct 17 17:13:46 2012
@@ -27,6 +27,7 @@ import org.apache.camel.Endpoint;
 import org.apache.camel.impl.DefaultComponent;
 import org.apache.camel.impl.ProcessorEndpoint;
 import org.apache.camel.processor.validation.ValidatingProcessor;
+import org.apache.camel.util.IOHelper;
 import org.apache.camel.util.ResourceHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -52,6 +53,8 @@ public class ValidatorComponent extends 
         // force loading of schema at create time otherwise concurrent
         // processing could cause thread safe issues for the 
javax.xml.validation.SchemaFactory
         validator.loadSchema();
+        // and make sure to close the input stream after the schema has been 
loaded
+        IOHelper.close(is);
 
         return new ProcessorEndpoint(uri, this, validator);
     }

Modified: 
camel/branches/camel-2.9.x/camel-core/src/main/java/org/apache/camel/processor/validation/ValidatingProcessor.java
URL: 
http://svn.apache.org/viewvc/camel/branches/camel-2.9.x/camel-core/src/main/java/org/apache/camel/processor/validation/ValidatingProcessor.java?rev=1399351&r1=1399350&r2=1399351&view=diff
==============================================================================
--- 
camel/branches/camel-2.9.x/camel-core/src/main/java/org/apache/camel/processor/validation/ValidatingProcessor.java
 (original)
+++ 
camel/branches/camel-2.9.x/camel-core/src/main/java/org/apache/camel/processor/validation/ValidatingProcessor.java
 Wed Oct 17 17:13:46 2012
@@ -18,27 +18,39 @@ package org.apache.camel.processor.valid
 
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
 import java.net.URL;
 import java.util.Collections;
-
 import javax.xml.XMLConstants;
+import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.transform.Result;
 import javax.xml.transform.Source;
 import javax.xml.transform.dom.DOMResult;
 import javax.xml.transform.dom.DOMSource;
 import javax.xml.transform.sax.SAXResult;
 import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stax.StAXSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
 import javax.xml.validation.Schema;
 import javax.xml.validation.SchemaFactory;
 import javax.xml.validation.Validator;
 
+import org.w3c.dom.Node;
 import org.w3c.dom.ls.LSResourceResolver;
-
 import org.xml.sax.SAXException;
 import org.xml.sax.SAXParseException;
 
 import org.apache.camel.Exchange;
+import org.apache.camel.ExpectedBodyTypeException;
 import org.apache.camel.Processor;
+import org.apache.camel.RuntimeTransformException;
+import org.apache.camel.TypeConverter;
+import org.apache.camel.converter.jaxp.XmlConverter;
+import org.apache.camel.util.IOHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * A processor which validates the XML version of the inbound message body
@@ -47,7 +59,8 @@ import org.apache.camel.Processor;
  * @version
  */
 public class ValidatingProcessor implements Processor {
-    // for lazy creation of the Schema
+    private static final Logger LOG = 
LoggerFactory.getLogger(ValidatingProcessor.class);
+    private XmlConverter converter = new XmlConverter();
     private String schemaLanguage = XMLConstants.W3C_XML_SCHEMA_NS_URI;
     private Schema schema;
     private Source schemaSource;
@@ -58,6 +71,7 @@ public class ValidatingProcessor impleme
     private boolean useDom;
     private boolean useSharedSchema = true;
     private LSResourceResolver resourceResolver;
+    private boolean failOnNullBody = true;
 
     public void process(Exchange exchange) throws Exception {
         Schema schema;
@@ -69,39 +83,59 @@ public class ValidatingProcessor impleme
 
         Validator validator = schema.newValidator();
 
-        Source source;
-        Result result;
+        // the underlying input stream, which we need to close to avoid 
locking files or other resources
+        Source source = null;
+        InputStream is = null;
         try {
-            if (useDom) {
-                source = exchange.getIn().getBody(DOMSource.class);
-                result = new DOMResult();
+            Result result = null;
+            // only convert to input stream if really needed
+            if (isInputStreamNeeded(exchange)) {
+                is = exchange.getIn().getBody(InputStream.class);
+                if (is != null) {
+                    source = getSource(exchange, is);
+                }
             } else {
-                source = exchange.getIn().getBody(SAXSource.class);
-                result = new SAXResult();
+                Object body = exchange.getIn().getBody();
+                if (body != null) {
+                    source = getSource(exchange, body);
+                }
             }
-        } catch (Exception e) {
-            throw new NoXmlBodyValidationException(exchange, e);
-        }
-        if (source == null) {
-            throw new NoXmlBodyValidationException(exchange);
-        }
 
-        // create a new errorHandler and set it on the validator
-        // must be a local instance to avoid problems with concurrency (to be
-        // thread safe)
-        ValidatorErrorHandler handler = errorHandler.getClass().newInstance();
-        validator.setErrorHandler(handler);
+            if (source == null && isFailOnNullBody()) {
+                throw new NoXmlBodyValidationException(exchange);
+            }
 
-        try {
-            validator.validate(source, result);
-        } catch (SAXParseException e) {
-            // can be thrown for non well formed XML
-            throw new SchemaValidationException(exchange, schema, 
Collections.singletonList(e),
-                    Collections.<SAXParseException> emptyList(),
-                    Collections.<SAXParseException> emptyList());
-        }
+            if (source instanceof DOMSource) {
+                result = new DOMResult();
+            } else if (source instanceof StreamSource) {
+                result = new StreamResult(new StringWriter());
+            } else if (source instanceof SAXSource) {
+                result = new SAXResult();
+            } else if (source instanceof StAXSource) {
+                result = null;
+            }
 
-        handler.handleErrors(exchange, schema, result);
+            if (source != null) {
+                // create a new errorHandler and set it on the validator
+                // must be a local instance to avoid problems with concurrency 
(to be
+                // thread safe)
+                ValidatorErrorHandler handler = 
errorHandler.getClass().newInstance();
+                validator.setErrorHandler(handler);
+
+                try {
+                    LOG.trace("Validating {}", source);
+                    validator.validate(source, result);
+                    handler.handleErrors(exchange, schema, result);
+                } catch (SAXParseException e) {
+                    // can be thrown for non well formed XML
+                    throw new SchemaValidationException(exchange, schema, 
Collections.singletonList(e),
+                            Collections.<SAXParseException> emptyList(),
+                            Collections.<SAXParseException> emptyList());
+                }
+            }
+        } finally {
+            IOHelper.close(is);
+        }
     }
 
     public void loadSchema() throws Exception {
@@ -177,17 +211,17 @@ public class ValidatingProcessor impleme
         this.errorHandler = errorHandler;
     }
 
+    @Deprecated
     public boolean isUseDom() {
         return useDom;
     }
 
     /**
-     * Sets whether DOMSource and DOMResult should be used, or SaxSource and
-     * SaxResult.
-     * 
-     * @param useDom
-     *            true to use DOM otherwise Sax is used
+     * Sets whether DOMSource and DOMResult should be used.
+     *
+     * @param useDom true to use DOM otherwise
      */
+    @Deprecated
     public void setUseDom(boolean useDom) {
         this.useDom = useDom;
     }
@@ -208,6 +242,14 @@ public class ValidatingProcessor impleme
         this.resourceResolver = resourceResolver;
     }
 
+    public boolean isFailOnNullBody() {
+        return failOnNullBody;
+    }
+
+    public void setFailOnNullBody(boolean failOnNullBody) {
+        this.failOnNullBody = failOnNullBody;
+    }
+
     // Implementation methods
     // -----------------------------------------------------------------------
 
@@ -236,4 +278,96 @@ public class ValidatingProcessor impleme
         }
         return factory.newSchema(getSchemaSource());
     }
+
+    /**
+     * Checks whether we need an {@link InputStream} to access the message 
body.
+     * <p/>
+     * Depending on the content in the message body, we may not need to convert
+     * to {@link InputStream}.
+     *
+     * @param exchange the current exchange
+     * @return <tt>true</tt> to convert to {@link InputStream} beforehand 
converting to {@link Source} afterwards.
+     */
+    protected boolean isInputStreamNeeded(Exchange exchange) {
+        Object body = exchange.getIn().getBody();
+        if (body == null) {
+            return false;
+        }
+
+        if (body instanceof InputStream) {
+            return true;
+        } else if (body instanceof Source) {
+            return false;
+        } else if (body instanceof String) {
+            return false;
+        } else if (body instanceof byte[]) {
+            return false;
+        } else if (body instanceof Node) {
+            return false;
+        } else if 
(exchange.getContext().getTypeConverterRegistry().lookup(Source.class, 
body.getClass()) != null) {
+            //there is a direct and hopefully optimized converter to Source
+            return false;
+        }
+        // yes an input stream is needed
+        return true;
+    }
+
+    /**
+     * Converts the inbound body to a {@link Source}, if the body is 
<b>not</b> already a {@link Source}.
+     * <p/>
+     * This implementation will prefer to source in the following order:
+     * <ul>
+     *   <li>DOM - DOM if explicit configured to use DOM</li>
+     *   <li>SAX - SAX as 2nd choice</li>
+     *   <li>Stream - Stream as 3rd choice</li>
+     *   <li>DOM - DOM as 4th choice</li>
+     * </ul>
+     */
+    protected Source getSource(Exchange exchange, Object body) {
+        if (isUseDom()) {
+            // force DOM
+            return 
exchange.getContext().getTypeConverter().tryConvertTo(DOMSource.class, 
exchange, body);
+        }
+
+        // body may already be a source
+        if (body instanceof Source) {
+            return (Source) body;
+        }
+        Source source = null;
+        if (body instanceof InputStream) {
+            return new StreamSource((InputStream)body);
+        }
+        if (body != null) {
+            TypeConverter tc = 
exchange.getContext().getTypeConverterRegistry().lookup(Source.class, 
body.getClass());
+            if (tc != null) {
+                source = tc.convertTo(Source.class, body);
+            }
+        }
+
+        if (source == null) {
+            // then try SAX
+            source = 
exchange.getContext().getTypeConverter().tryConvertTo(SAXSource.class, 
exchange, body);
+        }
+        if (source == null) {
+            // then try stream
+            source = 
exchange.getContext().getTypeConverter().tryConvertTo(StreamSource.class, 
exchange, body);
+        }
+        if (source == null) {
+            // and fallback to DOM
+            source = 
exchange.getContext().getTypeConverter().tryConvertTo(DOMSource.class, 
exchange, body);
+        }
+        if (source == null) {
+            if (isFailOnNullBody()) {
+                throw new ExpectedBodyTypeException(exchange, Source.class);
+            } else {
+                try {
+                    source = converter.toDOMSource(converter.createDocument());
+                } catch (ParserConfigurationException e) {
+                    throw new RuntimeTransformException(e);
+                }
+            }
+        }
+        return source;
+    }
+
 }
\ No newline at end of file

Modified: 
camel/branches/camel-2.9.x/camel-core/src/test/java/org/apache/camel/component/validator/FileValidatorRouteTest.java
URL: 
http://svn.apache.org/viewvc/camel/branches/camel-2.9.x/camel-core/src/test/java/org/apache/camel/component/validator/FileValidatorRouteTest.java?rev=1399351&r1=1399350&r2=1399351&view=diff
==============================================================================
--- 
camel/branches/camel-2.9.x/camel-core/src/test/java/org/apache/camel/component/validator/FileValidatorRouteTest.java
 (original)
+++ 
camel/branches/camel-2.9.x/camel-core/src/test/java/org/apache/camel/component/validator/FileValidatorRouteTest.java
 Wed Oct 17 17:13:46 2012
@@ -16,11 +16,14 @@
  */
 package org.apache.camel.component.validator;
 
+import java.io.File;
+
 import org.apache.camel.ContextTestSupport;
 import org.apache.camel.Exchange;
 import org.apache.camel.ValidationException;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.util.FileUtil;
 
 /**
  *
@@ -40,6 +43,10 @@ public class FileValidatorRouteTest exte
                 Exchange.FILE_NAME, "valid.xml");
 
         MockEndpoint.assertIsSatisfied(validEndpoint, invalidEndpoint, 
finallyEndpoint);
+
+        // should be able to delete the file
+        oneExchangeDone.matchesMockWaitTime();
+        assertTrue("Should be able to delete the file", 
FileUtil.deleteFile(new File("target/validator/valid.xml")));
     }
 
     public void testInvalidMessage() throws Exception {
@@ -51,12 +58,15 @@ public class FileValidatorRouteTest exte
                 Exchange.FILE_NAME, "invalid.xml");
 
         MockEndpoint.assertIsSatisfied(validEndpoint, invalidEndpoint, 
finallyEndpoint);
+
+        // should be able to delete the file
+        oneExchangeDone.matchesMockWaitTime();
+        assertTrue("Should be able to delete the file", 
FileUtil.deleteFile(new File("target/validator/invalid.xml")));
     }
 
     @Override
     protected void setUp() throws Exception {
         deleteDirectory("target/validator");
-
         super.setUp();
         validEndpoint = resolveMandatoryEndpoint("mock:valid", 
MockEndpoint.class);
         invalidEndpoint = resolveMandatoryEndpoint("mock:invalid", 
MockEndpoint.class);
@@ -68,7 +78,7 @@ public class FileValidatorRouteTest exte
         return new RouteBuilder() {
             @Override
             public void configure() throws Exception {
-                from("file:target/validator")
+                from("file:target/validator?noop=true")
                     .doTry()
                         
.to("validator:org/apache/camel/component/validator/schema.xsd")
                         .to("mock:valid")


Reply via email to