Accessing Tomcat JAAS-secured (Form-based) JAX-RS endpoint, from within Java EE client

Unlike my previous post here, sometimes we do need to access JAX-RS resource which is secured by the “traditional” approach of using FORM-based authentication (i.e. jsecuritycheck).

I thought it would be good to share how I do it from within Java client.

Please note that this post is based on (Bien 2013).

Firstly, the thing about ‘jsecurity’-check in Tomcat…

After much reading, I found some help from the following entry at a href=”www.stackoverflow.com” target=”_blank”>stackoverflow.com:

that to get some resource on the tomcat server with j_security_check auth it is necessary to implement three steps:

  1. Send GET request to the needed private resource, in response you get a cookie (Header “Set cookie”)
  2. Send request with cookie (from step 1) to the j_security_check. On response you should get code 302 – “Moved Temporarily”
  3. Now you can repeat request to the private resource with same cookie, on responce you get needed resource data.

(Alexander 2011)

Let’s look at some code

I have put comments in the code to explain what is going on in relation to the three points above. But basically what is happening is that we intercept (Kops 2013) the request in order to first make another request authenticating against jsecuritycheck.

– Register the ‘Authenticator’ when making the call

final Client client = ClientBuilder.newClient().register(new Authenticator("http://myapplication.com/myweb", "username", "password"));
final WebTarget target = client.target("http://myapplication.com/myweb/resources/person");
final JsonObject response = target.request(MediaType.APPLICATION_JSON_TYPE).get(JsonObject.class);
// do something with the response

– Our ‘Authenticator’, which is a ‘javax.ws.rs.client.ClientRequestFilter’

package id.co.lucyana.hr.util;

// imports omitted

@Provider
public final class Authenticator implements ClientRequestFilter {
    private static final String COOKIE = "Cookie";

    // security params
    private static final String J_SECURITY_CHECK = "j_security_check";
    private static final String J_USERNAME = "j_username";
    private static final String J_PASSWORD = "j_password";

    private final String username;
    private final String password;
    private final String baseUri;
    
    // requires by @Provider
    public Authenticator() {
        this.username = null;
        this.password = null;
        this.baseUri = null;
    }

    public Authenticator(final String baseUri, final String username, final String password) {
        this.username = username;
        this.password = password;
        this.baseUri = baseUri;
    }

    @Override
    public void filter(final ClientRequestContext requestContext) throws IOException {
        final List<Object> cookies = new ArrayList<>();

        /*
         * This is hitting the URL as requested by the ClientBuilder.
         * (Refer to the line: "1. Send GET request to the needed private resource,
         * in response you get a cookie (Header “Set cookie”)"
         */
        final Client initialClient =  ClientBuilder.newClient();
        final Response responseInitial = initialClient
                .target(requestContext.getUri())
                .request(requestContext.getAcceptableMediaTypes().get(0))
                .method(requestContext.getMethod());
        initialClient.close();

        /*
         * This section is getting the cookie above and use it to make the call against jsecuritycheck
         * (Refer to the line: 2. Send request with cookie (from step 1) to the j_security_check.
         * On response you should get code 302 – “Moved Temporarily”)
         */
        responseInitial.getCookies().values().stream().forEach((cookie) -> {
            cookies.add(cookie.toCookie());
        });
        final Client loginClient = ClientBuilder.newClient();
        final Form form = new Form();
        form.param(J_USERNAME, this.username);
        form.param(J_PASSWORD, this.password);
        final Response loginResponse = loginClient.target(this.baseUri).path(J_SECURITY_CHECK)
                .request(MediaType.APPLICATION_FORM_URLENCODED)
                .header(COOKIE, cookies)
                .post(Entity.form(form));
        loginClient.close();
        loginResponse.getCookies().values().stream().forEach((cookie) -> {
            cookies.add(cookie.toCookie());
        });

        /*
         * This is right before making the actual call. So we add the cookie (which the server will be
         * able to tell that this call has been authenticated).
         * (Refer to the line: 3. Now you can repeat request to the private resource with same cookie,
         * on responce you get needed resource data.)
         */
        requestContext.getHeaders().put(COOKIE, cookies);
    }
}

References

Alexander, 2011, ‘Request to j_security_check return 408 error only with right paramters’, stackoverflow.com, accessed on 28 November 2016

Bien, A, 2013, ‘Client-side HTTP Basic Authentication With JAX-RS 2.0’, Adam Bien’s weblog, accessed on 28 November 2016

Kops, M, 2013, ‘JAX-RS 2.0 REST Client Features by Example’, hasCode.com, accessed on 28 November 2016

JSON.stringify(result) prints empty collection

Just recently when I did a simple test on my JQuery-mobile page to print the result return from JAX-RS end point, I received an empty collection.

It says

localhost:8080 says:

{"adjustmentInfo":[{}]}

At the same time, I am sure my end point returned a collection containing one AdjustmentInfo object.

Here is my AdjustmentInfo class.

// imports omitted

@XmlRootElement // don't forget, required by JAX-RS
public final class AdjustmentInfo implements Serializable {
    
    private long adjustmentNo;
    private String reason;
    private BigDecimal amount;
    private boolean negating;

    /**
     * Required by JAX-RS
     */
    private AdjustmentInfo() {
        this.adjustmentNo = 0;
        this.reason = null;
        this.amount = null;
        this.negating = false;
    }

    AdjustmentInfo(final Adjustment adjustment) {
        this.adjustmentNo = adjustment.getAdjustmentNo();
        this.reason = adjustment.getReason();
        this.amount = adjustment.getAmount();
        this.negating = adjustment.isNegating();
    }

    public long getAdjustmentNo() { ... }
    public String getReason() { ... }
    public BigDecimal getAmount() { ... }
    public boolean isNegating() { ... }
}

After trial-and-error, I found that apparently I had to implement all mutator method (I had not implemented it before because I didn’t have any need of them).

// imports omitted

@XmlRootElement // don't forget, required by JAX-RS
public final class AdjustmentInfo implements Serializable {
    
    private long adjustmentNo;
    private String reason;
    private BigDecimal amount;
    private boolean negating;

    /**
     * Required by JAX-RS
     */
    private AdjustmentInfo() {
        this.adjustmentNo = 0;
        this.reason = null;
        this.amount = null;
        this.negating = false;
    }

    AdjustmentInfo(final Adjustment adjustment) {
        this.adjustmentNo = adjustment.getAdjustmentNo();
        this.reason = adjustment.getReason();
        this.amount = adjustment.getAmount();
        this.negating = adjustment.isNegating();
    }

    public long getAdjustmentNo() { ... }
    public String getReason() { ... }
    public BigDecimal getAmount() { ... }
    public boolean isNegating() { ... }

    // IMPLEMENT THESE MUTATORS

    public void setAdjustmentNo(long adjustmentNo) { ... }
    public void setReason(String reason) { ... }
    public void setAmount(BigDecimal amount) { ... }
    public void setNegating(boolean negating) { ... }
}

Is it in the spec? I wonder…

Sending email on Apache TomEE

Recently I am playing with Apache TomEE, since I want to create an application with only a subset of Java EE functionality. So, TomEE as Java EE Web-profile fits well😉

This time around I am blogging about my findings on sending email with TomEE. Actually having done all the troubleshooting, it is quite simple, and hence it is going to be quite a short article.

Setup

