Java Posts

Create JWT Token based on Public – Private Key Certificate On Spring Security

Previously im always creating JWT token using a simple 20digit random character string, despite it works well but somehow it feels like there is something wrong. It feels like it is less secure.
Therefore, after spending some time researching, im changing below code where im using a 20 digits random String character


	String signingKey = "V5iwaQYUyqyi71ij3OtP";
	
    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(signingKey);
        return converter;
    }

To signing and veryfing using a simple public and private key, first we need to create a simple public key file, public.txt

-----BEGIN PUBLIC KEY-----
MIICITANBgkqhkiG9w0BAQEFAAOCAg4AMIICCQKCAgBoDqqgVQd0VX0RFBxquFPI
7o0WabMhCV+BplU3MX+RVq8djCVvkb0FwZSuqZT8d3mvxLtEdHxHXI2vRL9qhOvD
pBgyXw6z+IrzSwYM1LrRj+vM61jCD9lhD5y5kbvzonNbTEGNX2//oJHKC0uwCXWP
ZNx6WdQkWrCD3FDtQyuDkPuGWeuXMqBwuLLrhMg1c4B3CF2DUyVxRLT21WfguRKE
8gkNBI6f9PsG/DAFyTbXJNRHjyqam4Uanc/kuiw/kUUCCdiNC7jUD64pPixJy0pC
8gXKH/iZZ4omSygFBCDHkcTJvCYdVOz36Smzzus8V38i2vx0gP5ri+eYgVdCsPG8
WkaL/8QwUB0zJIM6o7ELb4OyDDX8M99nOTovAsgSMvMu2dCAjsPAwwaJKb6Rnn84
zu3xWuDZNOTox/l4+YqV91p3+nvsizPbme3qiYNM6GJJX6mGUXCX7yaC7t4UEcQS
oLBvo/zXa1SOHrIMZVFhbT9PPpspBXC3YX+zS5/4XR1+m3PWvGiXBOpD47r1+Oz1
MWpZXqq5TT53AsbXOhGIGO54TLKq/m7lPJd1Y8KeKMdR5j+a1gmPy6kv68YBHPOx
npvlrpEvNFujNeBftfNrBoeHtiYCUKZjSTHLquCbuq2/w3FF8kRQNUc04btysTJ8
LdulnXqPagx9egAGjjG4QwIDAQAB
-----END PUBLIC KEY-----

and private key, private.txt. And put both of them under resources folder so accessible by our app

-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

And change our code to below,

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        Resource resource = new ClassPathResource("public.txt");
        Resource privateResource = new ClassPathResource("private.txt");
        String publicKey = null;
        String privateKey = null;
        try {
            publicKey = IOUtils.toString(resource.getInputStream());
            privateKey = IOUtils.toString(privateResource.getInputStream());
        } catch (final IOException e) {
            throw new RuntimeException(e);
        }
        converter.setVerifierKey(publicKey);
        converter.setSigningKey(privateKey);
        return converter;
    }

The downside of this new signing is it almost doubled the size of my JWT token, but hopefully makes it more secure.

[Java] Generate XML From XSD using xjc

For example i have an xsd, and i want to generate an XML based on it using java. This is my xsd example, and i save it as “my.xsd”

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
           xmlns:tns="http://tempuri.org/PurchaseOrderSchema.xsd"
           targetNamespace="http://tempuri.org/PurchaseOrderSchema.xsd"
           elementFormDefault="qualified">
 <xsd:element name="PurchaseOrder" type="tns:PurchaseOrderType"/>
 <xsd:complexType name="PurchaseOrderType">
  <xsd:sequence>
   <xsd:element name="ShipTo" type="tns:USAddress" maxOccurs="2"/>
   <xsd:element name="BillTo" type="tns:USAddress"/>
  </xsd:sequence>
  <xsd:attribute name="OrderDate" type="xsd:date"/>
 </xsd:complexType>

 <xsd:complexType name="USAddress">
  <xsd:sequence>
   <xsd:element name="name"   type="xsd:string"/>
   <xsd:element name="street" type="xsd:string"/>
   <xsd:element name="city"   type="xsd:string"/>
   <xsd:element name="state"  type="xsd:string"/>
   <xsd:element name="zip"    type="xsd:integer"/>
  </xsd:sequence>
  <xsd:attribute name="country" type="xsd:NMTOKEN" fixed="US"/>
 </xsd:complexType>
