tiainen

January 31, 2012

Easy OAuth using DaliCore and Glassfish: the service consumer

After having completed the first part of this OAuth story, it is now time to implement the second part: the service consumer. While the our service provider made use of the DaliCore module dalicore-oauth, the service consumer will use the module dalicore-externalnetwork to make our connection to the service provider easier.

As in part 1, it is probably best to download the sample service consumer application, which will make this tutorial a bit easier to follow.

Manage service consumers

Before we start with the service consumer application itself, we need to be able to register service consumers in our service provider. Create a new class in the service provider application with the name ServiceConsumerHandler next to the already existing OAuthHandler class.

@Path("serviceconsumer")
@ManagedBean
public class ServiceConsumerHandler {
  @Inject
  OAuthBean oauthBean;

  @GET
  @Produces("text/html")
  public String create(@QueryParam("name") String name) {
    if (name != null && ! name.trim().isEmpty()) {
      DaliServiceConsumer daliServiceConsumer = new DaliServiceConsumer();
      daliServiceConsumer.setName(name);
      daliServiceConsumer.setConsumerKey(StringUtil.getSecureRandomString(16));
      daliServiceConsumer.setConsumerSecret(StringUtil.getSecureRandomString(16));
      daliServiceConsumer.setStatus(DaliServiceConsumer.Status.ACTIVE);
      oauthBean.createDaliServiceConsumer(daliServiceConsumer);

      return "consumer key: " + daliServiceConsumer.getKey() + "
consumer secret: " + daliServiceConsumer.getSecret() + ""; } else { return "No name provided. Add ?name=service_consumer_name at the end of the URL."; } } }

After redeploying the application, we can create a service consumer by calling http://localhost:8080/oauthprovider/rest/serviceconsumer?name=ServiceConsumerName. The response will show us the service consumer key and secret that we will need later on when connecting to the service provider.

Creating the service consumer application

Now we can start with the creation of our oauth service consumer. Create a new Maven Web Application project in NetBeans.

Create a new Maven Web Application project in NetBeans

Maven setup

As in the first part of the series, we need to change the maven pom.xml file so that it will be able to download the LodgON artifacts. Include the following repository in your pom.xml:

<repositories>
  <repository>
    <id>lodgon</id>
    <url>http://maven.lodgon.com/maven2</url>
  </repository>
</repositories>

Afterwards add the following maven dependencies:

<dependency>
  <groupId>com.lodgon.dalicore</groupId>
  <artifactId>dalicore-externalnetwork</artifactId>
  <version>0.9.11-SNAPSHOT</version>
</dependency>
<dependency>
  <groupId>com.sun.jersey</groupId>
  <artifactId>jersey-server</artifactId>
  <version>1.8</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>com.sun.jersey.contribs.jersey-oauth</groupId>
  <artifactId>oauth-signature</artifactId>
  <version>1.8</version>
</dependency>

Jersey handler and jsp's

Now we can create a handler that will handle the connection to the service provider. Create a new class called HomeHandler with the following code:

@Path("home")
public class HomeHandler {
  private static final String PROVIDER_URL = "http://localhost:8080/oauthprovider/rest";
  private static final String CONSUMER_KEY = "";
  private static final String CONSUMER_SECRET = "";
  private static final String CONSUMER_CALLBACK = "http://localhost:8080/oauthconsumer/rest/home/callback";

  @Context
  HttpServletRequest request;

  @GET
  public Response home() throws URISyntaxException {
    ExternalToken accessToken = (ExternalToken) request.getSession().getAttribute("accessToken");
    if (accessToken == null) {
      DaliCoreExternalNetwork externalNetwork = new DaliCoreExternalNetwork(PROVIDER_URL,
            CONSUMER_KEY, CONSUMER_SECRET);
      return externalNetwork.connect(CONSUMER_CALLBACK);
    } else {
      return Response.ok(new Viewable("/home.jsp", accessToken)).build();
    }
  }

  @GET
  @Path("callback")
  public Response callback(@QueryParam("oauth_token") String requestToken,
          @QueryParam("oauth_verifier") String verifier) throws URISyntaxException {
    DaliCoreExternalNetwork externalNetwork = new DaliCoreExternalNetwork(PROVIDER_URL,
            CONSUMER_KEY, CONSUMER_SECRET);
    ExternalToken accessToken = externalNetwork.callback(requestToken, verifier);
    request.getSession().setAttribute("accessToken", accessToken);
    return Response.seeOther(new URI("/home")).build();
  }
}

This class contains two entry points. The home entry point is the URL that is being called to initiate a connection with our service provider. It detects if an access token exists on the active session. If an access token does not exist, we will connect with the dalicore-oauth service provider, by using the utility class DaliCoreExternalNetwork. The constructor of this class takes three parameters: the URL to the service provider, our consumer key and our consumer secret. Update the static fields at the top with the consumer key and secret you received by creating a service consumer as mentioned previously. When we have an instance of the DaliCoreExternalNetwork we simply call the connect method and provide the callback where the user will be redirected to after it authorized the service consumer.

The callback is handled by the second entry point, named callback. It accepts the request token and a verifier that we will need to request our access token. This can also be done through the DaliCoreExternalNetwork helper class. By calling the callback method we will finally receive a valid access token which we store on the active session. Finally, we redirect the user back to the home page, which will print our access token information in the browser.

web.xml configuration

Next we need to activate Jersey so our handler will be processed. Create a Standard Deployment Descriptor and put in the following code:

<?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">
  <servlet>
    <servlet-name>jerseyservlet</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>jerseyservlet</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
  <session-config>
    <session-timeout>
      30
    </session-timeout>
  </session-config>
</web-app>

JPA Configuration

Finally, we need to configure a connection with the database where the tokens will be stored. Create a persistence.xml file in the directory src/main/resources/META-INF with the following content:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
  <persistence-unit name="oauth_provider_pu" transaction-type="JTA">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <jta-data-source>jdbc/oauthconsumerdb</jta-data-source>
    <class>com.lodgon.dali.core.entity.Application</class>
    <class>com.lodgon.dali.core.entity.Content</class>
    <class>com.lodgon.dali.core.entity.ContentPermission</class>
    <class>com.lodgon.dali.core.entity.Field</class>
    <class>com.lodgon.dali.core.entity.Group</class>
    <class>com.lodgon.dali.core.entity.GroupPermission</class>
    <class>com.lodgon.dali.core.entity.TypePermission</class>
    <class>com.lodgon.dali.core.entity.User</class>
    <class>com.lodgon.dali.core.entity.UserPermission</class>
    <class>com.lodgon.dali.core.externalnetwork.entity.ExternalToken</class>
    <class>com.lodgon.dali.core.externalnetwork.entity.OnlineAccount</class>
    <properties>
      <property name="eclipselink.ddl-generation" value="create-tables"/>
    </properties>
  </persistence-unit>
</persistence>

Pre-deployment configuration

Again, just like we did when setting up the service provider, we need to add the JDBC resources to glassfish. Create a file called sun-resources.xml with the following content:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0 Resource Definitions //EN" "http://www.sun.com/software/appserver/dtds/sun-resources_1_3.dtd">
<resources>
  <jdbc-connection-pool connection-validation-method="auto-commit" datasource-classname="org.apache.derby.jdbc.EmbeddedDataSource" res-type="javax.sql.DataSource" name="derby_oauthconsumerdb_j2eepool" wrap-jdbc-objects="false">
    <property name="ConnectionAttributes" value=";create=true" />
    <property name="DatabaseName" value="memory:oauthconsumerdb" />
  </jdbc-connection-pool>
  <jdbc-resource enabled="true" jndi-name="jdbc/oauthconsumerdb" object-type="user" pool-name="derby_oauthconsumerdb_j2eepool"/>
</resources>

And add the resources to glassfish by calling the add-resources asadmin command:

$ asadmin add-resources sun-resources.xml

Building and deployment

Finally, you can build and deploy the application by pressing F6. Afterwards point your browser to http://localhost:8080/oauthconsumer/rest/home. Login with any username and password, authorize the service consumer and you should have your access token!

Easy OAuth using DaliCore and Glassfish: the service provider

DaliCore is really starting to get some momentum recently, so I thought it is time to write something about it. DaliCore currently contains three modules:
dalicore-ejb
contains everything related to managing the dalicore entities: User, Content, Group and Permission
dalicore-oauth
allows web applications to become an oauth service provider
dalicore-externalnetwork
allows web applications to setup a connection with oauth providers

This blog entry will focus on the second module. I'll show you how easy it is with dalicore-oauth to create a web application that can serve as an oauth service provider. In the next blog entry, we'll make a second web application that serves as the oauth service consumer that will use dalicore-externalnetwork to connect to our service provider. Our application server of choice is glassfish 3.1.1 and we'll be using Jersey for our web related components.

It's probably better to download the sample service provider application already. This should make it easier to follow all the steps below.

Creating an application

Allright, time to get started. We start with creating the web application that will act as our oauth service provider. Fire up NetBeans and create a new Maven Web Application project.

Create a new Maven Web Application project in NetBeans

Maven setup

To be able to make use of dalicore-oauth, we need to change the pom.xml and add LodgOn's maven repository. Include the following code in your pom.xml:

<repositories>
  <repository>
    <id>lodgon</id>
    <url>http://maven.lodgon.com/maven2</url>
  </repository>
</repositories>

Now we can add the maven dependency to dalicore-oauth and other modules that we will need. Add the following dependencies in your pom.xml, in the dependencies section:

<dependency>
  <groupId>com.lodgon.dalicore</groupId>
  <artifactId>dalicore-ejb</artifactId>
  <version>0.9.11-SNAPSHOT</version>
</dependency>
<dependency>
  <groupId>com.lodgon.dalicore</groupId>
  <artifactId>dalicore-oauth</artifactId>
  <version>0.9.11-SNAPSHOT</version>
</dependency>
<dependency>
  <groupId>com.sun.jersey</groupId>
  <artifactId>jersey-server</artifactId>
  <version>1.8</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>com.sun.jersey.contribs.jersey-oauth</groupId>
  <artifactId>oauth-signature</artifactId>
  <version>1.8</version>
</dependency>

Jersey handler and jsp's

An oauth service provider typically needs to provide three end points: one for requesting a request token, one to authenticate a user and a third to request an access token. The creation of the request and access tokens are automatically handled by dalicore-oauth. So, the only thing we need to add is the handling of user authentication. Create a new class called OAuthHandler with the following code:

@Path("oauth")
@ManagedBean
public class OAuthHandler {
  @Inject
  OAuthBean oauthBean;
  @Inject
  UserBean userBean;

  @GET
  @Path("authenticate")
  public Viewable authenticate(@QueryParam("oauth_token") String oauthToken) {
    return new Viewable("/login.jsp", new Model(oauthToken));
  }

  @POST
  @Path("login")
  public Viewable login(@FormParam("oauth_token") String oauthToken,
          @FormParam("username") String userName,
          @FormParam("password") String password) throws DaliCoreException {
    User user = userBean.getByUserId(userName);
    if (user == null) {
      user = new OAuthUser();
      user.setUserId(userName);
      user = userBean.create(user);
      userBean.setPassword(user.getId(), password);
    }

    if (userBean.validatePassword(user.getId(), password)) {
      DaliToken requestToken = oauthBean.findDaliToken(oauthToken, DaliToken.Type.OAUTH_REQUEST_TOKEN);
      return new Viewable("/authorize.jsp", new Model(oauthToken, user, oauthToken.getDaliServiceConsumer()));
    } else {
      return new Viewable("/login.jsp", new Model(oauthToken, "Login failed."));
    }
  }

  @POST
  @Path("authorize")
  public Response authorize(@FormParam("oauth_token") String oauthToken,
          @FormParam("userUid") String userUid,
          @DefaultValue("no") @FormParam("authorized") String authorized) throws DaliCoreException, URISyntaxException, UnsupportedEncodingException {
    DaliToken requestToken = oauthBean.findDaliToken(oauthToken, DaliToken.Type.OAUTH_REQUEST_TOKEN);

    if ("yes".equals(authorized)) {
      User user = userBean.getByUid(userUid);
      if (user != null) {
        String verifier = oauthBean.authorizeServiceConsumer(user.getId(), oauthToken, true);
        return Response.seeOther(new URI(requestToken.getCallback() + "?" + OAuthParameters.TOKEN + "=" + URLEncoder.encode(oauthToken, "UTF-8") + "&" + OAuthParameters.VERIFIER + "=" + URLEncoder.encode(verifier, "UTF-8"))).build();
      } else {
        return Response.ok(new Viewable("/login.jsp", new Model(oauthToken))).build();
      }
    } else {
      return Response.seeOther(new URI(requestToken.getCallback())).build();
    }
  }

  public static class Model {
    private String message;
    private String oauthToken;
    private DaliServiceConsumer serviceConsumer;
    private User user;

    public Model(String oauthToken) {
      this.oauthToken = oauthToken;
    }

    public Model(String oauthToken, String message) {
      this.oauthToken = oauthToken;
      this.message = message;
    }

    public Model(String oauthToken, User user, DaliServiceConsumer serviceConsumer) {
      this.oauthToken = oauthToken;
      this.user = user;
      this.serviceConsumer = serviceConsumer;
    }

    public String getMessage() {
      return message;
    }

    public void setMessage(String message) {
      this.message = message;
    }

    public String getOauthToken() {
      return oauthToken;
    }

    public void setOauthToken(String oauthToken) {
      this.oauthToken = oauthToken;
    }

    public DaliServiceConsumer getServiceConsumer() {
      return serviceConsumer;
    }

    public void setServiceConsumer(DaliServiceConsumer serviceConsumer) {
      this.serviceConsumer = serviceConsumer;
    }

    public User getUser() {
      return user;
    }

    public void setUser(User user) {
      this.user = user;
    }
  }
}

The class is a Jersey handler that contains three endpoints. The first endpoint authenticate will be called by service consumers that would like to authenticate a user for their application. We will show a basic login page where a user can provide a username and password.

The second endpoint login handles a user that tries to login. For the sake of simplicity, we will create a new user when no user was found with the provided username. Notice, that this user must be of type OAuthUser, which is an extension of the dalicore-ejb User. Upon successful login, the user will be redirected to an authorization page where he/she can choose whether to allow the service consumer to access his/her user data.

Which brings us to the third endpoint authorize, where a user authorizes the service consumer. When the user allows access to the service consumer, we will call the service consumer's callback. A service consumer should have provided this callback when it requested the request token earlier on in the oauth process. We will come back to that when we create the service consumer application.

Below you will find the two jsp's (login.jsp and authorize.jsp) that we will need, one to allow a user to login and another one to authorize a specific service consumer.

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>dalicore-oauth service provider</title>
  </head>
  <body>
    <h1>Authenticate</h1>

    <form method="POST" action="${pageContext.request.contextPath}/rest/oauth/login">
      <input type="hidden" name="oauth_token" value="${it.oauthToken}"/>
      username: <input type="text" name="username"/><br/>
      password: <input type="password" name="password"/><br/>
      <input type="submit" value="login"/>
    </form>
  </body>
</html>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>dalicore-oauth service provider</title>
  </head>
  <body>
    <h1>Authorize</h1>

    <h4>Do you want to allow the service consumer <b>${it.serviceConsumer.name}</b> to access your data:</h4>

    <form method="POST" action="${pageContext.request.contextPath}/rest/oauth/authorize">
      <input type="hidden" name="oauth_token" value="${it.oauthToken}"/>
      <input type="hidden" name="userUid" value="${it.user.uid}"/>
      <input type="submit" name="authorize" value="yes"/>
      <input type="submit" name="authorize" value="no"/>
    </form>
  </body>
</html>

CDI configuration

You probably noticed that at the top of the OAuthHandler class, we use CDI to inject a reference to the UserBean and the OAuthBean. In order for CDI to work, we need to add a beans.xml in the WEB-INF directory. So, create the directory WEB-INF in the Web Pages directory and add the following beans.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans 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/beans_1_0.xsd">
</beans>

web.xml configuration

The next steps are required to allow the dalicore-oauth module to be plugged in correctly. We need to configure the Jersey servlet so it can handle oauth requests. This is all done in the standard deployment descriptor (probably better known as web.xml). In NetBeans you can right-click on Web Pages and choose "New -> Standard Deployment Descriptor (web.xml)". In the web.xml file we will add the section for the jersey servlet:

<?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">
  <servlet>
    <servlet-name>jerseyservlet</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.oauth.ignorePathPattern</param-name>
      <param-value>requestToken|accessToken</param-value>
    </init-param>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <param-value>com.sun.jersey.oauth.server.api.resources;com.lodgon.dali.core.oauth;com.mycompany.oauthprovider</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>jerseyservlet</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>

  <session-config>
    <session-timeout>
      30
    </session-timeout>
  </session-config>
</web-app>

There are two init parameters for the Jersey servlet that require some explanation. When a URL on our web application is called with the Authorization HTTP header, it will be filtered by the Jersey OAuth filter. However, getting a request token or access token also contains the Authorization HTTP header. Since these aren't regular OAuth requests, these should be ignored by the OAuth filter. This can be done by specifying the com.sun.jersey.config.property.oauth.ignorePathPattern init parameter.

The second init parameter com.sun.jersey.config.property.packages is required to tell Jersey which packages to scan for providers and handlers. Be sure not to forget to specify the package in which you created the OAuthHandler class. Otherwise it won't be detected by Jersey and you'll receive 404 responses when trying to authenticate a user.

JPA configuration

The final requirement is to configure our connection to the database. This is needed because dalicore-oauth persistently stores its request and access tokens, its users and its list of service consumers. Create a persistence.xml file in the directory src/main/resources/META-INF with the following content:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
  <persistence-unit name="oauth_provider_pu" transaction-type="JTA">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <jta-data-source>jdbc/oauthproviderdb</jta-data-source>
    <class>com.lodgon.dali.core.entity.Application</class>
    <class>com.lodgon.dali.core.entity.Content</class>
    <class>com.lodgon.dali.core.entity.ContentPermission</class>
    <class>com.lodgon.dali.core.entity.Field</class>
    <class>com.lodgon.dali.core.entity.Group</class>
    <class>com.lodgon.dali.core.entity.GroupPermission</class>
    <class>com.lodgon.dali.core.entity.TypePermission</class>
    <class>com.lodgon.dali.core.entity.User</class>
    <class>com.lodgon.dali.core.entity.UserPermission</class>
    <class>com.lodgon.dali.core.oauth.entity.Attribute</class>
    <class>com.lodgon.dali.core.oauth.entity.DaliToken</class>
    <class>com.lodgon.dali.core.oauth.entity.DaliServiceConsumer</class>
    <class>com.lodgon.dali.core.oauth.entity.OAuthUser</class>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties>
      <property name="eclipselink.ddl-generation" value="create-tables"/>
    </properties>
  </persistence-unit>
</persistence>

Pre-deployment configuration

We're almost there. Before we can actually deploy our application, we need to add the JDBC resources to glassfish. Create a file called sun-resources.xml with the following content:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0 Resource Definitions //EN" "http://www.sun.com/software/appserver/dtds/sun-resources_1_3.dtd">
<resources>
  <jdbc-connection-pool connection-validation-method="auto-commit" datasource-classname="org.apache.derby.jdbc.EmbeddedDataSource" res-type="javax.sql.DataSource" name="derby_oauthproviderdb_j2eepool" wrap-jdbc-objects="false">
    <property name="ConnectionAttributes" value=";create=true" />
    <property name="DatabaseName" value="memory:oauthproviderdb" />
  </jdbc-connection-pool>
  <jdbc-resource enabled="true" jndi-name="jdbc/oauthproviderdb" object-type="user" pool-name="derby_oauthproviderdb_j2eepool"/>
</resources>

I choose to connect to an in memory derby database here, but you can of course configure your resources to connect to a real database. Add the resources to glassfish by calling the following asadmin command:

$ asadmin add-resources sun-resources.xml

Building and deployment

Now you can build the application and deploy it in glassfish. Building can be done in NetBeans by pressing F11 and deploying is done with the shortkey F6. You can of course also use the command line if you prefer that:

$ mvn clean install
$ asadmin deploy --name oauthprovider --contextroot oauthprovider target/oauthprovider-1.0-SNAPSHOT.war

The application should deploy successfully and now you can point your browser to the following location: http://localhost:8080/oauthprovider/rest/oauth/authenticate. This should show the login screen.

Now it's time to head over to part 2, where we will create the service consumer side that will connect to the service provider application we created here.

March 01, 2011

Load balancing with Glassfish 3.1 and Apache

Glassfish 3.1 was released on February 28th. This is the first application server that supports Java EE 6 in a clustered and high available environment. We at LodgON have a high interest in this, as Johan Vos already mentioned in his blog and we are eager to find out how easy it is to configure Glassfish to let it run multiple instances using Apache as a load balancer. I'll describe the steps I had to go through for setting up the following configuration:

  • machine1: Glassfish DAS, 1 instance, Apache HTTP server with mod_jk enabled

  • machine2: 1 instance


Configuring the nodes



Install Glassfish 3.1 on machine1. Once that is completed, we need to setup ssh so we can manage node machine2 centrally from machine1. So on machine1 enter the following command:

$ asadmin setup-ssh machine2


This is basically a frontend for the ssh-keygen tool. It will ask for the password of your user on machine2 and copies the necessary keyfiles over to that machine. We are now ready to install glassfish on machine2 by using the install-node command:

$ asadmin install-node --installdir /opt/glassfish3 machine2


This copies glassfish over to the node with the host name specified in the last parameter and installs it in the specified installation directory. I encountered a problem where it said that the command jar was not found. To resolve this in Ubuntu, make sure you specify java in your PATH in ~/.bashrc before the following lines:

# If not running interactively, don't do anything
[ -z "$PS1" ] && return


Everything after these lines will not be executed when logging in with an non-interactive shell like in our case, via ssh.

Now we can start the DAS on machine1 and begin configuring our instances.

$ asadmin start-domain


Configuring the cluster



We will now create a remote and a local instance into one cluster. We will first let the DAS know that we have another node on machine2 that we want to use by creating an ssh node:

$ asadmin create-node-ssh --installdir /opt/glassfish3 --nodehost machine2 node1


After that we can create the cluster and add two instances to it: one remote instance on node node1 and a local instance on the local node. This local node, with name localhost-domain1, is automatically available when installing glassfish.

$ asadmin create-cluster cluster1
$ asadmin create-instance --cluster cluster1 --node node1 inst1
$ asadmin create-instance --cluster cluster1 --node localhost-domain1 inst2


We can now start the cluster and deploy an application into it. It doesn't really matter which application you want to deploy. I will use a simple application containing only 1 jsp (download the application):

$ asadmin start-cluster cluster1
$ asadmin deploy --target cluster1 --name simple simple-application.war


When deploying, you specify our cluster1 as the target. This will deploy the application into every instance that was added to the cluster. When deployment was successful, you should be able to browse to the application using the following URL: http://machine2:28080/simple. It should also be running on the second instance at the following URL: http://machine1:28081/simple.

Configuring the load balancer



We have both instances running our application, so now we can start configuring Apache to run as the load balancer. To let Apache connect with Glassfish, you'll have to use the mod_jk module. Installing it in Ubuntu is very easy:

$ sudo apt-get install libapache2-mod-jk


Once the module is installed, create a file called jk.conf in the mods-available directory in your apache configuration directory (mine is located in /etc/apache2/mods-available) and link it into the mods-enabled directory.

$ sudo touch /etc/apache2/mods-available/jk.conf
$ sudo ln -s /etc/apache2/mods-available/jk.conf /etc/apache2/mods-enabled/jk.conf


In that file you can copy the following configuration:

JkWorkersFile /etc/apache2/worker.properties
JkLogFile /var/log/apache2/mod_jk.log
JkLogLevel error
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories
JkRequestLogFormat "%w %V %T"


The first line specifies the location of the workers file in which we will configure our Glassfish instances. So, create the file in the directory /etc/apache2 and add the following properties:

worker.list=worker1,worker2,loadbalancer

# default properties for workers
worker.template.type=ajp13
worker.template.port=28009
worker.template.lbfactor=50
worker.template.connection_pool_timeout=600
worker.template.socket_keepalive=1
worker.template.socket_timeout=300

# properties for worker1
worker.worker1.reference=worker.template
worker.worker1.host=machine1

# properties for worker2
worker.worker2.reference=worker.template
worker.worker2.host=machine2

# properties for loadbalancer
worker.loadbalancer.type=lb
worker.loadbalancer.balance_workers=worker1,worker2


Finally, in the default virtual host configuration file (on my machine this is located at /etc/apache2/sites-available/default) we add a line to tell mod_jk which URLs have to be forwarded to the load balancer:

JkMount /simple/* loadbalancer


You can read more about all these configuration directives and properties on the website of the Apache Tomcat Connector:


When you are done with this, we restart the Apache HTTP server to let the settings take effect:

$ sudo apache2ctl graceful


Finally, we need to add a HTTP listener in the Glassfish cluster that will listen for the calls from mod_jk. As we specified in the workers.properties file, this listener will be listening on port 28009. Adding this listener works with the following command:

$ asadmin create-network-listener --protocol http-listener-1 --listenerport 28009 --jkenabled true --target cluster1-config jk-connector


Restart the cluster and we should be able to connect to our application through Apache at the following URL: http://machine1/simple.

$ asadmin stop-cluster cluster1
$ asadmin start-cluster cluster1


Conclusion



You can see that it was quite easy to set up a clustered Glassfish with Apache running as the load balancer. You might want to watch the following video about the new features in Glassfish 3.1. It might give you a bit more insight in the way that clustering and load balancing works. It will also show you that you can configure everything by using the admin website (running at port 4848) instead of using the asadmin command as I did in this blog.

May 02, 2010

Isometric tile rendering in JavaFX

1) Introduction


I'm currently working on a strategy game in JavaFX and decided to use isometric projection for rendering the graphics. During development I encountered some performance issues specific to JavaFX 1.2. It was expensive to add many nodes in a single Group and it was furthermore expensive to add/remove a lot of nodes from a Group.

The first issue was resolved by having more levels of Groups. Instead of 1 Group containing all the nodes, I added several layers of Groups. Each Group in the bottom layer would then only hold a small portion of all the nodes.

Resolving the second issue was done by using a quadtree to quickly decide which nodes should be added and which nodes should be removed from the scene graph. This way I only had to add/remove a small portion from the scene graph during the render pass.

Of course, a few weeks ago, Oracle released JavaFX 1.3, which magically solved all these problems by itself. Rendering many nodes in one Group no longer was a problem. However, having the quadtree structure still has a major advantage. You only need to render the nodes that are actually visible on the screen. This should eat a lot less memory then when you would render all the nodes in your world. So, I'm still going to show you how I've done the quadtree implementation.

2) Brute force rendering


Before going over to using quadtrees, I will first show you the easy way: rendering all tiles in a brute force manner. My game world consists of a series of tiles laid out in a two-dimensional grid. Each tile has an X and a Y position to store it's position within that grid. Rendering brute force means that we will render all the tiles in the game world, even when they are not visible on screen.

var scene : Scene = Scene {
width : 1000
height : 800
content : BruteForceRenderer {
tilesWidth : 512
}
}

Stage {
scene : scene
}

def TILE_WIDTH = 32;
def TILE_HEIGHT = 16;

public class BruteForceRenderer extends CustomNode {
postinit {
var numberTiles = Math.pow(tilesWidth, 2);
tilesGroup.content = [
for (i in [0 .. numberTiles - 1]) {
var x = (i / tilesWidth) as Integer;
var y = (i mod tilesWidth) as Integer;
Polygon {
points : [ TILE_WIDTH / 2, 0.0, TILE_WIDTH, TILE_HEIGHT / 2, TILE_WIDTH / 2, TILE_HEIGHT, 0.0, TILE_HEIGHT / 2 ]
translateX : TILE_WIDTH / 2 * (x - y)
translateY : TILE_HEIGHT / 2 * (x + y)
fill : Color.GREEN
}
}
];
}

public-init var tilesWidth : Number = 64;

var tilesGroup : Group = Group {};

override protected function create () : Node {
tilesGroup
}
}

The BruteForceRenderer is a CustomNode that contains one Group in which all the tiles are rendered. The tilesWidth parameter defines how wide our grid must be. A value of 8 for instance means that we will have a grid that contains 64 tiles in total.

The tiles themselves are made up of a Polygon with a green color. They are added one by one into tilesGroup. The order in which the tiles are layed out is from top to bottom and from right to left. The following image, which is a render of 9 tiles, probably clarifies this a bit. The numbers between the brackets are the x and y coordinate of the tile that is being rendered.


The main advantage for using the brute force approach is that the code remains very simple to understand. The disadvantage will become clear when you start raising the tilesWidth parameter. Even though they've made lots of performance improvements in JavaFX 1.3, it's still becoming a bit sluggish for instance when implementing mousedragging to move around the world.

3) Using a QuadTree


With brute force rendering, we keep track of all tiles whether they are visible on screen or not. However, we should only render a tile when it is actually visible on the screen. On the next image we can see the tiles in green and our screen in blue.


This clearly shows how brute force rendering is done: all tiles are visible, even those that do not fall within the blue rectangle. To solve this, before rendering a tile, we could check to see if it's bounds fall within the screen view or not. The easiest way to do this is to check all the tiles one by one. However, a more elegant solution is to use a quadtree. A quadtree allows us to quickly discover which nodes are actually visible on screen without having to check every possible node.

The process is as follows:

  1. divide the world space into four equally sized squares

  2. for every square, check if it's bounds intersect with the screen bounds
    • if they don't intersect: skip this square

    • if they do intersect: divide this square into four equally sized squares and repeat step 2


We keep dividing until we reach a specific depth. When this depth is reached, we will just individually check all tiles that are still available and render those that fall within the screen. Again, I'll use an image to make this more clear.


Step 1 divides the world into 4 squares. We check the bounds of square 1 and see that it intersects with the screen bounds (again the blue rectangle). In step 2 we divide this square again into 4 squares and check the bounds of square 1. We see that it doesn't intersect with the screen, so we check square 2. This time we have an intersection. So again, in step 3, we will divide this square number 2 into 4 equally sized squares and repeat our intersection checks. We can clearly see that square number 1 doesn't intersect, but square number 2 does.

At this moment we have reached our maximum depth of 3. So instead of dividing this last square into 4 smaller squares, we will go over the nodes that are contained in this square and perform the bounds check on each of these nodes. If a node intersects it will be added to the scene graph, otherwise it will be skipped. This is shown in step 4.

Below is a sample of this application rendering a world containing 256 by 256 tiles. You can also download the code as a NetBeans project.


March 20, 2010

Beware of JavaFX and null references

Yesterday I was trying to implement a simple Animation class, that could for example be used for animating sprites in a game. I wrote the following code:

public class Animation extends ImageView {
var index : Integer;
public-init var images : Image[] on replace {
timeline.start();
}

var timeline : Timeline = Timeline {
repeatCount : Timeline.INDEFINITE
keyFrames : [
KeyFrame {
time : 0.2s
action : function() {
if (index + 1 >= images.size()) index = 0 else index++
}
}
]
}

override var image = bind images[index];
}


Let's now create an Animation object as follows:

var animation : Animation = Animation {
images : drillingWell
}


You might expect that this would work just fine and that the program would display an animation of a drilling well. Since we are assigning the sequence drillingWell to Animation.images, this would call the on replace trigger. Which it does. However, during object instantiation the timeline variable is still null. This means that calling timeline.play() won't have any effect because JavaFX quietly ignores null references. Throwing a NullPointerException here would have saved me quite some time and work ;-).

To fix the code I had to assign the timeline in the on replace trigger itself, like this:

public class Animation extends ImageView {
public-init var images : Image[] on replace {
Timeline {
repeatCount : Timeline.INDEFINITE
keyFrames : [
KeyFrame {
time : 0.2s
action : function() {
if (index + 1 >= images.size()) index = 0 else index++
}
}
]
}.play();
}

var index : Integer;
override var image = bind images[index];
}

February 18, 2010

Using Jersey to (un)marshall Java objects

When developing a REST API in Java, it is straightforward when you do that using Jersey. At least when implementing the server side. For instance, we can have the following methods to manage a Foo object:

@GET
@Path("{fooId}")
@Produces("application/xml")
public Foo getFoo(@PathParam("fooId") int fooId) {
return FooBean.getInstance().findFoo(fooId);
}

@GET
@Path("/list")
@Produces("application/xml")
public List<Foo> getFoos() {
return FooBean.getInstance().findAllFoos();
}

@PUT
@Produces("application/xml")
public Foo putFoo(@FormParam("name") String name, @FormParam("bar") String bar) {
Foo foo = new Foo();
foo.setName(name);
foo.setBar(bar);
return FooBean.getInstance().createFoo(foo);
}

@POST
@Path("/{fooId}")
@Produces("application/xml")
public Foo updateFoo(@PathParam("fooId") int fooId, @FormParam("name") String name, @FormParam("bar") String bar) {
Foo foo = FooBean.getInstance().findFoo(fooId);
if (foo != null) {
foo.setName(name);
foo.setBar(bar);
return FooBean.getInstance().updateFoo(foo);
}

return null;
}

@DELETE
@Path("/{fooId}")
public void deleteFoo(@PathParam("fooId") int fooId) {
FooBean.getInstance().deleteFoo(fooId);
}


The Foo class must be annotated with @XmlRootElement, otherwise Jersey won't know how to marshall the object into XML or JSON.

@XmlRootElement
public class Foo implements Serializable {
private int id;
private String name;
private String bar;
...
}


Recently I discovered that you can also use Jersey when developing client applications that make use of a REST API. The Jersey Client API, as it is called, can be used to easily produce and consume REST requests and responses. The code below shows a quick overview of how to call the methods listed above.

Client client = Client.create();

WebResource wr = client.resource("http://localhost:8080/jm/rest/foo");

// getting a single Foo object
Foo foo = wrGetFoo.path("/1").get(Foo.class);

// getting a list of Foo objects
List<Foo> foos = wr.path("/list").get(new GenericType<List<Foo>>() {});

// creating a Foo object
MultivaluedMap params = new MultivaluedMapImpl();
params.add("name", "Foo2");
params.add("bar", "bar2");
Foo foo2 = wr.put(Foo.class, params);

// updating a Foo object
params.clear();
params.add("name", "Foo2");
params.add("bar", "bar2updated");
foo2 = wr.path("/" + foo2.getId()).post(Foo.class, params);

// deleting a Foo object
wr.path("/" + foo2.getId()).delete();


As you can see, the methods on the WebResource class map perfectly with the HTTP methods generally used in a REST API: GET, POST, PUT and DELETE. You can download the sample project, jerseymarshall.zip, which contains two NetBeans projects: a web project for the server side and a plain java project for the client side.

January 06, 2010

Simple animation in JavaFX

Having two weeks off during the Christmas holidays gives you some time to create something in JavaFX. I had already used JavaFX before, but animations were a part I didn't yet have any experience in. So I was thinking of remaking a simple banner application that a client once needed for his website. That version was written in Flash and was created by a third party. You can have a look at that animation at http://vi.be.

The application is quite simple. It reads in an XML-file which contains a list of banners. Each banner has an image, a title, a description and a URL. After the XML-file has been read, it displays the banners one after another. The transition between two different banners is done by using a simple fading out/fading in animation. Doing this animation in JavaFX is simple.

First I create a variable to hold the current opacity and bind this to the opacity of the banner. The SingleBanner class used below is nothing more than a CustomNode in which the image, the title and the description are placed.

var bannerOpacity = 1.0;

insert SingleBanner {
banner : banner // the POJO containing all banner info
visible : false // initially all banners are invisible
opacity : bind bannerOpacity // bind the opacity variable to the banner

// when the mouse is hovering over the banner, pause the timeline
onMouseEntered : function(event : MouseEvent) {
timeline.pause();
}
onMouseExited : function(event : MouseEvent) {
timeline.play();
}
} into banners;


To do the actual animation in JavaFX, we need to create a Timeline. A Timeline does nothing more than perform actions at specified "keyframes". You can execute this Timeline once, a few times or even infinitely. Here's the code for our Timeline:

var timeline : Timeline = Timeline {
repeatCount : Timeline.INDEFINITE
keyFrames: [
at (4.75s) {
bannerOpacity => 1.0
},
at (4.85s) {
bannerOpacity => 0.0 tween Interpolator.EASEOUT
}
at (5.0s) {
bannerOpacity => 1.0 tween Interpolator.EASEIN
}
]
}


The syntax is easy to understand and you can clearly see what the Timeline does. 4.75 second after the Timeline was started, it will assign 1.0 to the variable bannerOpacity. Between 4.75 and 4.85 seconds the opacity will be brought down to zero using an EaseOut interpolation. Finally, the opacity will be brought back up to 1.0 with an EaseIn interpolation. The trick is now to make the current banner invisble when the opacity reaches 0. At the same time, the next banner in the array will be made visible. We can do that easily using a replace trigger.

var bannerOpacity = 1.0 on replace {
if (bannerOpacity == 0.0) {
counters[index].active = false;
if (index + 1 >= sizeof(banners)) {
index = 0;
} else {
index++;
}
counters[index].active = true;
}
}

var index = 0;
var banner = bind banners[index] on replace oldBanner {
oldBanner.visible = false;
banner.visible = true;
}


That's all that is required to do some simple animation. The code for the project can be downloaded here. There are only two problems that I couldn't resolve:

  • I have not yet found a way how to open a URL from within JavaFX. This can be achieved with: AppletStageExtension.showDocument(banner.link, "_new");

  • In browser mode the loading of the images takes a long time. In desktop mode it seems to go a lot quicker.



Below you can have a look at the banner application itself.