Setting up TomEE is very simple, so I will not go into that. Just a note here I am using GMail account, so go and create your GMail account.

Here is another important part. Make sure you you turn ON 'Allow less secure apps' on Google settings here.

Configuring ‘tomee.xml’

Next, configure the file on <installation location>/conf/tomee.xml

<?xml version="1.0" encoding="UTF-8"?>
<tomee>
    <!-- see http://tomee.apache.org/containers-and-resources.html -->

    <!-- activate next line to be able to deploy applications in apps -->
    <!-- <Deployments dir="apps" /> -->
    <Resource id="tomee/mail/GMailSMTP" type="javax.mail.Session">
	mail.smtp.host=smtp.gmail.com
	mail.smtp.starttls.enable=true
	mail.smtp.port=587
	mail.transport.protocol=smtp
	mail.smtp.auth=true
	mail.smtp.user=<!-- your email address -->
	password=<!-- your password, and not 'mail.smtp.password' -->	
    </Resource>
</tomee>

Use it by injection of ‘@Resource’

Now you can use it via injection

// imports omitted

@Stateless
@LocalBean
@Path(value = "workline")
public class MailService {
    @Resource(mappedName = "java:comp/env/tomee/mail/GMailSMTP")
    private Session smtpSession;

    public boolean sendMail() throws NamingException {
        final Message message = new MimeMessage(this.smtpSession);
        try {
            message.setRecipients(Message.RecipientType.TO, new Address[]{
                new InternetAddress("someone@gmail.com")
            });
            message.setSubject("Email from TomEE");
            message.setSentDate(new Date());
            message.setText("Email from TomEE");
            Transport.send(message);
        } catch (Exception e) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, e);
            return false;
        }
        return true;
    }
}

Troubleshooting:

I had an issue whereby the @Resource injection was null, with the log warning states: WARNING: Injection: No such property .... After hours of google-ing around, apparently this was caused by conflict of the mail library which may be included in my apps, against what is provided by the underlying TomEE server. Marking the scope as provided in my pom.xml fix this (darkzone, 2014).

References

darkZone, 2014, ‘java.lang.ClassCastException: javax.mail.Session cannot be cast to javax.mail.Session’, Stackoverflow.com, accessed on 17 February 2016

virtualskynet, 2013, Code sample: tomee.xml, GitHub, accessed on 17 February 2016

Java EE integration testing with Arquillian (incl. JAX-RS, JPA, and JSR-352 Batch, Wildfly) using Chameleon, Shrinkwrap, Drone/Graphene

Continuing from my previous post here, I think I have learnt a few new (and exciting) things about Java EE and also Arquillian, and testing, and I would like to share it with you.

But before we start, I would like to first draw your attention to the following points (These are purely my opinion):

  • In anticipation with Oracle announcement stopping the commercial support of Commercial, I guess I decided to take a look at another leading Java EE application server, Wildfly, and I was so glad I did.
  • In the previous post I used embedded glassfish for testing. Whilst embedded Wildfly is also available, I personally find that testing using remote application server is so much “real”, or getting us closer to simulate the real production environment. Just as an example, if I want to test my application which is running on Wildfly 10 (which uses Java Mail, JMS), currently point to database 'xyz', all I need to do is to unzip the same version of application server used in real environment (simply rename it with -test) and point to another copy of 'xyz' database.

Anyway, let’s start.

In this post, I am going to cover the following:

  • The testing scenario (covering JAX-RS and JSR-352 Batch)
  • Very brief setup of Wildfly
  • Programming JAX-RS and JSR-352 Batch
  • Setting up Arquillian with Chameleon (incl. ShrinkWrap)
  • Setting up of Drone/Graphene

The scenario

The test is to invoke a RESTFUL web service running on remote Wildfly (which in turn will invoke a JSR-352 Batch). But, instead of testing it manually, i.e. opening a browser or using curl, we are going to code it as an Arquillian test.

So, that is quite straight-forward.

Installing Wildfly

At the time of writing, I am using Wildfly 10 CR4.

  • Just download it in wildfly.org, and unzip it.
  • First, you need to add user. so open a terminal and navigate to <unzip location>/bin/ and run ./bin/add-user.sh. Just follow the prompt.
  • set a JAVA_HOME
  • First, you need to add user. so open a terminal and navigate to <unzip location>/bin/ and run ./bin/add-user.sh. Just follow the prompt.
  • To run wildfly simply run ./bin/standalone.sh. However, please note by default Wildfly start with Java EE 7 Web Profile (JBoss 2014). I might want to use feature such as JMS, so to start Java EE 7 Full Profile, do ./bin/standalone.sh --server-config=standalone-full.xml

Develop a JSR-352 Batch

A JSR-352 Batch (referred as Batch in this article) programming model is quite extensive, but very straight-forward to understand and to use. But basically you can either following the read-process-write model, or roll-your-own batchlet model (Gupta 2013). But for more details, you can refer to the Oracle tutorial here. (Kannan 2013).

In this example, I am going to walk you through a very simple read-process-write Batch.

– Write a job XML

  • Firstly, create an empty beans.xml to enable CDI.
  • Secondly, you need to create a folder batch-jobs under the META-INF. If you use maven, then this goes under resources.
  • Under the newly created forder, add a Job XML. Please note that by convention, the name of the batch job is nothing but the job JSL XML file name, minus the .xml extension (Kannan 2013). So, let’s say we call it testJob.xml.
  • The testJob.xml Comments interleaved.

    <?xml version="1.0" encoding="UTF-8"?>
    <job id="testJob" 
         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/jobXML_1_0.xsd"
         version="1.0">
        <step id="testStep">
            
            <!--
                Basically the way it works is this.
                
                A reader is executed to read an item, and then passed to the
                processor.
                
                Depending on the checkpoint-policy, a collection of processed
                items are then passed to the writer, to be written. And then
                transaction commit.
                
                Please note in this case since the checkpoint-policy is set to
                'item' and item-count="1", it means that each item processed is
                to be written and transactioni commit.
            -->
            <chunk checkpoint-policy="item" item-count="1">
                <reader ref="testBatchReader" />
                <processor ref="testBatchProcessor" />
                <writer ref="testBatchWriter" />
            </chunk>
        </step>
    </job>
    

– Write the Batch Reader, Processor and Writer

  • The TestBatchReader.java. Batch will stop processing when reader returns null. This is a very simple example that simply return an Integer up to 100. But it can be much sophisticated than this, such as reading a file, etc.

    package id.co.lucyana.hr.batch;
    
    import javax.batch.api.chunk.AbstractItemReader;
    import javax.inject.Named;
    
    @Named
    public final class TestBatchReader extends AbstractItemReader {
    
        private int counter = 0;
        private static final int MAX_COUNTER = 100;
    
        @Override
        public Object readItem() throws Exception {
            while (this.counter < MAX_COUNTER) {
                ++this.counter;
                return this.counter;
            }
            return null;
        }
    }
    
  • The TestBatchProcessor.java. Do nothing

    package id.co.lucyana.hr.batch;
    
    import javax.batch.api.chunk.ItemProcessor;
    import javax.inject.Named;
    
    @Named
    public final class TestBatchProcessor implements ItemProcessor {
    
        @Override
        public Object processItem(Object item) throws Exception {
            return item; // do nothing here
        }
    }
    
  • The TestBatchWriter.java. Again, this one can be much sophisticated, such as writing to the underlying datastore, etc.

    package id.co.lucyana.hr.batch;
    
    import java.util.List;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import javax.batch.api.chunk.AbstractItemWriter;
    import javax.inject.Named;
    
    @Named
    public final class TestBatchWriter extends AbstractItemWriter {
    
        @Override
        public void writeItems(List items) throws Exception {
            Logger.getLogger(TestBatchWriter.class.getName()).log(Level.INFO, items.toString());
        }
    }
    

