/* * Copyright (c) 2016 Red Hat, Inc. and/or its affiliates. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Cheng Fang - Initial API and implementation */ package org.jberet.camel; import java.util.List; import javax.batch.api.chunk.listener.ChunkListener; import javax.batch.api.chunk.listener.ItemProcessListener; import javax.batch.api.chunk.listener.ItemReadListener; import javax.batch.api.chunk.listener.ItemWriteListener; import javax.batch.api.chunk.listener.RetryProcessListener; import javax.batch.api.chunk.listener.RetryReadListener; import javax.batch.api.chunk.listener.RetryWriteListener; import javax.batch.api.chunk.listener.SkipProcessListener; import javax.batch.api.chunk.listener.SkipReadListener; import javax.batch.api.chunk.listener.SkipWriteListener; import javax.batch.runtime.context.StepContext; import javax.inject.Inject; import javax.inject.Named; import static org.jberet.camel.EventType.*; /** * An implementation of batch chunk listeners that sends chunk execution events * to the configured Camel endpoint. The following are the chunk listener * interfaces implemented by this class, and supported types of chunk * execution events: * <ul> * <li>{@code javax.batch.api.chunk.listener.ChunkListener} * <ul> * <li>{@value org.jberet.camel.EventType#BEFORE_CHUNK} * <li>{@value org.jberet.camel.EventType#ON_CHUNK_ERROR} * <li>{@value org.jberet.camel.EventType#AFTER_CHUNK} * </ul> * <li>{@code javax.batch.api.chunk.listener.ItemProcessListener} * <ul> * <li>{@value org.jberet.camel.EventType#BEFORE_PROCESS} * <li>{@value org.jberet.camel.EventType#AFTER_PROCESS} * <li>{@value org.jberet.camel.EventType#ON_PROCESS_ERROR} * </ul> * <li>{@code javax.batch.api.chunk.listener.ItemReadListener} * <ul> * <li>{@value org.jberet.camel.EventType#BEFORE_READ} * <li>{@value org.jberet.camel.EventType#AFTER_READ} * <li>{@value org.jberet.camel.EventType#ON_READ_ERROR} * </ul> * <li>{@code javax.batch.api.chunk.listener.ItemWriteListener} * <ul> * <li>{@value org.jberet.camel.EventType#BEFORE_WRITE} * <li>{@value org.jberet.camel.EventType#AFTER_WRITE} * <li>{@value org.jberet.camel.EventType#ON_WRITE_ERROR} * </ul> * <li>{@code javax.batch.api.chunk.listener.RetryProcessListener} * <ul> * <li>{@value org.jberet.camel.EventType#ON_RETRY_PROCESS_EXCEPTION} * </ul> * <li>{@code javax.batch.api.chunk.listener.RetryReadListener} * <ul> * <li>{@value org.jberet.camel.EventType#ON_RETRY_READ_EXCEPTION} * </ul> * <li>{@code javax.batch.api.chunk.listener.RetryWriteListener} * <ul> * <li>{@value org.jberet.camel.EventType#ON_RETRY_WRITE_EXCEPTION} * * </ul> * <li>{@code javax.batch.api.chunk.listener.SkipProcessListener} * <ul> * <li>{@value org.jberet.camel.EventType#ON_SKIP_PROCESS_ITEM} * </ul> * <li>{@code javax.batch.api.chunk.listener.SkipReadListener} * <ul> * <li>{@value org.jberet.camel.EventType#ON_SKIP_READ_ITEM} * </ul> * <li>{@code javax.batch.api.chunk.listener.SkipWriteListener} * <ul> * <li>{@value org.jberet.camel.EventType#ON_SKIP_WRITE_ITEM} * </ul> * </ul> * The body of the message sent is the current {@link ChunkExecutionInfo}. * Each message also contains a header to indicate the event type: * its key is {@value org.jberet.camel.EventType#KEY}, and value is * one from the above list. * <p> * The target Camel endpoint is configured through batch property * {@code endpoint} in job XML. For example, * <pre> * <job id="camelChunkListenerTest" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0"> * <step id="camelChunkListenerTest.step1"> * <listeners> * <listener ref="camelChunkListener"> * <properties> * <property name="endpoint" value="#{jobParameters['endpoint']}"/> * </properties> * </listener> * </listeners> * ... ... * </pre> * * @see CamelJobListener * @see CamelStepListener * @since 1.3.0 */ @Named public class CamelChunkListener extends CamelListenerBase implements ChunkListener, ItemProcessListener, ItemReadListener, ItemWriteListener, RetryProcessListener, RetryReadListener, RetryWriteListener, SkipProcessListener, SkipReadListener, SkipWriteListener { /** * Injection of {@code javax.batch.runtime.context.StepContext} by batch * runtime. */ @Inject protected StepContext stepContext; // ChunkListener methods: /** * {@inheritDoc} * <p> * This method sends {@value org.jberet.camel.EventType#BEFORE_CHUNK} * event to the configured Camel endpoint. The message contains a header: * <pre> * {@value org.jberet.camel.EventType#KEY}={@value org.jberet.camel.EventType#BEFORE_CHUNK} * </pre> * , and the message body is the current {@link org.jberet.camel.ChunkExecutionInfo}. * * @throws Exception */ @Override public void beforeChunk() throws Exception { sendBodyAndHeader(BEFORE_CHUNK, null, null, null, null); } /** * {@inheritDoc} * <p> * This method sends {@value org.jberet.camel.EventType#ON_CHUNK_ERROR} * event to the configured Camel endpoint. The message contains a header: * <pre> * {@value org.jberet.camel.EventType#KEY}={@value org.jberet.camel.EventType#ON_CHUNK_ERROR} * </pre> * , and the message body is the current {@link org.jberet.camel.ChunkExecutionInfo}, which * contains the exception along with other chunk execution info. * * @throws Exception */ @Override public void onError(final Exception ex) throws Exception { sendBodyAndHeader(ON_CHUNK_ERROR, null, null, null, ex); } /** * {@inheritDoc} * <p> * This method sends {@value org.jberet.camel.EventType#AFTER_CHUNK} * event to the configured Camel endpoint. The message contains a header: * <pre> * {@value org.jberet.camel.EventType#KEY}={@value org.jberet.camel.EventType#AFTER_CHUNK} * </pre> * , and the message body is the current {@link org.jberet.camel.ChunkExecutionInfo}. * * @throws Exception */ @Override public void afterChunk() throws Exception { sendBodyAndHeader(AFTER_CHUNK, null, null, null, null); } // ItemProcessListener /** * {@inheritDoc} * <p> * This method sends {@value org.jberet.camel.EventType#BEFORE_PROCESS} * event to the configured Camel endpoint. The message contains a header: * <pre> * {@value org.jberet.camel.EventType#KEY}={@value org.jberet.camel.EventType#BEFORE_PROCESS} * </pre> * , and the message body is the current {@link org.jberet.camel.ChunkExecutionInfo}, * which contains the current {@code item} being processed, along with other * chunk execution info. * * @throws Exception */ @Override public void beforeProcess(final Object item) throws Exception { sendBodyAndHeader(BEFORE_PROCESS, item, null, null, null); } /** * {@inheritDoc} * <p> * This method sends {@value org.jberet.camel.EventType#AFTER_PROCESS} * event to the configured Camel endpoint. The message contains a header: * <pre> * {@value org.jberet.camel.EventType#KEY}={@value org.jberet.camel.EventType#AFTER_PROCESS} * </pre> * , and the message body is the current {@link org.jberet.camel.ChunkExecutionInfo}, * which contains the current {@code item} being processed and {@code result}, * along with other chunk execution info. * * @throws Exception */ @Override public void afterProcess(final Object item, final Object result) throws Exception { sendBodyAndHeader(AFTER_PROCESS, item, result, null, null); } /** * {@inheritDoc} * <p> * This method sends {@value org.jberet.camel.EventType#ON_PROCESS_ERROR} * event to the configured Camel endpoint. The message contains a header: * <pre> * {@value org.jberet.camel.EventType#KEY}={@value org.jberet.camel.EventType#ON_PROCESS_ERROR} * </pre> * , and the message body is the current {@link org.jberet.camel.ChunkExecutionInfo}, * which contains the current {@code item} being processed and the exception, * along with other chunk execution info. * * @throws Exception */ @Override public void onProcessError(final Object item, final Exception ex) throws Exception { sendBodyAndHeader(ON_PROCESS_ERROR, item, null, null, ex); } // ItemReadListener /** * {@inheritDoc} * <p> * This method sends {@value org.jberet.camel.EventType#BEFORE_READ} * event to the configured Camel endpoint. The message contains a header: * <pre> * {@value org.jberet.camel.EventType#KEY}={@value org.jberet.camel.EventType#BEFORE_READ} * </pre> * , and the message body is the current {@link org.jberet.camel.ChunkExecutionInfo}. * * @throws Exception */ @Override public void beforeRead() throws Exception { sendBodyAndHeader(BEFORE_READ, null, null, null, null); } /** * {@inheritDoc} * <p> * This method sends {@value org.jberet.camel.EventType#AFTER_READ} * event to the configured Camel endpoint. The message contains a header: * <pre> * {@value org.jberet.camel.EventType#KEY}={@value org.jberet.camel.EventType#AFTER_READ} * </pre> * , and the message body is the current {@link org.jberet.camel.ChunkExecutionInfo}, * which contains the current {@code item} being read, along with other * chunk execution info. * * @throws Exception */ @Override public void afterRead(final Object item) throws Exception { sendBodyAndHeader(AFTER_READ, item, null, null, null); } /** * {@inheritDoc} * <p> * This method sends {@value org.jberet.camel.EventType#ON_READ_ERROR} * event to the configured Camel endpoint. The message contains a header: * <pre> * {@value org.jberet.camel.EventType#KEY}={@value org.jberet.camel.EventType#ON_READ_ERROR} * </pre> * , and the message body is the current {@link org.jberet.camel.ChunkExecutionInfo}, * which contains the exception, along with other chunk execution info. * * @throws Exception */ @Override public void onReadError(final Exception ex) throws Exception { sendBodyAndHeader(ON_READ_ERROR, null, null, null, ex); } // ItemWriteListener /** * {@inheritDoc} * <p> * This method sends {@value org.jberet.camel.EventType#BEFORE_WRITE} * event to the configured Camel endpoint. The message contains a header: * <pre> * {@value org.jberet.camel.EventType#KEY}={@value org.jberet.camel.EventType#BEFORE_WRITE} * </pre> * , and the message body is the current {@link org.jberet.camel.ChunkExecutionInfo}, * which contains the current {@code items} being written, along with other * chunk execution info. * * @throws Exception */ @Override public void beforeWrite(final List<Object> items) throws Exception { sendBodyAndHeader(BEFORE_WRITE, null, null, items, null); } /** * {@inheritDoc} * <p> * This method sends {@value org.jberet.camel.EventType#AFTER_WRITE} * event to the configured Camel endpoint. The message contains a header: * <pre> * {@value org.jberet.camel.EventType#KEY}={@value org.jberet.camel.EventType#AFTER_WRITE} * </pre> * , and the message body is the current {@link org.jberet.camel.ChunkExecutionInfo}, * which contains the current {@code items} being written, along with other * chunk execution info. * * @throws Exception */ @Override public void afterWrite(final List<Object> items) throws Exception { sendBodyAndHeader(AFTER_WRITE, null, null, items, null); } /** * {@inheritDoc} * <p> * This method sends {@value org.jberet.camel.EventType#ON_WRITE_ERROR} * event to the configured Camel endpoint. The message contains a header: * <pre> * {@value org.jberet.camel.EventType#KEY}={@value org.jberet.camel.EventType#ON_WRITE_ERROR} * </pre> * , and the message body is the current {@link org.jberet.camel.ChunkExecutionInfo}, * which contains the current {@code items} being written and the exception, * along with other chunk execution info. * * @throws Exception */ @Override public void onWriteError(final List<Object> items, final Exception ex) throws Exception { sendBodyAndHeader(ON_WRITE_ERROR, null, null, items, ex); } // RetryProcessListener /** * {@inheritDoc} * <p> * This method sends {@value org.jberet.camel.EventType#ON_RETRY_PROCESS_EXCEPTION} * event to the configured Camel endpoint. The message contains a header: * <pre> * {@value org.jberet.camel.EventType#KEY}={@value org.jberet.camel.EventType#ON_RETRY_PROCESS_EXCEPTION} * </pre> * , and the message body is the current {@link org.jberet.camel.ChunkExecutionInfo}, * which contains the current {@code item} being retry-processed and the cause exception, * along with other chunk execution info. * * @throws Exception */ @Override public void onRetryProcessException(final Object item, final Exception ex) throws Exception { sendBodyAndHeader(ON_RETRY_PROCESS_EXCEPTION, item, null, null, ex); } // RetryReadListener /** * {@inheritDoc} * <p> * This method sends {@value org.jberet.camel.EventType#ON_RETRY_READ_EXCEPTION} * event to the configured Camel endpoint. The message contains a header: * <pre> * {@value org.jberet.camel.EventType#KEY}={@value org.jberet.camel.EventType#ON_RETRY_READ_EXCEPTION} * </pre> * , and the message body is the current {@link org.jberet.camel.ChunkExecutionInfo}, * which contains the cause exception, along with other chunk execution info. * * @throws Exception */ @Override public void onRetryReadException(final Exception ex) throws Exception { sendBodyAndHeader(ON_RETRY_READ_EXCEPTION, null, null, null, ex); } // RetryWriteListener /** * {@inheritDoc} * <p> * This method sends {@value org.jberet.camel.EventType#ON_RETRY_WRITE_EXCEPTION} * event to the configured Camel endpoint. The message contains a header: * <pre> * {@value org.jberet.camel.EventType#KEY}={@value org.jberet.camel.EventType#ON_RETRY_WRITE_EXCEPTION} * </pre> * , and the message body is the current {@link org.jberet.camel.ChunkExecutionInfo}, * which contains the current {@code items} being retry-written and the cause exception, * along with other chunk execution info. * * @throws Exception */ @Override public void onRetryWriteException(final List<Object> items, final Exception ex) throws Exception { sendBodyAndHeader(ON_RETRY_WRITE_EXCEPTION, null, null, items, ex); } // SkipProcessListener /** * {@inheritDoc} * <p> * This method sends {@value org.jberet.camel.EventType#ON_SKIP_PROCESS_ITEM} * event to the configured Camel endpoint. The message contains a header: * <pre> * {@value org.jberet.camel.EventType#KEY}={@value org.jberet.camel.EventType#ON_SKIP_PROCESS_ITEM} * </pre> * , and the message body is the current {@link org.jberet.camel.ChunkExecutionInfo}, * which contains the skipped processing {@code item} and the cause exception, * along with other chunk execution info. * * @throws Exception */ @Override public void onSkipProcessItem(final Object item, final Exception ex) throws Exception { sendBodyAndHeader(ON_SKIP_PROCESS_ITEM, item, null, null, ex); } // SkipReadListener /** * {@inheritDoc} * <p> * This method sends {@value org.jberet.camel.EventType#ON_SKIP_READ_ITEM} * event to the configured Camel endpoint. The message contains a header: * <pre> * {@value org.jberet.camel.EventType#KEY}={@value org.jberet.camel.EventType#ON_SKIP_READ_ITEM} * </pre> * , and the message body is the current {@link org.jberet.camel.ChunkExecutionInfo}, * which contains the exception that caused skipping item, along with other * chunk execution info. * * @throws Exception */ @Override public void onSkipReadItem(final Exception ex) throws Exception { sendBodyAndHeader(ON_SKIP_READ_ITEM, null, null, null, ex); } // SkipWriteListener /** * {@inheritDoc} * <p> * This method sends {@value org.jberet.camel.EventType#ON_SKIP_WRITE_ITEM} * event to the configured Camel endpoint. The message contains a header: * <pre> * {@value org.jberet.camel.EventType#KEY}={@value org.jberet.camel.EventType#ON_SKIP_WRITE_ITEM} * </pre> * , and the message body is the current {@link org.jberet.camel.ChunkExecutionInfo}, * which contains the current {@code items} being skipped for writing and the cause exception, * along with other chunk execution info. * * @throws Exception */ @Override public void onSkipWriteItem(final List<Object> items, final Exception ex) throws Exception { sendBodyAndHeader(ON_SKIP_WRITE_ITEM, null, null, items, ex); } /** * Sends the chunk execution event message to the configured Camel endpoint. * The message has the current {@link ChunkExecutionInfo} as the body, and * a header to indicate the event type. * * @param eventType event type as defined in {@link EventType} * @param item the item being processed, if applicable; may be null * @param result the result from processing, if applicable; may be null * @param items the items for writing by the item writer, if applicable; may be null * @param exception any exception during read, write or processing; may be null */ protected void sendBodyAndHeader(final String eventType, final Object item, final Object result, final List<Object> items, final Exception exception) { final ChunkExecutionInfo chunkExecutionInfo = new ChunkExecutionInfo(jobContext.getExecutionId(), jobContext.getJobName(), stepContext.getStepExecutionId(), stepContext.getStepName(), item, result, items, exception); producerTemplate.sendBodyAndHeader(endpoint, chunkExecutionInfo, EventType.KEY, eventType); } }