diff --git a/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapMessageFactory.java b/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapMessageFactory.java index c32712b6..b2fd69f1 100644 --- a/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapMessageFactory.java +++ b/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapMessageFactory.java @@ -16,6 +16,7 @@ package org.springframework.ws.soap.axiom; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Iterator; @@ -36,11 +37,17 @@ import org.apache.axiom.soap.impl.llom.soap11.SOAP11Factory; import org.apache.axiom.soap.impl.llom.soap12.SOAP12Factory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.beans.factory.InitializingBean; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.ws.WebServiceMessage; +import org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor; +import org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping; import org.springframework.ws.soap.SoapMessageFactory; import org.springframework.ws.soap.SoapVersion; +import org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor; +import org.springframework.ws.soap.server.endpoint.mapping.SoapActionEndpointMapping; import org.springframework.ws.transport.TransportConstants; import org.springframework.ws.transport.TransportInputStream; @@ -48,13 +55,18 @@ import org.springframework.ws.transport.TransportInputStream; * Axiom-specific implementation of the {@link org.springframework.ws.WebServiceMessageFactory WebServiceMessageFactory} * interface. Creates {@link org.springframework.ws.soap.axiom.AxiomSoapMessage AxiomSoapMessages}. *

- * To increase reading performance on the the SOAP request created by this message context factory, you can set the - * payloadCaching property to false (default is true). This this will read the - * contents of the body directly from the stream. However, when this setting is enabled, the payload can only be - * read once. This means that any endpoint mappings or interceptors which are based on the message payload - * (such as the PayloadRootQNameEndpointMapping, the PayloadValidatingInterceptor, or the - * PayloadLoggingInterceptor) cannot be used. Instead, use an endpoint mapping that does not consume the - * payload (i.e. the SoapActionEndpointMapping). + * To increase reading performance on the the SOAP request created by this message factory, you can set the {@link + * #setPayloadCaching(boolean) payloadCaching} property to false (default is true). This this + * will read the contents of the body directly from the stream. However, when this setting is enabled, the + * payload can only be read once. This means that any endpoint mappings or interceptors which are based on the + * message payload (such as the {@link PayloadRootQNameEndpointMapping}, the {@link PayloadValidatingInterceptor}, or + * the {@link PayloadLoggingInterceptor}) cannot be used. Instead, use an endpoint mapping that does not consume the + * payload (i.e. the {@link SoapActionEndpointMapping}). + *

+ * Additionally, this message factory can cache large attachments to disk by setting the {@link + * #setAttachmentCaching(boolean) attachmentCaching} property to true (default is false). + * Optionally, the location where attachments are stored can be defined via the {@link #setAttachmentCacheDir(File) + * attachmentCacheDir} property (defaults to the system temp file path). *

* Mostly derived from org.apache.axis2.transport.http.HTTPTransportUtils and * org.apache.axis2.transport.TransportUtils, which we cannot use since they are not part of the Axiom @@ -79,6 +91,12 @@ public class AxiomSoapMessageFactory implements SoapMessageFactory, Initializing private boolean payloadCaching = true; + private boolean attachmentCaching = false; + + private File attachmentCacheDir; + + private int attachmentCacheThreshold = 4096; + // use SOAP 1.1 by default private SOAPFactory soapFactory = new SOAP11Factory(); @@ -88,14 +106,51 @@ public class AxiomSoapMessageFactory implements SoapMessageFactory, Initializing } /** - * Indicates whether the SOAP Body payload should be cached or not. Default is true. Setting this to - * false will increase performance, but also result in the fact that the message payload can only be - * read once. + * Indicates whether the SOAP Body payload should be cached or not. Default is true. + *

+ * Setting this to false will increase performance, but also result in the fact that the message + * payload can only be read once. */ public void setPayloadCaching(boolean payloadCaching) { this.payloadCaching = payloadCaching; } + /** + * Indicates whether SOAP attachments should be cached or not. Default is false. + *

+ * Setting this to true will cause Axiom to store larger attachments on disk, rather than in memory. + * This decreases memory consumption, but decreases performance. + */ + public void setAttachmentCaching(boolean attachmentCaching) { + this.attachmentCaching = attachmentCaching; + } + + /** + * Sets the directory where SOAP attachments will be stored. Only used when {@link #setAttachmentCaching(boolean) + * attachmentCaching} is set to true. + *

+ * The parameter should be an existing, writable directory. This property defaults to the temporary directory of the + * operating system (i.e. the value of the java.io.tmpdir system property). + */ + public void setAttachmentCacheDir(File attachmentCacheDir) { + Assert.notNull(attachmentCacheDir, "'attachmentCacheDir' must not be null"); + Assert.isTrue(attachmentCacheDir.isDirectory(), "'attachmentCacheDir' must be a directory"); + Assert.isTrue(attachmentCacheDir.canWrite(), "'attachmentCacheDir' must be writable"); + this.attachmentCacheDir = attachmentCacheDir; + } + + /** + * Sets the threshold for attachments caching, in bytes. Attachments larger than this threshold will be cached in + * the {@link #setAttachmentCacheDir(File) attachment cache directory}. Only used when {@link + * #setAttachmentCaching(boolean) attachmentCaching} is set to true. + *

+ * Defaults to 4096 bytes (i.e. 4 kilobytes). + */ + public void setAttachmentCacheThreshold(int attachmentCacheThreshold) { + Assert.isTrue(attachmentCacheThreshold > 0, "'attachmentCacheThreshold' must be larger than 0"); + this.attachmentCacheThreshold = attachmentCacheThreshold; + } + public void setSoapVersion(SoapVersion version) { if (SoapVersion.SOAP_11 == version) { soapFactory = new SOAP11Factory(); @@ -113,6 +168,10 @@ public class AxiomSoapMessageFactory implements SoapMessageFactory, Initializing if (logger.isInfoEnabled()) { logger.info(payloadCaching ? "Enabled payload caching" : "Disabled payload caching"); } + if (attachmentCacheDir == null) { + String tempDir = System.getProperty("java.io.tmpdir"); + setAttachmentCacheDir(new File(tempDir)); + } } public WebServiceMessage createWebServiceMessage() { @@ -172,7 +231,8 @@ public class AxiomSoapMessageFactory implements SoapMessageFactory, Initializing private AxiomSoapMessage createMultiPartAxiomSoapMessage(InputStream inputStream, String contentType, String soapAction) throws XMLStreamException { - Attachments attachments = new Attachments(inputStream, contentType); + Attachments attachments = new Attachments(inputStream, contentType, attachmentCaching, + attachmentCacheDir.getAbsolutePath(), Integer.toString(attachmentCacheThreshold)); XMLStreamReader reader = inputFactory.createXMLStreamReader(attachments.getSOAPPartInputStream(), getCharSetEncoding(attachments.getSOAPPartContentType())); StAXSOAPModelBuilder builder; diff --git a/core/src/test/java/org/springframework/ws/soap/axiom/AxiomSoap11MessageFactoryTest.java b/core/src/test/java/org/springframework/ws/soap/axiom/AxiomSoap11MessageFactoryTest.java index faebf117..f5e76458 100644 --- a/core/src/test/java/org/springframework/ws/soap/axiom/AxiomSoap11MessageFactoryTest.java +++ b/core/src/test/java/org/springframework/ws/soap/axiom/AxiomSoap11MessageFactoryTest.java @@ -27,31 +27,4 @@ public class AxiomSoap11MessageFactoryTest extends AbstractSoap11MessageFactoryT return factory; } -/* - public void testCreateMtom() throws Exception { - SOAP11Factory factory = new SOAP11Factory(); - SOAPEnvelope envelope = factory.getDefaultEnvelope(); - - OMNamespace namespace = factory.createOMNamespace("http://springframework.org/spring-ws/mtom", "sws"); - OMElement element = factory.createOMElement("image", namespace); - envelope.getBody().addChild(element); - DataHandler dataHandler = new javax.activation.DataHandler(new FileDataSource("/Users/arjen/spring-ws/src/site/resources/images/spring-ws.png")); - - //create an OMText node with the above DataHandler and set optimized to true - OMText textData = factory.createOMText(dataHandler, true); - - element.addChild(textData); - - OMOutputFormat format = new OMOutputFormat(); - format.setDoOptimize(true); - format.setSOAP11(true); - format.setCharSetEncoding("UTF-8"); - assertTrue(format.isOptimized()); - OutputStream os = new BufferedOutputStream(new FileOutputStream( - "/Users/arjen/Projects/Spring/spring-ws/core/src/test/resources/org/springframework/ws/soap/soap11/soap11-mtom.bin")); - try { - envelope.serialize(os, format); - } finally { - os.close(); - */ } \ No newline at end of file