That’s is for your Batch programming. Now let’s start with JAX-RS to trigger Batch to run.

Develop an Restful web service

JAX-RS has been awhile, so I shall not bore you with the details. But basically all we want to achieve is to be able to invoke a Restful webservice, with a job name as a parameter to trigger a batch process.

This is the ApplicationPath

package id.co.lucyana.hr.util;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath(value = "resources")
public class ApplicationConfig extends Application {
}

And, this is the JAX-RS end point, receiving a job name as a parameter

package id.co.lucyana.hr.batch;

import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.batch.operations.JobSecurityException;
import javax.batch.operations.JobStartException;
import javax.batch.runtime.BatchRuntime;
import javax.ejb.LocalBean;
import javax.ejb.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;

@Singleton
@LocalBean
@Path(value = "batch")
public class BatchManager {

    @Path(value = "start")
    @GET
    @Produces(value = "application/json")
    public long start(@QueryParam("job") String job) {
        Logger.getLogger(BatchManager.class.getName()).log(Level.INFO, 
                BatchRuntime.getJobOperator().getJobNames().toString());
        try {
            return BatchRuntime.getJobOperator().start(job, new Properties());
        } catch (JobStartException | JobSecurityException e) {
            Logger.getLogger(BatchManager.class.getName()).log(
                    Level.SEVERE, e.getMessage(), e);
            return -1l;
        }
    }
}

Okay, now take a deep breath… and LET’S TEST!!!

Set up Arquillian, Chameleon, Graphene, Drone, Selenium

Now after all of those development, it is time to test what we have written, and we are going to test it against the real running application server.

The first thing we need to do is to set up Arquillian/Chameleon

– Adding Arquillian/Chameleon

If you look at my previous post, referring to the pom.xml there, you may notice that there are a lot of information regarding to the container used scattered there, i.e. org.jboss.arquillian.container, etc. Chameleon hides this information and makes it so easy to moves between containers (Knutsen 2015).

So the changes on my pom.xml would be as follows.

<?xml version="1.0" encoding="UTF-8"?>
<!-- omitted -->

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.jboss.arquillian</groupId>
                <artifactId>arquillian-bom</artifactId>
                <version>1.1.8.Final</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.jboss.arquillian</groupId>
            <artifactId>arquillian-bom</artifactId>
            <version>1.1.9.Final</version>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.jboss.arquillian.junit</groupId>
            <artifactId>arquillian-junit-container</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.arquillian.container</groupId>
            <artifactId>arquillian-container-chameleon</artifactId>
            <version>1.0.0.Alpha5</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

<!-- omitted -->

And configure your arquillian.xml (which is located under your test/resources if you are using Maven) as follows:

<?xml version="1.0" encoding="UTF-8"?>
<arquillian xmlns="http://jboss.org/schema/arquillian"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
    <defaultProtocol type="Servlet 3.0" />

    <container qualifier="wildfly" default="true">
        <configuration>
            <property name="chameleonTarget">wildfly:10.0.0.CR4:remote</property>
            <property name="username"><!-- your username goes here --></property>
            <property name="password"><!-- your password goes here --></property>
        </configuration>
    </container>
</arquillian>

– Adding Graphene/Drone/Selenium

Now, remember what we are trying to emulate is as if the user enter a URL to invoke JAX-RS web service, passing a job name to start a Batch job. BUT, we want to code it as a JUnit test. In order to do that, we are going to use the combination of Graphene, Drone and Selenium. You can read more details about them here. But basically these technologies are part of the Arquillian test platform catered for web UI testing.

First we need to modify our pom.xml and add the following:

<!-- omitted -->
    <dependencyManagement>
        <dependencies>
            <!-- omitted -->
            <dependency>
                <groupId>org.jboss.arquillian.selenium</groupId>
                <artifactId>selenium-bom</artifactId>
                <version>2.43.1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>       
        <!-- Omitted -->
        <dependency>
            <groupId>org.jboss.arquillian.graphene</groupId>
            <artifactId>graphene-webdriver</artifactId>
            <version>2.0.3.Final</version>
            <type>pom</type>
            <scope>test</scope>
        </dependency>
<!-- omitted -->

And add snippet in our arquillian.xml.

<?xml version="1.0" encoding="UTF-8"?>
    <!-- omitted -->
    <extension qualifier="webdriver">
       <!--<property name="browser">firefox</property>-->
       <property name="remoteReusable">false</property>
   </extension>
</arquillian>

N.B. I could not get ‘firefox’ to work. So by default it would be the ‘htmlUnit’.

All good, the last part is to write the actual Arquillian test.

Write an Arquillian test case

First things first I need to share. And that is to package the application as an EAR. I have tried to deploy it as a (EJB) JAR, JAX-RS does not work, and then simply as WAR, Batch does not work. So, for the purpose of this testing, I deployed as EAR, and all seems to be happy (Nozaki 2015).

So, let’s code our Arquillian test. Comments interleaved.

package id.co.lucyana.hr.batch;

import id.co.lucyana.hr.util.ApplicationConfig;
import java.net.URL;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.RunAsClient;
import org.jboss.arquillian.drone.api.annotation.Drone;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ArchivePaths;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.EnterpriseArchive;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openqa.selenium.WebDriver;

@RunWith(Arquillian.class)
public class BatchManagerTest {

    @Drone
    private WebDriver driver;

    @Deployment
    public static Archive<?> createTestArchive() {
        // just add classes required in your test
        final JavaArchive ejbJar = ShrinkWrap.create(JavaArchive.class, "ejb-jar.jar")
                .addClass(ApplicationConfig.class)
                .addClass(BatchManager.class)
                .addClass(TestBatchReader.class)
                .addClass(TestBatchProcessor.class)
                .addClass(TestBatchWriter.class)
                .addAsManifestResource("test-persistence.xml",
                        ArchivePaths.create("persistence.xml"))
                .addAsManifestResource("META-INF/beans.xml", 
                        ArchivePaths.create("beans.xml"))
                .addAsManifestResource("batch-jobs/testJob.xml")
                .addAsResource("ValidationMessages.properties");

        /*
         * Embedding war package which contains the test class is needed
         * So that Arquillian can invoke test class through its servlet
         * test runner
         */
        final WebArchive testWar = ShrinkWrap.create(
                WebArchive.class, "test.war").addClass(BatchManagerTest.class);
        
        final EnterpriseArchive ear = ShrinkWrap.create(EnterpriseArchive.class)
                .setApplicationXML("test-application.xml")
                .addAsModule(ejbJar)
                .addAsModule(testWar);
        return ear;
    }

    @Before
    public void beforeEachTest() {
        this.driver.manage().deleteAllCookies();
    }
    
    /*
     * So that we do not hard-code the URL, simply use @ArquillianResource
     */
    @Test
    @RunAsClient
    public void testInvoke(@ArquillianResource URL url) {
        this.driver.get(url.toString() + "resources/batch/start?job=testJob");
        String pageSource = this.driver.getPageSource();
        System.out.println(pageSource);
        Assert.assertTrue(true);
    }
}

