or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

binary-attachments.mdconvenience-api.mdcore-binding.mddata-type-conversion.mdindex.mdtransform-integration.mdtype-adapters.mdvalidation-error-handling.mdxml-mapping-annotations.md

binary-attachments.mddocs/

0

# Binary Attachments

1

2

Jakarta XML Binding provides support for optimized binary data handling through MTOM (Message Transmission Optimization Mechanism) and SwA (SOAP with Attachments). This framework enables efficient processing of large binary data by avoiding base64 encoding overhead and supporting external attachment references.

3

4

## Capabilities

5

6

### AttachmentMarshaller

7

8

The AttachmentMarshaller enables optimized binary data marshalling by creating external attachments instead of inline base64 encoding.

9

10

```java { .api }

11

public abstract class AttachmentMarshaller {

12

// MTOM attachment support

13

public abstract String addMtomAttachment(

14

javax.activation.DataHandler data,

15

String elementNamespace,

16

String elementLocalName

17

);

18

19

public abstract String addMtomAttachment(

20

byte[] data,

21

int offset,

22

int length,

23

String mimeType,

24

String elementNamespace,

25

String elementLocalName

26

);

27

28

// SwA attachment support

29

public abstract String addSwaRefAttachment(javax.activation.DataHandler data);

30

31

// XOP package detection

32

public boolean isXOPPackage();

33

}

34

```

35

36

**Usage Examples:**

37

38

```java

39

// Custom AttachmentMarshaller implementation

40

public class FileAttachmentMarshaller extends AttachmentMarshaller {

41

private final File attachmentDir;

42

private final Map<String, String> attachmentMap = new HashMap<>();

43

private int attachmentCounter = 0;

44

45

public FileAttachmentMarshaller(File attachmentDir) {

46

this.attachmentDir = attachmentDir;

47

attachmentDir.mkdirs();

48

}

49

50

@Override

51

public String addMtomAttachment(DataHandler data, String elementNamespace, String elementLocalName) {

52

try {

53

String filename = "attachment_" + (++attachmentCounter) + ".dat";

54

File attachmentFile = new File(attachmentDir, filename);

55

56

// Write attachment data to file

57

try (InputStream is = data.getInputStream();

58

FileOutputStream fos = new FileOutputStream(attachmentFile)) {

59

byte[] buffer = new byte[8192];

60

int bytesRead;

61

while ((bytesRead = is.read(buffer)) != -1) {

62

fos.write(buffer, 0, bytesRead);

63

}

64

}

65

66

String cid = "cid:" + filename;

67

attachmentMap.put(cid, attachmentFile.getAbsolutePath());

68

69

System.out.printf("Created MTOM attachment: %s for element {%s}%s%n",

70

cid, elementNamespace, elementLocalName);

71

72

return cid;

73

74

} catch (IOException e) {

75

throw new RuntimeException("Failed to create attachment", e);

76

}

77

}

78

79

@Override

80

public String addMtomAttachment(byte[] data, int offset, int length, String mimeType,

81

String elementNamespace, String elementLocalName) {

82

try {

83

String filename = "attachment_" + (++attachmentCounter) +

84

getExtensionForMimeType(mimeType);

85

File attachmentFile = new File(attachmentDir, filename);

86

87

// Write binary data to file

88

try (FileOutputStream fos = new FileOutputStream(attachmentFile)) {

89

fos.write(data, offset, length);

90

}

91

92

String cid = "cid:" + filename;

93

attachmentMap.put(cid, attachmentFile.getAbsolutePath());

94

95

System.out.printf("Created MTOM attachment: %s (%s) for element {%s}%s%n",

96

cid, mimeType, elementNamespace, elementLocalName);

97

98

return cid;

99

100

} catch (IOException e) {

101

throw new RuntimeException("Failed to create attachment", e);

102

}

103

}

104

105

@Override

106

public String addSwaRefAttachment(DataHandler data) {

107

try {

108

String filename = "swa_" + (++attachmentCounter) + ".dat";

109

File attachmentFile = new File(attachmentDir, filename);

110

111

try (InputStream is = data.getInputStream();

112

FileOutputStream fos = new FileOutputStream(attachmentFile)) {

113

byte[] buffer = new byte[8192];

114

int bytesRead;

115

while ((bytesRead = is.read(buffer)) != -1) {

116

fos.write(buffer, 0, bytesRead);

117

}

118

}

119

120

String cid = "cid:" + filename;

121

attachmentMap.put(cid, attachmentFile.getAbsolutePath());

122

123

System.out.printf("Created SwA attachment: %s%n", cid);

124

125

return cid;

126

127

} catch (IOException e) {

128

throw new RuntimeException("Failed to create SwA attachment", e);

129

}

130

}

131

132

@Override

133

public boolean isXOPPackage() {

134

return true; // This marshaller creates XOP packages

135

}

136

137

private String getExtensionForMimeType(String mimeType) {

138

switch (mimeType.toLowerCase()) {

139

case "image/jpeg": return ".jpg";

140

case "image/png": return ".png";

141

case "image/gif": return ".gif";

142

case "application/pdf": return ".pdf";

143

case "text/plain": return ".txt";

144

default: return ".dat";

145

}

146

}

147

148

public Map<String, String> getAttachmentMap() {

149

return new HashMap<>(attachmentMap);

150

}

151

}

152

153

// Usage with marshaller

154

JAXBContext context = JAXBContext.newInstance(Document.class);

155

Marshaller marshaller = context.createMarshaller();

156

157

File attachmentDir = new File("attachments");

158

FileAttachmentMarshaller attachmentMarshaller = new FileAttachmentMarshaller(attachmentDir);

159

marshaller.setAttachmentMarshaller(attachmentMarshaller);

160

161

Document document = new Document();

162

document.setContent("Document content");

163

document.setImage(new DataHandler(new FileDataSource("large-image.jpg")));

164

165

marshaller.marshal(document, new File("document.xml"));

166

167

// Review created attachments

168

Map<String, String> attachments = attachmentMarshaller.getAttachmentMap();

169

System.out.println("Created attachments: " + attachments);

170

```

