July 20, 2015

Multitenancy Part 1 - Custom Login Module & Bcrypt Password Hashing on WildFly 9

Quick note on multi-tenancy

This example is for a shared schema approach, which is a common requirement in many SaaS applications. Hibernate currently supports separate database and separate schema, but not shared schema. There are plans for shared schema in Hibernate 5.  My next post will show you how to use this custom login module/principal to support shared schema multi-tenancy in your Java EE 7 application.

For an excellent overview on multitenacy, check this article.

Quick note on password hashing

The default options in WildFly for storing passwords (MD5, SHA-512 etc) are great for demo's or prototypes, but are not suitable for a live environment. For a good introduction to password hashing see here.

What is covered in this post

How to create a custom principal
How to create custom login module
How to apply non-default hashing/password algorithm (BCrypt in this example)
How to modify your custom principal for a multi-tenancy application (shared schema approach)

Works with WildFly 9 (picketbox 4.9.2.Final), minor modification is required for WildFly 8 (pickbox <= 4.0.21.Final)

Why a custom principal?

In addition to the username, I need to store a reference to the userId and the tenantId. I want to have the option of looking up a JPA User by their userId, not their username. Also, because this is for a multi-tenant application, I want to be able to quickly access the tenantId (for example when applying a filter to queries).

Why a custom login module?

Two reasons. First I need to set the tenantId and userId on the identity object (principal).
Second, I want to apply a strong hashing algorithm and a per-user salt. In this example I use the BCrypt hashing function.

Now to the code...

First add the maven dependencies:
<!-- provides security implementation for WildFly -->
<dependency>
    <groupId>org.picketbox</groupId>
    <artifactId>picketbox</artifactId>
    <version>4.9.2.Final</version>
    <scope>provided</scope>
</dependency>
<!-- Java BCrypt library -->
<dependency>
    <groupId>de.svenkubiak</groupId>
    <artifactId>jBCrypt</artifactId>
    <version>0.4</version>
</dependency>
<dependency>
    <groupId>javax.transaction</groupId>
    <artifactId>jta</artifactId>
    <version>1.1</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.jboss.logging</groupId>
    <artifactId>jboss-logging</artifactId>
    <version>3.3.0.Final</version>
    <scope>provided</scope>
</dependency>

Create the custom principal

In this example we will be extending org.jboss.security.SimplePrincipal with our own implementation, which will store a reference to the tenantId and userId in addition to the username.
public class DatabasePrincipal extends SimplePrincipal {

    private Long userId;
    private Integer tenantId;
    
    //required when creating principal See AbstractServerLoginModule.createIdentity();
    public DatabasePrincipal(String userName) {
        super(userName);
    }

    public Long getUserId() {
        return userId;
    }

    public Integer getTenantId() {
        return tenantId;
    }

    void setUserId(long userId) {
        this.userId = userId;
    }

    void setTenantId(int tenantId) {
        this.tenantId = tenantId;
    }
}

Create the custom database login module

We will extending DatabaseServerLoginModule and overriding validatePassword() so that we can use jBCrypt to check the raw password against the hashed password. To learn how to generate the hash in the first place you can check the jBCrypt docs. We also need to override the getUsersPassword() method as we need to get the tenantId and userId from PreparedStatement and then set them on our DatabasePrincipal.
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.security.auth.login.LoginException;
import javax.sql.DataSource;
import javax.transaction.SystemException;
import javax.transaction.Transaction;

import org.jboss.logging.Logger;
import org.jboss.security.PicketBoxMessages;
import org.jboss.security.auth.spi.DatabaseServerLoginModule;

public class MultitenantDatabaseServerLoginModule extends DatabaseServerLoginModule {

    private Logger log = Logger.getLogger(getClass());
 
    @Override
    protected boolean validatePassword(String enteredPassword, String encrypted) {
        
        if (enteredPassword == null || encrypted == null) {
            return false;
        }

        return BCrypt.checkpw(enteredPassword, encrypted)) {
    }