Please note the use of @ArquillianResource which allows us not to hard-code the application URL (Knutsen 2012).

Result

Yey!!! So, that’s it. Make sure you have your Wildfly up and running, you can now run your test. Shrinkwrap will package your EAR, then Arquillian/Chameleon will deploy it remotely to the running Wildfly server, then as client, Drone/Graphene/Selenium will invoke the URL to run a Batch called testJob.

Here is the snippet of the rest result:

18:07:00,585 INFO  [org.jboss.as.server] (management-handler-thread - 2) WFLYSRV0010: Deployed "a3ee3dad-d71c-41b0-9f57-9ae6e7ffe859.ear" (runtime-name : "a3ee3dad-d71c-41b0-9f57-9ae6e7ffe859.ear")
18:07:06,137 INFO  [id.co.lucyana.hr.batch.BatchManager] (default task-1) []
18:07:06,332 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [1]
18:07:06,333 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [2]
18:07:06,333 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [3]
18:07:06,334 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [4]
18:07:06,334 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [5]
18:07:06,334 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [6]
18:07:06,334 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [7]
18:07:06,335 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [8]
18:07:06,335 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [9]
18:07:06,335 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [10]
18:07:06,335 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [11]
18:07:06,336 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [12]
18:07:06,336 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [13]
18:07:06,336 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [14]
18:07:06,336 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [15]
18:07:06,337 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [16]
18:07:06,337 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [17]
18:07:06,337 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [18]
18:07:06,337 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [19]
18:07:06,338 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [20]
18:07:06,338 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [21]
18:07:06,338 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [22]
18:07:06,338 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [23]
18:07:06,339 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [24]
18:07:06,339 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [25]
18:07:06,339 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [26]
18:07:06,339 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [27]
18:07:06,340 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [28]
18:07:06,340 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [29]
18:07:06,340 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [30]
18:07:06,340 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [31]
18:07:06,341 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [32]
18:07:06,341 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [33]
18:07:06,341 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [34]
18:07:06,341 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [35]
18:07:06,341 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [36]
18:07:06,342 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [37]
18:07:06,342 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [38]
18:07:06,342 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [39]
18:07:06,343 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [40]
18:07:06,343 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [41]
18:07:06,343 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [42]
18:07:06,344 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [43]
18:07:06,344 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [44]
18:07:06,344 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [45]
18:07:06,345 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [46]
18:07:06,345 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [47]
18:07:06,345 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [48]
18:07:06,345 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [49]
18:07:06,346 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [50]
18:07:06,346 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [51]
18:07:06,346 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [52]
18:07:06,347 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [53]
18:07:06,347 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [54]
18:07:06,347 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [55]
18:07:06,347 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [56]
18:07:06,348 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [57]
18:07:06,348 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [58]
18:07:06,348 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [59]
18:07:06,348 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [60]
18:07:06,349 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [61]
18:07:06,349 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [62]
18:07:06,349 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [63]
18:07:06,349 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [64]
18:07:06,349 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [65]
18:07:06,350 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [66]
18:07:06,350 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [67]
18:07:06,350 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [68]
18:07:06,350 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [69]
18:07:06,350 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [70]
18:07:06,351 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [71]
18:07:06,351 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [72]
18:07:06,351 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [73]
18:07:06,351 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [74]
18:07:06,352 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [75]
18:07:06,352 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [76]
18:07:06,352 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [77]
18:07:06,352 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [78]
18:07:06,352 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [79]
18:07:06,352 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [80]
18:07:06,353 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [81]
18:07:06,353 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [82]
18:07:06,353 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [83]
18:07:06,353 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [84]
18:07:06,353 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [85]
18:07:06,354 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [86]
18:07:06,354 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [87]
18:07:06,354 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [88]
18:07:06,354 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [89]
18:07:06,354 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [90]
18:07:06,355 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [91]
18:07:06,355 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [92]
18:07:06,355 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [93]
18:07:06,355 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [94]
18:07:06,356 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [95]
18:07:06,356 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [96]
18:07:06,356 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [97]
18:07:06,357 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [98]
18:07:06,357 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [99]
18:07:06,357 INFO  [id.co.lucyana.hr.batch.TestBatchWriter] (Batch Thread - 1) [100]
18:07:06,885 INFO  [org.wildfly.extension.undertow] (ServerService Thread Pool -- 72) WFLYUT0022: Unregistered web context: /test

References

Allen et al., 2014, ‘Functional Testing using Drone and Graphene’, Red Hat Inc., accessed on 23 November 2015

Gupta, A, 2013, ‘Batch Applications in Java EE 7 – Undertanding JSR 352 Concepts: TOTD #192’, Oracle.com, accessed on 10 November 2015

JBoss, 2014, ‘Getting Started Guide – Wildfly 8’, JBoss.org, accessed on 10 November 2015

Kannan, M, 2013, ‘An Overview of Batch Processing in Java EE 7.0’, Oracle.com, accessed on 10 November 2015

Knutsen, A, 2012, ‘@ArquillianResource java.net.URL when test is run on the server’, JBoss Developer, accessed on 23 November 2015

Knutsen, A, 2015, ‘Arquillian Blog: Arquillian Container Chameleon 1.0.0.Alpha6 Released’, Red Hat Inc., accessed on 23 November 2015

Nozaki, K, 2015, ‘Arquillian EJB-JAR/EAR testing examples’, Kohei Nozaki’s blog, accessed on 23 November 2015

Oracle, 2013, ‘Java EE and GlassFish Server Roadmap Update’, Oracle.com, accessed on 10 November 2015

mvn release:prepare and release:perform with BitBucket

Whilst many people might find this is easy, I think I have google zillions of page just to get a simple mvn release:prepare and mvn release:perform with BitBucket.

I thought I am going to write a blog here just to help out those you might need a dummy-guide to get maven prepare and perform working.

Please note that I am using Linux, and also running the command with Netbeans.

The public Key

  • Firstly, do a sudo apt-get install git
  • The next thing we need to do is to identify WHO you are, and that is done by generating public key, and then install in on BitBucket.
  • Open another terminal
  • Generate with command ssh-keygen
  • Enter, enter passphrase etc, etc, just follow it. Until the key is generated.
  • You will see similar image like this one — I just took Atlassian sample (Atlassian, 2015a):
    $ ssh-keygen 
    Generating public/private rsa key pair.
    Enter file in which to save the key (/Users/emmap1/.ssh/id_rsa):
    Created directory '/Users/emmap1/.ssh'.
    Enter passphrase (empty for no passphrase):
    Enter same passphrase again:
    Your identification has been saved in /Users/emmap1/.ssh/id_rsa.
    Your public key has been saved in /Users/emmap1/.ssh/id_rsa.pub.
    The key fingerprint is:
    4c:80:61:2c:00:3f:9d:dc:08:41:2e:c0:cf:b9:17:69 emmap1@myhost.local
    The key's randomart image is:
    +--[ RSA 2048]----+
    |*o+ooo.          |
    |.+.=o+ .         |
    |. *.* o .        |
    | . = E o         |
    |    o . S        |
    |   . .           |
    |     .           |
    |                 |
    |                 |
    +-----------------+
    

     

  • You then need to access your BitBucket, go to avatar > Manage account and then click on SSH Keys. Again I took this example from Atlassian article (Atlassian, 2015b)
    add_ssh_key

