blob: f219931d9ea2debf7cc0c7c958c3bad07b2f1548 [file] [log] [blame]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>ActiveMQ</title>
<link rel="icon" type="image/png" href="/assets/img/favicon.png">
<link rel="stylesheet" href="/css/main.css">
<script defer src="/js/fontawesome-all.min.js" integrity="sha384-rOA1PnstxnOBLzCLMcre8ybwbTmemjzdNlILg8O7z1lUkLXozs4DHonlDtnE7fpc"></script>
<script src="/js/jquery.slim.min.js" integrity="sha384-5AkRS45j4ukf+JbWAfHL8P4onPA9p0KwwP7pUdjSQA3ss9edbJUJc/XcYAiheSSz"></script>
<script src="/js/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"></script>
<script src="/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"></script>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light fixed-top">
<div class="container">
<!-- <a class="navbar-brand mr-auto" href="#"><img style="height: 50px" src="assets/img/apache-feather.png" /></a> -->
<a class="navbar-brand mr-auto" href="/"><img src="/assets/img/activemq_logo_black_small.png" style="height: 50px"/></a>
<button class="navbar-toggler ml-auto" type="button" data-toggle="collapse" data-target="#navbarContent" aria-controls="navbarContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="ml-auto collapse navbar-collapse" id="navbarContent">
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a class="nav-link active" href="/news">News</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link" id="navbarDropdownComponents" data-target="#" href="" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Components<span class="caret"></span></a>
<ul class="dropdown-menu dropdown-menu-center" aria-labelledby="navbarDropdownComponents">
<div class="row">
<div class="col-12">
<ul class="multi-column-dropdown">
<li class="nav-item"><a class="dropdown-item" href="/components/classic">ActiveMQ Classic</a></li>
<li class="nav-item"><a class="dropdown-item" href="/components/artemis/">ActiveMQ Artemis</a></li>
<li class="nav-item"><a class="dropdown-item" href="/components/nms">NMS Clients</a></li>
<li class="nav-item"><a class="dropdown-item" href="/components/cms">CMS Client</a></li>
</ul>
</div>
</div>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link" id="navbarDropdownCommunity" data-target="#" href="" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Community<span class="caret"></span></a>
<ul class="dropdown-menu dropdown-menu-center multi-column columns-1" aria-labelledby="navbarDropdownCommunity">
<div class="row">
<div class="col-12">
<ul class="multi-column-dropdown">
<li class="nav-item"><a class="dropdown-item" href="/contact">Contact Us</a></li>
<li class="nav-item"><a class="dropdown-item" href="/contributing">Contribute</a></li>
<li class="nav-item"><a class="dropdown-item" href="/issues">Report Issues</a></li>
<li class="nav-item"><a class="dropdown-item" href="/support">Get Support</a></li>
</ul>
</div>
</div>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link" id="navbarDropdownTeam" data-target="#" href="" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><img src="/assets/img/feather.png" style="height:20px">Apache<span class="caret"></span></a>
<ul class="dropdown-menu dropdown-menu-center multi-column columns-1" aria-labelledby="navbarDropdownTeam">
<div class="row">
<div class="col-sm-12">
<ul class="multi-column-dropdown">
<li class="nav-item"><a class="dropdown-item" href="https://www.apache.org">The Apache Software Foundation</a></li>
<li class="nav-item"><a class="dropdown-item" href="https://www.apache.org/licenses/">License</a></li>
<li class="nav-item"><a class="dropdown-item" href="https://www.apache.org/foundation/sponsorship.html">Sponsorship</a></li>
<li class="nav-item"><a class="dropdown-item" href="https://www.apache.org/foundation/thanks.html">Thanks</a></li>
<li class="nav-item"><a class="dropdown-item" href="/security-advisories">Security</a></li>
<li class="nav-item"><a class="dropdown-item" href="https://www.apache.org/events/current-event">Events</a></li>
<li class="nav-item"><a class="dropdown-item" href="https://people.apache.org/phonebook.html?pmc=activemq">PMC & Committers</a></li>
<li class="nav-item"><a class="dropdown-item" href="https://whimsy.apache.org/board/minutes/ActiveMQ.html">Board Reports</a></li>
<li class="nav-item"><a class="dropdown-item" href="https://privacy.apache.org/policies/privacy-policy-public.html">Privacy Policy</a></li>
</ul>
</div>
</div>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<div class="content">
<div class="page-title-classic">
<div class="container">
<h1>KahaDB</h1>
</div>
</div>
<div class="container" >
<div class="row" style="margin-top: 30px">
<div class="col-12 classic">
<p>KahaDB is a file based persistence database that is local to the message broker that is using it. It has been optimized for fast persistence. It is the the default storage mechanism since <strong>ActiveMQ Classic 5.4</strong>. KahaDB uses less file descriptors and provides faster recovery than its predecessor, the <a href="amq-message-store">AMQ Message Store</a>.</p>
<h2 id="configuration">Configuration</h2>
<p>To use KahaDB as the broker’s persistence adapter configure ActiveMQ Classic as follows (example):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> &lt;broker brokerName="broker"&gt;
&lt;persistenceAdapter&gt;
&lt;kahaDB directory="activemq-data" journalMaxFileLength="32mb"/&gt;
&lt;/persistenceAdapter&gt;
&lt;/broker&gt;
</code></pre></div></div>
<h3 id="kahadb-properties">KahaDB Properties</h3>
<table>
<thead>
<tr>
<th>Property</th>
<th>Default</th>
<th>Comments</th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="language-plaintext highlighter-rouge">archiveCorruptedIndex</code></td>
<td><code class="language-plaintext highlighter-rouge">false</code></td>
<td>If <code class="language-plaintext highlighter-rouge">true</code>, corrupted indexes found at startup will be archived (not deleted).</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">archiveDataLogs</code></td>
<td><code class="language-plaintext highlighter-rouge">false</code></td>
<td>If <code class="language-plaintext highlighter-rouge">true</code>, will move a message data log to the archive directory instead of deleting it.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">checkForCorruptJournalFiles</code></td>
<td><code class="language-plaintext highlighter-rouge">false</code></td>
<td>If <code class="language-plaintext highlighter-rouge">true</code>, will check for corrupt journal files on startup and try and recover them.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">checkpointInterval</code></td>
<td><code class="language-plaintext highlighter-rouge">5000</code></td>
<td>Time (ms) before check-pointing the journal.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">checksumJournalFiles</code></td>
<td><code class="language-plaintext highlighter-rouge">true</code></td>
<td>Create a checksum for a journal file. The presence of a checksum is required in order for the persistence adapter to be able to detect corrupt journal files. Before <strong>ActiveMQ Classic 5.9.0</strong>: the default is <code class="language-plaintext highlighter-rouge">false</code>.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">cleanupInterval</code></td>
<td><code class="language-plaintext highlighter-rouge">30000</code></td>
<td>The interval (in ms) between consecutive checks that determine which journal files, if any, are eligible for removal from the message store. An eligible journal file is one that has no outstanding references.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">compactAcksAfterNoGC</code></td>
<td><code class="language-plaintext highlighter-rouge">10</code></td>
<td>From <strong>ActiveMQ Classic 5.14.0</strong>: when the acknowledgement compaction feature is enabled this value controls how many store GC cycles must be completed with no other files being cleaned up before the compaction logic is triggered to possibly compact older acknowledgements spread across journal files into a new log file.  The lower the value set the faster the compaction may occur which can impact performance if it runs to often.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">compactAcksIgnoresStoreGrowth</code></td>
<td><code class="language-plaintext highlighter-rouge">false</code></td>
<td>From <strong>ActiveMQ Classic 5.14.0</strong>: when the acknowledgement compaction feature is enabled this value controls whether compaction is run when the store is still growing or if it should only occur when the store has stopped growing (either due to idle or store limits reached).  If enabled the compaction runs regardless of the store still having room or being active which can decrease overall performance but reclaim space faster. </td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">concurrentStoreAndDispatchQueues</code></td>
<td><code class="language-plaintext highlighter-rouge">true</code></td>
<td>Enable the dispatching of Queue messages to interested clients to happen concurrently with message storage.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">concurrentStoreAndDispatchTopics</code></td>
<td><code class="language-plaintext highlighter-rouge">false</code></td>
<td>Enable the dispatching of Topic messages to interested clients to happen concurrently with message storage. <strong>Enabling this property is not recommended.</strong></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">directory</code></td>
<td><code class="language-plaintext highlighter-rouge">activemq-data</code></td>
<td>The path to the directory to use to store the message store data and log files.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">directoryArchive</code></td>
<td><code class="language-plaintext highlighter-rouge">null</code></td>
<td>Define the directory to move data logs to when they all the messages they contain have been consumed.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">enableAckCompaction</code></td>
<td><code class="language-plaintext highlighter-rouge">true</code></td>
<td>From <strong>ActiveMQ Classic 5.14.0</strong>: this setting controls whether the store will perform periodic compaction of older journal log files that contain only Message acknowledgements. By compacting these older acknowledgements into new journal log files the older files can be removed freeing space and allowing the message store to continue to operate without hitting store size limits.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">enableIndexWriteAsync</code></td>
<td><code class="language-plaintext highlighter-rouge">false</code></td>
<td>If <code class="language-plaintext highlighter-rouge">true</code>, the index is updated asynchronously.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">enableJournalDiskSyncs</code></td>
<td><code class="language-plaintext highlighter-rouge">true</code></td>
<td>Ensure every journal write is followed by a disk sync (JMS durability requirement). This property is deprecated as of <strong>ActiveMQ Classic</strong> <strong>5.14.0</strong>. From <strong>ActiveMQ Classic</strong> <strong>5.14.0</strong>: see <code class="language-plaintext highlighter-rouge">journalDiskSyncStrategy</code>.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">ignoreMissingJournalfiles</code></td>
<td><code class="language-plaintext highlighter-rouge">false</code></td>
<td>If <code class="language-plaintext highlighter-rouge">true</code>, reports of missing journal files are ignored.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">indexCacheSize</code></td>
<td><code class="language-plaintext highlighter-rouge">10000</code></td>
<td>Number of index pages cached in memory.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">indexDirectory</code></td>
<td> </td>
<td>From <strong>ActiveMQ Classic 5.10.0</strong>: If set, configures where the KahaDB index files (<code class="language-plaintext highlighter-rouge">db.data</code> and <code class="language-plaintext highlighter-rouge">db.redo</code>) will be stored. If not set, the index files are stored in the directory specified by the <code class="language-plaintext highlighter-rouge">directory</code> attribute.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">indexWriteBatchSize</code></td>
<td><code class="language-plaintext highlighter-rouge">1000</code></td>
<td>Number of indexes written in a batch.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">journalDiskSyncInterval</code></td>
<td><code class="language-plaintext highlighter-rouge">1000</code></td>
<td>Interval (ms) for when to perform a disk sync when <code class="language-plaintext highlighter-rouge">journalDiskSyncStrategy=periodic</code>. A sync will only be performed if a write has occurred to the journal since the last disk sync or when the journal rolls over to a new journal file.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">journalDiskSyncStrategy</code></td>
<td><code class="language-plaintext highlighter-rouge">always</code></td>
<td>From <strong>ActiveMQ Classic 5.14.0</strong>: this setting configures the disk sync policy. The list of available sync strategies are (in order of decreasing safety, and increasing performance): <code class="language-plaintext highlighter-rouge">always</code> Ensure every journal write is followed by a disk sync (JMS durability requirement). This is the safest option but is also the slowest because it requires a sync after every message write. This is equivalent to the deprecated property <code class="language-plaintext highlighter-rouge">enableJournalDiskSyncs=true</code>. <code class="language-plaintext highlighter-rouge">periodic</code> The disk will be synced at set intervals (if a write has occurred) instead of after every journal write which will reduce the load on the disk and should improve throughput. The disk will also be synced when rolling over to a new journal file. The default interval is 1 second. The default interval offers very good performance, whilst being safer than <code class="language-plaintext highlighter-rouge">never</code> disk syncing, as data loss is limited to a maximum of 1 second’s worth. See <code class="language-plaintext highlighter-rouge">journalDiskSyncInterval</code> to change the frequency of disk syncs. <code class="language-plaintext highlighter-rouge">never</code> A sync will never be explicitly called and it will be up to the operating system to flush to disk. This is equivalent to setting the deprecated property <code class="language-plaintext highlighter-rouge">enableJournalDiskSyncs=false</code>. This is the fastest option but is the least safe as there’s no guarantee as to when data is flushed to disk. Consequently message loss <em>can</em> occur on broker failure.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">journalMaxFileLength</code></td>
<td><code class="language-plaintext highlighter-rouge">32mb</code></td>
<td>A hint to set the maximum size of the message data logs.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">maxAsyncJobs</code></td>
<td><code class="language-plaintext highlighter-rouge">10000</code></td>
<td>The maximum number of asynchronous messages that will be queued awaiting storage (should be the same as the number of concurrent MessageProducers).</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">preallocationScope</code></td>
<td><code class="language-plaintext highlighter-rouge">entire_journal</code></td>
<td>From <strong>ActiveMQ Classic 5.14.0</strong>: this setting configures how journal data files are preallocated. The default strategy preallocates the journal file on first use using the appender thread. <code class="language-plaintext highlighter-rouge">entire_journal_async</code> will use preallocate ahead of time in a separate thread. <code class="language-plaintext highlighter-rouge">none</code> disables preallocation. On SSD, using <code class="language-plaintext highlighter-rouge">entire_journal_async</code> avoids delaying writes pending preallocation on first use. <strong>Note</strong>: on HDD the additional thread contention for disk has a negative impact. Therefore use the default.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">preallocationStrategy</code></td>
<td><code class="language-plaintext highlighter-rouge">sparse_file</code></td>
<td>From <strong>ActiveMQ Classic 5.12.0</strong>: This setting configures how the broker will try to preallocate the journal files when a new journal file is needed. <code class="language-plaintext highlighter-rouge">sparse_file</code> - sets the file length, but does not populate it with any data. <code class="language-plaintext highlighter-rouge">os_kernel_copy</code> - delegates the preallocation to the Operating System. <code class="language-plaintext highlighter-rouge">zeros</code>  - each preallocated journal file contains nothing but <code class="language-plaintext highlighter-rouge">0x00</code> throughout.</td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">storeOpenWireVersion</code></td>
<td><code class="language-plaintext highlighter-rouge">11</code></td>
<td>Determines the version of OpenWire commands that are marshaled to the KahaDB journal. Before <strong>ActiveMQ Classic 5.12.0</strong>: the default value is <code class="language-plaintext highlighter-rouge">6</code>. Some features of the broker depend on information stored in the OpenWire commands from newer protocol revisions and these may not work correctly if the store version is set to a lower value.  KahaDB stores from broker versions greater than 5.9.0 will in many cases still be readable by the broker but will cause the broker to continue using the older store version meaning newer features may not work as intended.  For KahaDB stores that were created in versions prior to <strong>ActiveMQ Classic 5.9.0</strong> it will be necessary to manually set <code class="language-plaintext highlighter-rouge">storeOpenWireVersion="6"</code> in order to start a broker without error.</td>
</tr>
</tbody>
</table>
<blockquote>
<p>For tuning locking properties see the options listed at <a href="pluggable-storage-lockers">Pluggable storage lockers.</a></p>
</blockquote>
<h3 id="slow-file-system-access-diagnostic-logging">Slow File System Access Diagnostic Logging</h3>
<p>You can configure a non zero threshold in milliseconds for database updates. If database operation is slower than that threshold (for example if you set it to <code class="language-plaintext highlighter-rouge">500</code>), you may see messages like:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Slow KahaDB access: cleanup took 1277 | org.apache.activemq.store.kahadb.MessageDatabase | ActiveMQ Classic Journal Checkpoint Worker
</code></pre></div></div>
<p>You can configure a threshold used to log these messages by using a system property and adjust it to your disk speed so that you can easily pick up runtime anomalies.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-Dorg.apache.activemq.store.kahadb.LOG_SLOW_ACCESS_TIME=1500
</code></pre></div></div>
<h1 id="multim-kahadb-persistence-adapter">Multi(m) kahaDB Persistence Adapter</h1>
<p>From <strong>ActiveMQ Classic 5.6</strong>: it’s possible to distribute destinations stores across multiple kahdb persistence adapters. When would you do this? If you have one fast producer/consumer destination and another periodic producer destination that has irregular batch consumption then disk usage can grow out of hand as unconsumed messages become distributed across multiple journal files. Having a separate journal for each ensures minimal journal usage. Also, some destination may be critical and require disk synchronization while others may not. In these cases you can use the <code class="language-plaintext highlighter-rouge">mKahaDB</code> persistence adapter and filter destinations using wildcards, just like with destination policy entries.</p>
<h3 id="transactions">Transactions</h3>
<p>Transactions can span multiple journals if the destinations are distributed. This means that two phase completion is necessary, which does impose a performance (additional disk sync) penalty to record the commit outcome. This penalty is only imposed if more than one journal is involved in a transaction.</p>
<h3 id="configuration-1">Configuration</h3>
<p>Each instance of <code class="language-plaintext highlighter-rouge">kahaDB</code> can be configured independently. If no destination is supplied to a <code class="language-plaintext highlighter-rouge">filteredKahaDB</code>, the implicit default value will match any destination, queue or topic. This is a handy catch all. If no matching persistence adapter can be found, destination creation will fail with an exception. The <code class="language-plaintext highlighter-rouge">filteredKahaDB</code> shares its wildcard matching rules with <a href="per-destination-policies">Per Destination Policies</a>.</p>
<p>From ActiveMQ Classic 5.15, <code class="language-plaintext highlighter-rouge">filteredKahaDB</code> support a StoreUsage attribute named <code class="language-plaintext highlighter-rouge">usage</code>. This allows individual disk limits to be imposed on matching queues.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;broker brokerName="broker"&gt;
 &lt;persistenceAdapter&gt;
