# Large Messages

Apache ActiveMQ Artemis supports sending and receiving of huge messages, even when the
client and server are running with limited memory. The only realistic
limit to the size of a message that can be sent or consumed is the
amount of disk space you have available. We have tested sending and
consuming messages up to 8 GiB in size with a client and server running
in just 50MiB of RAM!

To send a large message, the user can set an `InputStream` on a message
body, and when that message is sent, Apache ActiveMQ Artemis will read the
`InputStream`. A `FileInputStream` could be used for example to send a
huge message from a huge file on disk.

As the `InputStream` is read the data is sent to the server as a stream
of fragments. The server persists these fragments to disk as it receives
them and when the time comes to deliver them to a consumer they are read
back of the disk, also in fragments and sent down the wire. When the
consumer receives a large message it initially receives just the message
with an empty body, it can then set an `OutputStream` on the message to
stream the huge message body to a file on disk or elsewhere. At no time
is the entire message body stored fully in memory, either on the client
or the server.

## Configuring the server

Large messages are stored on a disk directory on the server side, as
configured on the main configuration file.

The configuration property `large-messages-directory` specifies where
large messages are stored.  For JDBC persistence the `large-message-table`
should be configured.

    <configuration xmlns="urn:activemq"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="urn:activemq /schema/artemis-server.xsd">
    ...
    <large-messages-directory>/data/large-messages</large-messages-directory>
    ...
    </configuration

By default the large message directory is `data/largemessages` and `large-message-table` is
configured as "LARGE_MESSAGE_TABLE".

For the best performance we recommend using file store with large messages directory stored
on a different physical volume to the message journal or paging directory.

## Configuring Parameters

Any message larger than a certain size is considered a large message.
Large messages will be split up and sent in fragments. This is
determined by the parameter `minLargeMessageSize`

> **Note**
>
> Apache ActiveMQ Artemis messages are encoded using 2 bytes per character so if the
> message data is filled with ASCII characters (which are 1 byte) the
> size of the resulting Apache ActiveMQ Artemis message would roughly double. This is
> important when calculating the size of a "large" message as it may
> appear to be less than the `minLargeMessageSize` before it is sent,
> but it then turns into a "large" message once it is encoded.

The default value is 100KiB.

### Using Core API

If the Apache ActiveMQ Artemis Core API is used, the minimal large message size is
specified by `ServerLocator.setMinLargeMessageSize`.

``` java
ServerLocator locator = ActiveMQClient.createServerLocatorWithoutHA(new TransportConfiguration(NettyConnectorFactory.class.getName()))

locator.setMinLargeMessageSize(25 * 1024);

ClientSessionFactory factory = ActiveMQClient.createClientSessionFactory();
```

[Configuring the transport directly from the client side](configuring-transports.md) will provide more information on how to instantiate the session
factory.

### Using JMS

If JNDI is used to instantiate and look up the connection factory, the
minimum large message size is configured in the JNDI context
environment, e.g. `jndi.properties`. Here's a simple example using the
"ConnectionFactory" connection factory which is available in the context
by default:

    java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory
    connectionFactory.myConnectionFactory=tcp://localhost:61616?minLargeMessageSize=250000


If the connection factory is being instantiated directly, the minimum
large message size is specified by
`ActiveMQConnectionFactory.setMinLargeMessageSize`.

### Compressed Large Messages

You can choose to send large messages in compressed form using `
                compress-large-messages` attributes.

#### `compressLargeMessages`

If you specify the boolean property `compressLargeMessages` on the
`server locator` or `ConnectionFactory` as true, The system will use the
ZIP algorithm to compress the message body as the message is transferred
to the server's side. Notice that there's no special treatment at the
server's side, all the compressing and uncompressing is done at the
client.

If the compressed size of a large message is below `
                minLargeMessageSize`, it is sent to server as regular
messages. This means that the message won't be written into the server's
large-message data directory, thus reducing the disk I/O.

###

If JNDI is used to instantiate and look up the connection factory, large
message compression can be configured in the JNDI context environment,
e.g. `jndi.properties`. Here's a simple example using the
"ConnectionFactory" connection factory which is available in the context
by default:

    java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory
    connectionFactory.myConnectionFactory=tcp://localhost:61616?compressLargeMessages=true

## Streaming large messages

Apache ActiveMQ Artemis supports setting the body of messages using input and output
streams (`java.lang.io`)

These streams are then used directly for sending (input streams) and
receiving (output streams) messages.

When receiving messages there are 2 ways to deal with the output stream;
you may choose to block while the output stream is recovered using the
method `ClientMessage.saveOutputStream` or alternatively using the
method `ClientMessage.setOutputstream` which will asynchronously write
the message to the stream. If you choose the latter the consumer must be
kept alive until the message has been fully received.