The ‘release’ branch

When we perform a release, of course we need to create a branch where the artifacts being uploaded. So, go and login to your BitBucket and create a branch and call it 'releases'.

Modify pom.xml

What needs to be done next is to modify our pom.xml to point to the correct BitBucket account. Bear in mind that our git can now identify WHO we are since we have do the 'public key work in the previous section.

In order to deploy artifacts on remote Git SCM repository, we need to use a plugin called wagon-git (Synergian, 2015).

The website provided in Synergian is very detail. Feel free to follow it. But I am going to show you snippet of my pom.xml anyway. comments interleaved.

<?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>id.co.lucyana.hr.email</groupId>
    <artifactId>lucyhr-api</artifactId>
    <version>1.5-SNAPSHOT</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <!-- FIRSTLY, add this. Please refer to Synergian documentation -->
    <pluginRepositories>
        <pluginRepository>
            <id>synergian-repo</id>
            <url>https://raw.github.com/synergian/wagon-git/releases</url>
        </pluginRepository>
    </pluginRepositories>

    <!--
        SECONDLY, you need to add your repository location. Just replace the one below
        particularly on 'dwuysan/lucyhr-api'.
    -->
    <repositories>
        <repository>
            <id>lucyhr-api</id>          
            <releases>
                <enabled>true</enabled>
            </releases>
            <url>https://api.bitbucket.org/1.0/repositories/dwuysan/lucyhr-api/raw/releases</url>  
        </repository>
    </repositories>

    <!--
        distributionManagement, it needs to point to the 'releases' branch we created. Pay attention to
        'git:releases://...'
    -->
    <distributionManagement>
        <repository>
            <id>lucyhr-api</id>
            <name>lucyhr-api</name>
            <url>git:releases://git@bitbucket.org:dwuysan/lucyhr-api.git</url>
        </repository>
    </distributionManagement>

    <!-- Again, make the changes accordingly to your own path -->
    <scm>
        <connection>scm:git:ssh://git@bitbucket.org/dwuysan/lucyhr-api.git</connection>
        <developerConnection>scm:git:ssh://git@bitbucket.org/dwuysan/lucyhr-api.git</developerConnection>
        <url>https://bitbucket.org/dwuysan/lucyhr-api.git</url>
        <tag>HEAD</tag>
    </scm>    
    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>

        <!-- 
            So that we can perform mvn:release prepare and mvn:release perform, we need to add
            maven-release-plugin. That one is quite basic.

            BUT

            remember to add wagon-git
        -->
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-release-plugin</artifactId>
                <version>2.5.3</version>
            </plugin>
        </plugins>        
        <extensions>
            <extension>
                <groupId>ar.com.synergian</groupId>
                <artifactId>wagon-git</artifactId>
                <version>0.2.5</version>
            </extension>
        </extensions>
    </build>
</project>

That’s it! You can do now mvn:release prepare and mvn:release perform, with your released artifacts uploaded to BitBucket.

References

Atlassian, 2015a, ‘Set up SSH for Git’, Atlassian, accessed on 27 October 2015

Atlassian, 2015b, ‘Add an SSH key to an account’, Atlassian, accessed on 27 October 2015

Synergian, 2015, ‘wagon-git’, Synergian, accessed on 27 October 2015

Copy a MySQL Schema

I don’t know much about database and SQL, since I am not a database administrator. This post is probably way to easy for you. Nevertheless, I thought I’ll share the findings from my googling around.

What I am trying to do is simple. I have a schema, and I want to copy it (for backing up, for integration testing, etc etc).

Let us just say the schema name is dwuysan_prod and I would like to create an integration schema dwuysan_integration.

Here is the step-by-step todo:

  • Open a terminal, we will be using command mysql
  • Firstly, connect to mysql

    mysql -u root -p;
    
  • Create database named dwuysan_integration

    CREATE DATABASE dwuysan_integration;
    
  • Grant all privileges

    GRANT ALL PRIVILEGES ON dwuysan_integration.* TO dwuysan_prod@"%";
    
  • Exit the terminal, and let’s use mysqldump

    mysqldump --add-drop-table --complete-insert -uroot -p<your password> dwuysan_prod | mysql -uroot -p<your password> dwuysan_integration;
    

References

Ottuzzi, P 2009, ‘How to Clone a Schema in MySQL’, Brucalipto.org, accessed on 22 October 2015

Using JSF 2.2 features to develop ajax, scrollable, lazy-loading data table

This time around, I thought I’ll share with you what I recently learnt of JSF 2.2 features. To do that, I decided to create a simple ajax, scrollable, lazy-loading data table.

Note that in NO WAY this is comparable to major libraries such as Primefaces, Richfaces or ICEFaces. This is only to show you what I have learnt. (Of course, no one can stop you from using it if you so wish to).

The Concept

Allow me to first tell you the concept, or the scenario if you like to call it so. It is actually very simple.

We have a list of Customer entity, and basically we are going to implement a data table, which is scrollable, lazy-loads the list and makes use of ajax.

Note: I assume you are comfortable with JPA and EJB, so while I might mention them here and there, I shall not discuss them in this post.

Using template

Just like any JSF application, I also started by using template. It is very easy to use template with Netbeans. Just select the option Facelets Template and choose your layout.

Notice that Netbeans also generate the two stylesheets, cssLayout.css and default.css. Also notice that in the newly generated template, they are located using the file location, as follows:

<h:head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link href="./../resources/css/default.css" rel="stylesheet" type="text/css" />
    <link href="./../resources/css/cssLayout.css" rel="stylesheet" type="text/css" />
    <title>Facelets Template</title>
</h:head>

I made a number of modifications to the default template.

Firstly, I encountered an issue when I started the application server, that the stylesheets were not loaded properly. So, to fix that, I use JSF Resource Locator (Lubke 2008).

I also create each of the sub pages, i.e. header, menu, and footer. They act as the default if none is specified.

So, the collection of my pages are as follows:

template.xhtml

<?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:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <h:outputStylesheet library="css" name="default.css" />
        <h:outputStylesheet library="css" name="cssLayout.css" />
        <title>Facelets Template</title>
    </h:head>
    <h:body>
        <div id="top">
            <ui:insert name="header">
                <ui:include src="header.xhtml" />
            </ui:insert>
        </div>
        <div>
            <div id="left">
                <ui:insert name="menu">
                    <ui:include src="menu.xhtml" />
                </ui:insert>
            </div>
            <div id="content" class="left_content">
                <ui:insert name="content">
                    <!-- empty content goes here -->
                </ui:insert>
            </div>
        </div>
        <div id="bottom">
            <ui:insert name="bottom">                
                <ui:include src="footer.xhtml" />
            </ui:insert>
        </div>
    </h:body>
</html>

Notice that instead of using the file locator ./../resources/css/default.css, i use h:outputStylesheet. With this approach, just put all your resources in the resources directory and point it by referring to the folder containing your resource via the attribute library. So, for example <h:outputStylesheet library="css" name="default.css" /> is really looking at the file at the location resources/css/default.css.

header.xhtml, footer.xhtml, menu.xhtml

<?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:ui="http://java.sun.com/jsf/facelets">
    <body>
        <ui:composition>
            <!--
            I created each file for header, footer and menu, but I simply list
            them here just to show you
            -->
            <h1>Default Header</h1>
            <!-- h1>Default Menu</h1 -->
            <!-- h1>Default Footer</h1 -->
        </ui:composition>	
    </body>
