July 10, 2017

Install MariaDB (MySQL Open Source) on RHEL 7 and JBoss EAP 7 DataSource

First what is MariaDB? "MariaDB is a community-developed fork of the MySQL relational database management system intended to remain free under the GNU GPL. Development is led by some of the original developers of MySQL, who forked it due to concerns over its acquisition by Oracle Corporation." [https://en.wikipedia.org/wiki/MariaDB]

Now lets install MariaDB Server (mariadb-server) and Client (mariadb) on RHEL 7.

yum install mariadb-server mariadb -y

And start it.

# systemctl start mariadb

When installing MariaDB fresh on RHEL7 there is a default user 'root' with empty password. To test this execute as user jboss and run mysql select on system table user:

# sudo -u jboss mysql -u root -e "select Host, User, Password from mysql.user;"

We will now install MySQL JDBC driver to JBoss and configure a test DataSource following https://access.redhat.com/documentation/en-us/red_hat_jboss_enterprise_application_platform/7.0/html-single/configuration_guide/#example_mysql_datasource

# yum install mysql-connector-java -y

After installing the Official JDBC driver for MySQL, we create the EAP module.

# mkdir -p /opt/rh/eap7/root/usr/share/wildfly/modules/com/mysql/main
# ln -s /usr/share/java/mysql-connector-java.jar /opt/rh/eap7/root/usr/share/wildfly/modules/com/mysql/main

# vi /opt/rh/eap7/root/usr/share/wildfly/modules/com/mysql/main/module.xml


  
    
  
  
    
    
  


# chown jboss:jboss -Rv /opt/rh/eap7/root/usr/share/wildfly/modules/com
# restorecon -RFv /opt/rh/eap7/root/usr/share/wildfly/modules/

Now configure EAP 7 datasource.

# vi /opt/rh/eap7/root/usr/share/wildfly/standalone/configuration/standalone.xml
...
        <subsystem xmlns="urn:jboss:domain:datasources:4.0">
            <datasources>
                ...
                <datasource jndi-name="java:jboss/MySqlDS" pool-name="MySqlDS">
                    <connection-url>jdbc:mysql://localhost:3306/test</connection-url>
                    <driver>mysql</driver>
                    <security>
                        <user-name>root</user-name>
                        <password></password>
                    </security>
                    <validation>
                        <valid-connection-checker class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker"/>
                        <validate-on-match>true</validate-on-match>
                        <background-validation>false</background-validation>
                        <exception-sorter class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter"/>
                    </validation>
                </datasource>
                <drivers>
                    <driver name="mysql" module="com.mysql">
                        <driver-class>com.mysql.jdbc.Driver</driver-class>
                        <xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
                    </driver>
                    ...
                </drivers>
            </datasources>
        </subsystem>
...

Now lets create a simple web, just containing one JSP file, so inside the war file there is only one JSP file. The code I copied from http://www.java2s.com/Code/Java/JSP/UsingaDataSource.htm, but for it to work with your datasource you need to change the JNDI lookup code and sql statement to.

Context context = new InitialContext();
ds =  (DataSource)context.lookup("java:jboss/MySqlDS");
if (ds != null) {
    conn = ds.getConnection();
    stmt = conn.createStatement();
    result = stmt.executeQuery("select Host, User, Password from mysql.user");
}

July 9, 2017

Configure mod_proxy on RHEL 7 for JBoss EAP 7

In my previous blog I described how to RPM install JBoss EAP 7 on RHEL 7.

Here I will describe how to configure Apache mod_proxy as a Non-load-balancing Proxy.

First install Apache 2.4 and mod_ssl.

# yum install httpd mod_ssl -y

Then configure mod_proxy as Non-load-balancing Proxy.

# vi /etc/httpd/conf.d/ssl.conf
    ...

    # A non-load-balancing proxy
    # https://access.redhat.com/documentation/en-us/red_hat_jboss_enterprise_application_platform/7.0/html-single/configuration_guide/#configure_mod_proxy_apache_http_server
    ProxyPreserveHost On
    ProxyPass / http://localhost:8080/
    ProxyPassReverse / http://localhost:8080/

</VirtualHost>

The final step is to fix SELinux. When you restart Apache and try your proxy you will get error in SSL log.

# cat /etc/httpd/logs/ssl_error_log 
[Thu Jul 06 08:20:09.211374 2017] [proxy:error] [pid 11338] (13)Permission denied: AH00957: HTTP: attempt to connect to 127.0.0.1:8080 (localhost) failed
[Thu Jul 06 08:20:09.211423 2017] [proxy:error] [pid 11338] AH00959: ap_proxy_connect_backend disabling worker for (localhost) for 60s
[Thu Jul 06 08:20:09.211432 2017] [proxy_http:error] [pid 11338] [client 192.168.122.1:60964] AH01114: HTTP: failed to make connection to backend: localhost

And also in syslog

# less /var/log/audit/audit.log
...
type=AVC msg=audit(1499321632.860:445): avc:  denied  { name_connect } for  pid=11300 comm="httpd" dest=8080 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:http_cache_port_t:s0 tclass=tcp_socket
...

To fix this add.

# setsebool httpd_can_network_connect 1

If this works add SELinux rule permanently.

# setsebool -P httpd_can_network_connect 1

July 5, 2017

How to RPM Install JBoss EAP 7 on RHEL 7

We will start fresh with a unregistered RHEL 7, so we first register the machine and their enter our username and password.

# subscription-manager register
...

After that we are ready to add EAP7 subscription. You can here use the auto attach feature, but I recommend using a more controlled way, by adding specific pools.

# subscription-manager list --available | less
...
Subscription Name:   JBoss Business Partner Self-Supported NFR
Provides:            Red Hat OpenShift Enterprise JBoss EAP add-on Beta
                     Red Hat JBoss A-MQ Clients
                     Red Hat Single Sign-On
                     Red Hat OpenShift Enterprise JBoss EAP add-on
                     JBoss Enterprise Application Platform
                     Red Hat JBoss Core Services
                     Red Hat JBoss Data Grid
                     JBoss Enterprise Web Server
                     Red Hat JBoss A-MQ Interconnect
...
Pool ID:             XXXXXXXXXXXXXXXXXXXXXXXXX
...
# subscription-manager attach --poolid=XXXXXXXXXXXXXXXXXXXXXXXXX

Now you think we are done, and check active RPM repos, but do not see any EAP repo. Why? Because the are two EAP 7 repos for RHEL7. A Current (always upgrade, i.e. 7.1, 7.2, etc) and a Minor (freeze to specific minor version, e.g. 7.0). I recommend using the Current. For further reading see https://access.redhat.com/documentation/en-us/red_hat_jboss_enterprise_application_platform/7.0/html-single/installation_guide/#choosing_a_repository.

# yum repolist
Loaded plugins: product-id, search-disabled-repos, subscription-manager
repo id                                             repo name                                                               status
rhel-7-server-nfv-rpms/7Server/x86_64               Red Hat Enterprise Linux for Real Time for NFV (RHEL 7 Server) (RPMs)   177
rhel-7-server-rpms/7Server/x86_64                   Red Hat Enterprise Linux 7 Server (RPMs)                                14 589
repolist: 14 766
# subscription-manager repos --list | grep eap-7
Repo ID:   jb-eap-7-for-rhel-7-server-source-rpms
Repo ID:   jb-eap-7-for-rhel-7-server-rpms
Repo ID:   jb-eap-7-for-rhel-7-server-debug-rpms
Repo ID:   jb-eap-7.0-for-rhel-7-server-debug-rpms
Repo ID:   jb-eap-7.0-for-rhel-7-server-source-rpms
Repo ID:   jb-eap-7.0-for-rhel-7-server-rpms
# subscription-manager repos --enable=jb-eap-7-for-rhel-7-server-rpms
# yum repolist
Loaded plugins: product-id, search-disabled-repos, subscription-manager
repo id                                             repo name                                                               status
jb-eap-7-for-rhel-7-server-rpms/7Server/x86_64      JBoss Enterprise Application Platform 7 (RHEL 7 Server) (RPMs)          777
rhel-7-server-nfv-rpms/7Server/x86_64               Red Hat Enterprise Linux for Real Time for NFV (RHEL 7 Server) (RPMs)   177
rhel-7-server-rpms/7Server/x86_64                   Red Hat Enterprise Linux 7 Server (RPMs)                                14 589
repolist: 15 543

When EAP repo in place, please go ahead and install, with groupinstall.

# yum grouplist hidden | grep -i eap
There is no installed groups file.
Maybe run: yum groups mark convert (see man yum)
   JBoss EAP 7
# yum groupinstall "JBoss EAP 7"

And then test it.

# systemctl restart eap7-standalone

# systemctl status eap7-standalone 
● eap7-standalone.service - The WildFly Application Server (standalone mode)
   Loaded: loaded (/usr/lib/systemd/system/eap7-standalone.service; disabled; vendor preset: disabled)
   Active: active (running) since ons 2017-07-05 15:23:10 CEST; 11s ago
 Main PID: 2039 (scl)
   CGroup: /system.slice/eap7-standalone.service
           ├─2039 /usr/bin/scl enable eap7 -- /opt/rh/eap7/root/usr/share/wildfly/bin/launch.sh /usr/lib/jvm/jre-1.8.0 /usr/lib/jvm/jre-1.8.0/bin /opt/rh/eap7/root/usr/share/wildfly/modules /opt/rh/eap7/...
           ├─2040 /bin/bash /var/tmp/sclFd4fWx
           ├─2043 /bin/sh /opt/rh/eap7/root/usr/share/wildfly/bin/launch.sh /usr/lib/jvm/jre-1.8.0 /usr/lib/jvm/jre-1.8.0/bin /opt/rh/eap7/root/usr/share/wildfly/modules /opt/rh/eap7/root/usr/share/wildf...
           ├─2044 /bin/sh /opt/rh/eap7/root/usr/share/wildfly/bin/standalone.sh -c standalone.xml -b 0.0.0.0
           └─2117 /usr/lib/jvm/jre-1.8.0/bin/java -D[Standalone] -server -verbose:gc -Xloggc:/opt/rh/eap7/root/usr/share/wildfly/standalone/log/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseG...

jul 05 15:23:10 localhost.localdomain systemd[1]: Started The WildFly Application Server (standalone mode).
jul 05 15:23:10 localhost.localdomain systemd[1]: Starting The WildFly Application Server (standalone mode)...

June 27, 2017

Postback Lifecycle in JSF 2.0 (EE 6)

"The request-response lifecycle handles two kinds of requests: initial requests and postbacks. An initial request occurs when a user makes a request for a page for the first time. A postback request occurs when a user submits the form contained on a page that was previously loaded into the browser as a result of executing an initial request."

Reference: Oracle Java EE 6 Tutorial The Lifecycle of a JavaServer Faces Application

Getting Started with JSF 2.0 (EE 6)

Introduction

JSF 2.0 is the standard Web Framework that ships with Java EE 6. Here I will build a simple JSF web app to get you started with the build stones.

Maven

With starting with Java EE 6, there is a ONE dependency for all Java EE, namely javax:javaee-api:[6.0|7.0]. A basic pom.xml file for Java EE 6 is.

<?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>se.magnuskkarlsson.examples</groupId>
    <artifactId>example-jsf20</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.build.outputEncoding>UTF-8</project.build.outputEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <failOnMissingWebXml>false</failOnMissingWebXml>
    </properties>

    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
    </build>
</project>

Deployment Descriptors

Starting with EE 6 the web.xml is no longer compulsory, so here we will skip it.

A new feature in JSF 2.0, is that you do not need to declare navigation in the faces-config.xml, but you need to declare. That is the same thing with CDI. To enable CDI you need to have beans.xml. So we have to deployment descriptors.

  • WEB-INF/faces-config.xml
  • WEB-INF/beans.xml

Another feature in JSF 2.0, is that you can use ordinary CDI annotation instead of JSF specific. This makes one thing less to remember.

So instead of @javax.faces.bean.ManagedBean we can use @javax.inject.Named.

And instead of @javax.faces.bean.RequestScoped we can use @javax.enterprise.context.RequestScoped.

So a simple request scope baking bean for JSF is.

package se.magnuskkarlsson.example.jsf;

import javax.enterprise.context.RequestScoped;
import javax.inject.Named;

@Named
@RequestScoped
// Shorthand for @Named and @RequestScoped is @javax.enterprise.inject.Model
public class UserBean {

    private String name;

    public String getMessage() {
        return (name != null) ? "Hello " + name : null;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

And finally our simple hello JSF page. Notice the commandButton and action, it points to JSF page hello, i.e. to the page itself.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html">

<h:head>
    <title>Example JSF 2.0 Hello</title>
</h:head>
<h:body>
    <h:form>
        <h:outputLabel value="What is your name?" for="name" />
        <h:inputText id="name" value="${userBean.name}" />
        <h:commandButton value="Submit" action="hello" />
    </h:form>
    <p>
        <h:outputText value="${userBean.message}"
            rendered="${not empty userBean.message}" />
    </p>
</h:body>
</html>

Now build and deploy it to e.g. JBoss EAP 6, the app is accessible from either

http://localhost:8080/example-jsf20-1.0.0-SNAPSHOT/hello.jsf

http://localhost:8080/example-jsf20-1.0.0-SNAPSHOT/faces/hello.xhtml

June 17, 2017

JSF 2.0 Facelets Tag Libraries

Tag Library

"JSP technology is considered to be a deprecated presentation technology for JavaServer Faces. Facelets is a part of the JavaServer Faces specification and also the preferred presentation technology for building JavaServer Faces technology-based applications."

Tag Library URI Prefix Example Contents
JavaServer Faces HTML Tag Library http://java.sun.com/jsf/html h: h:head
h:body
h:outputText
h:inputText
JavaServer Faces component tags for all UIComponent objects
JavaServer Faces Core Tag Library http://java.sun.com/jsf/core f: f:actionListener
f:attribute
Tags for JavaServer Faces custom actions that are independent of any particular render kit
JavaServer Faces Facelets Tag Library http://java.sun.com/jsf/facelets ui: ui:component
ui:insert
Tags for templating
JSTL Core Tag Library http://java.sun.com/jsp/jstl/core c: c:forEach
c:catch
JSTL 1.2 Core Tags
JSTL Functions Tag Library http://java.sun.com/jsp/jstl/functions fn: fn:toUpperCase
fn:toLowerCase
JSTL 1.2 Functions Tags

Reference: Oracle Java EE 6 Tutorial: What Is Facelets?

Facelet Template

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
 xmlns:h="http://java.sun.com/jsf/html"
 xmlns:f="http://java.sun.com/jsf/core"
 xmlns:ui="http://java.sun.com/jsf/facelets"
 xmlns:c="http://java.sun.com/jsp/jstl/core"
 xmlns:fn="http://java.sun.com/jsp/jstl/functions">
<h:head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
 <h:outputStylesheet library="css" name="default.css" />
 <title>Facelets Template</title>
</h:head>
<h:body>

 <h1>Hello</h1>

</h:body>
</html>

May 28, 2017

Roadmap Apache ActiveMQ, HornetQ and Apache ActiveMQ Artemis

HornetQ was the built in Message Broker in JBoss EAP 6.

Red Hat bought FuseSource in 2012, the company behind ActiveMQ commercial support, and rebranded the product JBoss A-MQ server. This left Red Hat with two competing Message Brokers.

In 2014 Red Hat and the HornetQ community decided to join efforts with the ActiveMQ community and the Apache ActiveMQ Artemis was created.

The current Red Hat JBoss A-MQ releases, at the time of 7.0.0, was still based on the original ActiveMQ project. It is expected a future release will be based on the Artemis project. This future release will feature the external appearance of the current A-MQ product (administration interfaces based on Fabric, protocol supported, and native client libraries) but with many of the internals from HornetQ (asynchronous I/O engine and high performance journaled disk storage).

The JBoss EAP 7 is shipped with the Apache ActiveMQ Artemis.

May 18, 2017

NamingException in RPM Installed Tomcat 7 in RHEL 7 and CentoIOS 7

If you are using datasource in rpm installed tomcat 7 on RHEL 7 and CentOS and do not specify factory, you will get a NamingException.

For details see https://bugzilla.redhat.com/show_bug.cgi?id=819087.

<Context>

    ...
    <Resource name="jdbc/ReviewDb" auth="Container" type="javax.sql.DataSource"
              factory="org.apache.commons.dbcp.BasicDataSourceFactory"
              driverClassName="org.postgresql.Driver"
              url="jdbc:postgresql://dbhost:5432/reviewdb"
              username="gerrit2" password="******" maxActive="20"
              maxIdle="10" maxWait="-1"/>
</Context>
javax.naming.NamingException: Could not create resource factory instance [Root exception is java.lang.ClassNotFoundException: org.apache.tomcat.dbcp.dbcp.BasicDataSourc
eFactory]
        at org.apache.naming.factory.ResourceFactory.getObjectInstance(ResourceFactory.java:119)
        at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:321)
        at org.apache.naming.NamingContext.lookup(NamingContext.java:842)
        at org.apache.naming.NamingContext.lookup(NamingContext.java:153)
        at org.apache.naming.NamingContextBindingsEnumeration.nextElementInternal(NamingContextBindingsEnumeration.java:117)
        at org.apache.naming.NamingContextBindingsEnumeration.next(NamingContextBindingsEnumeration.java:71)
        at org.apache.naming.NamingContextBindingsEnumeration.next(NamingContextBindingsEnumeration.java:34)
        at org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.createMBeans(GlobalResourcesLifecycleListener.java:138)
        at org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.createMBeans(GlobalResourcesLifecycleListener.java:110)
        at org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.lifecycleEvent(GlobalResourcesLifecycleListener.java:82)
        at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:117)
        at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:90)
        at org.apache.catalina.util.LifecycleBase.setStateInternal(LifecycleBase.java:402)
        at org.apache.catalina.util.LifecycleBase.setState(LifecycleBase.java:347)
        at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:724)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
        at org.apache.catalina.startup.Catalina.start(Catalina.java:689)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:321)
        at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:455)
