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

Advertisements

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