    @Override
    protected String getUsersPassword() throws LoginException {
        
        boolean trace = log.isTraceEnabled();
        String username = getUsername();
        String password = null;
        ResultSet rs = null;

        Transaction tx = null;
        if (suspendResume) {
            // tx = TransactionDemarcationSupport.suspendAnyTransaction();
            try {
                if (tm == null)
                    throw PicketBoxMessages.MESSAGES.invalidNullTransactionManager();
                tx = tm.suspend();
            } catch (SystemException e) {
                throw new RuntimeException(e);
            }
        }

        try {

            InitialContext ctx = new InitialContext();
            DataSource dataSource = (DataSource) ctx.lookup(dsJndiName);

            // Get the password
            if (trace) {
                log.trace("Excuting query: " + principalsQuery + ", with username: " + username);
            }
            try (Connection connection = dataSource.getConnection();
                    PreparedStatement statement = connection.prepareStatement(principalsQuery)) {

                statement.setString(1, username);
                rs = statement.executeQuery();

                if (rs.next() == false) {
                    if (trace) {
                        log.trace("Query returned no matches from db");
                    }
                    throw PicketBoxMessages.MESSAGES.noMatchingUsernameFoundInPrincipals();
                }

                Integer userId = rs.getInt(1);
                Integer tenantId = rs.getInt(2);
                password = rs.getString(3);

                //Set the tenant and user properties on the custom principal
                //there maybe a better place to do this, but this is the only place I can see it working without having to hit the database with another query. 
                DatabasePrincipal principle = (DatabasePrincipal) getIdentity();
                principle.setTenantId(tenantId);
                principle.setUserId(userId);

                if (trace) {
                    log.trace("Obtained user password");
                }

            } catch (SQLException ex) {
             LoginException le = new LoginException(PicketBoxMessages.MESSAGES.failedToProcessQueryMessage());
                le.initCause(ex.getCause());
                log.error(ex);
                throw le;
            } finally {
                if (rs != null) {
                    try {
                        rs.close();
                    } catch (SQLException e) {
                    }
                }
            }

        } catch (NamingException ex) {
            LoginException le = new LoginException(PicketBoxMessages.MESSAGES.failedToLookupDataSourceMessage(dsJndiName));
            le.initCause(ex);
            log.error(ex);
            throw le;
        } finally {
            if (suspendResume) {
                // TransactionDemarcationSupport.resumeAnyTransaction(tx);
                try {
                    tm.resume(tx);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                if (log.isTraceEnabled())
                    log.trace("resumeAnyTransaction");
            }
        }
        return password;
    }
}

Configure the application

We now need to tell our application to use the new security domain. For Servlets (and FORM based authentication) you will need to add a security domain to the jboss-web.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
    <security-domain>multitenantdomain</security-domain>
</jboss-web>

To use declarative security in your EJB's you will need to add the security domain to your jboss-ejb3.xml file:
<?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>multitenantdomain</s:security-domain>
        </s:security>
    </assembly-descriptor>
</jboss:jboss>

And to use the security domain in your EJB, use the org.jboss.ejb3.annotation.SecurityDomain annotation:
@Stateless
@SecurityDomain("other")
public class MyEJB {

}

Configure WildFly

Lastly we need to tell WildFly which login module to use, and which principal implementation we want it to instantiate. We also need to specify our query (returning user_id and tenant_id).
<security-domain name="multitenantdomain" cache-type="default">
    <authentication>
        <login-module code="com.ritchie.security.MultitenantDatabaseServerLoginModule" flag="required">
            <module-option name="dsJndiName" value="java:jboss/datasources/MultitenantDS"/>
            <module-option name="principalsQuery" value="select user_id, tenant_id, password from User where username=?"/>
            <module-option name="rolesQuery" value="select Role, 'Roles' from User where Username=?"/>
            <module-option name="password-stacking" value="useFirstPass"/>
            <module-option name="principalClass" value="com.ritchie.security.DatabasePrincipal"/>
        </login-module>
    </authentication>
</security-domain>

And... for the sake of completeness

Here is a simple login page and web.xml configuration
web.xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
    <login-config>
        <auth-method>FORM</auth-method>
        <form-login-config>
            <form-login-page>/login.html</form-login-page>
            <form-error-page>/access-denied.html</form-error-page>
        </form-login-config>
    </login-config>
    <security-constraint>
        <display-name>Open Content</display-name>
        <web-resource-collection>
            <web-resource-name>Unrestricted Content</web-resource-name>
            <url-pattern>/login.xhtml</url-pattern>
        </web-resource-collection>
    </security-constraint>
    <security-constraint>
        <display-name>Access Constraint</display-name>
 <web-resource-collection>
            <web-resource-name>Site Access Constraint</web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>*</role-name>
        </auth-constraint>
    </security-constraint>
    <security-role>
        <role-name>administrator</role-name>
    </security-role>
</web-app>
index.html
<!DOCTYPE html>
<html>
   <head>
      <meta charset="UTF-8">
      <title>Login Page</title>
   </head>
   <body>
      <form method="post" action="j_security_check">
         <label for="username">Username:</label>
         <input id="username" required="required" type="text" name="j_username"/>
         <label for="password">Password:</label>
         <input id="password" required="required" type="password" name="j_password"/>
         <button type="submit">Login</button>
      </form>
   </body>
</html>

In the next post we will build on this example and create a CrudService that will apply a filter to queries to ensure data isolation between tenants.


Ref:
https://msdn.microsoft.com/en-us/library/aa479086.aspx
https://docs.jboss.org/author/display/WFLY8/Authentication+Modules
https://docs.jboss.org/author/display/WFLY8/Securing+EJBs
https://dzone.com/articles/creating-custom-login-modules
http://throwingfire.com/storing-passwords-securely/#notpasswordhashes
http://www.mindrot.org/projects/jBCrypt/

June 26, 2015

Configure SSL on WildFly 8 - Self Signed Certificate

Create a self-signed certificate inside the $JBOSS_HOME/standalone/configuration/ directory:
keytool -genkeypair -alias serverkey -keyalg RSA -keysize 2048 -validity 365 -keystore keystore.jks -keypass mypassword -storepass mypassword -dname "CN=Server Administrator,O=My Organization,C=ZA"
Add the security realm to the management section:
/core-service=management/security-realm=SSLRealm/:add()

Configure the new realm:
/core-service=management/security-realm=SSLRealm/server-identity=ssl/:add(keystore-path=keystore.jks, keystore-relative-to=jboss.server.config.dir, keystore-password=mypassword, alias=serverkey, key-password=mypassword)

Add the https-listener to the undertow configuration:
/subsystem=undertow/server=default-server/https-listener=https/:add(socket-binding=https, security-realm=SSLRealm)

This adds the following to your standalone.xml
<security-realm name="SSLRealm">
    <server-identities>
        <ssl>
            <keystore path="keystore.jks" relative-to="jboss.server.config.dir" keystore-password="mypassword" alias="serverkey" key-password="mypassword"/>
        </ssl>
    </server-identities>
</security-realm>
And the following is added to your undertow subsystem:
<https-listener name="https" socket-binding="https" security-realm="SSLRealm"/>

You should now be able to access your application on https://localhost:8443

March 11, 2015

Inject external properties using CDI, Java and WildFly

Often you need to be able to configure different runtime properties based on environment.  Using the configuration below, you can have a different property file on each server (dev, production, test), each defining their own values.

How it works