Caused by: java.lang.ClassNotFoundException: org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory
        at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:264)
        at org.apache.naming.factory.ResourceFactory.getObjectInstance(ResourceFactory.java:115)
        ... 22 more

How to Generate Elliptic Curve Keys in Java 8

// Supported Providers in Java 8
for (Provider provider : Security.getProviders()) {
    System.out.println("Provider : " + provider);
}

// Supported Algorithm in Java: "DiffieHellman", "DSA", "RSA" and "EC"
// https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyPairGenerator

KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC", "SunEC");
// SecureRandom.getInstanceStrong() (Java 8) is the Recommended way to use initialize random,
// it chooses automatically the strongest algorithm for your OS
keyGen.initialize(256, SecureRandom.getInstanceStrong());
KeyPair kp = keyGen.genKeyPair();
PrivateKey privateKey = kp.getPrivate();
PublicKey publicKey = kp.getPublic();
System.out.println("PrivateKey : " + privateKey);
System.out.println("PublicKey : " + publicKey);
// the key in its primary encoding format
byte[] publicKeyInfo = publicKey.getEncoded();
c

How to RPM Install JBoss EAP 7 on RHEL 7

First you need to add JBoss EAP 7 subscription. To do that you need the pool id for your subscription. To find that out list all available subscription and then look for JBoss subscription.

