Java EE7: Developing a Batch Processing Application

Overview

Purpose

This tutorial demonstrates how to develop a sample batch application and run the batch application in Oracle GlassFish Server.

Time to Complete

Approximately 1 hour

Introduction

JSR 352 (Batch Applications for the Java Platform) defines the programming model for batch applications and a run time to run and manage batch jobs. The programming model for batch applications caters to batch processing concerns, such as jobs, steps, repositories, reader processor writer patterns, chunks, checkpoints, parallel processing, flow, split, transactions, retries, sequencing, and partitioning.

batch

As displayed in the diagram:

  • A job represents a series of closely related steps that perform a discrete business process.
  • Steps can be executed in sequence or in parallel.
  • Steps can also be optional, with the decision to execute or skip them conditioned on the outcome of prior steps in the same workflow.
  • Steps can be check-pointed and retried, if needed, and are generally transactional.
  • A repository stores information about the current jobs.
  • Jobs can be listed, started, stopped, paused, and cancelled through the operator.
  • The operator is typically invoked in a scheduled or ad hoc fashion. 

The entire batch process is put together through a Job Specification Language written in XML. Despite the robust concepts, the programming model is kept simple.

Scenario

A very simple scenario is considered for this tutorial. A batch job reads new-hire data for HR processing from a comma-separated values (CSV) file. Each line in the file contains a name and the hiring date for one employee. The batch job stores each line in the file to a NewHire object and then writes out the new hire's data into a database table.

Hardware and Software Requirements

The following is a list of hardware and software requirements:

  • Java Platform, Standard Edition 7 (Java SE 7; Java SE 7u11 recommended)
  • NetBeans 7.3.1 IDE for Java Platform, Enterprise Edition 7 (Java EE 7)
  • Oracle GlassFish Server

Prerequisites

Before starting this tutorial, you should:

  • Have some experience writing and deploying web applications.
  • Have some experience with Contexts and Dependency Injection beans and the Java Persistence API.
  • Have installed NetBeans 7.3.1, Java EE 7, and GlassFish 4.
  • Have started NetBeans.
  • Have unzipped theBatchExampleApp.zip file.
  • Have opened the BatchExampleApp project in NetBeans.

Setting Up the Table and Its Entity Class for the Application

In this section, you create the NewHire table in the sample database of the Java database (DB) server.

  1. On the Services tab, perform the following steps:

    1. Expand Databases.
    2. Right-click jdbc:derby://localhost:1527/sample [app on APP].
    3. Select Connect.

    alt description here

  2. In the Connect dialog box, enter app for the password and click OK.

    alt description here
  3. On the Projects tab, expand BatchExampleApp > Web Pages > resources > dbfiles and then click create.sql to open it in the code editor window.

    alt description here
  4. Select jdbc:derby://localhost:1527/sample [app on APP] from the Connection list and then click the Run SQL icon to the right of the list.

    The SQL script is executed in the sample database.

    alt description here
  5. On the Services tab, expand Databases > jdbc:derby://localhost:1527/sample [app on APP] > App > Tables and verify that the NEW_HIRE table was created.

    alt description here
  6. Perform the following steps:

    1. On the Projects tab, expand BatchExampleApp > Source Packages.
    2. Right-click com.example.entity.
    3. Select New.
    4. In the New File dialog box, select Persistence from Categories and Entity Classes from Database from File Types.
    5. Click Next.
  7. In the New Entity Classes from Database dialog box, select jdbc/sample in the Data Source: combo box, select NEW_HIRE from Available Tables, click Add, and then click Next.

  8. On the Entity Classes page, perform the following steps:

    1. Select Source Packages for Location.
    2. Select com.example.entity for Package.
    3. Click Finish.
  9. On the Projects tab, browse to BatchExampleApp > Source Packages > com.example.entity and verify that the NewHire.java entity class was created.

Creating the JSL File

