You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2026/08/26 07:57:37 UTC

(james-mime4j) branch master updated (c9819989 -> 995df2d5)

This is an automated email from the ASF dual-hosted git repository.

chibenwa pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/james-mime4j.git


    from c9819989 Set up default protection ruleset for default and release branches (#125)
     new adfd2a5d [DOC] Migrate content to Antora
     new 995df2d5 [DOC] Document the DOM

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 docs/antora.yml                                    |   8 +-
 .../modules/ROOT/assets/images/james-logo.png      | Bin
 docs/modules/ROOT/nav.adoc                         |  11 +
 docs/modules/ROOT/pages/development/build.adoc     |  62 ++++
 docs/modules/ROOT/pages/development/index.adoc     |  16 +
 docs/modules/ROOT/pages/dom.adoc                   | 396 +++++++++++++++++++++
 docs/modules/ROOT/pages/index.adoc                 |  39 +-
 docs/modules/ROOT/pages/samples.adoc               |  38 ++
 docs/modules/ROOT/pages/status.adoc                |  27 ++
 docs/modules/ROOT/pages/usage.adoc                 | 166 +++++++++
 10 files changed, 760 insertions(+), 3 deletions(-)
 copy james-logo.png => docs/modules/ROOT/assets/images/james-logo.png (100%)
 create mode 100644 docs/modules/ROOT/nav.adoc
 create mode 100644 docs/modules/ROOT/pages/development/build.adoc
 create mode 100644 docs/modules/ROOT/pages/development/index.adoc
 create mode 100644 docs/modules/ROOT/pages/dom.adoc
 create mode 100644 docs/modules/ROOT/pages/samples.adoc
 create mode 100644 docs/modules/ROOT/pages/status.adoc
 create mode 100644 docs/modules/ROOT/pages/usage.adoc


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


(james-mime4j) 01/02: [DOC] Migrate content to Antora

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

chibenwa pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-mime4j.git

commit adfd2a5d78739db07b5959011e6ae0a42653eb24
Author: Benoit TELLIER <bt...@linagora.com>
AuthorDate: Sun Aug 23 23:00:49 2026 +0700

    [DOC] Migrate content to Antora
---
 docs/antora.yml                                |   4 +-
 docs/modules/ROOT/assets/images/james-logo.png | Bin 0 -> 5243 bytes
 docs/modules/ROOT/nav.adoc                     |  10 ++
 docs/modules/ROOT/pages/development/build.adoc |  62 ++++++++++
 docs/modules/ROOT/pages/development/index.adoc |  16 +++
 docs/modules/ROOT/pages/index.adoc             |  39 +++++-
 docs/modules/ROOT/pages/samples.adoc           |  38 ++++++
 docs/modules/ROOT/pages/status.adoc            |  27 +++++
 docs/modules/ROOT/pages/usage.adoc             | 162 +++++++++++++++++++++++++
 9 files changed, 355 insertions(+), 3 deletions(-)

diff --git a/docs/antora.yml b/docs/antora.yml
index ec23c59f..020ee6df 100644
--- a/docs/antora.yml
+++ b/docs/antora.yml
@@ -1,4 +1,6 @@
 name: mime4j
 title: Apache James Mime4j
-version: '0.8.4-SNAPSHOT'
+version: '0.8.15-SNAPSHOT'
 prerelease: true
+nav:
+  - modules/ROOT/nav.adoc
diff --git a/docs/modules/ROOT/assets/images/james-logo.png b/docs/modules/ROOT/assets/images/james-logo.png
new file mode 100644
index 00000000..2ef3d9a1
Binary files /dev/null and b/docs/modules/ROOT/assets/images/james-logo.png differ
diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc
new file mode 100644
index 00000000..b4140ef4
--- /dev/null
+++ b/docs/modules/ROOT/nav.adoc
@@ -0,0 +1,10 @@
+* xref:index.adoc[Overview]
+* xref:status.adoc[Status]
+* xref:samples.adoc[Examples]
+* xref:usage.adoc[Usage]
+* xref:development/index.adoc[Developer corner]
+** xref:development/build.adoc[Build]
+* https://james.apache.org/mail.html#Mime4j[Mailing list]
+* https://james.apache.org/mime4j/apidocs/index.html[Javadoc]
+* https://issues.apache.org/jira/browse/MIME4J/[Issue tracker]
+* https://james.apache.org/download.cgi#Apache_Mime4J[Download releases]
diff --git a/docs/modules/ROOT/pages/development/build.adoc b/docs/modules/ROOT/pages/development/build.adoc
new file mode 100644
index 00000000..4b599373
--- /dev/null
+++ b/docs/modules/ROOT/pages/development/build.adoc
@@ -0,0 +1,62 @@
+= Building Mime4J
+
+== Using a stable release
+
+Go to the https://james.apache.org/download.cgi#Apache_Mime4J[download pages] and download
+the most recent release in your preferred format, either `james-mime4j-x.y-src.tar.gz` or
+`james-mime4j-x.y-src.zip`. Extracting the archived sources will create the directory
+`james-mime4j-x.y/`.
+
+== Checking out the sources
+
+Issue the following command in a shell:
+
+----
+git clone https://github.com/apache/james-mime4j.git
+----
+
+== Download and install Maven
+
+You will need to download and install https://maven.apache.org/[Maven] before building the
+sources. Maven 3.6.0 is the minimum required version.
+
+One of the main differences between Maven and plain ant is that Maven manages external
+dependencies for your projects and (at least in theory) you should no longer have to store
+third-party jar files in your source code tree. It maintains a local repository of
+versioned libraries and shares them between your Maven projects. If it can't find the
+necessary files there it will attempt to download them from Maven central. So to use the
+Maven build, you need to have a network connection available for the initial download of
+the project dependencies.
+
+== Building the Mime4J jar
+
+Once Maven has been installed, building the project should be as simple as typing
+
+----
+cd james-mime4j-x.y/ (cd james-mime4j/ if sources come from git)
+mvn package
+----
+
+from the command line. Maven will automatically run all test cases for you and create the
+jar file in the `target` directory.
+
+To install the jars into your local Maven repository run:
+
+----
+mvn install
+----
+
+Useful options include:
+
+* `-DskipTests` to skip the test suite.
+* `-T 4` to parallelize the build on several CPUs.
+
+[NOTE]
+====
+Mime4J uses https://javacc.github.io/javacc/[JavaCC] to generate parsers for header fields.
+If your IDE does not pick up the generated code automatically, add
+`target/generated-sources/javacc` and `target/generated-sources/jjtree` as source folders.
+====
+
+For more information on using Maven, have a look at the
+https://maven.apache.org/[Maven web site].
diff --git a/docs/modules/ROOT/pages/development/index.adoc b/docs/modules/ROOT/pages/development/index.adoc
new file mode 100644
index 00000000..e6d8f1fa
--- /dev/null
+++ b/docs/modules/ROOT/pages/development/index.adoc
@@ -0,0 +1,16 @@
+= Getting started with Mime4J
+
+Overview of the getting started documentation.
+
+[cols="1,2"]
+|===
+|Document |Description
+
+|https://james.apache.org/download.cgi#Apache_Mime4J[Download]
+|Before you can start using Mime4J, you'll have to download the distribution to your system
+(unless you plan on building the project from source). This document provides links to the
+various distributions available.
+
+|xref:development/build.adoc[Building Mime4J]
+|Describes how to build Mime4J from the sources.
+|===
diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc
index ad7ff8cb..f4da1517 100644
--- a/docs/modules/ROOT/pages/index.adoc
+++ b/docs/modules/ROOT/pages/index.adoc
@@ -1,4 +1,39 @@
-= Apache James jSieve documentation
+= Apache James Mime4J
 
+image::james-logo.png[link="https://james.apache.org"]
 
-TODO: This is a placeholder. Migrate older documentation.
+Apache James Mime4J is developed by the https://james.apache.org/[Apache James] team but
+now has a dedicated https://james.apache.org/mail.html#Mime4j[mailing list].
+
+Apache James Mime4J provides a parser,
+https://james.apache.org/mime4j/apidocs/org/apache/james/mime4j/parser/MimeStreamParser.html[`MimeStreamParser`],
+for e-mail message streams in plain rfc822 and MIME format. The parser uses a callback
+mechanism to report parsing events such as the start of an entity header, the start of a
+body, etc. If you are familiar with the http://www.saxproject.org/[SAX] XML parser
+interface you should have no problem getting started with Mime4J.
+
+The parser only deals with the structure of the message stream. It won't do any decoding
+of base64 or quoted-printable encoded header fields and bodies. This is intentional - the
+parser should only provide the most basic functionality needed to build more complex
+parsers. However, Mime4J does include facilities to decode bodies and fields and the
+`Message` class described below handles decoding of fields and bodies transparently.
+
+The parser has been designed to be extremely tolerant against messages violating the
+standards. It has been tested using a large corpus (>5000) of e-mail messages. As a
+benchmark the widely used perl `MIME::Tools` parser has been used. Mime4J and MIME::Tools
+rarely differ (<25 in those 5000). When they do (which only occurs for illegally formatted
+spam messages) we think Mime4J does a better job.
+
+Mime4J can also be used to build a tree representation of an e-mail message using the
+https://james.apache.org/mime4j/apidocs/org/apache/james/mime4j/message/Message.html[`Message`]
+class. Using this facility Mime4J automatically handles the decoding of fields and bodies
+and uses temporary files for large attachments. This representation is similar to the
+representation constructed by the JavaMail APIs but is more tolerant to messages violating
+the standards.
+
+== Examples
+
+As part of our git repository we provide a couple of
+https://github.com/apache/james-mime4j/tree/master/examples[examples] to ease the use of Mime4J.
+
+Read the xref:samples.adoc[examples page].
diff --git a/docs/modules/ROOT/pages/samples.adoc b/docs/modules/ROOT/pages/samples.adoc
new file mode 100644
index 00000000..200f8730
--- /dev/null
+++ b/docs/modules/ROOT/pages/samples.adoc
@@ -0,0 +1,38 @@
+= Examples
+
+The Mime4J distribution includes
+https://github.com/apache/james-mime4j/tree/master/examples[examples] which demonstrate how
+the library could be used. This section gives you a short review of those samples. For more
+information you should download the distribution and study the sample sources. The samples
+are in the `examples/` sub-directory.
+
+Each one of these examples can be run easily from an IDE.
+
+[cols="1,1"]
+|===
+|Sample |Description
+
+|https://github.com/apache/james-mime4j/tree/master/examples/src/main/java/org/apache/james/mime4j/samples/tree/MessageTree.java[org.apache.james.mime4j.samples.tree.MessageTree]
+a|Displays a tree of the contents of a Mime4J `Message` object in a Swing GUI. To try it out run
+
+----
+java org.apache.james.mime4j.samples.tree.MessageTree path/to/message.msg
+----
+
+The output is very useful if you want to study the structure of MIME messages.
+
+|https://github.com/apache/james-mime4j/tree/master/examples/src/main/java/org/apache/james/mime4j/samples/transform/TransformMessage.java[org.apache.james.mime4j.samples.transform.TransformMessage]
+|Illustrate how to transform a message into another message without modifying the original.
+
+|https://github.com/apache/james-mime4j/tree/master/examples/src/main/java/org/apache/james/mime4j/samples/dom/TextPlainMessage.java[org.apache.james.mime4j.samples.dom.TextPlainMessage]
+|Illustrate the use of Mime4J DOM API. This example generates a message very similar to the
+one from https://www.rfc-editor.org/rfc/rfc5322.txt[RFC 5322] Appendix A.1.1.
+
+|https://github.com/apache/james-mime4j/tree/master/examples/src/main/java/org/apache/james/mime4j/samples/dom/MultipartMessage.java[org.apache.james.mime4j.samples.dom.MultipartMessage]
+|Illustrate the use of Mime4J DOM API. This example creates a multipart/mixed message that
+consists of a text/plain and an image/png part. The image is created on the fly; a similar
+technique can be used to create PDF or XML attachments, for example.
+
+|https://github.com/apache/james-mime4j/tree/master/examples/src/main/java/org/apache/james/mime4j/samples/dom/ParsingMessage.java[org.apache.james.mime4j.samples.dom.ParsingMessage]
+|Illustrate the use of Mime4J DOM API in order to parse messages.
+|===
diff --git a/docs/modules/ROOT/pages/status.adoc b/docs/modules/ROOT/pages/status.adoc
new file mode 100644
index 00000000..30a413f4
--- /dev/null
+++ b/docs/modules/ROOT/pages/status.adoc
@@ -0,0 +1,27 @@
+= Status
+
+The 0.4 release brought a number of significant improvements in terms of supported
+capabilities, flexibility and performance.
+
+The 0.5 release addressed a number of important issues discovered since the 0.4 release.
+In particular it improved Mime4J ability to deal with malformed data streams including
+those intentionally crafted to cause excessive CPU and memory utilization that can lead to
+DoS conditions.
+
+The 0.6 release brought another round of API enhancements and performance optimizations.
+As of this release Mime4J requires a Java 1.5 compatible runtime.
+
+The 0.7 release brings another round of API enhancements, bug fixes and performance
+optimizations. A major effort has been put in code reorganization, separating parsing code
+from DOM manipulation code. Mime4J has been restructured into three separate modules:
+`core`, `dom` and `storage`. The `core` package provides an event-driven SAX style parser
+that relies on a callback mechanism to report parsing events such as the start of an
+entity header, the start of a body, etc. The `dom` package contains base/abstract classes
+and interfaces for MIME-DOM manipulation aiming to provide the base for a full featured
+traversable DOM. Per default the Mime4J DOM builder stores content of individual body
+parts in memory. The `storage` package provides support for more complex storage backends
+such as on-disk storage systems, overflow on max limit, or encrypted storage through the
+JSSE API.
+
+The next release will be version 0.8. As of this release Mime4J requires a Java 1.6
+compatible runtime.
diff --git a/docs/modules/ROOT/pages/usage.adoc b/docs/modules/ROOT/pages/usage.adoc
new file mode 100644
index 00000000..9ef09ec2
--- /dev/null
+++ b/docs/modules/ROOT/pages/usage.adoc
@@ -0,0 +1,162 @@
+= Usage
+
+Mime4J provides two different APIs: an event based API by using the
+https://james.apache.org/mime4j/apidocs/org/apache/james/mime4j/parser/MimeStreamParser.html[`MimeStreamParser`].
+Alternatively, you may use the iterative API, which is available through the
+https://james.apache.org/mime4j/apidocs/org/apache/james/mime4j/parser/MimeTokenStream.html[`MimeTokenStream`].
+In terms of speed, you should not note any differences.
+
+* <<Token streams>>
+* <<Sample token stream>>
+* <<Event handlers>>
+* <<Sample event stream>>
+
+== Token streams
+
+The iterative approach is using the class
+https://james.apache.org/mime4j/apidocs/org/apache/james/mime4j/parser/MimeTokenStream.html[`MimeTokenStream`].
+Here's an example, how you could use the token stream:
+
+[source,java]
+----
+MimeTokenStream stream = new MimeTokenStream();
+stream.parse(new FileInputStream("mime.msg"));
+for (EntityState state = stream.getState();
+     state != EntityState.T_END_OF_STREAM;
+     state = stream.next()) {
+  switch (state) {
+    case T_BODY:
+      System.out.println("Body detected, contents = "
+        + stream.getInputStream() + ", header data = "
+        + stream.getBodyDescriptor());
+      break;
+    case T_FIELD:
+      System.out.println("Header field detected: "
+        + stream.getField());
+      break;
+    case T_START_MULTIPART:
+      System.out.println("Multipart message detexted,"
+        + " header data = "
+        + stream.getBodyDescriptor());
+    ...
+  }
+}
+----
+
+The token stream provides a set of tokens. Tokens are identified by a state. Most states
+are simply event indicators, with no additional data available. However, there are some
+states, which provide additional data. For example, the state `T_BODY`, which indicates
+that an actual body is available. If you note this state, then you may ask for the bodies
+contents, which are provided through the `getInputStream()` method, or you might ask for
+the header data by invoking `getBodyDescriptor()`.
+
+== Sample token stream
+
+The following sample should give you a rough idea of the order, in which you'll receive
+tokens:
+
+----
+T_START_MESSAGE
+    T_START_HEADER
+        T_FIELD
+        T_FIELD
+        ...
+    T_END_HEADER
+    T_START_MULTIPART
+        T_PREAMBLE
+        T_START_BODYPART
+            T_START_HEADER
+                T_FIELD
+                T_FIELD
+                ...
+            T_END_HEADER
+            T_BODY
+        T_END_BODYPART
+        T_START_BODYPART
+            T_START_HEADER
+                T_FIELD
+                T_FIELD
+                ...
+            T_END_HEADER
+            T_BODY
+        T_END_BODYPART
+        T_EPILOGUE
+    T_END_MULTIPART
+ T_END_MESSAGE
+----
+
+The example shows a multipart message with two parts.
+
+== Event handlers
+
+The event based API requires, that you provide an event handler, which receives events.
+The event handler is an object, which implements the
+https://james.apache.org/mime4j/apidocs/org/apache/james/mime4j/parser/ContentHandler.html[`ContentHandler`]
+interface. Here's an example, how you could implement an event handler:
+
+[source,java]
+----
+public class MyContentHandler extends AbstractContentHandler {
+
+    public void body(BodyDescriptor bd, InputStream is)
+            throws MimeException, IOException {
+        System.out.println("Body detected, contents = "
+            + is + ", header data = " + bd);
+    }
+    public void field(String fieldData) throws MimeException {
+        System.out.println("Header field detected: "
+            + fieldData);
+    }
+    public void startMultipart(BodyDescriptor bd) throws MimeException {
+        System.out.println("Multipart message detexted, header data = "
+            + bd);
+    }
+    ...
+}
+----
+
+A little bit of additional code allows us to create an example, which is functionally
+equivalent to the example from the section on <<Token streams>>:
+
+[source,java]
+----
+ContentHandler handler = new MyContentHandler();
+MimeStreamParser parser = new MimeStreamParser();
+parser.setContentHandler(handler);
+parser.parse(new FileInputStream("mime.msg"));
+----
+
+== Sample event stream
+
+Like above for tokens, we provide an additional example, which demonstrates the typical
+order of events that you have to expect:
+
+----
+startMessage()
+    startHeader()
+        field(...)
+        field(...)
+        ...
+    endHeader()
+    startMultipart()
+        preamble(...)
+        startBodyPart()
+            startHeader()
+                field(...)
+                field(...)
+                ...
+            endHeader()
+            body()
+        endBodyPart()
+        startBodyPart()
+            startHeader()
+                field(...)
+                field(...)
+                ...
+            endHeader()
+            body()
+        endBodyPart()
+        epilogue(...)
+    endMultipart()
+endMessage()
+----


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


(james-mime4j) 02/02: [DOC] Document the DOM

Posted by bt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

chibenwa pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-mime4j.git

commit 995df2d5200e1c0de25f9990f068ef959405587d
Author: Benoit TELLIER <bt...@linagora.com>
AuthorDate: Sun Aug 23 23:17:34 2026 +0700

    [DOC] Document the DOM
---
 docs/antora.yml                    |   4 +
 docs/modules/ROOT/nav.adoc         |   1 +
 docs/modules/ROOT/pages/dom.adoc   | 396 +++++++++++++++++++++++++++++++++++++
 docs/modules/ROOT/pages/index.adoc |   2 +-
 docs/modules/ROOT/pages/usage.adoc |   4 +
 5 files changed, 406 insertions(+), 1 deletion(-)

diff --git a/docs/antora.yml b/docs/antora.yml
index 020ee6df..e22fae66 100644
--- a/docs/antora.yml
+++ b/docs/antora.yml
@@ -4,3 +4,7 @@ version: '0.8.15-SNAPSHOT'
 prerelease: true
 nav:
   - modules/ROOT/nav.adoc
+asciidoc:
+  attributes:
+    # Latest released version, as published on Maven central. Bump on release.
+    mime4j-version: '0.8.14@'
diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc
index b4140ef4..e646bd4e 100644
--- a/docs/modules/ROOT/nav.adoc
+++ b/docs/modules/ROOT/nav.adoc
@@ -2,6 +2,7 @@
 * xref:status.adoc[Status]
 * xref:samples.adoc[Examples]
 * xref:usage.adoc[Usage]
+* xref:dom.adoc[Using the DOM API]
 * xref:development/index.adoc[Developer corner]
 ** xref:development/build.adoc[Build]
 * https://james.apache.org/mail.html#Mime4j[Mailing list]
diff --git a/docs/modules/ROOT/pages/dom.adoc b/docs/modules/ROOT/pages/dom.adoc
new file mode 100644
index 00000000..451fbf0b
--- /dev/null
+++ b/docs/modules/ROOT/pages/dom.adoc
@@ -0,0 +1,396 @@
+= Using the DOM API
+
+Mime4J offers two ways of looking at a message. The xref:usage.adoc[streaming API] hands you
+parsing events one at a time and never holds the whole message in memory. The *DOM API*,
+described here, turns a message into a tree of objects that you can navigate, modify and
+write back out. It is the API you want when you need random access to the parts of a
+message, or when you need to *build* a message rather than read one.
+
+The DOM API lives in the `apache-mime4j-dom` artifact:
+
+[source,xml,subs=attributes+]
+----
+<dependency>
+    <groupId>org.apache.james</groupId>
+    <artifactId>apache-mime4j-dom</artifactId>
+    <version>{mime4j-version}</version>
+</dependency>
+----
+
+`apache-mime4j-dom` pulls in `apache-mime4j-core` transitively. The optional
+`apache-mime4j-storage` artifact is only needed if you want body parts stored somewhere
+other than the heap; see <<Choosing where body parts are stored>>.
+
+== The object model
+
+A parsed message is a tree of *entities*. An
+https://james.apache.org/mime4j/apidocs/org/apache/james/mime4j/dom/Entity.html[`Entity`]
+is anything with a header and a body: the message itself, and every body part inside it.
+
+[source]
+----
+Entity
+ |- Header          a list of Field (name / value)
+ |- Body            one of:
+     |- SingleBody      a leaf part
+     |   |- TextBody        text/* — readable as characters
+     |   |- BinaryBody      everything else — readable as bytes
+     |- Multipart       a container, holds a List<Entity>
+     |- Message         a nested message/rfc822 part
+----
+
+The two concrete entity types are
+https://james.apache.org/mime4j/apidocs/org/apache/james/mime4j/dom/Message.html[`Message`]
+(the root, or a nested `message/rfc822` part) and
+https://james.apache.org/mime4j/apidocs/org/apache/james/mime4j/message/BodyPart.html[`BodyPart`]
+(a part inside a `Multipart`). Both expose `getHeader()`, `getBody()`, `getMimeType()`,
+`getCharset()`, `getDispositionType()` and `getFilename()`.
+
+Note that a body is *not* an entity: a `Multipart` has no header of its own, it borrows the
+`Content-Type` of the entity that holds it.
+
+== Parsing a message
+
+The shortest way to parse is `Message.Builder.of(InputStream)`:
+
+[source,java]
+----
+Message message = Message.Builder.of(inputStream).build();
+----
+
+Real-world mail is frequently malformed. `MimeConfig.PERMISSIVE` relaxes the limits and
+recovery rules of the parser, which is usually what you want when parsing messages you did
+not produce yourself:
+
+[source,java]
+----
+Message message = Message.Builder.of()
+        .use(MimeConfig.PERMISSIVE)
+        .parse(inputStream)
+        .build();
+----
+
+`MimeConfig` also comes as `DEFAULT` (the default) and `STRICT` (rejects anything that
+violates the RFCs), and `MimeConfig.custom()` lets you tune individual limits such as
+`setMaxLineLen`, `setMaxHeaderCount` or `setMaxContentLen`. Those limits matter: they are
+what protects you from hostile messages crafted to exhaust CPU or memory.
+
+The same thing can be done with a reusable
+https://james.apache.org/mime4j/apidocs/org/apache/james/mime4j/message/DefaultMessageBuilder.html[`DefaultMessageBuilder`],
+which is handy when you parse many messages with the same settings:
+
+[source,java]
+----
+DefaultMessageBuilder builder = new DefaultMessageBuilder();
+builder.setMimeEntityConfig(MimeConfig.PERMISSIVE);
+
+Message message = builder.parseMessage(inputStream);
+----
+
+== Reading headers
+
+Common fields have typed accessors that give you parsed values rather than raw strings:
+
+[source,java]
+----
+String subject = message.getSubject();
+Date date = message.getDate();
+
+for (Mailbox mailbox : message.getFrom()) {
+    System.out.println(mailbox.getName() + " <" + mailbox.getAddress() + ">");
+}
+----
+
+`getFrom()` returns a `MailboxList`; `getTo()`, `getCc()`, `getBcc()` and `getReplyTo()`
+return an `AddressList`, whose elements are either a `Mailbox` or a `Group` of mailboxes.
+
+Any other field is reachable through the header. A header may hold several fields with the
+same name — `Received` is the usual example — so both a single-valued and a list-valued
+accessor exist:
+
+[source,java]
+----
+Field field = message.getHeader().getField("X-Spam-Score");
+if (field != null) {
+    System.out.println(field.getName() + ": " + field.getBody());
+}
+
+List<Field> received = message.getHeader().getFields("Received");
+----
+
+== Reading the body
+
+For a simple `text/plain` message the body is a `TextBody`, which decodes the transfer
+encoding and the charset for you:
+
+[source,java]
+----
+if (message.getBody() instanceof TextBody) {
+    TextBody body = (TextBody) message.getBody();
+    try (Reader reader = body.getReader()) {
+        StringWriter writer = new StringWriter();
+        reader.transferTo(writer);
+        return writer.toString();
+    }
+}
+----
+
+A multipart message needs to be walked. The three cases to handle are the ones from the
+object model above — a container, a nested message, and a leaf:
+
+[source,java]
+----
+void walk(Entity entity, int depth) throws IOException {
+    String indent = "  ".repeat(depth);
+    System.out.println(indent + entity.getMimeType()
+            + (entity.getFilename() != null ? " (" + entity.getFilename() + ")" : ""));
+
+    Body body = entity.getBody();
+    if (body instanceof Multipart) {
+        for (Entity part : ((Multipart) body).getBodyParts()) {
+            walk(part, depth + 1);
+        }
+    } else if (body instanceof Message) {
+        walk((Message) body, depth + 1);
+    } else if (body instanceof SingleBody) {
+        System.out.println(indent + "  " + ((SingleBody) body).size() + " bytes");
+    }
+}
+----
+
+On a forwarded message that prints:
+
+----
+multipart/mixed
+  text/plain
+    22 bytes
+  message/rfc822
+    multipart/mixed
+      text/plain
+        33 bytes
+      application/pdf (invoice.pdf)
+        13 bytes
+----
+
+The same traversal is how you pull attachments out. `SingleBody.writeTo(OutputStream)`
+writes the *decoded* content, so you get the actual file bytes, not base64:
+
+[source,java]
+----
+void extractAttachments(Entity entity, OutputStream sink) throws IOException {
+    Body body = entity.getBody();
+    if (body instanceof Multipart) {
+        for (Entity part : ((Multipart) body).getBodyParts()) {
+            extractAttachments(part, sink);
+        }
+    } else if ("attachment".equalsIgnoreCase(entity.getDispositionType())) {
+        ((SingleBody) body).writeTo(sink);
+    }
+}
+----
+
+== Building a message
+
+`Message.Builder` builds a message from scratch. Setting a header field is a method call,
+and `setBody` sets the `Content-Type` for you:
+
+[source,java]
+----
+Message message = Message.Builder.of()
+        .setFrom("John Doe <jd...@machine.example>")
+        .setTo("Mary Smith <ma...@example.net>")
+        .setSubject("Saying hello")
+        .setDate(new Date())
+        .generateMessageId("machine.example")
+        .setBody("Hello, Mary!", StandardCharsets.UTF_8)
+        .build();
+----
+
+`Date` and `From` are required by RFC 5322, and a `Message-ID` should be present. The
+address setters take either a `Mailbox`/`Address` object or a string, and the string forms
+throw `ParseException` if the address does not parse.
+
+A multipart message is assembled with `MultipartBuilder` and `BodyPartBuilder`, which offer
+the same header methods per part:
+
+[source,java]
+----
+Message message = Message.Builder.of()
+        .setFrom("John Doe <jd...@machine.example>")
+        .setTo("Mary Smith <ma...@example.net>")
+        .setSubject("Invoice")
+        .setDate(new Date())
+        .setBody(MultipartBuilder.create("mixed")
+                .addBodyPart(BodyPartBuilder.create()
+                        .setBody("Please find the invoice attached.", StandardCharsets.UTF_8)
+                        .setContentTransferEncoding("quoted-printable")
+                        .build())
+                .addBodyPart(BodyPartBuilder.create()
+                        .setBody(pdf, "application/pdf")
+                        .setContentTransferEncoding("base64")
+                        .setContentDisposition("attachment", "invoice.pdf")
+                        .build())
+                .build())
+        .build();
+----
+
+`setContentDisposition("attachment", "invoice.pdf")` sets both the disposition type and the
+filename. Boundaries are generated for you.
+
+`MultipartBuilder.addTextPart` and `addBinaryPart` are shorthands for the common cases, and
+`setBody(text, subtype, charset)` picks the text subtype:
+
+[source,java]
+----
+Message message = Message.Builder.of()
+        .setSubject("Hello")
+        .setBody(MultipartBuilder.create("alternative")
+                .addTextPart("Hello, Mary!", StandardCharsets.UTF_8)
+                .addBodyPart(BodyPartBuilder.create()
+                        .setBody("<p>Hello, <b>Mary</b>!</p>", "html", StandardCharsets.UTF_8)
+                        .build())
+                .build())
+        .build();
+----
+
+For a header field Mime4J has no dedicated setter for, pass a `Field`. `RawField` is the
+simplest implementation, and
+https://james.apache.org/mime4j/apidocs/org/apache/james/mime4j/field/Fields.html[`Fields`]
+has factory methods that build correctly encoded structured fields:
+
+[source,java]
+----
+Message message = Message.Builder.of()
+        .setField(new RawField("X-Mailer", "my-app 1.0"))
+        .setBody("Hello", StandardCharsets.UTF_8)
+        .build();
+----
+
+Nesting a whole message as a `message/rfc822` part — forwarding — is just another
+`setBody` overload:
+
+[source,java]
+----
+Message forwarded = Message.Builder.of()
+        .setFrom("John Doe <jd...@machine.example>")
+        .setSubject("Fwd: " + original.getSubject())
+        .setBody(MultipartBuilder.create("mixed")
+                .addTextPart("See the message below.", StandardCharsets.UTF_8)
+                .addBodyPart(BodyPartBuilder.create()
+                        .setBody(original)
+                        .build())
+                .build())
+        .build();
+----
+
+== Modifying an existing message
+
+`Message.Builder.of(Message)` copies an existing message into a builder. The copy can be
+modified without touching the original:
+
+[source,java]
+----
+Message.Builder builder = Message.Builder.of(original);
+builder.setSubject("Re: " + original.getSubject());
+builder.removeFields("Message-ID");
+builder.generateMessageId("machine.example");
+
+Message reply = builder.build();
+----
+
+`builder.getBody()` gives access to the copied body, so you can cast it to `Multipart` and
+use `addBodyPart`, `removeBodyPart` or `replaceBodyPart` to restructure the message before
+building it. An entity you remove from a tree no longer has an owner — call `dispose()` on
+it, as explained below.
+
+== Writing a message out
+
+https://james.apache.org/mime4j/apidocs/org/apache/james/mime4j/message/DefaultMessageWriter.html[`DefaultMessageWriter`]
+serializes a message back to the wire format:
+
+[source,java]
+----
+MessageWriter writer = new DefaultMessageWriter();
+writer.writeMessage(message, outputStream);
+----
+
+For small messages there is a shortcut:
+
+[source,java]
+----
+byte[] raw = DefaultMessageWriter.asBytes(message);
+----
+
+The writer also has `writeEntity`, `writeMultipart`, `writeHeader` and `writeBody` if you
+need to serialize a fragment.
+
+== Choosing where body parts are stored
+
+By default Mime4J keeps the content of every body part *in memory*
+(`BasicBodyFactory`). That is fine for ordinary mail and terrible for a 200 MB attachment.
+The `apache-mime4j-storage` artifact provides body factories that spill content elsewhere:
+
+[source,xml,subs=attributes+]
+----
+<dependency>
+    <groupId>org.apache.james</groupId>
+    <artifactId>apache-mime4j-storage</artifactId>
+    <version>{mime4j-version}</version>
+</dependency>
+----
+
+[source,java]
+----
+// keep the first 100 kB of each part in memory, write the remainder to a temporary file
+StorageProvider storageProvider =
+        new ThresholdStorageProvider(new TempFileStorageProvider(), 100 * 1024);
+StorageBodyFactory bodyFactory = new StorageBodyFactory(storageProvider, DecodeMonitor.SILENT);
+
+DefaultMessageBuilder builder = new DefaultMessageBuilder();
+builder.setBodyFactory(bodyFactory);
+
+Message message = builder.parseMessage(inputStream);
+----
+
+`ThresholdStorageProvider` keeps the first `thresholdSize` bytes of a part in memory and
+sends only the remainder to its back-end, so small parts never touch the disk at all.
+
+Providers compose. `MemoryStorageProvider` and `TempFileStorageProvider` are the two that
+actually store content; `ThresholdStorageProvider` and `CipherStorageProvider` decorate
+another provider — the latter scrambles whatever the back-end writes, through the JCE API:
+
+[source,java]
+----
+StorageProvider provider = new ThresholdStorageProvider(
+        new CipherStorageProvider(new TempFileStorageProvider()), 100 * 1024);
+----
+
+The same body factory can be handed to `MultipartBuilder.use(...)` and
+`BodyPartBuilder.use(...)` when you are building a message with large parts.
+
+== Releasing resources
+
+Once a body part may live in a temporary file, releasing it is your responsibility.
+`Message` and `BodyPart` implement `Disposable`: calling `dispose()` on a message disposes
+of the whole tree underneath it.
+
+[source,java]
+----
+Message message = Message.Builder.of(inputStream).build();
+try {
+    // use the message
+} finally {
+    message.dispose();
+}
+----
+
+Disposing is harmless with the default in-memory factory, so make it a habit — it is what
+lets you switch to a storage-backed factory later without leaking files. The one case that
+needs attention is an entity you detached from its tree with `removeBodyPart`: nothing will
+dispose of it for you.
+
+== Complete examples
+
+Runnable versions of all of the above live in the
+https://github.com/apache/james-mime4j/tree/master/examples/src/main/java/org/apache/james/mime4j/samples[examples]
+module — see xref:samples.adoc[Examples] for a description of each one.
diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc
index f4da1517..99db20b7 100644
--- a/docs/modules/ROOT/pages/index.adoc
+++ b/docs/modules/ROOT/pages/index.adoc
@@ -29,7 +29,7 @@ https://james.apache.org/mime4j/apidocs/org/apache/james/mime4j/message/Message.
 class. Using this facility Mime4J automatically handles the decoding of fields and bodies
 and uses temporary files for large attachments. This representation is similar to the
 representation constructed by the JavaMail APIs but is more tolerant to messages violating
-the standards.
+the standards. See xref:dom.adoc[Using the DOM API] to get started with it.
 
 == Examples
 
diff --git a/docs/modules/ROOT/pages/usage.adoc b/docs/modules/ROOT/pages/usage.adoc
index 9ef09ec2..66cc5e7e 100644
--- a/docs/modules/ROOT/pages/usage.adoc
+++ b/docs/modules/ROOT/pages/usage.adoc
@@ -6,6 +6,10 @@ Alternatively, you may use the iterative API, which is available through the
 https://james.apache.org/mime4j/apidocs/org/apache/james/mime4j/parser/MimeTokenStream.html[`MimeTokenStream`].
 In terms of speed, you should not note any differences.
 
+Both are low-level: they report the structure of a message as it is read. If you would
+rather manipulate a message as a tree of objects — or build one — see
+xref:dom.adoc[Using the DOM API].
+
 * <<Token streams>>
 * <<Sample token stream>>
 * <<Event handlers>>


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org