Java Posts

Scrapping Data from BPS Website using Java

BPS (Badan Pusat Statistik) is a non-departmental government institute of Indonesia that is responsible for conducting statistical surveys. On its website, we can see that there are a lot of data available, especially regarding to spatial and regional such as number of provinces, zipcodes, cities, and others.

One BPS website which is contains interesting data is https://sig.bps.go.id/, an on this sample im trying to crawl and read the zipcode data from it.

Lets start with pom file,

<?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>ScrappingBPS</artifactId>
    <version>1.0</version>

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

    <dependencies>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>4.9.3</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.5</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.25</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.13.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.15.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.15.0</version>
        </dependency>

        <dependency>
            <groupId>com.lmax</groupId>
            <artifactId>disruptor</artifactId>
            <version>3.4.4</version>
        </dependency>

    </dependencies>
</project>

Since im using MyBatis, so lets create a SessionFactory class.

package com.edw.config;

import com.edw.mapper.KodeposMapper;
import com.edw.mapper.RegionsMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.Reader;

public class MyBatisSqlSessionFactory {
    private static final SqlSessionFactory FACTORY;

    static {
        try {
            Reader reader = Resources.getResourceAsReader("configuration.xml");
            FACTORY = new SqlSessionFactoryBuilder().build(reader);
            FACTORY.getConfiguration().addMapper(KodeposMapper.class);
            
        } catch (IOException e) {
            throw new RuntimeException("Fatal Error. Cause: " + e, e);
        }
    }

    public static SqlSessionFactory getSqlSessionFactory() {
        return FACTORY;
    }
}

And a bean for table representation, and a mapper interface.

package com.edw.bean;

import java.io.Serializable;

public class Kodepos implements Serializable {
    private Long id;
    private String kelurahan;
    private String kecamatan;
    private String kabupaten;
    private String provinsi;
    private String kodepos;

    public Kodepos() {
    }

    // other setter and getter
}
package com.edw.mapper;

import com.edw.bean.Kodepos;
import org.apache.ibatis.annotations.Insert;

public interface KodeposMapper {

    @Insert("INSERT INTO `db_kodepos`.`tbl_kodepos` " +
            "(`kelurahan`, `kecamatan`, `kabupaten`, `provinsi`, `kodepos`) " +
            "VALUES (#{kelurahan}, #{kecamatan}, #{kabupaten}, #{provinsi}, #{kodepos})")
    Integer insert(Kodepos kodepos);
}

And last is our main class, im using okhttp to do http request and jackson for parsing json to java object

package com.edw;

import com.edw.bean.Kodepos;
import com.edw.config.MyBatisSqlSessionFactory;
import com.edw.mapper.KodeposMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.ibatis.session.SqlSession;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

public class KodeposScrapping {

    private SqlSession sqlSession = null;
    private KodeposMapper kodeposMapper  = null;

    private static final String BASE_URL = "https://sig.bps.go.id";

    private Logger logger = LogManager.getLogger(KodeposScrapping.class);

    public KodeposScrapping() {
    }

    public static void main(String[] args) throws IOException {
        KodeposScrapping kodeposScrapping = new KodeposScrapping();
        kodeposScrapping.doScrapping();
    }

    private void doScrapping() throws IOException {
        getProvinsis();
    }

    /**
     *  <pre> curl "https://sig.bps.go.id/rest-drop-down/getwilayah" </pre>
     */
    private void getProvinsis() throws IOException {
        Request request = new Request.Builder()
                .url(BASE_URL + "/rest-drop-down/getwilayah")
                .get()
                .build();

        List<HashMap> hashMaps = doHttpCall(request);

        for (HashMap hashMap : hashMaps) {
            Kodepos kodepos = new Kodepos();
            kodepos.setProvinsi(hashMap.get("nama").toString());

            logger.info("start processing {}", hashMap.get("nama").toString());

            // get Kabupatens from Provinsi
            getKabupatens(hashMap.get("kode").toString(), kodepos);

            logger.info("done processing {}", hashMap.get("nama").toString());
        }
    }

