/* * Copyright (c) 2014-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.support.io; import java.io.Serializable; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import javax.batch.api.BatchProperty; import javax.batch.api.chunk.ItemReader; import javax.enterprise.context.Dependent; import javax.inject.Inject; import javax.inject.Named; import javax.jms.JMSException; import javax.jms.MapMessage; import javax.jms.Message; import javax.jms.MessageConsumer; import javax.jms.ObjectMessage; import javax.jms.TextMessage; import org.jberet.support._private.SupportLogger; import org.jberet.support._private.SupportMessages; /** * An implementation of {@code javax.batch.api.chunk.ItemReader} that reads data items from a JMS destination. It can * reads the following JMS message types: * <p> * <ul> * <li>{@code ObjectMessage}: the object contained in the message is retrieved and returned; * <li>{@code MapMessage}: a new {@code java.util.Map} is created, populated with the data contained in the * incoming {@code MapMessage}, and returned; * <li>{@code TextMessage}: the text contained in the message is retrieved and returned; * <li>{@code Message}: but not one of its subtype, null is returned. * </ul> * <p> * If {@link #beanType} is set to {@code javax.jms.Message}, {@link #readItem()} returns the incoming JMS message as is. * Otherwise, {@link #readItem()} method determines the actual data type based on the message type. * <p> * This reader ends when any of the following occurs: * <ul> * <li>{@link #receiveTimeout} (in milliseconds) has elapsed when trying to receive a message from the destination; * <li>any {@code null} body is retrieved from a message; * <li>any message is of type {@code Message}, but not one of its subtype. * </ul> * * @see JmsItemWriter * @see JmsItemReaderWriterBase * @since 1.1.0 */ @Named @Dependent public class JmsItemReader extends JmsItemReaderWriterBase implements ItemReader { /** * The number of milliseconds a JMS {@code MessageConsumer} blocks until a message arrives. Optional property, and * defaults to 0, which means it blocks indefinitely. */ @Inject @BatchProperty protected long receiveTimeout; /** * Only messages with properties matching the message selector expression are delivered. A value of null or an * empty string indicates that there is no message selector for the message consumer. * See JMS API {@link javax.jms.Session#createConsumer(javax.jms.Destination, java.lang.String)} */ @Inject @BatchProperty protected String messageSelector; /** * The fully-qualified class name of the data item to be returned from {@link #readItem()} method. Optional * property and defaults to null. If it is specified, its valid value is: * <p> * <ul> * <li>{@code javax.jms.Message}: an incoming JMS message is returned as is. * </ul> * <p> * When this property is not specified, {@link #readItem()} method returns an object whose actual type is * determined by the incoming JMS message type. */ @Inject @BatchProperty protected Class beanType; protected MessageConsumer consumer; @Override public void open(final Serializable checkpoint) throws Exception { super.open(checkpoint); consumer = session.createConsumer(destination, messageSelector); connection.start(); } @Override public Object readItem() throws Exception { final Object result; final Message message = consumer.receive(receiveTimeout); if (message == null) { //no more messages after receiveTimeout return null; } if (beanType == Message.class) { return message; } if (message instanceof ObjectMessage) { final ObjectMessage objectMessage = (ObjectMessage) message; result = objectMessage.getObject(); if (!skipBeanValidation) { ItemReaderWriterBase.validate(result); } } else if (message instanceof MapMessage) { final MapMessage mapMessage = (MapMessage) message; final Map<String, Object> mapResult = new HashMap<String, Object>(); final Enumeration mapNames = mapMessage.getMapNames(); if (mapNames != null) { while (mapNames.hasMoreElements()) { final String k = (String) mapNames.nextElement(); mapResult.put(k, mapMessage.getObject(k)); } } result = mapResult; } else if (message instanceof TextMessage) { final TextMessage textMessage = (TextMessage) message; result = textMessage.getText(); } else { throw SupportMessages.MESSAGES.unexpectedJmsMessageType("ObjectMessage | MapMessage | TextMessage", message.getJMSType(), message.toString()); } return result; } @Override public Serializable checkpointInfo() throws Exception { return null; } @Override public void close() { super.close(); if (consumer != null) { try { consumer.close(); } catch (final JMSException e) { SupportLogger.LOGGER.tracef(e, "Failed to close JMS consumer %s%n", consumer); } consumer = null; } } }