activemq

Compress and Deleting AMQ Broker Logs Regularly

AMQ Broker, or its Open Source product which is ActiveMQ Artemis, can generate a log to a specific file and can be use for auditing purpose. However sometimes we ned to maintain some log retentions by archieving and deleting logs after some period of time to prevent logs filling up our storage.

We can achieve that by using log4j2 configuration which is comes out of the box in AMQ instance. For example, this configuration below would create a new log file every 5 minutes, compress old logs, and deleting logs that is older than 10 minutes.

# Log file appender
appender.log_file.type = RollingFile
appender.log_file.name = log_file
appender.log_file.fileName = ${sys:artemis.instance}/log/artemis.log
appender.log_file.filePattern = ${sys:artemis.instance}/log/artemis.log.%d{yyyyMMdd.HHmm}.gz
appender.log_file.layout.type = PatternLayout
appender.log_file.layout.pattern = %d %-5level [%logger] %msg%n
appender.log_file.policies.type = Policies
appender.log_file.policies.cron.type = CronTriggeringPolicy
appender.log_file.policies.cron.schedule = 0 */5 * ? * *
appender.log_file.policies.cron.evaluateOnStartup = true
appender.log_file.strategy.type = DefaultRolloverStrategy
appender.log_file.strategy.action.type = Delete
appender.log_file.strategy.action.basePath = ${sys:artemis.instance}/log/
appender.log_file.strategy.action.maxDepth = 1
appender.log_file.strategy.action.condition.type = IfFileName
appender.log_file.strategy.action.condition.glob = artemis.log.*.gz
appender.log_file.strategy.action.ifAny.type = IfAny
appender.log_file.strategy.action.ifAny.ifLastModified.type = IfLastModified
appender.log_file.strategy.action.ifAny.ifLastModified.age = PT10M

We can change 10minutes period into something else by changing the value of “age” following ISO-8601 duration format,

https://logging.apache.org/log4j/2.x/javadoc/log4j-core/org/apache/logging/log4j/core/appender/rolling/action/Duration.html#parse(java.lang.CharSequence)

While log compressing, can be modified by changing the “cron schedule” value,

https://logging.apache.org/log4j/2.12.x/log4j-core/apidocs/org/apache/logging/log4j/core/util/CronExpression.html

“No name matching” and “No subject alternative names present” when Connecting to a Secure Broker on Red Hat AMQ Broker

I had this error when using Artemis to connect to a secure broker on Openshift 4.10. AMQ Broker version is 7.10 and being installed by using Operator.

Caused by: java.security.cert.CertificateException: No name matching enterprise-rhamq-ss-0.enterprise-rhamq-hdls-svc.enterprise-rhamq.svc.cluster.local found
        at java.base/sun.security.util.HostnameChecker.matchDNS(HostnameChecker.java:234) [java.base:]
        at java.base/sun.security.util.HostnameChecker.match(HostnameChecker.java:103) [java.base:]
        at java.base/sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:458) [java.base:]
        at java.base/sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:418) [java.base:]
        at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:292) [java.base:]
        at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:144) [java.base:]
        at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1335) [java.base:]
        ... 28 more

And when accessing with a direct IP, it would give below error

Caused by: java.security.cert.CertificateException: No subject alternative names present
        at java.base/sun.security.util.HostnameChecker.matchIP(HostnameChecker.java:142) [java.base:]
        at java.base/sun.security.util.HostnameChecker.match(HostnameChecker.java:101) [java.base:]
        at java.base/sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:458) [java.base:]
        at java.base/sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:432) [java.base:]
        at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:292) [java.base:]
        at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:144) [java.base:]
        at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1335) [java.base:]
        ... 28 more

Apparently it is due to Java certificate validation. Workaround is quite easy, just adding below configuration on Artemis URL and it should works,

verifyHost=false

Creating an Embeddable JMS Server using Artemis for Unit Testing

In this blog now we are trying to implement an embeddable JMS server for doing testing in Java. Embeddable JMS server is needed to reduce the dependency to a third party, in this case is JMS provider like ActiveMQ, and it is very lightweight therefore we can run it very fast and in multiple times. It is also needed for integration test to see whether our code can integrate smoothly with others. For this scenario im using JUnit 5, which is perhaps the latest version of JUnit at this time.