    /**
     *  <pre> curl "https://sig.bps.go.id/rest-drop-down/getwilayah?level=kabupaten&parent=11" </pre>
     */
    private void getKabupatens(String parent, Kodepos kodepos) throws IOException {
        Request request = new Request.Builder()
                .url(BASE_URL + "/rest-drop-down/getwilayah?level=kabupaten&parent="+parent)
                .get()
                .build();

        List<HashMap> hashMaps = doHttpCall(request);

        for (HashMap hashMap : hashMaps) {
            kodepos.setKabupaten(hashMap.get("nama").toString());

            // get Kecamatans from Kabupaten
            getKecamatans(hashMap.get("kode").toString(), kodepos);
        }
    }

    /**
     *  <pre> curl "https://sig.bps.go.id/rest-drop-down/getwilayah?level=kecamatan&parent=1101" </pre>
     */
    private void getKecamatans(String parent, Kodepos kodepos) throws IOException {
        Request request = new Request.Builder()
                .url(BASE_URL + "/rest-drop-down/getwilayah?level=kecamatan&parent="+parent)
                .get()
                .build();

        List<HashMap> hashMaps = Collections.synchronizedList(doHttpCall(request));

        for (HashMap hashMap : hashMaps) {
            kodepos.setKecamatan(hashMap.get("nama").toString());
            getKelurahans(hashMap.get("kode").toString(), kodepos);
        }
    }

    /**
     *  <pre> curl "https://sig.bps.go.id/rest-bridging-pos/getwilayah?level=desa&parent=1101050" </pre>
     */
    private void getKelurahans(String parent, Kodepos kodepos) throws IOException {
        Request request = new Request.Builder()
                .url(BASE_URL + "/rest-bridging-pos/getwilayah?level=desa&parent="+parent)
                .get()
                .build();

        List<HashMap> hashMaps = Collections.synchronizedList(doHttpCall(request));

        for (final HashMap hashMap : hashMaps) {
            kodepos.setKelurahan(hashMap.get("nama_bps").toString());
            kodepos.setKodepos(hashMap.get("kode_pos").toString());

            insert(kodepos);
        }
    }

    private void insert(Kodepos kodepos) {
        try {
            sqlSession = MyBatisSqlSessionFactory.getSqlSessionFactory().openSession(true);
            kodeposMapper = sqlSession.getMapper(KodeposMapper.class);

            kodeposMapper.insert(kodepos);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }

    private List<HashMap> doHttpCall(Request request) throws IOException {
        Call call = new OkHttpClient().newBuilder()
                .retryOnConnectionFailure(true)
                .connectTimeout(300, TimeUnit.SECONDS)
                .readTimeout(300, TimeUnit.SECONDS)
                .writeTimeout(300, TimeUnit.SECONDS).build().newCall(request);
        Response response = call.execute();

        ObjectMapper objectMapper = new ObjectMapper();
        List<HashMap> hashMaps = objectMapper.readValue(response.body().string(), List.class);

        response.close();
        return hashMaps;
    }
}

Code can be found in my github repository,

https://github.com/edwin/bps-data-scrapper

Running JUnit Testing Sequentially

One problem that keep showing when im doing unit test is how can i make my unit test runs sequentially across multiple unit test classes. Usually i need this for having one unit test to start first for initializing all the data, and one unit test that run last to delete all the generated data.

Usually run multiple unit testing classes is like below image, it is something that is unpredictable and sometimes can be different between executions.

In order to make it sequential, the trick is to use JUnit with version 5.8.0 minimum.

<?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>sequential-unit-test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>11</java.version>
        <spring-boot-bom.version>2.3.6.Final-redhat-00001</spring-boot-bom.version>
        <junit-version>5.8.0</junit-version>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>

		<!-- ....  -->

        <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.8.1</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>json-path</artifactId>
            <scope>test</scope>
        </dependency>
		
