eap

Deploying a Java Apps and JBoss EAP into Openshift 4

Sometimes we still have to maintain an application which is still deployed on top of JBoss EAP and in a Virtual Machine, and planning in onboarding them into Openshift.

Deploying this kind of applications is almost the same as deploying a Spring Boot applications on Openshift. Only need one command for doing it.

So lets start with a basic 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</groupId>
    <artifactId>HelloWorldWar</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <optimize>true</optimize>
                    <debug>true</debug>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.6</version>
                <configuration>
                    <warName>${project.name}</warName>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Lets set the context root of this app,

<jboss-web>
    <context-root>/</context-root>
</jboss-web>

And a servlet for serving some content,

package com.edw;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet(name = "HelloServlet", urlPatterns = "/")
public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response)
            throws ServletException, IOException {

        response.setContentType("text/html");

        PrintWriter out = response.getWriter();
        out.println("<h1> Hello World </h1>");

    }
}

And make sure the structure of our project is like below,

+--- .gitignore
+--- pom.xml
+--- README.md
+--- src
|   +--- main
|   |   +--- java
|   |   |   +--- com
|   |   |   |   +--- edw
|   |   |   |   |   +--- HelloServlet.java
|   |   +--- webapp
|   |   |   +--- WEB-INF
|   |   |   |   +--- jboss-web.xml

We can deploy our code to test-project namespace in our Openshift by using below command,

$ oc new-app openshift/jboss-eap73-openjdk11-openshift:latest~. \ 
	--name=hello-world -n test-project

Code for this tutorial can be seen in below Github url

https://github.com/edwin/hello-world-jboss-eap

Create a Protected JBoss EAP UDP Cluster with Authentication

This is the second part of my previous article about how to run multiple containerized Keycloak and make them able to communicate one and another thru UDP protocol. But this approach have a problem, what if an authorized JBoss EAP suddenly joining the cluster and do malicious thing such as intercepting message or even deleting clustered caches. To prevent this, JBoss EAP have a mechanism called AUTH protocol. Which means only instances of JBoss EAP which have a specific credentials can join in the cluster group.

So lets try to simulate this in one JBoss EAP instance on a containerized Keycloak,

docker run -p 8081:8080 -e PROXY_ADDRESS_FORWARDING=true \
 -e DB_VENDOR="mysql" -e DB_ADDR="192.168.56.1" -e DB_USER="keycloak" \
 -e DB_PASSWORD="password" -e DB_PORT="3306" -e DB_DATABASE="keycloak_db" \
 --add-host=HOST:192.168.56.1 jboss/keycloak

Check running image’s containerId by using docker ps command, and copy standalone-ha.xml file to our host folder. For this example, our containerId would be 3cdab1375336.

docker cp 3cdab1375336:/opt/jboss/keycloak/standalone/configuration/standalone-ha.xml .

Edit our standalone-ha.xml and adding this part. Im using password123 as cluster’s password, which means a JBoss EAP instance can only join a cluster when they have the same password.

<stack name="udp">
	<transport type="UDP" socket-binding="jgroups-udp"/>
	<protocol type="PING"/>
	<protocol type="MERGE3"/>
	<socket-protocol type="FD_SOCK" socket-binding="jgroups-udp-fd"/>
	<protocol type="FD_ALL"/>
	<protocol type="VERIFY_SUSPECT"/>
	<protocol type="pbcast.NAKACK2"/>
	<protocol type="UNICAST3"/>
	<protocol type="pbcast.STABLE"/>
	<protocol type="AUTH">
		<property name="auth_class">org.jgroups.auth.MD5Token</property>
		<property name="auth_value">password123</property>
		<property name="token_hash">SHA</property>
	</protocol>
	<protocol type="pbcast.GMS"/>
	<protocol type="UFC"/>
	<protocol type="MFC"/>
	<protocol type="FRAG3"/>
</stack>

Create a Dockerfile,

FROM jboss/keycloak
COPY standalone-ha.xml /opt/jboss/keycloak/standalone/configuration/

Re-build the image,

docker build -t mykeycloak .

And run the new modified image,

docker run -p 8081:8080 -e PROXY_ADDRESS_FORWARDING=true  \
 -e DB_VENDOR="mysql" -e DB_ADDR="192.168.56.1" -e DB_USER="keycloak"  \
 -e DB_PASSWORD="password" -e DB_PORT="3306" -e DB_DATABASE="keycloak_db"  \
 --add-host=HOST:192.168.56.1 mykeycloak

If we try run the original image which are not having any AUTH password at all, an error would occur showing that the corresponding JBoss EAP is unable to join the cluster.

07:23:11,866 WARN  [org.jgroups.protocols.UNICAST3] (thread-7,ejb,0f9a0d05f1eb) 
JGRP000039: 0f9a0d05f1eb: failed to deliver OOB message [ebf69c82cffd to 0f9a0d05f1eb, 0 bytes, flags=OOB|INTERNAL]: 
java.lang.IllegalStateException: found GmsHeader[JOIN_REQ]: mbr=ebf69c82cffd from ebf69c82cffd but no AUTH header