# subscription-manager list --available --all > /tmp/subscription.txt
# less /tmp/subscription.txt
Subscription Name:   JBoss ....
Provides:            Red Hat OpenShift Enterprise JBoss EAP add-on Beta
                     Red Hat JBoss A-MQ Clients
                     Red Hat Single Sign-On
                     Red Hat OpenShift Enterprise JBoss EAP add-on
                     JBoss Enterprise Application Platform
                     Red Hat JBoss Core Services
                     Red Hat JBoss Data Grid
                     JBoss Enterprise Web Server
                     Red Hat JBoss A-MQ Interconnect
...
Pool ID:             XXXXXXXXXXXXXX
...

Now add that subscription with above pool id.

# subscription-manager attach --pool=XXXXXXXXXXXXXX

Then you need to enable the JBoss EAP 7 repo. There are multiple repos. Here we wil run JBoss EAP 7 on RHEL 7 and for that there are two repos:

  • Current JBoss EAP 7 Repository (repo id: jb-eap-7-for-rhel-7-server-rpms)
  • Recommended. Always use the latest.

  • Minor JBoss EAP 7 Repository (repo id: jb-eap-7.[MINOR_VERSION]-for-rhel-7-server-rpms, where MINOR_VERSION is the wanted lock down version, i.e. 0)
  • Only use a specific minor version and never upgrade to next minor, e.g. 7.1, 7.2, etc.

So to use the latest version enable repo by

# subscription-manager repos --enable=jb-eap-7-for-rhel-7-server-rpms

Finally verify that JBoss EAP 7 repo had been enabled with yum.

# yum repolist
repo id                                                                                            repo name
jb-eap-7-for-rhel-7-server-rpms/7Server/x86_64                                                     JBoss Enterprise Application Platform 7 (RHEL 7 Server) (RPMs)
rhel-7-server-rpms/7Server/x86_64                                                                  Red Hat Enterprise Linux 7 Server (RPMs)

Now we install JBoss EAP 7 as a RPM group. To list all

# yum grouplist
Available Environment Groups:
   Minimal Install
...
Available Groups:
...
   JBoss EAP 7
...

And to also list hidden groups.

# yum grouplist hidden