</xsd:schema>

Next step is generate java bean using xjc using below command

xjc -p com.edw.bean my.xsd

And it will gives below output,

parsing a schema...
compiling a schema...
com\edw\bean\ObjectFactory.java
com\edw\bean\PurchaseOrderType.java
com\edw\bean\USAddress.java
com\edw\bean\package-info.java

Import those generated java files, and print a simple xml on console based on it.

package com.edw;

import com.edw.bean.ObjectFactory;
import com.edw.bean.PurchaseOrderType;
import com.edw.bean.USAddress;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.datatype.DatatypeFactory;
import java.math.BigInteger;
import java.util.Date;

public class Main {
    public static void main(String[] args) throws Exception {
        new Main().doSomething();
    }

    private void doSomething() throws Exception {
        PurchaseOrderType purchaseOrderType = new PurchaseOrderType();
        USAddress address = new USAddress();
        address.setCity("aaa");
        address.setCountry("US");
        address.setName("cccc");
        address.setState("dddd");
        address.setStreet("eee");
        address.setZip(BigInteger.ONE);

        purchaseOrderType.setBillTo(address);
        purchaseOrderType.getShipTo().add(address);
        purchaseOrderType.setOrderDate(DatatypeFactory.newInstance().newXMLGregorianCalendar(new Date().toInstant().toString()));

        JAXBElement<PurchaseOrderType> element = new ObjectFactory().createPurchaseOrder(purchaseOrderType);
        toXml(element);
    }

    private void toXml(JAXBElement<PurchaseOrderType> element) throws Exception {
        JAXBContext ctx = JAXBContext.newInstance(PurchaseOrderType.class);
        Marshaller marshaller = ctx.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        marshaller.marshal(element, System.out);
    }
}

It will print below output,

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<PurchaseOrder xmlns="http://tempuri.org/PurchaseOrderSchema.xsd" OrderDate="2019-11-11Z">
    <ShipTo country="US">
        <name>cccc</name>
        <street>eee</street>
        <city>aaa</city>
        <state>dddd</state>
        <zip>1</zip>
    </ShipTo>
    <BillTo country="US">
        <name>cccc</name>
        <street>eee</street>
        <city>aaa</city>
        <state>dddd</state>
        <zip>1</zip>
    </BillTo>
</PurchaseOrder>

Final step is we can validate our generated xml with provided xsd, and see whether generated xml is valid.

Distributed Tracing on Openshift using Jaeger and Spring Sleuth

There is one big issue when we are using microservices environment, that is sometimes we are unable to see messages goes from each microservice goes to which microservice and also unable to see latency for each microservices.

Luckily we have Jaeger to do that. According to its website, Jaeger is an open source, end-to-end distributed tracing for monitor and troubleshoot transactions in complex distributed systems. And it can also be installed easily on Openshift with a very simple oc command,

oc process -f https://raw.githubusercontent.com/jaegertracing/jaeger-openshift/master/all-in-one/jaeger-all-in-one-template.yml | oc create -f -

After installed, you will see Jaeger pod on Openshift project dashboard with several opened ports and a url for accessing query dashboard. See the red box on the image, it is the url for accessing zipkin api from other pods internally.

Next is creating two simple java app, one as backend, and another one as api gateway.
As usual, we’ll start with a simple maven file for our backend service,

<?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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.edw.test</groupId>
    <artifactId>HelloWorld</artifactId>
    <version>1.0.1</version>
    <name>Hello World</name>
    <description>A Simple Hello World</description>

    <properties>
        <java.version>1.8</java.version>

        <version.fabric8.plugin>3.5.38</version.fabric8.plugin>
        <fabric8.generator.fromMode>istag</fabric8.generator.fromMode>
        <fabric8.generator.from>redhat-openjdk18-openshift:1.0</fabric8.generator.from>

    </properties>

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

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

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sleuth</artifactId>
            <version>2.1.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
            <version>2.1.3.RELEASE</version>
        </dependency>


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

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

            <plugin>
                <groupId>io.fabric8</groupId>
                <artifactId>fabric8-maven-plugin</artifactId>
                <version>${version.fabric8.plugin}</version>

                <executions>
                    <execution>
                        <id>fmp</id>
                        <goals>
                            <goal>resource</goal>
                            <goal>build</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build>