  1. The location of your property file is found by looking at a system property configured in the WildFly configuration file. This allows you to change the location of the property file, without having to recompile/redeploy the application.
  2. The property file is loaded and the values are stored in a HashMap inside a @Singleton session bean
  3. The properties are then injected into your CDI beans, making them accessible to your application code. This is achieved by creating a CDI Qualifier and producer method.

How its done

1. Create and populate a properties file inside the WildFly configuration folder
$ echo 'docs.dir=/var/documents' >> .standalone/configuration/application.properties

 2. Add a system property to the WildFly configuration file.
$ ./bin/jboss-cli.sh --connect
[standalone@localhost:9990 /] /system-property=application.properties:add(value=${jboss.server.config.dir}/application.properties)

This will add the following to your server configuration file (standalone.xml or domain.xml):
<system-properties>
    <property name="application.properties" value="${jboss.server.config.dir}/application.properties"/>
</system-properties>

3. Create the singleton session bean that loads and stores the application wide properties
package com.ritchie.chris.properties;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.annotation.PostConstruct;
import javax.ejb.Singleton;

@Singleton
public class PropertyFileResolver {
     
    private Map<String, String> properties = new HashMap<>();
     
    @PostConstruct
    private void init() throws IOException {
         
        //matches the property name as defined in the system-properties element in WildFly
        String propertyFile = System.getProperty("application.properties");
        File file = new File(propertyFile);
        Properties properties = new Properties();
         
        try {
            properties.load(new FileInputStream(file));
        } catch (IOException e) {
            System.out.println("Unable to load properties file" + e);
        }
         
        HashMap hashMap = new HashMap<>(properties);
        this.properties.putAll(hashMap);
    }
 
    public String getProperty(String key) {
        return properties.get(key);
    }
}

4. Create the CDI Qualifier. We will use this annotation on the Java variables we wish to inject into.
package com.ritchie.chris.properties;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.inject.Qualifier;

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR })
public @interface ApplicationProperty {

    // no default meaning a value is mandatory
    @Nonbinding
    String name();
}