171

172

### AttachmentUnmarshaller

173

174

The AttachmentUnmarshaller enables processing of optimized binary data during unmarshalling by resolving attachment references.

175

176

```java { .api }

177

public abstract class AttachmentUnmarshaller {

178

// Retrieve attachment as DataHandler

179

public abstract javax.activation.DataHandler getAttachmentAsDataHandler(String cid);

180

181

// Retrieve attachment as byte array

182

public abstract byte[] getAttachmentAsByteArray(String cid);

183

184

// XOP package detection

185

public boolean isXOPPackage();

186

}

187

```

188

189

**Usage Examples:**

190

191

```java

192

// Custom AttachmentUnmarshaller implementation

193

public class FileAttachmentUnmarshaller extends AttachmentUnmarshaller {

194

private final File attachmentDir;

195

private final Map<String, String> attachmentMap;

196

197

public FileAttachmentUnmarshaller(File attachmentDir, Map<String, String> attachmentMap) {

198

this.attachmentDir = attachmentDir;

199

this.attachmentMap = new HashMap<>(attachmentMap);

200

}

201

202

@Override

203

public DataHandler getAttachmentAsDataHandler(String cid) {

204

String filePath = attachmentMap.get(cid);

205

if (filePath == null) {

206

throw new IllegalArgumentException("Attachment not found: " + cid);

207

}

208

209

File attachmentFile = new File(filePath);

210

if (!attachmentFile.exists()) {

211

throw new IllegalArgumentException("Attachment file not found: " + filePath);

212

}

213

214

System.out.printf("Retrieving attachment as DataHandler: %s -> %s%n", cid, filePath);

215

216

return new DataHandler(new FileDataSource(attachmentFile));

217

}

218

219

@Override

220

public byte[] getAttachmentAsByteArray(String cid) {

221

String filePath = attachmentMap.get(cid);

222

if (filePath == null) {

223

throw new IllegalArgumentException("Attachment not found: " + cid);

224

}

225

226

File attachmentFile = new File(filePath);

227

if (!attachmentFile.exists()) {

228

throw new IllegalArgumentException("Attachment file not found: " + filePath);

229

}

230

231

try {

232

System.out.printf("Retrieving attachment as byte array: %s -> %s%n", cid, filePath);

233

return Files.readAllBytes(attachmentFile.toPath());

234

235

} catch (IOException e) {

236

throw new RuntimeException("Failed to read attachment: " + filePath, e);

237

}

238

}

239

240

@Override

241

public boolean isXOPPackage() {

242

return true; // This unmarshaller handles XOP packages

243

}

244

}

245

246

// Usage with unmarshaller

247

JAXBContext context = JAXBContext.newInstance(Document.class);

248

Unmarshaller unmarshaller = context.createUnmarshaller();

249

250

// Set up attachment unmarshaller with attachment map from marshalling

251

File attachmentDir = new File("attachments");

252

Map<String, String> attachmentMap = loadAttachmentMap(); // Load from previous marshalling

253

FileAttachmentUnmarshaller attachmentUnmarshaller =

254

new FileAttachmentUnmarshaller(attachmentDir, attachmentMap);

255

256

unmarshaller.setAttachmentUnmarshaller(attachmentUnmarshaller);

257

258

Document document = (Document) unmarshaller.unmarshal(new File("document.xml"));

259

260

// Access binary data

261

DataHandler imageData = document.getImage();

262

if (imageData != null) {

263

try (InputStream is = imageData.getInputStream();

264

FileOutputStream fos = new FileOutputStream("restored-image.jpg")) {

265

266

byte[] buffer = new byte[8192];

267

int bytesRead;

268

while ((bytesRead = is.read(buffer)) != -1) {

269

fos.write(buffer, 0, bytesRead);

270

}

271

}

272

}

273

```