</project>

Create a simple java app,

package com.edw.test.demo;

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

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
package com.edw.test.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;

@RestController
public class IndexController {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @GetMapping("/")
    public HashMap sayHelloApi(@RequestParam String id) {
        logger.debug("say something, anything - {}", id);
        return new HashMap(){{
            put("Message", "Hello My World "+id);
        }};
    }
}

A logback.xml file for logging format,

<configuration>
    <statusListener class="ch.qos.logback.core.status.NopStatusListener"/>
    <springProperty scope="context" name="springAppName" source="spring.application.name"/>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %-5level [${springAppName},%X{X-B3-SpanId:-}] %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    <logger name="com.edw" level="DEBUG" additivity="false">
        <appender-ref ref="STDOUT"/>
    </logger>
    <root level="ERROR" additivity="false">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

And finally, a properties file for storing our configuration.

spring.application.name=Hello World
spring.zipkin.baseUrl: http://zipkin:9411/
spring.sleuth.sampler.probability=1.0

Next is creating our Api Gateway class, we’ll start with a simple 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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/>
    </parent>
    <groupId>com.edw</groupId>
    <artifactId>ApiGateway</artifactId>
    <version>1.0</version>

    <name>ApiGateway</name>
    <description>Demo project for Api Gateway</description>

    <properties>
        <java.version>1.8</java.version>

        <version.fabric8.plugin>3.5.38</version.fabric8.plugin>
        <fabric8.generator.fromMode>istag</fabric8.generator.fromMode>
        <fabric8.generator.from>redhat-openjdk18-openshift:1.0</fabric8.generator.from>

    </properties>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>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-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sleuth</artifactId>
            <version>2.1.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
            <version>2.1.3.RELEASE</version>
        </dependency>


    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

            <plugin>
                <groupId>io.fabric8</groupId>
                <artifactId>fabric8-maven-plugin</artifactId>
                <version>${version.fabric8.plugin}</version>

                <executions>
                    <execution>
                        <id>fmp</id>
                        <goals>
                            <goal>resource</goal>
                            <goal>build</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build>
</project>

And several java classes,

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);
    }
}
package com.edw.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.Date;
import java.util.UUID;

@RestController
public class IndexController {

    @Autowired
    private RestTemplate restTemplate;

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @GetMapping(value="/", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})
    public String indexApi() throws Exception {
        String result = "";
        for (int i = 0; i< 3; i++) {
            logger.debug("firing");
            result = restTemplate.getForObject("http://helloworld:8080/?id="+ UUID.randomUUID().toString()+"&timestamp="+new Date().getTime(), String.class);
            logger.debug("response is {}, MDC is {}", result, MDC.get("X-B3-SpanId"));
        }
        return result;
    }
}
package com.edw.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * <pre>
 *     com.edw.config.RestTemplateConfig
 * </pre>
 *
 * @author Muhammad Edwin < emuhamma at redhat dot com >
 * 23 Sep 2019 10:47
 */
@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate getRestTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate;
    }
}

And a logback.xml, and application.properties.

<configuration>
    <statusListener class="ch.qos.logback.core.status.NopStatusListener"/>
    <springProperty scope="context" name="springAppName" source="spring.application.name"/>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %-5level [${springAppName},%X{X-B3-SpanId:-}] %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    <logger name="com.edw" level="DEBUG" additivity="false">
        <appender-ref ref="STDOUT"/>
    </logger>
    <root level="ERROR" additivity="false">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>
spring.application.name=API Gateway
spring.zipkin.baseUrl: http://zipkin:9411/
spring.sleuth.sampler.probability=1.0

We can deploy both project to Openshift using fabric8 command. And this is the result after deployed successfully and hitting Api Gateway url from browser,

And we can see the detail for each request by click on it,

To see a more detailed information, we can click more and see class and method name, and also information span.

And there is one good feature when using Jaeger, is that we can visualize how a message is delivered among different microservices,