Before you install JBoss EAP 7, you want to install java (JRE) in a controlled way, so it is not transative installed.

# yum install java-1.8.0-openjdk

Then install JBoss EAP 7 RPM group

# yum groupinstall "JBoss EAP 7"

Finally auto start jboss on reboot

# systemctl enable eap7-standalone

And then start it

# systemctl enable eap7-standalone

JBoss EAP 7 binds default to any network interface.

# netstat -tulnp | grep 8080
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      2210/java           

And if this is a remote machine, you need to open port 8080 in local firewall.

# firewall-cmd --add-port 8080/tcp --zone public --permanent
# firewall-cmd --reload
# firewall-cmd --list-all-zones

JBOSS_HOME: /opt/rh/eap7/root/usr/share/wildfly/

A note about jboss home long path. This is due to docker and to be able to ship different software version on same machine, then you cannot install software in default directory, i.e. jboss default directory is /usr/share/wildfly/. To read more, please see Red Hat Software Collections (RHSCL).

# ll /opt/rh/eap7/root/usr/share/wildfly/
total 40
drwxrwxr-x. 2 root root    27 17 maj 21.59 appclient
drwxr-xr-x. 3 root root  4096 17 maj 21.59 bin
drwxrwxr-x. 2 root root    52 17 maj 21.59 docs
drwxr-xr-x. 2 root root    76 17 maj 21.59 domain
-rw-rw-r--. 1 root root   419 21 feb 10.15 JBossEULA.txt
lrwxrwxrwx. 1 root root    50 17 maj 21.59 jboss-modules.jar -> /opt/rh/eap7/root/usr/share/java/jboss-modules.jar
-rw-rw-r--. 1 root root 26530 21 feb 10.15 LICENSE.txt
drwxrwxr-x. 3 root root    20 17 maj 21.59 modules
drwxr-xr-x. 2 root root    91 17 maj 21.59 standalone
-rw-rw-r--. 1 root root    65 21 feb 10.15 version.txt
drwxrwxr-x. 4 root root   158 17 maj 21.59 welcome-content
# ll /opt/rh/eap7/root/usr/share/wildfly/standalone/
total 0
lrwxrwxrwx. 1 root root 35 17 maj 21.59 configuration -> /etc/opt/rh/eap7/wildfly/standalone
lrwxrwxrwx. 1 root root 44 17 maj 21.59 data -> /var/opt/rh/eap7/lib/wildfly/standalone/data
lrwxrwxrwx. 1 root root 51 17 maj 21.59 deployments -> /var/opt/rh/eap7/lib/wildfly/standalone/deployments
lrwxrwxrwx. 1 root root 43 17 maj 21.59 lib -> /var/opt/rh/eap7/lib/wildfly/standalone/lib
lrwxrwxrwx. 1 root root 39 17 maj 21.59 log -> /var/opt/rh/eap7/log/wildfly/standalone
lrwxrwxrwx. 1 root root 45 17 maj 21.59 tmp -> /var/opt/rh/eap7/cache/wildfly/standalone/tmp

May 15, 2017

Configure Java KeyStore and TrustStore

KeyStore

javax.net.ssl.keyStore
javax.net.ssl.keyStorePassword
javax.net.ssl.keyStoreType Typical [JKS|PKCS12]

And how to use it.

$ java -Djavax.net.ssl.keyStore=/path/to/file -Djavax.net.ssl.keyStorePassword=changeit -Djavax.net.ssl.keyStoreType=JKS

TrustStore

javax.net.ssl.trustStore
javax.net.ssl.trustStorePassword
javax.net.ssl.trustStoreType Typical [JKS|PKCS12]

Miscellaneous

javax.net.debug [ssl|debug|all]
jdk.tls.client.protocols E.g. TLSv1,TLSv1.1

Reference: Java Secure Socket Extension (JSSE) Reference Guide Customizing JSSE

May 14, 2017

Tomcat Standard Security Realms

Tomcat comes out of the box with the following security realms, i.e. modules that does Authentication and Authorization.

Name CIS Tomcat 8 Benchmark Note *
JDBCRealm NOT for Production
DataSourceRealm  
JNDIRealm (LDAP)  
UserDatabaseRealm NOT for Large-Scale Installations
MemoryRealm NOT for Production
JAASRealm NOT widely used and therefore the code is not as mature as the other realms.

*) CIS_Apache_Tomcat_8_Benchmark_v1.0.1.pdf

This leaves us with only two production ready realms: DataSourceRealm and JNDIRealm (LDAP)

There are two other Realms (CombinedRealm and LockOutRealm), but they do not do authentication and authorization.

How to handle Configuration in Tomcat with Context

Background

In Tomcat there seems no way to handle war deployment with version number in file name.

"When autoDeploy or deployOnStartup operations are performed by a Host, the name and context path of the web application are derived from the name(s) of the file(s) that define(s) the web application. Consequently, the context path may not be defined in a META-INF/context.xml embedded in the application and there is a close relationship between the context name, context path, context version and the base file name (the name minus any .war or .xml extension) of the file." [https://tomcat.apache.org/tomcat-8.0-doc/config/context.html]

So in your maven pom, you need to set <build><finalName>${project.artifactId}</finalName>...</build>.

Context Configuration

The context.xml can be placed:

  • $CATALINA_BASE/conf/[enginename]/[hostname]/[your_war_file_name].xml, e.g. $CATALINA_BASE/Catalina/localhost/example-tomcat.xml. Only visible inside example-tomcat.war.
  • $CATALINA_BASE/conf/context.xml. Global visible.
  • [your_war_file]/META-INF/context.xml. Deployed in your war file. This is not suited for configurable values.

The only sensible alternative of the above is the first. This is also good for RPM, since each application have a separate configuration file and no file collision from different RPM.

A little note about the path $CATALINA_BASE/conf/[enginename]/[hostname]/. If you look at your $CATALINA_BASE/conf/server.xml file, you see where [enginename] and [hostname] come from.

<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">

    ...
    <Service name="Catalina">

        ...
        <Engine name="Catalina" defaultHost="localhost">

            <Host name="localhost" appBase="webapps" unpackWARs="true"
                autoDeploy="true">

                ...
            </Host>
        </Engine>
    </Service>
</Server>

More Configuration in Context

You can also put general configuration in you Context.

 <Context>
      ...
     <Parameter name="companyName" value="My Company, Incorporated"
          override="false"/>
       ...
 </Context>

   This is equivalent to the inclusion of the following element in the web application deployment descriptor (/WEB-INF/web.xml):

 <context-param>
       <param-name>companyName</param-name>
       <param-value>My Company, Incorporated</param-value>
 </context-param>

And retrieve it by

ServletContext sc = getServletContext();  
String companyName = sc.getInitParameter("companyName"); 

Tomcat JavaBean Resources Factory

Introduction

This works for Tomcat 7, 8 and 9, but here I will use Tomcat 8.

Purpose

Tomcat have DI (Dependency Injection), much the same found in Spring and Java EE, which can be used for mocking or a loose coupling architecture.

POJO

First lets write our POJO class that we want to lookup inside our application.

package se.magnuskkarlsson.example.tomcat;

public class FooBean {

    private String text;

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }
}

XML Configuration

Now we are ready to declare our POJO in either $APP_HOME/META-INF/context.xml (only available inside appl) or $TOMCAT_HOME/context.xml (available across all appl).