The sample code for this article can be seen at below github page,

https://github.com/edwin/jboss-eap-clustered-with-auth

Have fun using JBoss EAP 🙂

Error Starting JBOSS EAP, Service is already registered

Got a weird error today when deploying a new war to an existing jboss eap environment, it is said that my url is already registered. Here is the complete stacktrace,

2017-10-24 18:47:49,721 ERROR [org.jboss.as.controller.management-operation] (External Management Request Threads -- 1) WFLYCTL0013: Operation ("add") failed - address: ([("deployment" => "my-local-1.0.0-20171024.1141.war")]) - failure description: {
    "WFLYCTL0080: Failed services" => {"jboss.deployment.unit.\"my-local-1.0.0-20171024.1141.war\".INSTALL" => "org.jboss.msc.service.StartException in service jboss.deployment.unit.\"my-local-1.0.0-20171024.1141.war\".INSTALL: WFLYSRV0153: Failed to process phase INSTALL of deployment \"my-local-1.0.0-20171024.1141.war\"
    Caused by: org.jboss.msc.service.DuplicateServiceException: Service jboss.undertow.deployment.default-server.default-host./.session is already registered"},
    "WFLYCTL0412: Required services that are not installed:" => ["jboss.deployment.unit.\"my-local-1.0.0-20171024.1141.war\".INSTALL"],
    "WFLYCTL0180: Services with missing/unavailable dependencies" => undefined
}

2017-10-24 18:47:49,723 ERROR [org.jboss.as.server] (External Management Request Threads -- 1) WFLYSRV0021: Deploy of deployment "my-local-1.0.0-20171024.1141.war" was rolled back with the following failure message:
{
    "WFLYCTL0080: Failed services" => {"jboss.deployment.unit.\"my-local-1.0.0-20171024.1141.war\".INSTALL" => "org.jboss.msc.service.StartException in service jboss.deployment.unit.\"my-local-1.0.0-20171024.1141.war\".INSTALL: WFLYSRV0153: Failed to process phase INSTALL of deployment \"my-local-1.0.0-20171024.1141.war\"
    Caused by: org.jboss.msc.service.DuplicateServiceException: Service jboss.undertow.deployment.default-server.default-host./.session is already registered"},
    "WFLYCTL0412: Required services that are not installed:" => ["jboss.deployment.unit.\"my-local-1.0.0-20171024.1141.war\".INSTALL"],
    "WFLYCTL0180: Services with missing/unavailable dependencies" => undefined
}

The error is actually located on my jboss-web.xml, im deploying 2 war files which have the same context-root url. Changing the deployed war’s context url solve this problem.

<jboss-web>
    <context-root>/</context-root>
</jboss-web>

How to Deploy War File Manually On JBOSS EAP 6.2.0

I usually deploy my war file into JBOSS EAP, using JBOSS’ web console. But i found a very weird condition where i cannot access my web console, so i need to deploy my war file manually from the server’s local disk.

This is how my EAP web console looks like, where i used to deploy my War file.
eap

So basically what i do is i use FTP to transfer my war file from my local disk to the server’s disk, and after that i copy my war file to EAP’s deployment folder. Well in my case, this is my folder location

/usr/share/eap/standalone/deployments

After a while, there will be a file, with the same name as my war file, but with an added extension (.deployed). It means that my war file, has successfully deployed.

eap2

Those new file with extension are called marker files, and they have a different function depends on their extension. Different marker file suffixes have different meanings. And the relevant marker file types are, based on README.txt file,

.dodeploy — Placed by the user to indicate that the given content should be deployed into the runtime (or redeployed if already deployed in the runtime.)

.skipdeploy — Disables auto-deploy of the content for as long as the file is present. Most useful for allowing updates to exploded content without having the scanner initiate redeploy in the middle of the update. Can be used with zipped content as well, although the scanner will detect in-progress changes to zipped content and wait until changes are complete.

.isdeploying — Placed by the deployment scanner service to indicate that it has noticed a .dodeploy file or new or updated auto-deploy mode content and is in the process of deploying the content. This marker file will be deleted when the deployment process completes.

.deployed — Placed by the deployment scanner service to indicate that the given content has been deployed into the runtime. If an end user deletes this file, the content will be undeployed.

.failed — Placed by the deployment scanner service to indicate that the given content failed to deploy into the runtime. The content of the file will include some information about the cause of the failure. Note that with auto-deploy mode, removing this file will make the deployment eligible for deployment again.

.isundeploying — Placed by the deployment scanner service to indicate that it has noticed a .deployed file has been deleted and the content is being undeployed. This marker file will be deleted when the undeployment process completes.

.undeployed — Placed by the deployment scanner service to indicate that the given content has been undeployed from the runtime. If an end user deletes this file, it has no impact.

.pending — Placed by the deployment scanner service to indicate that it has noticed the need to deploy content but has not yet instructed the server to deploy it. This file is created if the scanner detects that some auto-deploy content is still in the process of being copied or if there is some problem that prevents auto-deployment. The scanner will not instruct the server to deploy or undeploy any content (not just the directly affected content) as long as this condition holds.