5. Create the producer method; this generates the object to be injected
package com.ritchie.chris.properties;

import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.inject.Inject;

public class ApplicaitonPropertyProducer {

    @Inject
    private PropertyFileResolver fileResolver;

    @Produces
    @ApplicationProperty(name = "")
    public String getPropertyAsString(InjectionPoint injectionPoint) {
        
        String propertyName = injectionPoint.getAnnotated().getAnnotation(ApplicationProperty.class).name();
        String value = fileResolver.getProperty(propertyName);
        
        if (value == null || propertyName.trim().length() == 0) {
            throw new IllegalArgumentException("No property found with name " + value);
        }
        return value;
    }
    
    @Produces
    @ApplicationProperty(name="")
    public Integer getPropertyAsInteger(InjectionPoint injectionPoint) {
        
        String value = getPropertyAsString(injectionPoint);
        return value == null ? null : Integer.valueOf(value);
    }
}

6. Lastly inject the property into one of your CDI beans
package com.ritchie.chris.properties;

import javax.ejb.Stateless;
import javax.inject.Inject;

@Stateless
public class MySimpleEJB {

    @Inject
    @ApplicationProperty(name = "docs.dir")
    private String myProperty;
    
    public String getProperty() {
        return myProperty;
    }
}

Source code can be found on GitHub



March 10, 2015

Obtaining a reference to a CDI managed bean

Most of the time you can inject your CDI beans using the @Inject annotation. There may be occasions when you need to access your CDI beans inside a class where you can not use @Inject, you have a couple of options.

To programmatically access a CDI managed bean, first you need to get hold of the BeanManager (analogous to the ApplicationContext in Spring), by using CDI.current() or by doing a JNDI lookup.

Using CDI.current()
BeanManager bm = CDI.current().getBeanManager();

Using JNDI:
BeanManager bm = null;
try {
    InitialContext context = new InitialContext();
    bm = (BeanManager) context.lookup("java:comp/BeanManager");
} catch (Exception e) {
    e.printStackTrace();
}

Now you have the BeanManager you can access your CDI beans by doing either a type-based lookup or a name-based lookup.

Type based:
Bean<CrudService> bean = (Bean<CrudService>) bm.getBeans(CrudService.class).iterator().next();
CreationalContext<CrudService> ctx = bm.createCreationalContext(bean);
CrudService crudService = (CrudService) bm.getReference(bean, CrudService.class, ctx);

Name-based
Bean bean = bm.getBeans("crudService").iterator().next();
CreationalContext ctx = bm.createCreationalContext(bean);
CrudService crudService = bm.getReference(bean, bean.getClass(), ctx);

When using name-based lookup, your name has to match the value you pass into your @Named annotation. If do not pass a value then it is the name of your class, in camel case. Example:
@Named("crudService")
public class CrudService {}

And a full code example using CDI.current() and type-based lookup:
BeanManager bm = CDI.current().getBeanManager();
Bean<CrudService> bean = (Bean<CrudService>) bm.getBeans(CrudService.class).iterator().next();
CreationalContext<CrudService> ctx = bm.createCreationalContext(bean);
CrudService crudService = (CrudService) bm.getReference(bean, CrudService.class, ctx);


February 27, 2015

Configure WildFly, Apache and websocket connections on Ubuntu 14.04

This tutorial assumes that you have already installed WildFly and you want to configure Apache as a proxy in front of WildFly, and you want to allow websocket connections. For a detailed guide on installing and configuring WildFly, see this post.

In this guide are going to install and configure Apache, mod_proxy and proxy_wstunnel. proxy_wstunnel is required if you want successfully connect to WildFly via websockets (thru Apache). Any attempt to connect via websockets without proxy_wstunnel will fail as the HTTP Upgrade headers will be removed from your request.

Apache install

First install Apache and the required mods:
sudo apt-get install apache2
sudo apt-get install libapache2-mod-proxy-html
sudo a2enmod proxy_http
sudo a2enmod proxy_wstunnel
sudo /etc/init.d/apache2 restart

Apache configuration

Now configure your VirtualHost. For simplicity we are going to update the 000-default.conf file. In this example the context root of the application is app. Update yours as needed.
vim /etc/apache2/sites-available/000-default.conf

These two lines are required to proxy your main http connections
ProxyPass         /app  http://localhost:8080/pss
ProxyPassReverse  /app  http://localhost:8080/pss

These two lines will proxy your websocket connections
ProxyPass         /app/ws/  ws://localhost:8080/app/ws/
ProxyPassReverse  /app/ws/  ws://localhost:8080/app/ws/