&lt;mKahaDB directory="${activemq.base}/data/kahadb"&gt;
&lt;filteredPersistenceAdapters&gt;
&lt;!-- match all queues --&gt;
&lt;filteredKahaDB queue="&gt;"&gt;
&lt;usage&gt;
&lt;storeUsage limit="1g" /&gt;
&lt;/usage&gt;
&lt;persistenceAdapter&gt;
&lt;kahaDB journalMaxFileLength="32mb"/&gt;
&lt;/persistenceAdapter&gt;
&lt;/filteredKahaDB&gt;
&lt;!-- match all destinations --&gt;
&lt;filteredKahaDB&gt;
&lt;persistenceAdapter&gt;
&lt;kahaDB enableJournalDiskSyncs="false"/&gt;
&lt;/persistenceAdapter&gt;
&lt;/filteredKahaDB&gt;
&lt;/filteredPersistenceAdapters&gt;
&lt;/mKahaDB&gt;
&lt;/persistenceAdapter&gt;
&lt;/broker&gt;
</code></pre></div></div>
<h3 id="automatic-per-destination-persistence-adapter">Automatic Per Destination Persistence Adapter</h3>
<p>Set <code class="language-plaintext highlighter-rouge">perDestination="true"</code> on the catch all, i.e., when no explicit destination is set, <code class="language-plaintext highlighter-rouge">filteredKahaDB</code> entry. Each matching destination will be assigned its own <code class="language-plaintext highlighter-rouge">kahaDB</code> instance.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;broker brokerName="broker"&gt;
 &lt;persistenceAdapter&gt;
