0
# MIME Attachment Support
1
2
MIME attachment support enables optimized handling of binary data in XML through MIME attachments using MTOM (Message Transmission Optimization Mechanism), XOP (XML-binary Optimized Packaging), or SwA (SOAP with Attachments) protocols.
3
4
## Core Interfaces
5
6
### MimeMarshaller
7
8
Extended marshaller interface that supports MIME attachments during marshalling.
9
10
```java { .api }
11
public interface MimeMarshaller extends Marshaller {
12
/**
13
* Marshals the object graph with the given root into the provided Result,
14
* writing binary data to a MimeContainer.
15
* @param graph the root of the object graph to marshal
16
* @param result the result to marshal to
17
* @param mimeContainer the MIME container to write extracted binary content to
18
* @throws XmlMappingException if the given object cannot be marshalled to the result
19
* @throws IOException if an I/O exception occurs
20
*/
21
void marshal(Object graph, Result result, @Nullable MimeContainer mimeContainer)
22
throws XmlMappingException, IOException;
23
}
24
```
25
26
### MimeUnmarshaller
27
28
Extended unmarshaller interface that supports MIME attachments during unmarshalling.
29
30
```java { .api }
31
public interface MimeUnmarshaller extends Unmarshaller {
32
/**
33
* Unmarshals the given provided Source into an object graph,
34
* reading binary attachments from a MimeContainer.
35
* @param source the source to marshal from
36
* @param mimeContainer the MIME container to read extracted binary content from
37
* @return the object graph
38
* @throws XmlMappingException if the given source cannot be mapped to an object
39
* @throws IOException if an I/O Exception occurs
40
*/
41
Object unmarshal(Source source, @Nullable MimeContainer mimeContainer)
42
throws XmlMappingException, IOException;
43
}
44
```
45
46
### MimeContainer
47
48
Container interface for managing MIME attachments and XOP packages.
49
50
```java { .api }
51
public interface MimeContainer {
52
/**
53
* Indicate whether this container is a XOP package.
54
* @return true when the constraints specified in XOP Documents are met
55
*/
56
boolean isXopPackage();
57
58
/**
59
* Turn this message into a XOP package.
60
* @return true when the message actually is a XOP package
61
*/
62
boolean convertToXopPackage();
63
64
/**
65
* Add the given data handler as an attachment to this container.
66
* @param contentId the content id of the attachment
67
* @param dataHandler the data handler containing the data of the attachment
68
*/
69
void addAttachment(String contentId, DataHandler dataHandler);
70
71
/**
72
* Return the attachment with the given content id, or null if not found.
73
* @param contentId the content id
74
* @return the attachment, as a data handler
75
*/
76
@Nullable
77
DataHandler getAttachment(String contentId);
78
}
79
```
80
81
## Usage Examples
82
83
### Basic MIME Marshalling
84
85
```java
86
import org.springframework.oxm.mime.MimeMarshaller;
87
import org.springframework.oxm.mime.MimeContainer;
88
import jakarta.activation.DataHandler;
89
import jakarta.activation.ByteArrayDataSource;
90
import javax.xml.transform.stream.StreamResult;
91
import java.io.StringWriter;
92
93
// Assuming you have a MimeMarshaller implementation (like Jaxb2Marshaller)
94
MimeMarshaller mimeMarshaller = // ... get mime marshaller
95
96
// Create a MimeContainer implementation
97
MimeContainer mimeContainer = new MimeContainer() {
98
private Map<String, DataHandler> attachments = new HashMap<>();
99
private boolean isXop = false;
100
101
@Override
102
public boolean isXopPackage() { return isXop; }
103
104
@Override
105
public boolean convertToXopPackage() {
106
isXop = true;
107
return true;
108
}
109
110
@Override
111
public void addAttachment(String contentId, DataHandler dataHandler) {
112
attachments.put(contentId, dataHandler);
113
}
114
115
@Override
116
public DataHandler getAttachment(String contentId) {
117
return attachments.get(contentId);
118
}
119
};
120
121
// Object with binary data
122
DocumentWithAttachment document = new DocumentWithAttachment();
123
document.setTitle("My Document");
124
document.setContent("Document content");
125
document.setBinaryData(new byte[]{ /* large binary data */ });
126
127
// Marshal with MIME container
128
StringWriter writer = new StringWriter();
129
mimeMarshaller.marshal(document, new StreamResult(writer), mimeContainer);
130
131
// The XML will contain references to attachments
132
String xml = writer.toString();
133
// Binary data is stored in mimeContainer attachments
134
```
135
136
### MIME Unmarshalling
137
138
```java
139
import org.springframework.oxm.mime.MimeUnmarshaller;
140
import javax.xml.transform.stream.StreamSource;
141
import java.io.StringReader;
142
143
// Assuming you have the XML and populated MimeContainer from marshalling
144
String xmlWithReferences = // ... XML with attachment references
145
MimeContainer populatedContainer = // ... container with attachments
146
147
MimeUnmarshaller mimeUnmarshaller = // ... get mime unmarshaller
148
149
// Unmarshal with MIME container
150
StringReader reader = new StringReader(xmlWithReferences);
151
DocumentWithAttachment document = (DocumentWithAttachment)
152
mimeUnmarshaller.unmarshal(new StreamSource(reader), populatedContainer);
153
154
// Binary data is restored from attachments
155
byte[] binaryData = document.getBinaryData();
156
```
157
158
### JAXB with MIME Support
159
160
The `Jaxb2Marshaller` implements both `MimeMarshaller` and `MimeUnmarshaller`:
161
162
```java
163
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
164
import jakarta.xml.bind.annotation.XmlRootElement;
165
import jakarta.xml.bind.annotation.XmlMimeType;
166
import jakarta.activation.DataHandler;
167
168
// JAXB class with MIME type annotation
169
@XmlRootElement
170
public class DocumentWithImage {
171
private String title;
172
173
@XmlMimeType("image/jpeg")
174
private DataHandler imageData;
175
176
// getters and setters...
177
}
178
179
// Configure JAXB marshaller for MIME support
180
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
181
marshaller.setClassesToBeBound(DocumentWithImage.class);
182
marshaller.setMtomEnabled(true); // Enable MTOM support
183
marshaller.afterPropertiesSet();
184
185
// Create document with image
186
DocumentWithImage doc = new DocumentWithImage();
187
doc.setTitle("Document with Image");
188
189
// Create DataHandler for image
190
byte[] imageBytes = // ... load image data
191
DataHandler imageHandler = new DataHandler(
192
new ByteArrayDataSource(imageBytes, "image/jpeg")
193
);
194
doc.setImageData(imageHandler);
195
196
// Marshal with MIME container
197
MimeContainer container = // ... your container implementation
198
StringWriter writer = new StringWriter();
199
marshaller.marshal(doc, new StreamResult(writer), container);
200
```
201
202
### XOP Package Handling
203
204
```java
205
// Working with XOP packages
206
MimeContainer container = // ... your container
207
208
// Check if container supports XOP
209
if (!container.isXopPackage()) {
210
// Convert to XOP package for optimized binary handling
211
boolean converted = container.convertToXopPackage();
212
if (converted) {
213
System.out.println("Container converted to XOP package");
214
}
215
}
216
217
// Add binary data as attachment
218
byte[] binaryData = // ... your binary data
219
DataHandler dataHandler = new DataHandler(
220
new ByteArrayDataSource(binaryData, "application/octet-stream")
221
);
222
container.addAttachment("binary-content-1", dataHandler);
223
```
224
225
### Custom MimeContainer Implementation
226
227
```java
228
import java.util.concurrent.ConcurrentHashMap;
229
import java.util.Map;
230
231
public class SimpleMimeContainer implements MimeContainer {
232
private final Map<String, DataHandler> attachments = new ConcurrentHashMap<>();
233
private volatile boolean isXopPackage = false;
234
235
@Override
236
public boolean isXopPackage() {
237
return isXopPackage;
238
}
239
240
@Override
241
public boolean convertToXopPackage() {
242
this.isXopPackage = true;
243
return true;
244
}
245
246
@Override
247
public void addAttachment(String contentId, DataHandler dataHandler) {
248
if (contentId == null || dataHandler == null) {
249
throw new IllegalArgumentException("ContentId and DataHandler cannot be null");
250
}
251
attachments.put(contentId, dataHandler);
252
}
253
254
@Override
255
public DataHandler getAttachment(String contentId) {
256
return attachments.get(contentId);
257
}
258
259
// Additional utility methods
260
public Set<String> getAttachmentIds() {
261
return attachments.keySet();
262
}
263
264
public int getAttachmentCount() {
265
return attachments.size();
266
}
267
268
public void clearAttachments() {
269
attachments.clear();
270
}
271
}
272
```
273
274
## JAXB MIME Annotations
275
276
When using JAXB with MIME support, use these annotations:
277
278
```java
279
import jakarta.xml.bind.annotation.*;
280
import jakarta.activation.DataHandler;
281
import java.awt.Image;
282
283
@XmlRootElement
284
public class MultimediaDocument {
285
286
@XmlElement
287
private String title;
288
289
// Binary data as DataHandler with MIME type
290
@XmlMimeType("image/jpeg")
291
private DataHandler photo;
292
293
// Binary data as byte array
294
@XmlMimeType("application/pdf")
295
private byte[] pdfContent;
296
297
// Image data
298
@XmlMimeType("image/png")
299
private Image diagram;
300
301
// getters and setters...
302
}
303
```
304
305
## MTOM Configuration
306
307
Message Transmission Optimization Mechanism (MTOM) configuration:
308
309
```java
310
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
311
312
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
313
marshaller.setClassesToBeBound(MultimediaDocument.class);
314
315
// Enable MTOM for optimized binary transfer
316
marshaller.setMtomEnabled(true);
317
318
// Configure attachment marshaller/unmarshaller if needed
319
marshaller.setAttachmentMarshaller(customAttachmentMarshaller);
320
marshaller.setAttachmentUnmarshaller(customAttachmentUnmarshaller);
321
322
marshaller.afterPropertiesSet();
323
```
324
325
## Error Handling
326
327
```java
328
try {
329
MimeContainer container = new SimpleMimeContainer();
330
DocumentWithAttachment doc = new DocumentWithAttachment();
331
332
StringWriter writer = new StringWriter();
333
mimeMarshaller.marshal(doc, new StreamResult(writer), container);
334
335
} catch (XmlMappingException e) {
336
System.err.println("MIME marshalling failed: " + e.getMessage());
337
338
// Check for specific MIME-related errors
339
Throwable cause = e.getCause();
340
if (cause instanceof jakarta.xml.bind.MarshalException) {
341
System.err.println("JAXB marshalling error: " + cause.getMessage());
342
}
343
}
344
```
345
346
## Required Imports
347
348
```java
349
import org.springframework.oxm.mime.MimeMarshaller;
350
import org.springframework.oxm.mime.MimeUnmarshaller;
351
import org.springframework.oxm.mime.MimeContainer;
352
import org.springframework.oxm.XmlMappingException;
353
354
import jakarta.activation.DataHandler;
355
import jakarta.activation.DataSource;
356
import jakarta.activation.ByteArrayDataSource;
357
import jakarta.xml.bind.annotation.XmlMimeType;
358
import jakarta.xml.bind.attachment.AttachmentMarshaller;
359
import jakarta.xml.bind.attachment.AttachmentUnmarshaller;
360
361
import javax.xml.transform.Result;
362
import javax.xml.transform.Source;
363
import javax.xml.transform.stream.StreamResult;
364
import javax.xml.transform.stream.StreamSource;
365
366
import java.io.IOException;
367
import java.util.Map;
368
import java.util.HashMap;
369
import java.util.concurrent.ConcurrentHashMap;
370
```
371
372
## Best Practices
373
374
1. **Use MTOM for large binary data**: Enable MTOM when dealing with large binary attachments
375
2. **Content-ID management**: Use meaningful content IDs for attachments
376
3. **Memory considerations**: Be aware that large attachments are held in memory
377
4. **Security**: Validate attachment content types and sizes
378
5. **Cleanup**: Properly dispose of DataHandler resources when done
379
6. **XOP package conversion**: Convert to XOP packages for better optimization when dealing with multiple binary attachments