Here is the full VirtualHost file, with the mod_proxy and proxy_wstunnel configuration highlighted:
<VirtualHost *:80>
    ServerName www.example.com
    ServerAlias example.com

    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/example.com

    ProxyPass         /app  http://localhost:8080/pss
    ProxyPassReverse  /app  http://localhost:8080/pss
    ProxyPass         /app/ws/  ws://localhost:8080/app/ws/
    ProxyPassReverse  /app/ws/  ws://localhost:8080/app/ws/

    ErrorLog ${APACHE_LOG_DIR}/example.com-error.log
    CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined
</VirtualHost>
Reload Apache for the changes to take effect
sudo /etc/init.d/apache2 reload

Java configuration

Make sure that the value you pass into your @ServerEndPoint annotation matches that in your Apache VirtualHost configuration.
@ServerEndpoint("/ws")
public class WebSocketEndpoint {
 
    private final Logger logger = Logger.getLogger(this.getClass().getName());
 
    @OnOpen
    public void onConnectionOpen(Session session) {
        logger.info("Connection opened");
    }
 
    @OnMessage
    public String onMessage(String message) {
        return "Message received";
    }
 
    @OnClose
    public void onConnectionClose(Session session) {
        logger.info("Connection closed");
    }
}

WildFly configuration

Make sure that you allow connections to your public interface from localhost (127.0.0.1) only. While this is not mandatory, leaving it open will allow connections to circumvent Apache.
<interfaces>
    <interface name="management">
        <inet-address value="0.0.0.0"/>
    </interface>
    <interface name="public">
        <inet-address value="${jboss.bind.address:127.0.0.1}"/>
    </interface>
    <interface name="unsecure">
        <inet-address value="${jboss.bind.address.unsecure:127.0.0.1}"/>
    </interface>
</interfaces>

In WildFly websockets work out of the box, so no other configuration required. Here is the default undertow configuration, with the relevant parts highlighted.
<subsystem xmlns="urn:jboss:domain:undertow:1.2">
    <buffer-cache name="default"/>
    <server name="default-server">
        <http-listener name="default" socket-binding="http"/>
        <host name="default-host" alias="localhost">
            <location name="/" handler="welcome-content"/>
            <filter-ref name="server-header"/>
            <filter-ref name="x-powered-by-header"/>
        </host>
    </server>
    <servlet-container name="default">
        <jsp-config/>
        <websockets/>
    </servlet-container>
</subsystem>
You should now be able to deploy your websocket applications to WildFly and access them via Apache.

February 17, 2015

Installing WildFly on Ubuntu 14.04 cloud server

This post covers the main aspects of installing WildFly on a fresh cloud Ubuntu server (or any clean install). This tutorial shows how WildFly can be configured to be accessed directly. If you want to put WildFly behind Apache using mod_proxy, see this post.

Topics covered: 


Adding swap to a new cloud instance 
  • Checking for swap
  • Creating swap
  • Changing swappiness 
Updating the server using apt-get 

Installing Java
  • Downloading the JDK to the server
  • Installing the JDK
  • Setting the default JDK
Installing WildFly
  • Downloading WildFly
  • Creating the wildfly user
  • Installing the init.d scripts
  • Starting WildFly as a service
Configuring WildFly
  • Open public interface to all IP's
  • Open the management interface to all IP's (optional)
  • Remove the welcome content (optional)

Adding swap to a new cloud instance  

Swap is an area on your hard drive that can store data when your RAM is full. First we need to check to see if a swap area is configured:
sudo swapon -s
If you get the headers back only, you have no swap space:

Now lets create a 4 gig swap:
sudo fallocate -l 4G /swapfile
Verify that is has been created and is the correct size
ls -lh /swapfile 
You should see something like this:

Now change the folder permissions:
sudo chmod 600 /swapfile
Set up swap
sudo mkswap /swapfile
You will should see the output below.


Now enable swap
sudo swapon /swapfile
Lets check to see if we have swap space
sudo swapon -s
We should get the following output:

To make the changes permanent you need to update the fstab file
sudo echo '/swapfile   none    swap    sw    0   0' >> /etc/fstab

Update swappiness

Swappiness can be configured between 0 and 100. The nearer to 0 the more data will be put into the RAM rather than the swap.

To view the current swappiness:
cat /proc/sys/vm/swappiness
To change the swapiness to 10:
sudo echo 'vm.swappiness=10' >> /etc/sysctl.conf
This only takes effect on reboot, so also run this command to effect this change immediately
sudo sysctl vm.swappiness=10