A Job Specification Language (JSL) file specifies the order in which steps must be executed to accomplish the job. In this section, you create a JSL file.

  1. On the Projects tab, expand BatchExampleApp > Web Pages > resources > META-INF.

    A JSL file must be placed in the META-INF/batch-jobs directory.

  2. In the META-INF folder, create a folder and name it batch-jobs.

    alt description here
  3. In this batch job there is one step. It is a chunk-style step and has an ItemReader, an ItemProcessor, and an ItemWriter. Perform the following steps to specify the step and the chunk:

    1. Create an XML file in the batch-jobs folder and name it newHireJob.
    2. Remove the <root></root> tag from the file.
    3. Add the following content to the newHireJob.xml file:
    <job id="newHireJob" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
        <step id="firstStep" >
            <chunk item-count="3">
                <reader ref="newHireItemReader"/>
                <processor ref="newHireItemProcessor"/>
                <writer ref="newHireItemWriter"/>
            </chunk>	
        </step>
    </job>

Developing the ItemReader, ItemProcessor, and ItemWriter Classes

The JSL file specifies that the newHireJob has a step that uses an ItemReader named NewHireItemReader, an ItemProcessor named NewHireItemProcessor, and an ItemWriter named NewHireItemWriter. In this section, you create these classes.

Writing the NewHireItemReader and NewHireItemProcessor Classes

  1. On the Projects tab, expand BatchExampleApp > Source Packages > com.example.batch and then perform the following steps:

    1. Create a Java class and name it NewHireItemReader. This ItemReader implementation extends AbstractItemReader.
    2. Override the open method and the readItem method.
    3. Add code to read input data from a CSV file.
    4. Add the required import statements.
    @Named
    public class NewHireItemReader extends AbstractItemReader {
    
        private BufferedReader reader;
    
        @Override
        public void open(Serializable checkpoint) throws Exception {
            reader = new BufferedReader(
                    new InputStreamReader(
                    this
                    .getClass()
                    .getClassLoader()
                    .getResourceAsStream("/META-INF/newHiredata.csv")));
        }
    
        @Override
        public String readItem() {
            try {
                return reader.readLine();
            } catch (IOException ex) {
                Logger.getLogger(NewHireItemReader.class.getName()).log(Level.SEVERE, null, ex);
            }
            return null;
        }
    }
  2. On the Projects tab, expand BatchExampleApp > Source Packages > com.example.batch and then perform the following steps:

    1. Create a Java class and name it NewHireItemProcessor. It should implement ItemProcessor.
    2. Override the processItem method.
    3. Use the StringTokenizer class to extract each comma-separated data of a record and store it in the NewHire object.
    4. Add the required import statements.
  3. @Named
    public class NewHireItemProcessor implements ItemProcessor {
    
        SimpleDateFormat format = new SimpleDateFormat("M/dd/yy");
    
        @Override
        public NewHire processItem(Object t) {
            System.out.println("processItem: " + t);
    
            StringTokenizer tokens = new StringTokenizer((String) t, ",");
    
            String name = tokens.nextToken();
            String date;
    
            try {
                date = tokens.nextToken();
                format.setLenient(false);
                format.parse(date);
            } catch (ParseException e) {
                return null;
            }
    
            return new NewHire(name, date);
        }
    }
    

Writing the NewHireItemWriter Class

  1. On the Projects tab, expand BatchExampleApp > Source Packages > com.example.batch and then perform the following steps:

    1. Create a Java class and name it NewHireItemWriter. This ItemWriter implementation should extend AbstarctItemWriter.
    2. Override the writeItems method.
    3. Add code to persist the object to the table in the database.
    4. Add the required import statements.
    @Named
    public class NewHireItemWriter extends AbstractItemWriter {
    
        @PersistenceContext
        EntityManager em;
    
        @Override
        public void writeItems(List list) {
            System.out.println("writeItems: " + list);
            for (Object newhire : list) {
                em.persist(newhire);
            }
        }
    }

Creating a Servlet to Start a Batch Job

