1616
1717package org .springframework .ws .soap .axiom ;
1818
19+ import java .io .File ;
1920import java .io .IOException ;
2021import java .io .InputStream ;
2122import java .util .Iterator ;
3637import org .apache .axiom .soap .impl .llom .soap12 .SOAP12Factory ;
3738import org .apache .commons .logging .Log ;
3839import org .apache .commons .logging .LogFactory ;
40+
3941import org .springframework .beans .factory .InitializingBean ;
42+ import org .springframework .util .Assert ;
4043import org .springframework .util .StringUtils ;
4144import org .springframework .ws .WebServiceMessage ;
45+ import org .springframework .ws .server .endpoint .interceptor .PayloadLoggingInterceptor ;
46+ import org .springframework .ws .server .endpoint .mapping .PayloadRootQNameEndpointMapping ;
4247import org .springframework .ws .soap .SoapMessageFactory ;
4348import org .springframework .ws .soap .SoapVersion ;
49+ import org .springframework .ws .soap .server .endpoint .interceptor .PayloadValidatingInterceptor ;
50+ import org .springframework .ws .soap .server .endpoint .mapping .SoapActionEndpointMapping ;
4451import org .springframework .ws .transport .TransportConstants ;
4552import org .springframework .ws .transport .TransportInputStream ;
4653
4754/**
4855 * Axiom-specific implementation of the {@link org.springframework.ws.WebServiceMessageFactory WebServiceMessageFactory}
4956 * interface. Creates {@link org.springframework.ws.soap.axiom.AxiomSoapMessage AxiomSoapMessages}.
5057 * <p/>
51- * To increase reading performance on the the SOAP request created by this message context factory, you can set the
52- * <code>payloadCaching</code> property to <code>false</code> (default is <code>true</code>). This this will read the
53- * contents of the body directly from the stream. However, <strong>when this setting is enabled, the payload can only be
54- * read once</strong>. This means that any endpoint mappings or interceptors which are based on the message payload
55- * (such as the <code>PayloadRootQNameEndpointMapping</code>, the <code>PayloadValidatingInterceptor</code>, or the
56- * <code>PayloadLoggingInterceptor</code>) cannot be used. Instead, use an endpoint mapping that does not consume the
57- * payload (i.e. the <code>SoapActionEndpointMapping</code>).
58+ * To increase reading performance on the the SOAP request created by this message factory, you can set the {@link
59+ * #setPayloadCaching(boolean) payloadCaching} property to <code>false</code> (default is <code>true</code>). This this
60+ * will read the contents of the body directly from the stream. However, <strong>when this setting is enabled, the
61+ * payload can only be read once</strong>. This means that any endpoint mappings or interceptors which are based on the
62+ * message payload (such as the {@link PayloadRootQNameEndpointMapping}, the {@link PayloadValidatingInterceptor}, or
63+ * the {@link PayloadLoggingInterceptor}) cannot be used. Instead, use an endpoint mapping that does not consume the
64+ * payload (i.e. the {@link SoapActionEndpointMapping}).
65+ * <p/>
66+ * Additionally, this message factory can cache large attachments to disk by setting the {@link
67+ * #setAttachmentCaching(boolean) attachmentCaching} property to <code>true</code> (default is <code>false</code>).
68+ * Optionally, the location where attachments are stored can be defined via the {@link #setAttachmentCacheDir(File)
69+ * attachmentCacheDir} property (defaults to the system temp file path).
5870 * <p/>
5971 * Mostly derived from <code>org.apache.axis2.transport.http.HTTPTransportUtils</code> and
6072 * <code>org.apache.axis2.transport.TransportUtils</code>, which we cannot use since they are not part of the Axiom
@@ -79,6 +91,12 @@ public class AxiomSoapMessageFactory implements SoapMessageFactory, Initializing
7991
8092 private boolean payloadCaching = true ;
8193
94+ private boolean attachmentCaching = false ;
95+
96+ private File attachmentCacheDir ;
97+
98+ private int attachmentCacheThreshold = 4096 ;
99+
82100 // use SOAP 1.1 by default
83101 private SOAPFactory soapFactory = new SOAP11Factory ();
84102
@@ -88,14 +106,51 @@ public AxiomSoapMessageFactory() {
88106 }
89107
90108 /**
91- * Indicates whether the SOAP Body payload should be cached or not. Default is <code>true</code>. Setting this to
92- * <code>false</code> will increase performance, but also result in the fact that the message payload can only be
93- * read once.
109+ * Indicates whether the SOAP Body payload should be cached or not. Default is <code>true</code>.
110+ * <p/>
111+ * Setting this to <code>false</code> will increase performance, but also result in the fact that the message
112+ * payload can only be read once.
94113 */
95114 public void setPayloadCaching (boolean payloadCaching ) {
96115 this .payloadCaching = payloadCaching ;
97116 }
98117
118+ /**
119+ * Indicates whether SOAP attachments should be cached or not. Default is <code>false</code>.
120+ * <p/>
121+ * Setting this to <code>true</code> will cause Axiom to store larger attachments on disk, rather than in memory.
122+ * This decreases memory consumption, but decreases performance.
123+ */
124+ public void setAttachmentCaching (boolean attachmentCaching ) {
125+ this .attachmentCaching = attachmentCaching ;
126+ }
127+
128+ /**
129+ * Sets the directory where SOAP attachments will be stored. Only used when {@link #setAttachmentCaching(boolean)
130+ * attachmentCaching} is set to <code>true</code>.
131+ * <p/>
132+ * The parameter should be an existing, writable directory. This property defaults to the temporary directory of the
133+ * operating system (i.e. the value of the <code>java.io.tmpdir</code> system property).
134+ */
135+ public void setAttachmentCacheDir (File attachmentCacheDir ) {
136+ Assert .notNull (attachmentCacheDir , "'attachmentCacheDir' must not be null" );
137+ Assert .isTrue (attachmentCacheDir .isDirectory (), "'attachmentCacheDir' must be a directory" );
138+ Assert .isTrue (attachmentCacheDir .canWrite (), "'attachmentCacheDir' must be writable" );
139+ this .attachmentCacheDir = attachmentCacheDir ;
140+ }
141+
142+ /**
143+ * Sets the threshold for attachments caching, in bytes. Attachments larger than this threshold will be cached in
144+ * the {@link #setAttachmentCacheDir(File) attachment cache directory}. Only used when {@link
145+ * #setAttachmentCaching(boolean) attachmentCaching} is set to <code>true</code>.
146+ * <p/>
147+ * Defaults to 4096 bytes (i.e. 4 kilobytes).
148+ */
149+ public void setAttachmentCacheThreshold (int attachmentCacheThreshold ) {
150+ Assert .isTrue (attachmentCacheThreshold > 0 , "'attachmentCacheThreshold' must be larger than 0" );
151+ this .attachmentCacheThreshold = attachmentCacheThreshold ;
152+ }
153+
99154 public void setSoapVersion (SoapVersion version ) {
100155 if (SoapVersion .SOAP_11 == version ) {
101156 soapFactory = new SOAP11Factory ();
@@ -113,6 +168,10 @@ public void afterPropertiesSet() throws Exception {
113168 if (logger .isInfoEnabled ()) {
114169 logger .info (payloadCaching ? "Enabled payload caching" : "Disabled payload caching" );
115170 }
171+ if (attachmentCacheDir == null ) {
172+ String tempDir = System .getProperty ("java.io.tmpdir" );
173+ setAttachmentCacheDir (new File (tempDir ));
174+ }
116175 }
117176
118177 public WebServiceMessage createWebServiceMessage () {
@@ -172,7 +231,8 @@ private WebServiceMessage createAxiomSoapMessage(InputStream inputStream, String
172231 private AxiomSoapMessage createMultiPartAxiomSoapMessage (InputStream inputStream ,
173232 String contentType ,
174233 String soapAction ) throws XMLStreamException {
175- Attachments attachments = new Attachments (inputStream , contentType );
234+ Attachments attachments = new Attachments (inputStream , contentType , attachmentCaching ,
235+ attachmentCacheDir .getAbsolutePath (), Integer .toString (attachmentCacheThreshold ));
176236 XMLStreamReader reader = inputFactory .createXMLStreamReader (attachments .getSOAPPartInputStream (),
177237 getCharSetEncoding (attachments .getSOAPPartContentType ()));
178238 StAXSOAPModelBuilder builder ;
0 commit comments