</html>

Next, is using the template. So, let’s create a file called customers.xhtml to use it.

customers.xhtml

<?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:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html">
    <body>
        <ui:composition template="./common/template.xhtml">
            <ui:define name="content">
                <h:outputText value="List of customers goes here!!!" />
            </ui:define>
        </ui:composition>
    </body>
</html>

You can then run the application server and accessed the page via http://<the path to your application>/customers.jsf. The output should look like the following illustration.

template

Add the facility to create a customer

Firstly, in a template, let us add a facility to add customers in the customers.xhtml. We can simply use JSF ManagedBean to do this. However, Core Javaserver Faces, 3rd recommends:

It is a historical accident that there are two separate mechanisms, CDI beans and JSF managed beans, for beans that can be used in JSF pages. We suggest that you use CDI beans unless your application must work on a plain servlet runner such as Tomcat.

(Geary, Horstmann 2010)

Going along with the suggestion, I will use CDI beans from now on.

In real application, I am sure there are a lot more data to capture. In this example, however, let us only capture the customer’s name.

Basically we need a Model and a Controller. So, let us first create these classes.

CustomerData.java

package id.co.dwuysan.customer;

import javax.enterprise.inject.Model;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Model
public class CustomerData {
    
    @NotNull
    @Size(min = 1, max = 50)
    private String name;

    public String getName() {
        return name;
    }

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

CustomerData.java

package id.co.dwuysan.customer;

import id.co.dwuysan.service.CustomerService;
import javax.ejb.EJBException;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.inject.Inject;
import javax.inject.Named;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

@Named
public class CustomerRegistationAction {

    @Inject
    private CustomerData customer;
    
    @Inject
    private CustomerService customerService;
    
    public void register(ActionEvent event) {
        try {
            // calls EJB and do registration here
        } catch (EJBException e) {
            FacesContext.getCurrentInstance().addMessage(event.getComponent().getClientId(), new FacesMessage(e.getMessage()));
            throw new AbortProcessingException(e.getMessage());
        }
        this.customer.clear();
    }
}

Notice that in this case we use @Named annotation. Since we did not give it any scope annotation, it defaults to @RequestScoped (BalusC 2011a).

The next step is to modify our customers.xhtml to make use of these classes so that we can register customers.

customers.xhtml

<?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:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ez="http://java.sun.com/jsf/composite/ezcomp">
    <body>
        <ui:composition template="./common/template.xhtml">
            <ui:define name="content">
                <f:view transient="true">
                    <h:form>
                        <h:panelGrid columns="3">
                            <h:outputLabel for="txtCustomerName" value="Customer name" />
                            <h:inputText id="txtCustomerName" value="#{customerData.name}" />
                            <h:message for="txtCustomerName" styleClass="error" />
                            <div />
                            <h:commandButton id="cmdRegisterCustomer" value="Submit" action="customers" actionListener="#{customerRegistationAction.register}" />
                            <h:message for="cmdRegisterCustomer" styleClass="error" />
                        </h:panelGrid>
                    </h:form>
                </f:view>
            </ui:define>
        </ui:composition>
    </body>
</html>

Okay, once that’s done, you can register the customer. The screen should look like this.

createCust

Define the ‘Contract’

This part is the real deal. What I refer to the ‘contract’ is not the actual contract per-se. Rather I am referring to how we start by designing the Facelet xhtml first, which will drive the actual backing bean class.

So let’s first outline out requirement:

  • The records should be paginated in a table, lazy-loaded from the database
  • The user should be able to select how many records to be displayed in a page
  • The user should be able to see what the current page is, total pages and records
  • The user should be able to scroll through records, next and previous
  • Use AJAX

In an application, there bound to be a lot of listing. Therefore, the best way to move forward is to create a component which we can reuse easily. Since the component itself is comprised of a number of other components, I believe this feature is officially referred as composite component. In the era of JSF 1.x, this was quite painful. A developer needs to write a lot of classes/files. With JSF 2.x, this is very easy (and quite elegant, I must say).

Let us start by creating the component.

Creating composite component

I am going to name this component as paginator.

I am sure you can find a lot of explanation on how to do this, so I shall skip the ‘explanation’ part and simply show you what I did.

I created a file paginator.xhtml under the directory resources/ezcomp. Hence, I refer to this component later on with namespace xmlns:ez="http://java.sun.com/jsf/composite/ezcomp.

paginatorFile

Let us move on to the actual implementation of the paginator.xhtml. Please refer to the following source, and then I will take you through step-by-step.

paginator.xhtml

<?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:cc="http://java.sun.com/jsf/composite"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">

    <!-- INTERFACE -->
    <cc:interface>
        <cc:attribute name="paginateModel" /> <!-- 01 -->
    </cc:interface>

    <!-- IMPLEMENTATION -->
    <cc:implementation>
        <!-- 02 -->
        <h:inputHidden value="#{cc.attrs.paginateModel.sortField}" />
        <h:inputHidden value="#{cc.attrs.paginateModel.ascending}" />
        <h:inputHidden value="#{cc.attrs.paginateModel.page}" />

        <!-- 03 -->
        <h:panelGrid columns="2" cellpadding="5" cellspacing="5">
            <h:outputLabel value="Rows-per-page" />
            <h:selectOneRadio value="#{cc.attrs.paginateModel.rowsPerPage}">
                <f:selectItem itemValue="5" itemLabel="5" />
                <f:selectItem itemValue="10" itemLabel="10" />
                <f:selectItem itemValue="20" itemLabel="20" />
                <f:selectItem itemValue="100" itemLabel="100" />
                <f:ajax execute="@form" render="@form" listener="#{cc.attrs.paginateModel.updateRowsPerPage}" />
            </h:selectOneRadio>
        </h:panelGrid>