<?xml version="1.0" encoding="UTF-8"?>
<Context path="/example-tomcat">

    ...
    <Resource name="bean/FooBean" auth="Container"
        type="se.magnuskkarlsson.example.tomcat.FooBean" factory="org.apache.naming.factory.BeanFactory"
        text="Magnus K Karlsson" />
</Context>

Application

And this how to retrieve it inside your application.

package se.magnuskkarlsson.example.tomcat;

import java.io.IOException;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

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

    private static final long serialVersionUID = 1L;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            Context ctx = new InitialContext();
            FooBean bean = (FooBean) ctx.lookup("java:/comp/env/bean/FooBean");
            resp.getWriter().println("Data from bean " + bean.getText());
        } catch (Exception e) {
            super.log("FAILED to read from bean. Cause " + e.getMessage());
            resp.getWriter().println("FAILED to read from bean. Cause " + e.getMessage());
        }
    }
}

How to use DataSource in Tomcat 8

Introduction

This works for Tomcat 7, 8 and 9, but here I will use Tomcat 8.

Installation

Copy wanted jdbc driver to $TOMCAT_HOME/lib.

$ cp ~/.m2/repository/com/h2database/h2/1.4.185/h2-1.4.185.jar ~/bin/apache-tomcat-8.5.15/lib/

$ cp ~/.m2/repository/mysql/mysql-connector-java/5.1.34/mysql-connector-java-5.1.34.jar ~/bin/apache-tomcat-8.5.15/lib/

DataSource Configuration

Add global datasource configuration in $TOMCAT_HOME/conf/server.xml. Here is a minimal server.xml with H2 database.

<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">

    <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
    <Listener className="org.apache.catalina.core.AprLifecycleListener"
        SSLEngine="on" />
    <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
    <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
    <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

    <GlobalNamingResources>

        <Resource name="UserDatabase" auth="Container"
            type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved"
            factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
            pathname="conf/tomcat-users.xml" />

        <Resource name="ExampleDS" auth="Container" type="javax.sql.DataSource"
            driverClassName="org.h2.Driver" url="jdbc:h2:mem:example" username="sa"
            password="" maxTotal="20" maxIdle="10" maxWaitMillis="-1" />
    </GlobalNamingResources>

    <Service name="Catalina">

        <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000"
            redirectPort="8443" />

        <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

        <Engine name="Catalina" defaultHost="localhost">

            <Host name="localhost" appBase="webapps" unpackWARs="true"
                autoDeploy="true">

                <Valve className="org.apache.catalina.valves.AccessLogValve"
                    directory="logs" prefix="localhost_access_log" suffix=".txt"
                    pattern="%h %l %u %t "%r" %s %b" />

            </Host>
        </Engine>
    </Service>
</Server>

And here is for MySQL

<Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource" 
    driverClassName="com.mysql.jdbc.Driver"
    url="jdbc:mysql://localhost:3306/javatest"
    username="javauser" password="javadude" 
    maxTotal="20" maxIdle="10" maxWaitMillis="-1" />

For a complete list of configuration for Apache Commons DBCP, see http://commons.apache.org/proper/commons-dbcp/configuration.html.

Application

Now add a resource link in you application $APP_HOME/META-INF/context.xml.

<?xml version="1.0" encoding="UTF-8"?>
<Context path="/example-tomcat">

    <!-- java:/comp/env/jdbc/ExampleDS -->
    <ResourceLink name="jdbc/ExampleDS" global="ExampleDS"
        type="javax.sql.DataSource" />
</Context>

And to access inside your tomcat app.

package se.magnuskkarlsson.example.tomcat;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
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 javax.sql.DataSource;

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

    private static final long serialVersionUID = 1L;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            resp.getWriter().println("Data from database " + getDBValue());
        } catch (Exception e) {
            super.log("FAILED to read from database. Cause " + e.getMessage());
            resp.getWriter().println("FAILED to read from database. Cause " + e.getMessage());
        }
    }

    protected int getDBValue() throws NamingException, SQLException {
        Connection conn = null;
        PreparedStatement prepstmt = null;
        ResultSet rs = null;
        try {
            Context ctx = new InitialContext();
            DataSource ds = (DataSource) ctx.lookup("java:/comp/env/jdbc/ExampleDS");
            conn = ds.getConnection();
            prepstmt = conn.prepareStatement("SELECT 1");
            rs = prepstmt.executeQuery();
            rs.next();
            return rs.getInt(1);
        } finally {
            closeQuietly(rs);
            closeQuietly(prepstmt);
            closeQuietly(conn);
        }
    }

    protected void closeQuietly(AutoCloseable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (Exception IGNORE) {
            }
        }
    }

}

May 9, 2017

Remote JMS Client with Security in JBoss EAP 6

Background

We want to write a remote JMS client for the following MDB. And we are using JBoss EAP 6 standalone-full.xml, i.e. we are using the built in HornetQ in JBoss.

package se.magnuskkarlsson.example.ejb;

import java.security.Principal;
import java.util.logging.Logger;

import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RunAs;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.ejb.MessageDrivenContext;
import javax.inject.Inject;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
import javax.security.auth.Subject;
import javax.security.jacc.PolicyContext;

@MessageDriven(activationConfig = {
        @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
        @ActivationConfigProperty(propertyName = "destination", propertyValue = "jms/queue/javaEE6SecurityQueue") })
@RunAs("ROLE_ADMIN")
// @org.jboss.ejb3.annotation.RunAsPrincipal("admin") vs [WEB-INF|META-INF]/jboss-ejb3.xml
// @org.jboss.ejb3.annotation.SecurityDomain("java-ee6-security") vs [WEB-INF|META-INF]/jboss-ejb3.xml
public class EchoMDB implements MessageListener {

    private Logger log = Logger.getLogger(EchoMDB.class.getName());

    @Resource
    private MessageDrivenContext messageDrivenContext;

    @PermitAll
    @Override
    public void onMessage(Message message) {
        try {
            if (message instanceof TextMessage) {
                log.info("MESSAGE BEAN: Message received: " + ((TextMessage) message).getText());

                // will always return "anonymous" when placed inside the MDB
                Principal principal = messageDrivenContext.getCallerPrincipal();
                log.info("Principal : " + principal);

                // will always return "anonymous" when placed inside the MDB
                Subject caller = (Subject) PolicyContext.getContext("javax.security.auth.Subject.container");
                log.info("Caller : " + caller);
            } else {
                log.warning("Message of wrong type: " + message.getClass().getName());
            }
        } catch (JMSException e) {
            e.printStackTrace();
            messageDrivenContext.setRollbackOnly();
        } catch (Throwable te) {
            te.printStackTrace();
        }
    }
}

Remote JMS Client

To this to work we need a separate maven module that holds the JMS client and have no maven dependency to javax:javaee-api or any other JMS dependency.

Then we need to add $JBOSS_HOME/bin/client/jboss-client.jar to our maven module. This jar file contains all the jboss remoting, jms api and hornetq jms implementation for connection factory and queue.

package se.magnuskkarlsson.example.mdb;

import java.util.Properties;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;

public class EchoMDBTest {