274

275

### Annotation Support

276

277

Jakarta XML Binding provides annotations for controlling binary attachment behavior.

278

279

```java { .api }

280

@Target({ElementType.FIELD, ElementType.METHOD})

281

@Retention(RetentionPolicy.RUNTIME)

282

public @interface XmlAttachmentRef {

283

}

284

285

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PACKAGE})

286

@Retention(RetentionPolicy.RUNTIME)

287

public @interface XmlInlineBinaryData {

288

}

289

290

@Target({ElementType.FIELD, ElementType.METHOD})

291

@Retention(RetentionPolicy.RUNTIME)

292

public @interface XmlMimeType {

293

String value();

294

}

295

```

296

297

**Annotation Usage Examples:**

298

299

```java

300

@XmlRootElement

301

public class Document {

302

@XmlElement

303

private String title;

304

305

// Binary data as attachment reference

306

@XmlAttachmentRef

307

@XmlMimeType("image/jpeg")

308

private DataHandler profileImage;

309

310

// Force inline encoding (disable optimization)

311

@XmlInlineBinaryData

312

private byte[] signature;

313

314

// Large document with MIME type specification

315

@XmlAttachmentRef

316

@XmlMimeType("application/pdf")

317

private DataHandler documentPdf;

318

319

// Small binary data (will be inlined)

320

@XmlElement

321

private byte[] checksum;

322

323

// Constructors, getters, setters...

324

public Document() {}

325

326

public String getTitle() { return title; }

327

public void setTitle(String title) { this.title = title; }

328

329

public DataHandler getProfileImage() { return profileImage; }

330

public void setProfileImage(DataHandler profileImage) { this.profileImage = profileImage; }

331

332

public byte[] getSignature() { return signature; }

333

public void setSignature(byte[] signature) { this.signature = signature; }

334

335

public DataHandler getDocumentPdf() { return documentPdf; }

336

public void setDocumentPdf(DataHandler documentPdf) { this.documentPdf = documentPdf; }

337

338

public byte[] getChecksum() { return checksum; }

339

public void setChecksum(byte[] checksum) { this.checksum = checksum; }

340

}

341

```

342

343

### MTOM Configuration and Usage

344

345

MTOM (Message Transmission Optimization Mechanism) provides efficient binary data transmission by avoiding base64 encoding.

346

347

**Complete MTOM Example:**

348

349