		<!-- ....  -->

    </dependencies>
</project>

And create a junit-platform.properties file,

junit.jupiter.testclass.order.default=org.junit.jupiter.api.ClassOrderer$DisplayName
junit.jupiter.testmethod.order.default=org.junit.jupiter.api.MethodOrderer$DisplayName

And the result is something like below image,

Full code for this sample can be found in below github link.

https://github.com/edwin/sequential-unit-testing

ps.
Im using unit testing’s Display Name as orderer so all my unit test classes are ordered Ascending by Display Name.

@DisplayName("03. Class Run Third")
public class SomewhatUnitTest {


    @Test
    @DisplayName("01. Test First")
    public void testSomething() throws Exception {
        assertTrue(true);
    }
}

however there are multiple other ways of doing ordering, such as by Class name or by Order annotation. Detail can be seen on below url,

https://junit.org/junit5/docs/snapshot/api/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.html

Deploying Fuse 7 on Top of Spring Boot to Openshift 4

Red Hat Fuse is an Open Source Integration platform which provide a very agile and lightweight artifact, which make it very suitable for a microservice deployment. And in this sample, im going to deploy Fuse on top of Red Hat OpenShift Container Platform.

First as always, we need to create a simple pom file. In here im using the latest version of Fuse. And that is 7.9.

<?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>hello-world-fuse-on-ocp</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <fuse.version>7.9.0.fuse-sb2-790065-redhat-00001</fuse.version>
        <spring-boot.version>2.1.4.RELEASE-redhat-00001</spring-boot.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.jboss.redhat-fuse</groupId>
                <artifactId>fuse-springboot-bom</artifactId>
                <version>${fuse.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-http-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-servlet-starter</artifactId>
        </dependency>
    </dependencies>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.jboss.redhat-fuse</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <version>${fuse.version}</version>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

Create a main class,

package com.edw;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Create its route,

package com.edw.routes;

import org.apache.camel.Exchange;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;

@Component
public class HelloWorldRoute extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        rest()
                .get("hello")
                .route()
                .setHeader(Exchange.HTTP_RESPONSE_CODE, simple("200"))
                .setHeader(Exchange.CONTENT_TYPE, simple("application/json"))
                .setBody(constant("{\"hello\":\"world\"}"))
                .endRest()
        ;
    }
}

Set application.properties for our application’s configuration. One of the most important is settingup Camel’s context path for serving API endpoints.

# The Camel context name
camel.springboot.name=hello-world-fuse-on-ocp

# enable all management endpoints
endpoints.enabled=true
management.security.enabled=false

camel.component.servlet.mapping.contextPath=/api/*
logging.level.root=info

And a settings.xml file for providing Red Hat repository location,

<?xml version="1.0"?>
<settings>

    <profiles>
        <profile>
            <id>extra-repos</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <repositories>
                <repository>
                    <id>central</id>
                    <url>https://repo1.maven.org/maven2</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>false</enabled>
                    </snapshots>
                </repository>
                <repository>
                    <id>redhatga</id>
                    <name>Enterprise Releases</name>
                    <url>https://maven.repository.redhat.com/ga</url>
                </repository>
                <repository>
                    <id>redhatearly</id>
                    <name>Enterprise Releases</name>
                    <url>https://maven.repository.redhat.com/earlyaccess/all</url>
                </repository>
            </repositories>

            <pluginRepositories>
                <pluginRepository>
                    <id>central</id>
                    <url>https://repo1.maven.org/maven2</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>false</enabled>
                    </snapshots>
                </pluginRepository>
                <pluginRepository>
                    <id>redhatga</id>
                    <name>Enterprise Releases</name>
                    <url>https://maven.repository.redhat.com/ga</url>
                </pluginRepository>
                <pluginRepository>
                    <id>redhatearly</id>
                    <name>Enterprise Releases</name>
                    <url>https://maven.repository.redhat.com/earlyaccess/all</url>
                </pluginRepository>
            </pluginRepositories>
        </profile>
    </profiles>

</settings>

Execute below command to run on our local,

$ mvn spring-boot:run -s settings.xml

And run curl to see whether our application’s endpoint is ready to accept request or not,

$ curl -kv http://localhost:8080/api/hello

*   Trying ::1:8080...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET /api/hello HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.65.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200
< accept: */*
< breadcrumbId: 123
< user-agent: curl/7.65.0
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Tue, 17 Aug 2021 14:21:05 GMT
<
* Connection #0 to host localhost left intact
{"hello":"world"} 