        <!-- pagination -->
        <h:panelGrid columns="3" cellpadding="5" cellspacing="5">
            <!-- 04 -->
            <h:commandLink value="&lt;&lt;" actionListener="#{cc.attrs.paginateModel.navigatePage(false)}" style="display: #{cc.attrs.paginateModel.page gt 1 ? 'block' : 'none'}">
                <f:ajax execute="@form" render="@form" />
            </h:commandLink>
            <!-- 05 -->
            <h:outputLabel value="#{cc.attrs.paginateModel.page} &frasl; #{cc.attrs.paginateModel.totalPages} " />
            <!-- 06 -->
            <h:commandLink value="&gt;&gt;" actionListener="#{cc.attrs.paginateModel.navigatePage(true)}" style="display: #{cc.attrs.paginateModel.page lt cc.attrs.paginateModel.totalPages ? 'block' : 'none'}">
                <f:ajax execute="@form" render="@form" />
            </h:commandLink>
        </h:panelGrid>
        <cc:insertChildren /><!-- 07 -->
        <br />
        <!-- 08 -->
        <h:outputFormat value="There are {0} record(s).">
            <f:param value="#{cc.attrs.paginateModel.recordCount}" />
        </h:outputFormat>
    </cc:implementation>
</html>

The following section explains each numbered point I have made in the customers.xhtml.

01. cc:attribute and paginatedModel

This part is the 'interface' between the implementation in this xhtml as well as the client/user of this component later. To put it in the plain words, think of it as a param that must be supplied when using this component.

02. Hidden parameters

Now this part has a lot of explanation.

Firstly, I need to fast-forward a little bit to tell you that I am using RequestScoped bean to backed the record listing. Another important thing to notice that I actually use the <f:view transient="true">, which is the feature available in JSF 2.2 and therefore making this page stateless. If you open-source in your browser, the state is now:

<input type="hidden" name="javax.faces.ViewState" id="j_id1:javax.faces.ViewState:0" value="stateless" autocomplete="off" />

Because this page is stateless, you need to manage your own state, between invocations (Riem 2013). Hence the use of hidden parameters. So imagine, suppose you were displaying page 4 of the 100 pages, and the user decided to navigate to the next page, since it is stateless, you would need to know what page is now, whether the current display is being listed ascending or descending and by what it is currently sorted.

03. Rows-per-page selection

This part is quite self-explanatory, except one part. Basically, we are listing the choice for the user to select on how many records to be displayed per page. When the user selects though, we need to re-execute the query and update the page, using AJAX. Hence, the use of this section:

<f:ajax execute="@form" render="@form" listener="#{cc.attrs.paginateModel.updateRowsPerPage}" />

What this section is saying is basically on user selection, execute the method updateRowsPerPage and update this form. The 'execute' also implies that we are taking into account all of the params in this form, as mentioned in section 02.

04/06. Navigate ‘back’ and ‘forward’

I will explain part 04 and 06 together in this section because they are quite similar.

Let us start with part 04. This will basically render a link to allow the user to navigate 'Back'. I simply play around with the display attribute to conditional render only if the current page is not the first page (otherwise, it would not be possible to navigate back on the first page, would it?). Part 06 is quite similar, with condition to only render if the current page is not the last one.

The next part I would like to explain is the actionListener on clicking that page. Basically we know at the moment what the current page is because of h:inputHidden (See section 02). So, with the two command links in part 04 and 06, we need to flag what kind of navigation is that. For that I simply use boolean value.

Last, because we want to do the navigation using AJAX, we also need to use the <f:ajax>. Also, the same reason as previous sections regarding the execute and render on @form.

05 Current page and total pages

This part is simply rendering what the current page is and the total pages available. Quite self-explanatory

06 See section 04

See section 04

07 insertChildren

Whilst we can re-use the paginator, I thought it would be very difficult to create a component that would auto-render your data model into a table. At the very least, the page designer would need to determine what columns to render (I am sure there must be a magical way to auto-render everything in a tabular manner. Maybe Oracle ADF Faces does that for you. In any case, I couldn’t be bother finding out about it).

When using this tag, basically later on when we use <ezcomp:paginator>...</ezcomp:paginator>, whatever we put in between will be rendered on part 07. Think about it a little bit like <ui:decorate>.

08 Displaying the total records

For this section we simply display the total records. Quite self-explanatory.

Using the composite component

Now we basically need to use this composite component. So we need to modify our customers.xhtml as follows:

customers.xhtml

<?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:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ez="http://java.sun.com/jsf/composite/ezcomp"> <!-- 01 -->
    <body>
        <ui:composition template="./common/template.xhtml">
            <ui:define name="content">
                <f:view transient="true">
                    <h:form id="frmCreateCustomer">
                        <h:panelGrid columns="3">
                            <h:outputLabel for="txtCustomerName" value="Customer name" />
                            <h:inputText id="txtCustomerName" value="#{customerData.name}" />
                            <h:message for="txtCustomerName" styleClass="error" />
                            <div />
                            <h:commandButton id="cmdRegisterCustomer" value="Submit" action="customers" actionListener="#{customerRegistationAction.register}">
                                <f:actionListener binding="#{listCustomers.sortByDefault}" />
                                <f:ajax execute="@form" render="@form :frmListCustomers" /> <!-- 03 -->
                            </h:commandButton>
                            <h:message for="cmdRegisterCustomer" styleClass="error" />
                        </h:panelGrid>
                    </h:form>
                    <br />
                    <h:form id="frmListCustomers"> <!-- 02 -->
                        <ez:paginator paginateModel="#{listCustomers}"> <!-- 04 -->
                            <h:dataTable value="#{listCustomers.data}" var="cust"> <!-- 05 -->
                                <h:column>
                                    <f:facet name="header">
                                        <h:commandLink value="Name" actionListener="#{listCustomers.sort('name')}">
                                            <f:ajax execute="@form" render="@form" /> <!-- 06-->
                                        </h:commandLink>
                                    </f:facet>
                                    <h:outputText value="#{cust.name}" />
                                </h:column>
                            </h:dataTable>
                        </ez:paginator>
                    </h:form>
                </f:view>
            </ui:define>
        </ui:composition>
    </body>
</html>

Again, I shall walk you through one-by-one:

01 The namespace

As mentioned previously, since we put our paginator on resources/ezcomp/paginator.xhtml, JSF allows us to refer to it by using the namespace http://java.sun.com/jsf/composite/ezcomp. So later on (as shown in 04), you can use the newly created paginator by using ezcomp:paginator.

02 The list table in its own form

Let us jump a little bit further to part 02. Part 02 shows that in this customers.xhtml, we use two forms. Mainly because I want to the interaction you are doing in with the table, you don’t want it affects or be affected by another form, which in this case the Create Customer section.

Let us say, suppose we only use one form for the entire customers.xhtml. On clicking the button to sort (part 06), the validation on the create text box may be trigger, e.g. the customer name cannot be empty. We don’t want that.

We also need to give this form a name, for reason outlined in section 03.

03 Re-render another form

The intention here is to be able to refresh the table whenever one adds a new customer. Notice that the naming container (in relation to section 02) is the second h:form. Consequently, because the Create customer and the table are residing on different naming container, let us just refresh the entire second form, and this is done by using the correct ‘Search Expression’. By using ‘:’ at the start, it means that JSF will search the tree from the root of the document (BalusC, 2011b).

04 Using our newly created composite component

This is simply the way to use our newly created paginator component, i.e. by passing the appropriate param into it (later on this).

See also the paginator.xhtml and its explanation for more details.

05 The actual data table

This is the actual data table. As I said previously, whilst we can make a generic paginator, I thought it would be very difficult to make a generic table rendering. So, in this case, I simply want to show you the case where we determine what column to render.

One thing to note though that we called the listCustomers.data to supply the javax.faces.DataModel for our data table. This ‘data’ should be the correct data, based on the page, the size, the sorting, etc. If you still don’t understand, please do not worry. Next section, it will become clearer.

06 The sorting

As per requirements above, we need to implement the sorting. This one is quite self-explanatory, and the use of f:ajax is of the same reasons as previously mentioned.

Create the class to support pagination

Okay, we have created the ‘contract/requirement’, with the paginator.xhtml and then use it on customers.xhtml. What we need now, is to implement a class that support the functionality.

What I am going to do, is to go through customers.xhtml and paginator.xhtml. You can also do that, so that you can easily understand each of the points listed below:

  • Firstly, looking at customers.xhtml the class is called ‘listCustomers’. Since we are using CDI, we need to use @Named to expose the bean to JSF environment. We can create a ListCustomers.java, or populate the value in the @Named annotation.

  • In the past, I would use @ViewScoped for this bean. However, since we use the JSF ‘stateless’ feature with f:view transient="true", we can no longer use @ViewScoped. These kinds of beans are tied to views being managed by the JSF framework. But with this stateless feature, the view is always recreated, and so are the beans on this scope (Busscher 2013). So, the best scope to use is @RequestScoped.

