Batch and EJB Timer Service Applications With Wildfly 10

Batch Processing is one of the common enterprise application. Batch process is compose of set of tasks which is executed periodically without any user interface. Batch processing is know as chunk processing, mostly all the tasks included in the batch are run in the background.

Before diving into the example, some definitions first: what is a step? put it simply, a step is an independent and sequential phase of a batch job. A step it self can contain chunk-oriented steps and task-oriented steps.
Batch Job


Chunk-oriented steps process data by reading items from a source, applying some transformation/business logic to each item, and storing the results. Chunk steps operate on one item at a time and group the results into a chunk. The results are stored when the chunk reaches a configurable size. Chunk-oriented processing makes storing results more efficient and facilitates transaction demarcation.
Chunk

Task-oriented steps, on the other hand, execute actions other than processing single items from a source. A typical example of task-oriented step might be some DDL on a database or operation on a file system. In terms of comparison a chunk oriented step can be used for massive, long running tasks whilst a task oriented step might be fit for a set of batch operations that are to be executed periodically.

Chunk-oriented steps. Each chunk step is in turn broken in three parts:
  1. The Read chunk part which is used to read the single items from a source of data (database/fs/ldap etc.)
  2. The Processor chunk part manipulates one item at a time using the logic defined by the application. (e.g. sorting, filtering data, trasnforming data etc.)
  3. The Writer chunk part is used to write the item which has been processed in the earlier phase.

Batch Application

In order to define batch steps, you need to create XML file under /META-INF/batch-jobs which define chunks. To create batch jobs, you need to create EJB project.

Create ItemReader Class

ItemReader need to be an subclass of AbstractItemReader as below.
package pro.itstikk.wildfly.chunk;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Serializable;

import javax.batch.api.chunk.AbstractItemReader;
import javax.enterprise.context.Dependent;
import javax.inject.Named;

import org.jboss.logging.Logger;

@Dependent
@Named("bookReader")
public class BookReader extends AbstractItemReader {
    private static Logger logger = Logger.getLogger(BookReader.class.getName());
    private BufferedReader reader;
 
    @Override
    public void open(Serializable checkpoint) throws Exception {
        reader = new BufferedReader(
                new InputStreamReader(
                    this
                    .getClass()
                    .getClassLoader()
                    .getResourceAsStream("/META-INF/mydata.csv")
                )
            );
    }
    
    @Override
    public Object readItem() throws Exception {
try {
            return reader.readLine();
        } catch (IOException ex) {
            logger.info(null, ex);
        }
        return null;
}

}

Create ItemProcessor Class

Next is to create ItemProcess class which implement ItemProcessor interfaces
package pro.itstikk.wildfly.chunk;

import java.util.StringTokenizer;


import javax.batch.api.chunk.ItemProcessor;
import javax.enterprise.context.Dependent;
import javax.inject.Named;

import org.jboss.logging.Logger;

import com.jpa.model.Book;

@Dependent
@Named("bookProcessor")
public class BookProcessor implements ItemProcessor {
private static Logger logger = Logger.getLogger(BookProcessor.class.getName());
@Override
public Object processItem(Object obj) throws Exception {
logger.info("processItem: " + obj);
        
        StringTokenizer tokens = new StringTokenizer((String)obj, ",");
        String sid = tokens.nextToken();
        String author = tokens.nextToken();
        String title = tokens.nextToken();
         
        return new Book(sid, author,title);
}

}


Finally, we need to create ItemWriter class.

Create ItemWriter Class 

create ItemWrite which is subclass of AbstractItemWriter as below.

package pro.itstikk.wildfly.chunk;

import java.util.List;

import javax.batch.api.chunk.AbstractItemWriter;
import javax.enterprise.context.Dependent;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.jboss.logging.Logger;

import com.jpa.model.Book;
@Dependent
@Named("bookWriter")
public class BookWriter extends AbstractItemWriter {
private static Logger logger = Logger.getLogger(BookWriter.class.getName());
@PersistenceContext(name="java-ee-01-jpa")
private EntityManager em;
@Override
public void writeItems(List<Object> objs) throws Exception {
for(Object obj : objs) {
Book book = (Book) obj;
logger.info(book.toString());
em.merge(book);
}
}

}

Create Batch Runner

In order to run the batch you need call and run batch jobs as below.

package pro.itstikk.wildfly.batch;

import java.util.Properties;

import javax.batch.operations.JobOperator;
import javax.batch.runtime.BatchRuntime;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;

import org.jboss.logging.Logger;

/**
 * Session Bean implementation class BookBatchEjb
 */
@Stateless(mappedName = "bookBatchEjb")
@LocalBean
public class BookBatchEjb {
private static Logger logger = Logger.getLogger(BookBatchEjb.class.getName());


    /**
     * Default constructor. 
     */
    public BookBatchEjb() {
        super();
    }
    
    public void run() {
    JobOperator jo = BatchRuntime.getJobOperator();
long jid = jo.start("batchJob", new Properties());
logger.info("Run Batch ID :"+jid); 
    }

}

Create batchJob.xml file

Then, in order to define batch, we need to create batchJob.xml under META/batch-jobs folder to define batch procedure.

<?xml version="1.0" encoding="UTF-8"?>
<job id="batchJob" xmlns="http://batch.jsr352/jsl">
    <step id="batchStep" >
        <chunk item-count="3">
            <reader ref="bookReader"/>
            <processor ref="bookProcessor"/>
            <writer ref="bookWriter"/>
        </chunk>    
    </step>
</job> 
your folder should look like
Folder Structure


Create Timer Service

To run batch, you can set timer to schedule batch running time. JAVE EE provides very simple timer to create any schedule.
package pro.itstikk.wildfly.batch;

import javax.ejb.EJB;
import javax.ejb.LocalBean;
import javax.ejb.Schedule;
import javax.ejb.Singleton;
import javax.ejb.Timer;

/**
 * Session Bean implementation class BatchAutoScheduler
 */
@Singleton(mappedName = "batchAutoScheduler")
@LocalBean
public class BatchAutoScheduler {
@EJB
private BookBatchEjb bookBatchEjb;
    /**
     * Default constructor. 
     */
    public BatchAutoScheduler() {
        super();
    }
    @Schedule(second="0", minute="12", hour="*")
    public void execute(Timer timer) {
    bookBatchEjb.run();
    }
}

Next is to deploy your batch project in the application project. Then you package to EAR package and deploy the Application Server (Wildfly). Please refer mydata.csv here.

No comments:

Post a Comment

Feature Recently

Running Wildfly Application Server in Domain Mode

  Wildfly application server provides two modes of how to run application one wildfly application server. It is very simple if you run your ...

Most Views