You must initiate a batch job explicitly. In this section, you create a servlet to start a batch job.

  1. On the Projects tab, expand BatchExampleApp > Source Packages and then perform the following steps:

    1. Right-click com.example.batch and select New > Servlet to display the New Servlet dialog box.
    2. Enter BatchServlet for Class Name.
    3. Click Finish.
  2. alt description here
  3. Modify Batchservlet.java to start the NewHireBatch job.

    1. Declare an instance of EntityManagerFactory
    2. Locate processRequestMethod.
    3. Modify the value of the title tag and h1 tag to CSV-to-Database Chunk Job.
    4. Initialize an instance of JobOperator by invoking BatchRuntime.getJobOperator;.
    5. Invoke the start method of JobOperator by specifying the name of the job.
    6. Handle the JobStartException and JobSecurityException classes.
    7. Import the required classes.
  4. public class BatchServlet extends HttpServlet {
    
        @PersistenceUnit
        EntityManagerFactory em;
        protected void processRequest(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            response.setContentType("text/html;charset=UTF-8");
            try (PrintWriter out = response.getWriter()) {
                out.println("<html>");
                out.println("<head>");
                out.println("<title>CSV-to-Database Chunk Job</title>");
                out.println("</head>");
                out.println("<body>");
                out.println("<h1>CSV-to-Database Chunk Job</h1>");
                JobOperator jo = BatchRuntime.getJobOperator();
                long jid = jo.start("newHireJob", new Properties());
                out.println("Job submitted: " + jid + "<br>");
                out.println("<br><br>Check server.log for output, also look at \"newHireJob.xml\" for Job XML.");
                out.println("</body>");
                out.println("</html>");
            } catch (JobStartException | JobSecurityException ex) {
                Logger.getLogger(BatchServlet.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
       
  5. On the Projects tab, expand BatchExampleApp > Web Pages and open index.jsp.

    1. Modify index.jsp to create a hyperlink named Start the Job within <body>.
    2. Refer the hyperlink to the BatchServlet servlet.
    3. Start the <a href="${pageContext.request.contextPath}/BatchServlet"/>job</a>

Packaging, Deploying, and Testing the Batch Processing Application

  1. Perform the following steps:

    1. On the Projects tab, expand BatchExampleApp > Web Pages > resources and copy META-INF folder.
    2. Paste the META-INF folder to BatchExampleApp > Source Packages folder.
    3. Verify the contents of the Source Packages folder.
    alt description here
  2. Package the JSL file under the WEB-INF/classes/META-INF/batch-jobs directory.

    1. On the Projects tab, right-click BatchExampleApp and select Clean and Build.
    2. On the Files tab, verify that a build folder was created under the BatchExampleApp folder.
    3. Verify that the contents of BatchExampleApp > build > web >WEB-INF > classes > META-INF match the following screenshot:
    4. alt description here
    5. Open and examine the newHiredata.csv file.
    6. Each line contains comma-separated values of new hire data.

      alt description here
  3. On the Projects tab, right-click BatchExampleApp and select Run.

    The home page opens in the web browser.

  4. Click the job link.

    alt description here

    The BatchServlet displays the job ID and a message in the web browser.

    alt description here
  5. On the Services tab, expand Databases > jdbc:derby://localhost:1527/sample [app on APP] > App > Tables.

  6. Right-click NEW_HIRE and select View Data.

    The six records that were in the CSV file appear in the table.

     

Summary

In this tutorial, you learned how to:

  • Use a few batch processing APIs from JSR 352
  • Create a JSL file
  • Develop ItemReader, ItemProcessor, and ItemWriter classes
  • Start a batch job
  • Write, package, and run a simple batch application that uses chunk-style steps

Resources

To learn more about Java EE 7 and the batch processing API, see the following resources:

Credits

  • Lead Curriculum Developer: Paromita Dutta
  • QA: Anjulaponni Azhagulekshmi
  • Editor: Susan Moxley

To navigate this Oracle by Example tutorial, note the following:

Topic List:
Click a topic to navigate to that section.
Expand All Topics:
Click the button to show or hide the details for the sections. By default, all topics are collapsed.
Hide All Images:
Click the button to show or hide the screenshots. By default, all images are displayed.
Print:
Click the button to print the content. The content that is currently displayed or hidden is printed.

To navigate to a particular section in this tutorial, select the topic from the list.