```java

350

public class MTOMExample {

351

352

public static void demonstrateMTOM() throws Exception {

353

// Create document with large binary data

354

Document document = new Document();

355

document.setTitle("Sample Document");

356

357

// Add large image as attachment

358

File imageFile = new File("large-photo.jpg");

359

DataHandler imageHandler = new DataHandler(new FileDataSource(imageFile));

360

document.setProfileImage(imageHandler);

361

362

// Add PDF document as attachment

363

File pdfFile = new File("specifications.pdf");

364

DataHandler pdfHandler = new DataHandler(new FileDataSource(pdfFile));

365

document.setDocumentPdf(pdfHandler);

366

367

// Add small signature (will be inlined)

368

document.setSignature("SIGNATURE".getBytes("UTF-8"));

369

370

// Add checksum (small, no optimization)

371

document.setChecksum(calculateChecksum(document));

372

373

// Marshall with MTOM support

374

JAXBContext context = JAXBContext.newInstance(Document.class);

375

Marshaller marshaller = context.createMarshaller();

376

marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

377

378

File attachmentDir = new File("mtom-attachments");

379

FileAttachmentMarshaller attachmentMarshaller =

380

new FileAttachmentMarshaller(attachmentDir);

381

marshaller.setAttachmentMarshaller(attachmentMarshaller);

382

383

File xmlOutput = new File("document-mtom.xml");

384

marshaller.marshal(document, xmlOutput);

385

386

System.out.println("Marshalled document with MTOM attachments");

387

System.out.println("XML file: " + xmlOutput.getAbsolutePath());

388

System.out.println("Attachments: " + attachmentMarshaller.getAttachmentMap());

389

390

// Unmarshall with MTOM support

391

Unmarshaller unmarshaller = context.createUnmarshaller();

392

FileAttachmentUnmarshaller attachmentUnmarshaller =

393

new FileAttachmentUnmarshaller(attachmentDir, attachmentMarshaller.getAttachmentMap());

394

unmarshaller.setAttachmentUnmarshaller(attachmentUnmarshaller);

395

396

Document restoredDocument = (Document) unmarshaller.unmarshal(xmlOutput);

397

398

System.out.println("Unmarshalled document: " + restoredDocument.getTitle());

399

400

// Verify binary data integrity

401

verifyBinaryData(restoredDocument, imageFile, pdfFile);

402

}

403

404

private static byte[] calculateChecksum(Document document) {

405

// Simple checksum calculation

406

return document.getTitle().getBytes().length > 0 ?

407

new byte[]{0x12, 0x34, 0x56, 0x78} :

408

new byte[]{0x00, 0x00, 0x00, 0x00};

409

}

410

411

private static void verifyBinaryData(Document document, File originalImage, File originalPdf)

412

throws Exception {

413

// Verify image data

414

if (document.getProfileImage() != null) {

415

try (InputStream original = new FileInputStream(originalImage);

416

InputStream restored = document.getProfileImage().getInputStream()) {

417

418

if (streamsEqual(original, restored)) {

419

System.out.println("✓ Image data integrity verified");

420

} else {

421

System.out.println("✗ Image data integrity check failed");

422

}

423

}

424

}

425

426

// Verify PDF data

427

if (document.getDocumentPdf() != null) {

428

try (InputStream original = new FileInputStream(originalPdf);

429

InputStream restored = document.getDocumentPdf().getInputStream()) {

430

431

if (streamsEqual(original, restored)) {

432

System.out.println("✓ PDF data integrity verified");

433

} else {

434

System.out.println("✗ PDF data integrity check failed");

435

}

436

}

437

}

438

439

// Verify signature (inlined data)

440

if (document.getSignature() != null) {

441

String signature = new String(document.getSignature(), "UTF-8");

442

if ("SIGNATURE".equals(signature)) {

443

System.out.println("✓ Signature data integrity verified");

444

} else {

445

System.out.println("✗ Signature data integrity check failed");

446

}

447

}

448

}

449

450

private static boolean streamsEqual(InputStream is1, InputStream is2) throws IOException {

451

byte[] buffer1 = new byte[8192];

452

byte[] buffer2 = new byte[8192];

453

454

int bytesRead1, bytesRead2;

455

while ((bytesRead1 = is1.read(buffer1)) != -1) {

456

bytesRead2 = is2.read(buffer2);

457

458

if (bytesRead1 != bytesRead2) {

459

return false;

460

}

461

462

for (int i = 0; i < bytesRead1; i++) {

463

if (buffer1[i] != buffer2[i]) {

464

return false;

465

}

466

}

467

}

468

469

return is2.read() == -1; // Ensure second stream is also at end

470

}

471

}

472

```

473

474

### Web Service Integration

475

476

MTOM and SwA are commonly used in web service scenarios for efficient binary data transmission.

477

478