Updating the server using apt-get 

If you have not already updated your new server, do so now:
sudo apt-get update
sudo apt-get upgrade

Installing Java

Personally I like to install the Oracle JDK rather than OpenJDK. As you need to accept the Oracle licence before downloading, you will need to browse to the Java download page using your browser. Agree to the terms, and then click on download. As soon as it starts, pause it, and copy the download link.
Go to your server and use wget to download the file using the download link you just copied:
wget -c http://download.oracle.com/otn-pub/java/jdk/8u31-b13/jdk-8u31-linux-x64.tar.gz?AuthParam=1423681612_d8066e1256e66a7a4895ad467fc7cd07
When is has finished downloading, extract it to the /opt/java folder (or any other folder of your choosing)
mkdir /opt/java
tar -xvzf jdk-8u31-linux-x64.tar.gz -C /opt/java
Now add the following to the end of /etc/environment:
JAVA_HOME="/opt/java/jdk1.8.0_31"
PATH="$JAVA_HOME/bin:$PATH"
Now source the file to load the new $PATH and $JAVA_HOME properties
. /etc/environment

Installing WildFly

First install WildFly by downloading and extracting it to the /opt directory
cd /opt
wget -c http://download.jboss.org/wildfly/8.2.0.Final/wildfly-8.2.0.Final.tar.gz
tar -xzvf wildfly-8.2.0.Final.tar.gz

Create the wildfly user and group

addgroup wildfly
useradd -g wildfly wildfly
Change the ownership of the wildfly folder recursively:
chown -R wildfly:wildfly /opt/wildfly-8.2.0.Final/ 
Create a symbolic link so that if you change WildFly versions, you don't have to update any other configuration:
ln -s wildfly-8.2.0.Final /opt/wildfly

Installing the init.d scripts

To easiest way to start and stop WildFly is to use a startup script. Copy and paste the init.d script within the WildFly install to the /etc/init.d/ folder, change the permissions, and make it executable:
cp /opt/wildfly/bin/init.d/wildfly-init-debian.sh /etc/init.d/wildfly
sudo chown root:root /etc/init.d/wildfly
sudo chmod ug+x /etc/init.d/wildfly
To start/stop WildFly you will simply run the commands
sudo /etc/init.d/wildfly start
sudo /etc/init.d/wildfly stop

Starting WildFly as a service

To enable WildFly to start automatically when the server starts, you need to add it to the linux run-levels:
sudo update-rc.d wildfly defaults 
You should see the following output:

Configuring WildFly - open the public interface to all IP's

To access WildFly from an IP other than 127.0.0.1 (localhost) you will need to update the bind address for the public interface. Update this:
<interface name="public">
    <inet-address value="${jboss.bind.address:127.0.0.1}"/>
</interface>
to this:
<interface name="public">
    <any-address/>
</interface>
You will now be able to access WildFly from any IP using port 8080


Open the management interface to all IP's (optional) 

If you want to access the management console (on port 9990), you will need to update the bind address as you did for the public interface:
<interface name="management">
    <any-address/>
</interface>
You will now be able to access the management interface on port 9990 from any computer.

Remove the welcome content (optional)

If you are deploying your application to the context root, you will need to remove the default welcome content from the WildFly configuration. Remove the lines highlighted in bold from the undertow subsystem in your standalone.xml file
<server name="default-server">
    <http-listener name="default" socket-binding="http"/>
    <host name="default-host" alias="localhost">
        <location name="/" handler="welcome-content"/>
        <filter-ref name="server-header"/>
        <filter-ref name="x-powered-by-header"/>
    </host>
</server>
<handlers>
    <file name="welcome-content" path="${jboss.home.dir}/welcome-content"/>
</handlers>

You will now be able to deploy your application to WildFly and view it on your_ip:8080. Make sure you configure your jboss-web.xml and set the context root to /.
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
    <context-root>/</context-root>
</jboss-web>
Enjoy :)

January 5, 2015

JavaMail example with Zoho/GMail using SMTPS

Here is a simple example of how to send emails securely using JavaMail via your Gmail or Zoho account.

The first example uses TLS, which should be your preferred encryption mechanism. The second example uses SSL.

The only difference between the two mechanisms is the port number, and the mail.smtp.startXXX.enable property.

For SSL use:

port 465
mail.smtp.startssl.enable

For TLS use:

port 987
mail.smtp.starttls.enable
 