And we can search the request SpanId on Kibana,

Well, hopefully it helps. (^)

Creating a Simple Spring Boot @Cacheable Cache using JBoss DataGrid on top of Openshift

Creating a Simple Spring Boot @Cacheable Cache using JBoss DataGrid on top of Openshift

Several days ago, one of my team member were asking me on how to connect Spring Boot to JBoss Datagrid as its primary cache server. Since we are using Openshift, im using Openshift template “Red Hat JBoss Data Grid 7.2 (Ephemeral, no https)” as base image deployment.

First we need to create instance of Datagrid on Openshift,

Create our cache name,

And finally, we can see our JBoss Datagrid endpoint here, and fyi we are using HotRod protocol to connect to JDG so our endpoint name should be “jdg-datagrid-app-01-hotrod” with port “11333”.

And start our project by creating a pom.xml

<?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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.edw</groupId>
    <artifactId>TestingJDGCacheable</artifactId>
    <version>1.0</version>

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



        <dependency>
            <groupId>org.infinispan</groupId>
            <artifactId>infinispan-client-hotrod</artifactId>
            <version>8.4.0.Final-redhat-2</version>
        </dependency>

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


    </dependencies>

    <profiles>
        <profile>
            <id>openshift</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>io.fabric8</groupId>
                        <artifactId>fabric8-maven-plugin</artifactId>
                        <version>3.5.28</version>
                        <executions>
                            <execution>
                                <id>fmp</id>
                                <phase>package</phase>
                                <goals>
                                    <goal>resource</goal>
                                    <goal>build</goal>
                                </goals>
                            </execution>
                        </executions>
                        <configuration>
                            <generator>
                                <config>
                                    <spring-boot>
                                        <fromMode>isTag</fromMode>
                                        <from>redhat-openjdk18-openshift:1.2</from>
                                    </spring-boot>
                                </config>
                            </generator>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>

    <repositories>
        <repository>
            <id>jboss</id>
            <url>https://repository.jboss.org/nexus/content/groups/public-jboss/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>redhat-techpreview</id>
            <name>Red Hat Tech Preview</name>
            <url>https://maven.repository.redhat.com/techpreview/all/</url>
            <layout>default</layout>
            <releases>
                <enabled>true</enabled>
                <updatePolicy>never</updatePolicy>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>redhat-ga</id>
            <name>Red Hat GA</name>
            <url>https://maven.repository.redhat.com/ga/</url>
            <layout>default</layout>
            <releases>
                <enabled>true</enabled>
                <updatePolicy>never</updatePolicy>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>jboss-plugins</id>
            <url>https://repository.jboss.org/nexus/content/groups/public-jboss/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>redhat-techpreview</id>
            <url>https://maven.repository.redhat.com/techpreview/all/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>redhat-ga</id>
            <url>https://maven.repository.redhat.com/ga/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

After that, we can create our Spring Boot project, and start with a main java class,

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

Register your JBoss Datagrid cache

@Configuration
public class CacheConfig {
    @Bean
    public RemoteCacheManager remoteCacheManager() {
        ConfigurationBuilder builder = new ConfigurationBuilder();
        builder.addServers("jdg-datagrid-app-01-hotrod:11333");
        return new RemoteCacheManager(builder.build());
    }
}

And a simple controller where our cache will be used, as on below code we are using “bakerooo” cache.

@RestController
public class IndexController {
    @GetMapping("/")
    @Cacheable("bakerooo")
    public Long getNow() {
        return System.currentTimeMillis();
    }
}

We can deploy our code by using maven’s fabric8 plugin,

mvn clean package fabric8:deploy

And test it by using a simple curl command.
Feel free to ask question, and you can download the full code at my github repo here.

Building A Quarkus Native Image Using GraalVM, Dockerize, And Deploy It on Top of Openshift Platform

Right now we are going to talk about Quarkus. According to its website, Quarkus tailors your application for GraalVM and HotSpot. Which will provide an amazingly fast boot time, incredibly low RSS memory (not just heap size!) offering near instant scale up and high density memory utilization in container orchestration platforms like Kubernetes.

