Skip to main content
phase4 provides a message dump facility that captures the raw byte streams of incoming and outgoing AS4 messages. This is useful for debugging, auditing, and non-repudiation purposes.

Overview

Dumping is managed through two global interfaces registered in AS4DumpManager:
InterfacePurpose
IAS4IncomingDumperCaptures incoming HTTP request bodies
IAS4OutgoingDumperCaptures outgoing HTTP request and response bodies
Both are null by default (no dumping). Once set, every message is passed through the dumper.

Enabling file-based dumping

1
Configure the dump base path
2
In your phase4.properties (or equivalent configuration source):
3
# Relative to the application startup directory
# Default value: "phase4-dumps"
phase4.dump.path=/var/data/phase4-dumps
4
The AS4Configuration helper reads this value:
5
import com.helger.phase4.config.AS4Configuration;

File dumpDir = AS4Configuration.getDumpBasePathFile();
// Resolves to an absolute File for "phase4.dump.path"
6
Register the incoming dumper
7
import com.helger.phase4.dump.AS4DumpManager;
import com.helger.phase4.dump.AS4IncomingDumperFileBased;

// Use the default path (phase4.dump.path + "incoming/")
AS4DumpManager.setIncomingDumper(new AS4IncomingDumperFileBased());

// Or specify a custom base directory
AS4DumpManager.setIncomingDumper(
    AS4IncomingDumperFileBased.createForDirectory(
        new File("/var/data/phase4-dumps/incoming")
    )
);
8
Register the outgoing dumper
9
import com.helger.phase4.dump.AS4OutgoingDumperFileBased;

// Use the default path (phase4.dump.path + "outgoing/")
AS4DumpManager.setOutgoingDumper(new AS4OutgoingDumperFileBased());

// Or specify a custom base directory
AS4DumpManager.setOutgoingDumper(
    AS4OutgoingDumperFileBased.createForDirectory(
        new File("/var/data/phase4-dumps/outgoing")
    )
);

IAS4IncomingDumper

Called once per incoming AS4 request:
public interface IAS4IncomingDumper {
    /**
     * Called when a new AS4 request arrives.
     * Return an OutputStream to capture the body, or null to skip dumping.
     * The caller is responsible for closing the stream.
     */
    @Nullable OutputStream onNewRequest(
        @NonNull IAS4IncomingMessageMetadata aIncomingMessageMetadata,
        @NonNull HttpHeaderMap aHttpHeaderMap
    ) throws IOException;

    /**
     * Called after the request has been fully processed.
     * Only called if onNewRequest returned non-null.
     */
    void onEndRequest(
        @NonNull IAS4IncomingMessageMetadata aIncomingMessageMetadata,
        @Nullable Exception aCaughtException
    );
}
The raw HTTP request body (including MIME boundaries for MTOM/SwA messages) is written to the returned OutputStream as bytes are read.

IAS4OutgoingDumper

Called once per outgoing AS4 message (and once per retry attempt):
public interface IAS4OutgoingDumper {
    /**
     * Called before sending an AS4 message.
     * - eMsgMode: REQUEST for outgoing messages, RESPONSE for outgoing receipts/errors.
     * - nTry: 0 = initial attempt, 1 = first retry, etc.
     * Return an OutputStream to capture the body, or null to skip dumping.
     */
    @Nullable OutputStream onBeginRequest(
        @NonNull EAS4MessageMode eMsgMode,
        @Nullable IAS4IncomingMessageMetadata aIncomingMessageMetadata,
        @Nullable IAS4IncomingMessageState aIncomingState,
        @NonNull @Nonempty String sMessageID,
        @Nullable HttpHeaderMap aCustomHeaders,
        @Nonnegative int nTry
    ) throws IOException;

    /**
     * Called after the send attempt completes.
     * Only called if onBeginRequest returned non-null.
     */
    void onEndRequest(
        @NonNull EAS4MessageMode eMsgMode,
        @Nullable IAS4IncomingMessageMetadata aIncomingMessageMetadata,
        @Nullable IAS4IncomingMessageState aIncomingState,
        @NonNull @Nonempty String sMessageID,
        @Nullable Exception aCaughtException
    );
}

nTry parameter

The nTry index tracks which send attempt produced a dump:
  • 0 = initial send
  • 1 = first HTTP retry
  • n = nth HTTP retry
The default AS4OutgoingDumperFileBased uses this index to create separate files per attempt.

Custom dumper implementation

You can write a custom dumper to send dumps to a message queue, object store, or database:
public class MyCustomIncomingDumper implements IAS4IncomingDumper {
    @Override
    public OutputStream onNewRequest(
            final IAS4IncomingMessageMetadata metadata,
            final HttpHeaderMap headers) throws IOException {
        // Return a stream that writes to your target storage
        // e.g. an S3 upload stream, Kafka producer stream, etc.
        return new MyStorageOutputStream(metadata.getIncomingUniqueID());
    }

    @Override
    public void onEndRequest(
            final IAS4IncomingMessageMetadata metadata,
            final Exception caughtException) {
        // Commit the upload, close connections, etc.
    }
}

// Register it globally
AS4DumpManager.setIncomingDumper(new MyCustomIncomingDumper());

Including HTTP headers in dumps

AS4IncomingDumperFileBased and AS4OutgoingDumperFileBased both extend abstract base classes that support optional header inclusion:
// Include HTTP headers in the dump file
new AS4IncomingDumperFileBased().setIncludeHeaders(true);
new AS4OutgoingDumperFileBased().setIncludeHeaders(true);
Including headers in dumps will capture all HTTP headers, which may include sensitive information such as Authorization tokens. Ensure dump files are stored securely.

Default file naming

The default file providers (IAS4IncomingDumperFileProvider, IAS4OutgoingDumperFileProvider) use a date-partitioned directory structure:
phase4-dumps/
  incoming/
    2026/03/19/
      {uniqueMessageID}.as4in
  outgoing/
    2026/03/19/
      {messageID}-{nTry}.as4out

Single-use dumpers

For capturing just one message (e.g. in a test), use the single-use variants:
import com.helger.phase4.dump.AS4IncomingDumperSingleUse;
import com.helger.phase4.dump.AS4OutgoingDumperSingleUse;

// Dumps only the next incoming message, then stops
AS4DumpManager.setIncomingDumper(new AS4IncomingDumperSingleUse(
    (metadata, headers) -> new FileOutputStream("/tmp/single-dump.as4")
));
To disable dumping at any time, call AS4DumpManager.setIncomingDumper(null) or AS4DumpManager.setOutgoingDumper(null).

Build docs developers (and LLMs) love