Get the source on GitHub here

Example 1: Zoho/Gmail with TLS

package com.ritchie.email;

import java.util.Date;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class SendEmailService {

    private Logger logger = Logger.getLogger(SendEmailService.class.getName());

    public void sendEmail() {

        final String GMAIL_HOST = "smtp.gmail.com";
        final String ZOHO_HOST = "smtp.zoho.com";
        final String TLS_PORT = "897";

        final String SENDER_EMAIL = "username@zoho.com";
        final String SENDER_USERNAME = "username@zoho.com";
        final String SENDER_PASSWORD = "zoho-password";

        // protocol properties
        Properties props = System.getProperties();
        props.setProperty("mail.smtps.host", ZOHO_HOST); // change to GMAIL_HOST for gmail                                                         // for gmail
        props.setProperty("mail.smtp.port", TLS_PORT);
        props.setProperty("mail.smtp.starttls.enable", "true");
        props.setProperty("mail.smtps.auth", "true");
        // close connection upon quit being sent
        props.put("mail.smtps.quitwait", "false");

        Session session = Session.getInstance(props, null);

        try {
            // create the message
            final MimeMessage msg = new MimeMessage(session);

            // set recipients and content
            msg.setFrom(new InternetAddress(SENDER_EMAIL));
            msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse("recipient@gmail.com", false));
            msg.setSubject("Demo");
            msg.setText("Message Sent via JavaMail", "utf-8", "html");
            msg.setSentDate(new Date());

            // this means you do not need socketFactory properties
            Transport transport = session.getTransport("smtps");

            // send the mail
            transport.connect(ZOHO_HOST, SENDER_USERNAME, SENDER_PASSWORD);
            transport.sendMessage(msg, msg.getAllRecipients());
            transport.close();

        } catch (MessagingException e) {
            logger.log(Level.SEVERE, "Failed to send message", e);

        }
    }
}

Example 2: Zoho/Gmail with SSL

package com.ritchie.email;

import java.util.Date;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class SendEmailService {

    private Logger logger = Logger.getLogger(SendEmailService.class.getName());

    public void sendEmail() {

        final String GMAIL_HOST = "smtp.gmail.com";
        final String ZOHO_HOST = "smtp.zoho.com";
        final String SSL_PORT = "465";

        final String SENDER_EMAIL = "username@zoho.com";
        final String SENDER_USERNAME = "username@zoho.com";
        final String SENDER_PASSWORD = "zoho-password";

        // protocol properties
        Properties props = System.getProperties();
        props.setProperty("mail.smtps.host", ZOHO_HOST); // change to GMAIL_HOST for gmail                                                         // for gmail
        props.setProperty("mail.smtp.port", SSL_PORT);
        props.setProperty("mail.smtp.startssl.enable", "true");
        props.setProperty("mail.smtps.auth", "true");
        // close connection upon quit being sent
        props.put("mail.smtps.quitwait", "false");

        Session session = Session.getInstance(props, null);

        try {
            // create the message
            final MimeMessage msg = new MimeMessage(session);

            // set recipients and content
            msg.setFrom(new InternetAddress(SENDER_EMAIL));
            msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse("recipient@gmail.com", false));
            msg.setSubject("Demo");
            msg.setText("Message Sent via JavaMail", "utf-8", "html");
            msg.setSentDate(new Date());

            // this means you do not need socketFactory properties
            Transport transport = session.getTransport("smtps");

            // send the mail
            transport.connect(ZOHO_HOST, SENDER_USERNAME, SENDER_PASSWORD);
            transport.sendMessage(msg, msg.getAllRecipients());
            transport.close();

        } catch (MessagingException e) {
            logger.log(Level.SEVERE, "Failed to send message", e);

        }
    }
} 


January 3, 2015

Installing Meld on Mac OS using macports

After playing around with various merge tools on Mac, I have come to the conclusion that none match the ease of use that Meld offers. Here is how I managed to install it on my Mac OS X 10.8.5.
  1. First install macports
  2. If you are running Mac OS 10.8 and greater, you will need to install xquartz which provides the X Window system required for Meld. If you have Mac OS prior to 10.8 you can skip this step.
  3. Install Meld and its dependencies:
    sudo port install rarian
    sudo port install meld
    
  4. Set your locale (I hard coded this so I do not have to export it each time) by updating the /opt/local/bin/meld script by changing line 75 from:
    locale.setlocale(locale.LC_ALL,'')
    to
    locale.setlocale(locale.LC_ALL,'en_US')
  5. Start the service at boot
    launchctl load -w /Library/LaunchAgents/org.freedesktop.dbus-session.plist
  6. Finally run meld using spotlight or via the command line: /opt/local/bin/meld

 