  • It needs to have getData() which return javax.faces.DataModel<T>. This should return the list of Customer to be displayed.

  • It needs to support public void sort(final String sortField) to support header sorting.

  • Accessor for String getSortField(), boolean isAscending(), int getPage(), AND their corresponding mutators/setters. These are required, related to our hidden inputs.

  • Accessor/mutator for rowsPerPage to support our rows-per-page selection.

  • It needs to support public void updateRowsPerPage(AjaxBehaviorEvent event), so that we can update the listing based on user’s rows-per-page selection.

  • It needs to support public void navigatePage(final boolean forward).

  • Accessor for recordCount

Anyway, you should be able to implement the class now.

The way I implement it, however, is to split it into two parts. If you think about it, let us say in the future there are multiple lists in your application, e.g. List of Orders, Persons, Invoices and so on. They willl be implemented in a same way. I thought it would be a better idea to create a base class and then extend it later with actual entities.

First, the base class.

DataListingSupport.java

package id.co.dwuysan.util;

import java.io.Serializable;
import javax.faces.event.AjaxBehaviorEvent;
import javax.faces.model.DataModel;

public abstract class DataListingSupport<T extends Serializable> implements Serializable {
    private int recordCount = 0;
    private int totalPages = 0;
    private DataModel<T> data;
    
    private int page = 1;
    private Integer rowsPerPage = null;
    private boolean ascending = true;
    private String sortField;
    
    public void navigatePage(final boolean forward) {
        setPage((forward) ? ++page : --page);
        refresh();
    }
    
    public void sort(final String sortField) {
        setSortField(sortField);
        setAscending(getSortField().equals(sortField) ? !isAscending() : true);
        refresh();
    }
    
    public void updateRowsPerPage(final AjaxBehaviorEvent event) {
        setPage(1); // page must reset to the first one
        refresh();
    }
    
    public void refresh() {
        // hook to populate count and data
        populateCountAndData();
        // compute total pages
        setTotalPages(countTotalPages(getRecordCount(), getRowsPerPage()));
    }
    
    /**
     * The concreate implementation of this class must perform data retrieval
     * based on the current information available (accessible via methods such
     * as {@link #getSortField()}, {@link #isAscending()}, etc.
     * <p>
     * The implementation is responsible in populating the values for {@link #setRecordCount(int)}
     * and {@link #setData(javax.faces.model.DataModel)}
     */
    protected abstract void populateCountAndData();
    
    /************************************************************** HELPER(S) */
    
    private static int countTotalPages(int totalRecord, int rowsPerPage) {
        int pageCounter = 0;
        for (int pageCountTracker = 0; pageCountTracker < totalRecord; ++pageCounter) {
            pageCountTracker += rowsPerPage;
        }
        return pageCounter;
    }
    
    /************************************************* ACCESSORS AND MUTATORS */

    public int getPage() {
        return page;
    }

    public void setPage(int page) {
        this.page = page;
    }

    public boolean isAscending() {
        return ascending;
    }

    public void setAscending(boolean ascending) {
        this.ascending = ascending;
    }

    public Integer getRowsPerPage() {
        return rowsPerPage;
    }

    public void setRowsPerPage(Integer rowsPerPage) {
        this.rowsPerPage = rowsPerPage;
    }

    public DataModel<T> getData() {
        return data;
    }

    public void setData(DataModel<T> data) {
        this.data = data;
    }

    public String getSortField() {
        return sortField;
    }

    public void setSortField(String sortField) {
        this.sortField = sortField;
    }

    public int getRecordCount() {
        return recordCount;
    }

    public void setRecordCount(int recordCount) {
        this.recordCount = recordCount;
    }

    public int getTotalPages() {
        return totalPages;
    }

    public void setTotalPages(int totalPages) {
        this.totalPages = totalPages;
    }
}

The next is how we extends that class to create our backing bean to support listing of Customer.

DataListingSupport.java

package id.co.dwuysan.customer;

import id.co.dwuysan.entity.Customer;
import id.co.dwuysan.service.CustomerService;
import id.co.dwuysan.util.DataListingSupport;
import javax.enterprise.context.RequestScoped;
import javax.faces.model.ListDataModel;
import javax.inject.Inject;
import javax.inject.Named;

@RequestScoped
@Named
public class ListCustomers extends DataListingSupport<Customer> {

    @Inject
    private CustomerService customerService;

    public ListCustomers() {
        setSortField("name");
        setRowsPerPage(10);
    }
    
    @Override
    protected void populateCountAndData() {
        /*
         * This is where we call an EJB (or whatever service layer you have)
         * to perform data retrieval.
         *
         * You need to make sure to retrieve the result (paginated, sorted), and
         * also the total number of records.
         */
        setRecordCount(result.getCount());
        setData(new ListDataModel<>(result.getResult()));
    }
}

So you see, using this approach, should I need another kind of listings, i.e. lists of orders, we can easily extend DataListingSupport<>.

The Result

Before I show you the full result, one thing we might want to add, is an action to load all the current customers from the database, on display of the page. JSF 2.2 adds a feature, called View Action, which makes this very very easy (McGinn 2011).

All you need to do, is to add this f:viewAction after the f:view as follows:

customers.xhtml

<?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:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ez="http://java.sun.com/jsf/composite/ezcomp"> <!-- 01 -->
    <body>
        <ui:composition template="./common/template.xhtml">
            <ui:define name="content">
                <f:view transient="true">
                    <!-- to load on display of the page -->
                    <f:metadata>
                        <f:viewAction transient="true" action="#{listCustomers.refresh}"/>
                    </f:metadata>
                    <h:form id="frmCreateCustomer">
                        <!-- the rest of the code remains unchanged -->
                    </h:form>
                </f:view>
            </ui:define>
        </ui:composition>
    </body>
</html>

Notice that we simply call DataListingSupport<>#refresh(). The viewAction actually analysis the String return to perform the implicit navigation. In this case, our refresh() method actually returns void, so no navigation is performed (McGinn 2011).

The result should be like this:

result

I took the list of the customers from NASDAQ 100.

So, there you go, now you have the an AJAX, scrollable, lazy-loading data table.

References

BalusC, 2011a, ‘What is the default Managed Bean Scope in a JSF 2 application in netbeans?’, Stack Overflow, accessed on 06 November 2013

BalusC, 2011b, ‘How to reference components in JSF ajax? Cannot find component with identifier “foo” in view’, Stack Overflow, accessed on 11 November 2013

Busscher, R, 2013, ‘JSF 2.2 Stateless views explained’, JSF Corner, accessed on 12 November 2013

Geary, D, Horstmann, C, 2010, ‘Core JavaServer Faces (3rd Edition)’, 3rd edn, Prentice Hall, California USA

Lubke, R, 2008, ‘JSF 2.0 New Feature Preview Series (Part 4) Resource Re-location’, Oracle.com, accessed on 07 October 2013

McGinn, T, 2011, ‘New JavaServer Faces 2.2 Feature: The viewAction Component’, Oracle.com, accessed on 17 November 2013

Oracle, 2013, ‘Composite Component’, Oracle.com, accessed on 07 November 2013

Riem, M, 2013, ‘JSF Tip #26 – JSF is going Stateless’, Java.net, accessed on 09 November 2013