Once we are confident that the code is working, we can deploy it to Openshift Container Platform by using below command.

oc new-app registry.access.redhat.com/ubi8/openjdk-8~https://github.com/edwin/hello-world-fuse-on-ocp

Full code for this sample can be downloaded on below link.

https://github.com/edwin/hello-world-fuse-on-ocp

Thanks for reading and dont forget to have fun using Fuse.

Injecting Openshift Secret and Reading it as an Environment Variables in Spring Boot

In this writing, im planning to create a simple Spring Boot application but with a dynamic configuration that is going to be fetched from environment variables. Usually we are using this for securing some sensitive values such as Database credentials or endpoints.

For this scenario, im trying to make password variables as parameterized inside Spring Boot’s application.properties. Binds it with environment variables with the name of OPENSHIFT_APP_PASSWORD.

server.port=8080
server.password=${OPENSHIFT_APP_PASSWORD}

And call it from our controller,

package com.edw.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
public class IndexController {

    @Value("${server.password}")
    private String serverPassword;

    @GetMapping("/")
    public Map helloWorld() {
        return new HashMap() {{
            put("hello", "world");
            put("password", serverPassword);
        }};
    }
}

Dont forget setting up maven’s configuration,

<?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>ocpsecret</artifactId>
    <version>1.0-SNAPSHOT</version>

    <repositories>
        <repository>
            <id>redhat-early-access</id>
            <name>Red Hat Early Access Repository</name>
            <url>https://maven.repository.redhat.com/earlyaccess/all/</url>
        </repository>
        <repository>
            <id>redhat-ga</id>
            <name>Red Hat GA Repository</name>
            <url>https://maven.repository.redhat.com/ga/</url>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>redhat-early-access</id>
            <name>Red Hat Early Access Repository</name>
            <url>https://maven.repository.redhat.com/earlyaccess/all/</url>
        </pluginRepository>
        <pluginRepository>
            <id>redhat-ga</id>
            <name>Red Hat GA Repository</name>
            <url>https://maven.repository.redhat.com/ga/</url>
        </pluginRepository>
    </pluginRepositories>

    <properties>
        <snowdrop-bom.version>2.3.6.Final-redhat-00001</snowdrop-bom.version>
        <spring-boot.version>2.1.4.RELEASE-redhat-00001</spring-boot.version>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <start-class>com.edw.Main</start-class>
    </properties>

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

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

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

After we commit all the code into github, we can pull them from Openshift with a simple oc command.

$ oc new-app registry.access.redhat.com/ubi8/openjdk-11~https://github.com/edwin/spring-boot-and-ocp-secret

We can create a variable as a Secret by using below oc command

$ oc create secret generic mypassword --from-literal=OPENSHIFT_APP_PASSWORD=whatever

And inject it into our application,

$ oc set env --from=secret/mypassword dc/spring-boot-and-ocp-secret

Expose our app’s endpoint,

$ oc expose service spring-boot-and-ocp-secret

And do a curl to see that variable “password” has been filled with “whatever” which comes from our OCP Secret.

$ curl -kv http://ocp-endpoint/

* Mark bundle as not supporting multiuse
< HTTP/1.1 200
< Content-Type: application/json
<
{"password":"whatever","hello":"world"}

Code for this can be found on below link

https://github.com/edwin/spring-boot-and-ocp-secret

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