Notes: 

If you try to run Meld without having xquartz installed then you will get the following error:
(process:65163): Gtk-WARNING **: Locale not supported by C library.
 Using the fallback 'C' locale.
/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/gtk-2.0/gtk/__init__.py:57: GtkWarning: could not open display
  warnings.warn(str(e), _gtk.Warning)
/opt/local/bin/meld:126: GtkWarning: GtkIconTheme *gtk_icon_theme_get_for_screen(GdkScreen *): assertion 'GDK_IS_SCREEN (screen)' failed
  gtk.icon_theme_get_default().append_search_path(meld.paths.icon_dir())
Traceback (most recent call last):
  File "/opt/local/bin/meld", line 126, in 
    gtk.icon_theme_get_default().append_search_path(meld.paths.icon_dir())
AttributeError: 'NoneType' object has no attribute 'append_search_path'
logout 
 

Reference:

http://support.apple.com/en-us/HT201341
http://thebugfreeblog.blogspot.com/2014/03/installing-meld-on-mac-os-x.html

September 29, 2014

LocalDate java 8 Custom Serializer Jackson JSON Example

In this previous example we use serialize and deserialize classes provided by a Jackson third party datatype. For more control over the date formatting you can opt to create your own serialize and deserialize classes.

WildFly 8.1.0 uses Jackson 2.3.2 so we can add the following dependencies to the pom with a scope of provided:
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.4.2</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.4.2</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.4.2</version>
    <scope>provided</scope>
</dependency>
Then on the LocalDate property of your bean add the following annotations:
public class MyBean {

    @JsonDeserialize(using = JsonDateDeserializer.class)
    @JsonSerialize(using = JsonDateSerializer.class)
    private LocalDate date;
 
    public LocalDate getDate() {
        return date;
    }

    private void setDate(LocalDate date) {
        this.date = date;
    }
} 
Now create the custom JsonDateSerializer:
public class JsonDateSerializer extends JsonSerializer<LocalDate> {

    private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
 
    @Override
    public void serialize(LocalDate date, JsonGenerator generator,
            SerializerProvider provider) throws IOException,
            JsonProcessingException {

        String dateString = date.format(formatter);
        generator.writeString(dateString);
    }
}
And now the custom JsonDateDeserializer:
public class JsonDateDeserializer extends JsonDeserializer<LocalDate> {

    @Override
    public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {

        ObjectCodec oc = jp.getCodec();
        TextNode node = (TextNode) oc.readTree(jp);
        String dateString = node.textValue();

        Instant instant = Instant.parse(dateString);
        LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
        LocalDate date = LocalDate.of(dateTime.getYear(), dateTime.getMonth(), dateTime.getDayOfMonth());
        return date;
    }
}

NOTE: The package names have changed in Jackson 2. When you use the Jackson annotations make sure you import the one from the com.fasterxml.jackson.databind.annotation.* package and not the ones from the org.codehaus.jackson.map.annotate.* package. If you import the latter, your annotations will be ignored. WildFly 8 does not load the classes from the 1.9.2 jackson module.

September 23, 2014

Java 8 LocalDate with Jackson Serialize and Deserialize Example - RESTful WS

Although I having using Joda Time for years, it is now time to migrate over to the Java 8 Date and Time API (JSR310), and make use of the new LocalDate and LocalTime classes.

WildFly 8.1.0 uses Jackson 2.3.2 which does not know how to (de)serialize the JSR310 Date Time classes. So in order to use the Date and Time API we need to add a Jackson third party datatype dependency to our pom:
<dependency>
    <groupid>com.fasterxml.jackson.datatype</groupid>
    <artifactid>jackson-datatype-jsr310</artifactid>
    <version>2.4.0</version>
</dependency>
Then to deserialize your LocalDate add the @JsonDeserialize annotation and use the LocalDateDeserializer:
@Path("/resource")
@Produces("application/json")
public class MyRestResource {

    @GET
    @Path("now")
    @JsonDeserialize(using = LocalDateDeserializer.class)
    public LocalDate get() {
        return LocalDate.now();
    }
}
To serialize your LocalDate add @JsonSerialize annotation and use the LocalDateSerializer:
@JsonSerialize(using = LocalDateSerializer.class)

To create your own serializer/deserializer check this example, which shows you how you can format the date.