So lets start with a pom file, for describing what libraries are needed to this. For this example, im using snowdrop bom from Red Hat.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.edw</groupId>
    <artifactId>embeddable-artemis-mq-junit5</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>11</java.version>
        <spring-boot.version>2.1.6.RELEASE</spring-boot.version>
        <spring-boot-bom.version>2.3.6.Final-redhat-00001</spring-boot-bom.version>
        <junit-version>5.7.2</junit-version>

        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <!-- Exclude the Tomcat dependency -->
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-artemis</artifactId>
        </dependency>

        <!-- artemis -->
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>artemis-server</artifactId>
            <version>2.12.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>artemis-core-client</artifactId>
            <version>2.12.0</version>
        </dependency>
        <!-- artemis -->

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>${junit-version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit-version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-commons</artifactId>
            <version>1.7.2</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>dev.snowdrop</groupId>
                <artifactId>snowdrop-dependencies</artifactId>
                <version>${spring-boot-bom.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.1.3.RELEASE</version>
                <configuration>
                    <mainClass>com.edw.Main</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.0.0-M5</version>
            </plugin>

        </plugins>
    </build>

</project>

One of the most important thing is we need to create an xml file called broker.xml. The purpose is to store configuration for our embeddable JMS, one of the most important configuration would be the roles and type of privileges.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configuration xmlns="urn:activemq" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:activemq ../../../../src/schema/artemis-configuration.xsd">
    <core xmlns="urn:activemq:core">
        <bindings-directory>./data/messaging/bindings</bindings-directory>
        <journal-directory>./data/messaging/journal</journal-directory>
        <large-messages-directory>./data/messaging/largemessages</large-messages-directory>
        <paging-directory>./data/messaging/paging</paging-directory>
        <management-notification-address>notificationsTopic</management-notification-address>
        <acceptors>
            <acceptor name="netty">tcp://localhost:61616</acceptor>
        </acceptors>

        <max-disk-usage>100</max-disk-usage>

        <security-settings>
            <security-setting match="#">
                <permission roles="admin" type="createDurableQueue"/>
                <permission roles="admin" type="deleteDurableQueue"/>
                <permission roles="admin" type="createNonDurableQueue"/>
                <permission roles="admin" type="deleteNonDurableQueue"/>
                <permission roles="admin" type="consume"/>
                <permission roles="admin" type="browse"/>
                <permission roles="admin" type="send"/>
                <permission roles="admin" type="createAddress"/>
                <permission roles="admin" type="deleteAddress"/>
            </security-setting>
        </security-settings>
    </core>
</configuration>

Last step is creating a simple java files, dont forget to create a user and give them a role based on the defined roles in broker.xml.

package com.edw.jms;

import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration;
import org.apache.activemq.artemis.core.server.embedded.EmbeddedActiveMQ;
import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager;
import org.apache.activemq.artemis.spi.core.security.jaas.InVMLoginModule;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class JMSServiceTest {

    @Autowired
    private JMSService jmsService;

    private static EmbeddedActiveMQ embedded = new EmbeddedActiveMQ();

    @BeforeAll
    public static void before() throws Exception {
        SecurityConfiguration configuration = new SecurityConfiguration();
        configuration.addUser("user", "password");
        configuration.addRole("user", "admin");

        final ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager (InVMLoginModule.class.getName(), configuration);

        embedded.setSecurityManager(securityManager);
        embedded.start();
    }

    @AfterAll
    public static void after() throws Exception {
        embedded.stop();
    }

    /**
     * send message to local embeddable jms, and read it.
     *
     * @throws Exception
     */
    @Test
    @DisplayName("This test is to check if a valid message is being sent to MQ thru JMS is success, and we can read it after")
    public void sendJMSData_expectSuccess() throws Exception {
        Assertions.assertDoesNotThrow(()-> jmsService.send("trysmething"));
        Thread.sleep(2000);
        Assertions.assertEquals("trysmething", jmsService.read());
    }

}

Run the test cases by using below command,

$ mvn clean package -s settings.xml

And for the full code for this article can be found here,

https://github.com/edwin/embeddable-artemis-server-and-junit-5

Creating a Red Hat AMQ Broker or ActiveMQ Artemis Stress Testing using JMeter

Red Hat AMQ Broker (or its Opensource version, Apache ActiveMQ), is a flexible, high-performance messaging platform that delivers information reliably, enabling real-time integration. It can handle lot of messages at the same time, up to 21-22thousands messages/seconds, as per below link.

https://activemq.apache.org/performance.html

In this article, im trying to try and see how many transaction can one AMQ handle by using a JMeter script. Lets start by installing ActiveMQ Artemis from docker,

docker run -it --rm \
  -p 8161:8161 \
  -p 61616:61616 \
  -e ARTEMIS_USERNAME=admin \
  -e ARTEMIS_PASSWORD=password \
  vromero/activemq-artemis

Next step is downloading JMeter, and creating a test plan. But first, we need to download artemis-jms-client first and put it on our JMeter_FOLDER/lib/ext folder.

Next is opening our JMeter and create a thread group, for this example im creating 2 loops , with 2 threads each.

Create JMS Publisher, with below parameter

Initial Context Factory : org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory
Provider URL : tcp://localhost:61616
User Authorization : admin
Password Authorization : password
Connection Factory : ConnectionFactory
Destination : dynamicQueues/queue01

JMSCorrelationID : ${__counter(FALSE,)} with class of value java.lang.String


Create JMS Subscriber, with below parameter

Initial Context Factory : org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory
Provider URL : tcp://localhost:61616
User Authorization : admin
Password Authorization : password
Connection Factory : ConnectionFactory
Destination : dynamicQueues/queue01


Run JMeter and we can see the result on both Result Tree and Summary Report,

Have fun stresstesting AMQ 🙂