&lt;mKahaDB directory="${activemq.base}/data/kahadb"&gt;
&lt;filteredPersistenceAdapters&gt;
&lt;!-- kahaDB per destinations --&gt;
&lt;filteredKahaDB perDestination="true"&gt;
&lt;persistenceAdapter&gt;
&lt;kahaDB journalMaxFileLength="32mb"/&gt;
&lt;/persistenceAdapter&gt;
&lt;/filteredKahaDB&gt;
&lt;/filteredPersistenceAdapters&gt;
&lt;/mKahaDB&gt;
&lt;/persistenceAdapter&gt;
&lt;/broker&gt;
</code></pre></div></div>
<blockquote>
<p>Specifying both <code class="language-plaintext highlighter-rouge">perDestination="true"</code> <em>and</em> <code class="language-plaintext highlighter-rouge">queue="&gt;"</code> on the same <code class="language-plaintext highlighter-rouge">filteredKahaDB</code> entry has not been tested. It <em>may</em> result in the following exception being raised:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Reason: java.io.IOException: File '/opt/java/apache-activemq-5.9.0/data/mKahaDB/lock' could not be locked as lock is already held for this jvm`
</code></pre></div> </div>
</blockquote>
</div>
</div>
</div>
</div>
<div class="row sitemap">
<div class="col-sm-12">
<div class="container">
<div class="row">
<div class="col-sm-12">
<div class="row">
<div class="col-sm-3">
<div >
<img class="float-left" style="max-height: 100px" src="/assets/img/activemq_logo_white_vertical_small.png"/>
</div>
</div>
<div style="text-align: center; margin-bottom: 0px; margin-top: 30px; font-size: 65%" class="col-sm-6">
<p><a href="https://www.apache.org/foundation/marks/list/">Apache, ActiveMQ, Apache ActiveMQ</a>, the Apache feather logo, and the Apache ActiveMQ project logo are trademarks of The Apache Software Foundation. Copyright &copy; 2024, The Apache Software Foundation. Licensed under <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>.</p>
</div>
<div class="col-sm-3">
<div >
<a href="https://www.apache.org"><img class="float-right" style="margin-top: 10px; max-height: 80px" src="/assets/img/apache-logo-small.png"/></a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>