You can use any kind of stream you like. The most common use case is to
send files stored in your disk, but you could also send things like JDBC
Blobs, `SocketInputStream`, things you recovered from `HTTPRequests`
etc. Anything as long as it implements `java.io.InputStream` for sending
messages or `java.io.OutputStream` for receiving them.

### Streaming over Core API

The following table shows a list of methods available at `ClientMessage`
which are also available through JMS by the use of object properties.

<table summary="Server Configuration" border="1">
    <colgroup>
        <col/>
        <col/>
        <col/>
        <col/>
    </colgroup>
    <thead>
    <tr>
        <th>Name</th>
        <th>Description</th>
        <th>JMS Equivalent</th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td>setBodyInputStream(InputStream)</td>
        <td>Set the InputStream used to read a message body when sending it.</td>
        <td>JMS_AMQ_InputStream</td>
    </tr>
    <tr>
        <td>setOutputStream(OutputStream)</td>
        <td>Set the OutputStream that will receive the body of a message. This method does not block.</td>
        <td>JMS_AMQ_OutputStream</td>
    </tr>
    <tr>
        <td>saveOutputStream(OutputStream)</td>
        <td>Save the body of the message to the `OutputStream`. It will block until the entire content is transferred to the `OutputStream`.</td>
        <td>JMS_AMQ_SaveStream</td>
    </tr>
    </tbody>
</table>

  : org.apache.activemq.artemis.api.core.client.ClientMessage API

To set the output stream when receiving a core message:

``` java
ClientMessage msg = consumer.receive(...);


// This will block here until the stream was transferred
msg.saveOutputStream(someOutputStream);

ClientMessage msg2 = consumer.receive(...);

// This will not wait the transfer to finish
msg2.setOutputStream(someOtherOutputStream);
```

Set the input stream when sending a core message:

``` java
ClientMessage msg = session.createMessage();
msg.setInputStream(dataInputStream);
```

Notice also that for messages with more than 2GiB the getBodySize() will
return invalid values since this is an integer (which is also exposed to
the JMS API). On those cases you can use the message property
_AMQ_LARGE_SIZE.

### Streaming over JMS

When using JMS, Apache ActiveMQ Artemis maps the streaming methods on the core API (see
ClientMessage API table above) by setting object properties . You can use the method
`Message.setObjectProperty` to set the input and output streams.

The `InputStream` can be defined through the JMS Object Property
JMS_AMQ_InputStream on messages being sent:

``` java
BytesMessage message = session.createBytesMessage();

FileInputStream fileInputStream = new FileInputStream(fileInput);

BufferedInputStream bufferedInput = new BufferedInputStream(fileInputStream);

message.setObjectProperty("JMS_AMQ_InputStream", bufferedInput);

someProducer.send(message);
```

The `OutputStream` can be set through the JMS Object Property
JMS_AMQ_SaveStream on messages being received in a blocking way.

``` java
BytesMessage messageReceived = (BytesMessage)messageConsumer.receive(120000);

File outputFile = new File("huge_message_received.dat");

FileOutputStream fileOutputStream = new FileOutputStream(outputFile);

BufferedOutputStream bufferedOutput = new BufferedOutputStream(fileOutputStream);

// This will block until the entire content is saved on disk
messageReceived.setObjectProperty("JMS_AMQ_SaveStream", bufferedOutput);
```

Setting the `OutputStream` could also be done in a non blocking way
using the property JMS_AMQ_OutputStream.

``` java
// This won't wait the stream to finish. You need to keep the consumer active.
messageReceived.setObjectProperty("JMS_AMQ_OutputStream", bufferedOutput);
```

> **Note**
>
> When using JMS, Streaming large messages are only supported on
> `StreamMessage` and `BytesMessage`.

## Streaming Alternative

If you choose not to use the `InputStream` or `OutputStream` capability
of Apache ActiveMQ Artemis You could still access the data directly in an alternative
fashion.

On the Core API just get the bytes of the body as you normally would.

``` java
ClientMessage msg = consumer.receive();

byte[] bytes = new byte[1024];
for (int i = 0 ;  i < msg.getBodySize(); i += bytes.length)
{
   msg.getBody().readBytes(bytes);
   // Whatever you want to do with the bytes
}
```

If using JMS API, `BytesMessage` and `StreamMessage` also supports it
transparently.
``` java
BytesMessage rm = (BytesMessage)cons.receive(10000);

byte data[] = new byte[1024];

for (int i = 0; i < rm.getBodyLength(); i += 1024)
{
   int numberOfBytes = rm.readBytes(data);
   // Do whatever you want with the data
}
```

## Large message example

Please see the [examples](examples.md) chapter for an example which shows how large message is configured
and used with JMS.