    public static void main(String[] args) throws Exception {
        InitialContext initialContext = null;
        Connection connection = null;
        try {
            Properties jndiProps = new Properties();
            jndiProps.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
            jndiProps.put("java.naming.factory.url.pkgs", "org.jboss.ejb.client.naming");
            jndiProps.put(Context.PROVIDER_URL, "remote://127.0.0.1:4447");
            // credentials remoting
            jndiProps.put(Context.SECURITY_PRINCIPAL, "remote");
            jndiProps.put(Context.SECURITY_CREDENTIALS, "ch5nge!t");
            initialContext = new InitialContext(jndiProps);

            ConnectionFactory connectionFactory = (ConnectionFactory) initialContext
                    .lookup("jms/RemoteConnectionFactory");
            // credentials messaging
            connection = connectionFactory.createConnection("remote", "ch5nge!t");
            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

            Queue queue = (Queue) initialContext.lookup("jms/queue/javaEE6SecurityQueue");
            MessageProducer messageProducer = session.createProducer(queue);
            connection.start();

            messageProducer.send(session.createTextMessage("Hello from " + EchoMDBTest.class.getName()));
            System.out.println("SUCCESSFULLY SENT");
        } catch (Exception e) {
            System.out.println(e.getMessage());
            throw e;
        } finally {
            if (initialContext != null) {
                try {
                    initialContext.close();
                } catch (Exception IGNORE) {
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (Exception IGNORE) {
                }
            }
        }
    }
}

Setting up HornetQ Queue

<subsystem xmlns="urn:jboss:domain:messaging:1.4">
    <hornetq-server>
        ...
        <jms-connection-factories>
            ...
            <connection-factory name="RemoteConnectionFactory">
                <connectors>
                    <connector-ref connector-name="netty"/>
                </connectors>
                <entries>
                    <entry name="java:jboss/exported/jms/RemoteConnectionFactory"/>
                </entries>
            </connection-factory>
        </jms-connection-factories>
        ...
        <jms-destinations>
            ...
            <jms-queue name="javaEE6SecurityQueue">
                <entry name="java:jboss/exported/jms/queue/javaEE6SecurityQueue"/>
            </jms-queue>
        </jms-destinations>
    </hornetq-server>
</subsystem>

NOTE: All JMS instances that are to be remote accessible must be prefixed with 'java:jboss/exported/'. In your MDB or JMS client code that prefix is NOT used.

Remoting and HornetQ Security

All code are now in place and queue configuration done. Now we need to understand how the clients calls HornetQ.

First is a remote JNDI lookup done, which goes through jboss remoting.

<subsystem xmlns="urn:jboss:domain:remoting:1.2">
    <connector name="remoting-connector" socket-binding="remoting" security-realm="ApplicationRealm"/>
</subsystem>

The jboss remoting is using security-realm="ApplicationRealm".

<management>
    <security-realms>
        ...
        <security-realm name="ApplicationRealm">
            <authentication>
                <local default-user="$local" allowed-users="*" skip-group-loading="true"/>
                <properties path="application-users.properties" relative-to="jboss.server.config.dir"/>
            </authentication>
            <authorization>
                <properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
            </authorization>
        </security-realm>
    </security-realms>
    ...
</management>

Which default uses users and roles from properties file. Those are created with add-user.sh.

$ ~/bin/jboss-eap-6.4.0/bin$ ./add-user.sh 

What type of user do you wish to add? 
 a) Management User (mgmt-users.properties) 
 b) Application User (application-users.properties)
(a): b

Enter the details of the new user to add.
Using realm 'ApplicationRealm' as discovered from the existing property files.
Username : remote
Password requirements are listed below. To modify these restrictions edit the add-user.properties configuration file.
 - The password must not be one of the following restricted values {root, admin, administrator}
 - The password must contain at least 8 characters, 1 alphabetic character(s), 1 digit(s), 1 non-alphanumeric symbol(s)
 - The password must be different from the username
Password : 
Re-enter Password : 
What groups do you want this user to belong to? (Please enter a comma separated list, or leave blank for none)[  ]: guest
About to add user 'remote' for realm 'ApplicationRealm'
Is this correct yes/no? yes
Added user 'remote' to file '/home/magnus/bin/jboss-eap-6.4.0/standalone/configuration/application-users.properties'
Added user 'remote' to file '/home/magnus/bin/jboss-eap-6.4.0/domain/configuration/application-users.properties'
Added user 'remote' with groups guest to file '/home/magnus/bin/jboss-eap-6.4.0/standalone/configuration/application-roles.properties'
Added user 'remote' with groups guest to file '/home/magnus/bin/jboss-eap-6.4.0/domain/configuration/application-roles.properties'
Is this new user going to be used for one AS process to connect to another AS process? 
e.g. for a slave host controller connecting to the master or for a Remoting connection for server to server EJB calls.
yes/no? no

After the remote JNDI lookup is a JMS javax.jms.Connection made. HornetQ default security domain is not default visible in the standalone-full.xml, but can be read with jboss-cli.sh.