Let’s start with a simple maven 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.quarkus</groupId>
    <artifactId>com.edw.quarkus</artifactId>
    <version>1.0.0</version>

    <properties>
        <quarkus.version>0.19.1</quarkus.version>
        <surefire-plugin.version>2.22.1</surefire-plugin.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

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


    <dependencies>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-resteasy</artifactId>
            <version>${quarkus.version}</version>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-resteasy</artifactId>
            <version>${quarkus.version}</version>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-junit5</artifactId>
            <version>${quarkus.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${surefire-plugin.version}</version>
                <configuration>
                    <systemProperties>
                        <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
                    </systemProperties>
                </configuration>
            </plugin>
            <plugin>
                <groupId>io.quarkus</groupId>
                <artifactId>quarkus-maven-plugin</artifactId>
                <version>${quarkus.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>build</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <profiles>
        <profile>
            <id>native</id>
            <activation>
                <property>
                    <name>native</name>
                </property>
            </activation>
            <build>
                <plugins>
                    <plugin>
                        <groupId>io.quarkus</groupId>
                        <artifactId>quarkus-maven-plugin</artifactId>
                        <version>${quarkus.version}</version>
                        <executions>
                            <execution>
                                <goals>
                                    <goal>native-image</goal>
                                </goals>
                                <configuration>
                                    <enableHttpUrlHandler>true</enableHttpUrlHandler>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>
                    <plugin>
                        <artifactId>maven-failsafe-plugin</artifactId>
                        <version>${surefire-plugin.version}</version>
                        <executions>
                            <execution>
                                <goals>
                                    <goal>integration-test</goal>
                                    <goal>verify</goal>
                                </goals>
                                <configuration>
                                    <systemProperties>
                                        <native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
                                    </systemProperties>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>
</project>

And a simple java hello-world rest api endpoint,

package com.edw;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/")
public class IndexController {
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public String hello() {
        return "{\"Hello World\":\"Using Quarkus\"}";
    }
}

Next is downloading GraalVM to compile our java code to a native, we can download it here

https://github.com/oracle/graal/releases?

For this blog, im using below version,

graalvm-ce-19.1.1

Next is extracting our graal download and putting it on our JVM runtime. Im using MacOS, so im using below command

sudo mv Downloads/graalvm-ce-19.1.1 /Library/Java/JavaVirtualMachines

In order to create a native docker image using GraalVM, we need to install a specific component using below command,

/Library/Java/JavaVirtualMachines/graalvm-ce-19.1.1/Contents/Home/bin/gu install native-image

After we install both GraalVM and its Native Image component, set JDK path to GraalVM and use below maven command to build our app into native apps,

mvn package -Pnative -Dnative-image.docker-build=true

And we can see the result of our maven build on target folder. Depends on our operating system, we can run our maven build generated file directly using below command

./com.edw.quercus-1.0.0-runner

Next is create a Dockerfile to dockerize our executable file

FROM registry.access.redhat.com/ubi8/ubi-minimal
WORKDIR /work/
COPY target/*-runner /work/application
RUN chmod 775 /work
EXPOSE 8080
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

And run below command,

docker build -t my-quarkus .

To validate our build, we can use below command

docker images | grep my-quarkus

If our docker creation is success, it will shows below output.

REPOSITORY                   TAG                 IMAGE ID            CREATED             SIZE
my-quarkus                   latest              35108d40afdb        24 seconds ago      110MB

We can test running it by using this command,

docker run -p 8080:8080 my-quarkus

And test it by opening browser directly or using curl

curl -v http://localhost:8080/

Once we convince that our dockerized quarkus app runs perfectly, next step is to tag it and push it to, either directly to openshift registry or to dockerhub. The easiest way would be directly, but sometimes we dont have the admin privilege to access registry so on this tutorial we’ll try to push it to dockerhub and then pull the docker image from openshift.

Make sure to create an account on docker hub (https://hub.docker.com/), and input the credentials on your local environment

docker login

After that, you can tag and push corresponding docker images to docker hub using below command,

docker tag my-quarkus edwinkun/my-quarkus
docker push edwinkun/my-quarkus

If push is success, you can see your docker image on docker hub

We can pull on Openshift using “Deploy Image” option,

Select your project, and put your dockerhub image tag

And deploy it afterward, after successfully deployed it will shows pod like this,

Have fun (Y)