```java

479

// JAX-WS web service with MTOM support

480

@WebService

481

@MTOM(enabled = true)

482

public class DocumentService {

483

484

@WebMethod

485

public Document uploadDocument(

486

@WebParam(name = "title") String title,

487

@WebParam(name = "content") DataHandler content

488

) {

489

Document document = new Document();

490

document.setTitle(title);

491

document.setDocumentPdf(content);

492

493

// Process document...

494

return document;

495

}

496

497

@WebMethod

498

public DataHandler downloadDocument(

499

@WebParam(name = "documentId") String documentId

500

) {

501

// Retrieve document...

502

File documentFile = new File("documents/" + documentId + ".pdf");

503

return new DataHandler(new FileDataSource(documentFile));

504

}

505

}

506

507

// Client-side MTOM usage

508

@WebServiceClient

509

public class DocumentServiceClient {

510

511

public void uploadLargeDocument() throws Exception {

512

DocumentService service = new DocumentService();

513

DocumentServicePortType port = service.getDocumentServicePort();

514

515

// Enable MTOM on client side

516

BindingProvider bindingProvider = (BindingProvider) port;

517

bindingProvider.getRequestContext().put(

518

JAXWSProperties.MTOM_THRESOLD_VALUE,

519

1024 // Use MTOM for attachments > 1KB

520

);

521

522

// Upload large document

523

File largeFile = new File("specifications.pdf");

524

DataHandler fileHandler = new DataHandler(new FileDataSource(largeFile));

525

526

Document result = port.uploadDocument("Specifications", fileHandler);

527

System.out.println("Uploaded document: " + result.getTitle());

528

529

// Download document

530

DataHandler downloadedContent = port.downloadDocument(result.getId());

531

532

// Save downloaded content

533

try (InputStream is = downloadedContent.getInputStream();

534

FileOutputStream fos = new FileOutputStream("downloaded-specs.pdf")) {

535

536

byte[] buffer = new byte[8192];

537

int bytesRead;

538

while ((bytesRead = is.read(buffer)) != -1) {

539

fos.write(buffer, 0, bytesRead);

540

}

541

}

542

}

543

}

544

```

545

546

### Performance Considerations

547

548

Binary attachment optimization provides significant performance benefits for large data:

549

550

```java

551

// Performance comparison utility

552

public class AttachmentPerformanceComparison {

553

554

public static void compareMethods(File binaryFile) throws Exception {

555

byte[] fileData = Files.readAllBytes(binaryFile.toPath());

556

557

// Method 1: Inline base64 encoding (traditional)

558

long startTime = System.currentTimeMillis();

559

Document docInline = new Document();

560

docInline.setTitle("Inline Document");

561

docInline.setSignature(fileData); // Will be base64 encoded

562

563

JAXBContext context = JAXBContext.newInstance(Document.class);

564

Marshaller marshallerInline = context.createMarshaller();

565

566

StringWriter inlineWriter = new StringWriter();

567

marshallerInline.marshal(docInline, inlineWriter);

568

String inlineXml = inlineWriter.toString();

569

570

long inlineTime = System.currentTimeMillis() - startTime;

571

long inlineSize = inlineXml.getBytes("UTF-8").length;

572

573

// Method 2: MTOM attachment (optimized)

574

startTime = System.currentTimeMillis();

575

Document docMtom = new Document();

576

docMtom.setTitle("MTOM Document");

577

docMtom.setProfileImage(new DataHandler(new FileDataSource(binaryFile)));

578

579

Marshaller marshallerMtom = context.createMarshaller();

580

FileAttachmentMarshaller attachmentMarshaller =

581

new FileAttachmentMarshaller(new File("temp-attachments"));

582

marshallerMtom.setAttachmentMarshaller(attachmentMarshaller);

583

584

StringWriter mtomWriter = new StringWriter();

585

marshallerMtom.marshal(docMtom, mtomWriter);

586

String mtomXml = mtomWriter.toString();

587

588

long mtomTime = System.currentTimeMillis() - startTime;

589

long mtomSize = mtomXml.getBytes("UTF-8").length;

590

591

// Report results

592

System.out.printf("File size: %d bytes%n", fileData.length);

593

System.out.printf("Inline method: %d ms, XML size: %d bytes%n", inlineTime, inlineSize);

594

System.out.printf("MTOM method: %d ms, XML size: %d bytes%n", mtomTime, mtomSize);

595

System.out.printf("Size reduction: %.1f%%%n",

596

100.0 * (inlineSize - mtomSize) / inlineSize);

597

System.out.printf("Time improvement: %.1f%%%n",

598

100.0 * (inlineTime - mtomTime) / inlineTime);

599

}

600

}

601

```

602

603

Binary attachments are particularly beneficial when:

604

- Processing files larger than 1KB (common MTOM threshold)

605

- Working with images, documents, or other binary content

606

- Transmitting data over networks where bandwidth is constrained

607

- Integrating with web services that support MTOM/SwA standards