$ ~/bin/jboss-eap-6.4.0/bin$ ./jboss-cli.sh -c
[standalone@localhost:9999 /] /subsystem=messaging:read-resource(include-defaults=true, include-runtime=true, recursive=true)
{
    "outcome" => "success",
    "result" => {
        "hornetq-server" => {"default" => {
...
            "security-domain" => "other",
            "security-enabled" => true,
...

So HornetQ is using default security domain 'other', which is using the 'ApplicationRealm', that we already had setup.

<subsystem xmlns="urn:jboss:domain:security:1.2">
    <security-domains>
        <security-domain name="other" cache-type="default">
            <authentication>
                <login-module code="Remoting" flag="optional">
                    <module-option name="password-stacking" value="useFirstPass"/>
                </login-module>
                <login-module code="RealmDirect" flag="required">
                    <module-option name="password-stacking" value="useFirstPass"/>
                </login-module>
            </authentication>
        </security-domain>
        ...
    </security-domains>
</subsystem>

So now we are ready to run the client and the server logs looks like.

$ ./standalone.sh -c standalone-full.xml
...
00:21:25,744 INFO  [se.magnuskkarlsson.example.ejb.EchoMDB] (Thread-0 (HornetQ-client-global-threads-1965998011)) MESSAGE BEAN: Message received: Hello from se.magnuskkarlsson.example.mdb.EchoMDBTest
00:21:25,746 INFO  [se.magnuskkarlsson.example.ejb.EchoMDB] (Thread-0 (HornetQ-client-global-threads-1965998011)) Principal : anonymous
00:21:25,746 INFO  [se.magnuskkarlsson.example.ejb.EchoMDB] (Thread-0 (HornetQ-client-global-threads-1965998011)) Caller : Subject:
 Principal: anonymous

00:21:25,749 INFO  [se.magnuskkarlsson.example.ejb.HelloSLSB] (Thread-0 (HornetQ-client-global-threads-1965998011))  *** helloAdmin from EJB : [roles=[ROLE_ADMIN],principal=admin]
00:21:25,750 INFO  [se.magnuskkarlsson.example.ejb.EchoMDB] (Thread-0 (HornetQ-client-global-threads-1965998011)) Hello from HelloSLSB : helloAdmin from EJB : [roles=[ROLE_ADMIN],principal=admin], guest : false, admin : true

May 8, 2017

Java EE 6 Security

Background

A general introduction can be found at Oracle Java EE 6 Tutorial Security. Here follow a summary with code examples and some pitfalls for JBoss EAP 6.

Web

URL security is done WEB-INF/web.xml, as which authentication technique you want - BASIC, FORM, DIGEST or CLIENT-CERT. Only one factor authentication is supported in EE 6.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Secure Content</web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>ROLE_GUEST</role-name>
        </auth-constraint>
    </security-constraint>

    <login-config>
        <auth-method>BASIC</auth-method>
    </login-config>

    <security-role>
        <role-name>ROLE_GUEST</role-name>
    </security-role>
    <security-role>
        <role-name>ROLE_ADMIN</role-name>
    </security-role>
</web-app>

Choosing security domain is done in the container specific web deployment descriptor. For jboss it is in WEB-INF/jboss-web.xml.

<?xml version="1.0" encoding="UTF-8"?>
<jboss-web xmlns="http://www.jboss.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-web_7_0.xsd" version="7.0">

    <context-root>/java-ee6-security</context-root>
    <security-domain>java-ee6-security</security-domain>
</jboss-web>

In JBoss EAP 6 you do not need the security domain prefix - java:/jaas/. Do not use since it will be consistent when using security domain in jboss-ejb3.xml, where jndi prefix is not supported. It is only kept in jboss-web.xml for backward compatibility.

JAX-RS (REST)

JAX-RS annotated classes is technically a servlet, so use URL security in your web.xml. You can add EJB annotation (@javax.ejb.Stateless or @javax.ejb.Stateful) to your JAX-RS class for monitoring reason and even add EJB security annotations, but I would recommend to separate logic into separate EJB class.

JAX-WS (Web Service)

The same as for JAX-RS goes for JAX-WS. A JAX-WS annotated class is a servlet, use standard web.xml security and keep things easy and easy to understand, by putting all logic into separate EJB.

EJB

All EJB security annotations are in the javax.annotation.security package. Defines all roles at the class level and method specific role restrictions to methods.

package se.magnuskkarlsson.example.ejb;

import java.util.logging.Logger;

import javax.annotation.Resource;
import javax.annotation.security.DeclareRoles;
import javax.annotation.security.RolesAllowed;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;

@Stateless
@DeclareRoles(value = { "ROLE_ADMIN", "ROLE_GUEST" })
// @org.jboss.ejb3.annotation.SecurityDomain("java-ee6-security") vs [WEB-INF|META-INF]/jboss-ejb3.xml
public class HelloSLSB {

    private Logger log = Logger.getLogger(HelloSLSB.class.getName());

    @Resource
    private SessionContext sessionContext;

    @RolesAllowed(value = { "ROLE_GUEST" })
    public String helloGuest() {
        log.info(" *** helloGuest from EJB : " + sessionContext.getCallerPrincipal());
        return "helloGuest from EJB : " + sessionContext.getCallerPrincipal() + ", guest : "
                + sessionContext.isCallerInRole("ROLE_GUEST") + ", admin : "
                + sessionContext.isCallerInRole("ROLE_ADMIN");
    }

    @RolesAllowed(value = { "ROLE_ADMIN" })
    public String helloAdmin() {
        log.info(" *** helloAdmin from EJB : " + sessionContext.getCallerPrincipal());
        return "helloAdmin from EJB : " + sessionContext.getCallerPrincipal() + ", guest : "
                + sessionContext.isCallerInRole("ROLE_GUEST") + ", admin : "
                + sessionContext.isCallerInRole("ROLE_ADMIN");
    }
}

In JBoss EAP 6 there are EJB security annotations, but I discourage you to use them, to keep your code so vendor neutral as possible. Use vendor deployment descriptor instead.

jboss-ejb3.xml

<?xml version="1.0" encoding="UTF-8"?>
<jboss:jboss xmlns="http://java.sun.com/xml/ns/javaee" xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:s="urn:security:1.1" version="3.1" impl-version="2.0">

    <assembly-descriptor>
        <s:security>
            <ejb-name>*</ejb-name>
            <s:security-domain>java-ee6-security</s:security-domain>
        </s:security>
        ...
    </assembly-descriptor>
</jboss:jboss>

MDB

MDB is a special case. Java EE 6 does not support security on MDB, you can add security to your connection factory and queues, but not MDB. What you can do is add @javax.annotation.security.RunAs("role_name"), to enable MDB call a secured EJB.

package se.magnuskkarlsson.example.ejb;

import java.security.Principal;
import java.util.logging.Logger;

import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RunAs;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.ejb.MessageDrivenContext;
import javax.inject.Inject;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
import javax.security.auth.Subject;
import javax.security.jacc.PolicyContext;

@MessageDriven(activationConfig = {
        @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
        @ActivationConfigProperty(propertyName = "destination", propertyValue = "jms/queue/javaEE6SecurityQueue") })
@RunAs("ROLE_ADMIN")
// @org.jboss.ejb3.annotation.RunAsPrincipal("admin") vs [WEB-INF|META-INF]/jboss-ejb3.xml
// @org.jboss.ejb3.annotation.SecurityDomain("java-ee6-security") vs [WEB-INF|META-INF]/jboss-ejb3.xml
public class EchoMDB implements MessageListener {

    private Logger log = Logger.getLogger(EchoMDB.class.getName());

    @Resource
    private MessageDrivenContext messageDrivenContext;

    @Inject
    private HelloSLSB hello;

    @PermitAll
    @Override
    public void onMessage(Message message) {
        try {
            if (message instanceof TextMessage) {
                log.info("MESSAGE BEAN: Message received: " + ((TextMessage) message).getText());

                // will always return "anonymous" when placed inside the MDB
                Principal principal = messageDrivenContext.getCallerPrincipal();
                log.info("Principal : " + principal);

                // will always return "anonymous" when placed inside the MDB
                Subject caller = (Subject) PolicyContext.getContext("javax.security.auth.Subject.container");
                log.info("Caller : " + caller);

                log.info("Hello from HelloSLSB : " + hello.helloAdmin());
            } else {
                log.warning("Message of wrong type: " + message.getClass().getName());
            }
        } catch (JMSException e) {
            e.printStackTrace();
            messageDrivenContext.setRollbackOnly();
        } catch (Throwable te) {
            te.printStackTrace();
        }
    }
}

And ones again we don't use vendor specific annotations and instead use external xml file configuration.

jboss-ejb3.xml

<?xml version="1.0" encoding="UTF-8"?>
<jboss:jboss xmlns="http://java.sun.com/xml/ns/javaee" xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:s="urn:security:1.1" version="3.1" impl-version="2.0">

    <assembly-descriptor>
        <s:security>
            <ejb-name>*</ejb-name>
            <s:security-domain>java-ee6-security</s:security-domain>
        </s:security>
        <s:security>
            <ejb-name>EchoMDB</ejb-name>
            <s:security-domain>java-ee6-security</s:security-domain>
            <s:run-as-principal>admin</s:run-as-principal>
        </s:security>
    </assembly-descriptor>
</jboss:jboss>

March 17, 2017

Subscription Management in RHEL 7

Register first, then manually add a subscription.

# subscription-manager register

List all available subscription.

# subscription-manager list --available --all

Attach a specific subscription.

# subscription-manager attach --pool=XXXXXXXXXXXXXX

Verify that you now have access to subscribed rpm repos.

# yum repolist

Check subscriptions

# subscription-manager list

Unregistering a system

# subscription-manager remove --all
# subscription-manager unregister
# subscription-manager clean

Debugging HTTP Request in JBoss EAP 7

<subsystem xmlns="urn:jboss:domain:undertow:3.1">
    ...
    <server name="default-server">
        ...
        <host name="default-host" alias="localhost">
            ...
            <filter-ref name="request-dumper"/>
        </host>
    </server>
    ...
    <filters>
        ...
        <filter name="request-dumper" class-name="io.undertow.server.handlers.RequestDumpingHandler" module="io.undertow.core"/>
    </filters>
</subsystem>

How to Set Hostname on RHEL 7

To change permanent:

# hostnamectl set-hostname openam.example.com

Reboot and verify.

# hostnamectl status

January 23, 2017

Elliptic Curve Keys with OpenSSL

List pre-defined curves in OpenSSL:

Generate private key:

Generate public key:

Reference: https://wiki.openssl.org/index.php/Command_Line_Elliptic_Curve_Operations

January 12, 2017

Jackson Standard JSON library for Java

Introduction

Jackson is build up by the following modules:

  • Streaming: "jackson-core"
  • Annotations: "jackson-annotations"
  • Databind: "jackson-databind" implements data-binding (and object serialization)

Maven

Jackson JSON Parser and Generator

To do JSON parsing and generation you only need one class com.fasterxml.jackson.databind.ObjectMapper.

To parse a json file.

And to generate json.

Different ObjectMapper#readValue(...) and ObjectMapper#writeValue(...)

Different parse methods:

  • com.fasterxml.jackson.databind.ObjectMapper.readValue(File, Class)
  • com.fasterxml.jackson.databind.ObjectMapper.readValue(URL, Class)
  • com.fasterxml.jackson.databind.ObjectMapper.readValue(String, Class)
  • com.fasterxml.jackson.databind.ObjectMapper.readValue(Reader, Class)
  • com.fasterxml.jackson.databind.ObjectMapper.readValue(InputStream, Class)
  • com.fasterxml.jackson.databind.ObjectMapper.readValue(byte[], Class)
  • com.fasterxml.jackson.databind.ObjectMapper.readValue(DataInput, Class)

Different generation methods:

  • com.fasterxml.jackson.databind.ObjectMapper.writeValue(File, Object)
  • com.fasterxml.jackson.databind.ObjectMapper.writeValue(OutputStream, Object)
  • com.fasterxml.jackson.databind.ObjectMapper.writeValue(DataOutput, Object)
  • com.fasterxml.jackson.databind.ObjectMapper.writeValue(Writer, Object)
  • com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(Object)
  • com.fasterxml.jackson.databind.ObjectMapper.writeValueAsBytes(Object)

Configure ObjectMapper

January 6, 2017

BDD Testing with Cucumber, Java and JUnit

Introduction

BDD (Behaviour-Driven Development) is about designing code by defining your application in terms of behavior.

You start by gathering Tester, Developer and Product Owner (The Three Amigos) and define User Stories. These User Stories are given a Feature name and each Feature is broken down into a Scenario with Steps defines with the keywords: Given, When and Then

Cucumber

Now in Cucumber you write your User Stories (=Feature) in a language called Gherkin. Example:


Feature: Cash withdrawal 

Scenario: Withdrawal from an account in credit 
    Given I have deposited $100.00 in my account 
    When I withdraw $20 
    Then $20 should be dispensed 

Maven Dependency

Cucumber Code Structure

All code (production code, test code and gherkin code) must be placed in the same catalog structure.

  • Production Code - src/main/java/se/magnuskkarlsson/examples/cucumber/Account.java
  • Test Code - src/test/java/se/magnuskkarlsson/examples/cucumber/AccountTest.java
  • Test Code - src/test/java/se/magnuskkarlsson/examples/cucumber/AccountSteps.java
  • Gherkin Code - src/test/resources/se/magnuskkarlsson/examples/cucumber/cash_withdrawal.feature

Cucumber Test Code

We need two classes: one JUnit wrapper class and one Cucumber class that implements the steps in your Gherkin Code.

Verify

You can now either run you JUnit test class AccountTest inside your IDE or run complete test from command line as usual in maven: mvn clean install.

January 5, 2017

JDK Logger/Java Logging API

Background

Since JDK 1.4 there is a default Logging API in Java. The entire Logging API is contained in the package java.util.logging.

Reference: https://docs.oracle.com/javase/8/docs/technotes/guides/logging/overview.html

Usage

Default Logging Configuration

Java comes with a default logging configuration file, that only contains a ConsoleHandler and writes to standard error.

$JAVA_HOME/lib/logging.properties

Configuration

To change default configuration, create a new configuration file and add a system property java.util.logging.config.file to you java process, which point to yours configuration file. Example:

The available handlers/appenders and their configuration are:

Log Levels

Java Logging API has the following levels: OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL

You define a global log level with .level, then if you need specific configuration you add: <package>[.class].level=<level>

NOTE: ConsoleHandler is special, for that you need to set ConsoleHandler.level for others you don't!

Log Formatter

There exists only two formatter and you typically want to use the SimpleFormatter.

NOTE: Most handlers uses the SimpleFormatter as default, but some do not. For them you need to set the formatter configuration.

Configure java.util.logging.SimpleFormatter

The default format is quite odd, so you want to change that.

The configuration is done with java.util.logging.SimpleFormatter.format. And the value is for the java call in java.util.logging.SimpleFormatter.format(LogRecord)

To understand the java.lang.String.format(String, Object...) syntax, read https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html#syntax.

Configuration in Java EE 6

Background

Dependency Injection has come strong in Java EE 6, which is widely influenced from Spring Framework.

You can use the same pattern to handle configuration.

Code

And to use it

And finally you need to add and empty WEB-INF/beans.xml to your achieve to make CDI work.

January 4, 2017

Java 7 - Implement equals() and hashCode() with java.util.Objects

When using JPA you should implement equals() and hashCode() if you

  • Intend to put instances of persistent classes in a Set.
  • Intend to use reattachment of detached instances.

For JDK 7 and above, you can use the new java.util.Objects class to generate the equals and hash code values.

Test code

January 3, 2017

MySQL – Backup and Restore

Backup

Restore

Internationalization (i18n) with ResourceBundle and Properties

To externalize local messages, Java have the java.util.ResourceBundle class.

Create a properties file for the default locale. Then create a new properties for each locale with the suffix _<language>_<COUNTRY>, in the same way you initialize java.util.Locale.Locale(String, String).

Example

To load the localized text, use java.util.ResourceBundle.getString(String).

Example

messages.properties

messages_sv_SE.properties

And the Java code to load them.

Output

Java 8 - Base64

Java 8 now supports java.util.Base64.

  • Base64.getEncoder()/Base64.getDecoder() - Output is mapped to a set of characters lying in A-Za-z0-9+/.
  • Base64.getUrlEncoder()/Base64.getUrlDecoder() - Output is mapped to set of characters lying in A-Za-z0-9+_. Output is URL and filename safe.
  • Base64.getMimeEncoder()/Base64.getMimeDecoder() - Simple format, but represented in lines of 76 characters and seperated with '\r\n'.

Example

Output