jpos : how to handle messages with different headers with different length listening to the same endpoint and using different packager - jpos

it is not a duplicate question since the previous question some functionality changed and this as a extension of the below. created a new post so helpful for others too who look for different solutions
jpos : how to handle messages with different headers with different length listening to the same endpoint and using same packager
The scenario is a third party application will send messages to jpos. possibility is that it will send two types of messages with different message header length say one will be length 22 bytes and another will be 44 bytes.the packager will be different.
As advised in the above solution I created a custom channel and it works fine.but now the challenge is how can i use different packager for each different request type
example
header1 -- use x packager
header2 -- use y packager
do I have to create new packager if so how can i link the server and the packager and the header types.
looks very challenging and jpos is getting very interesting.
<property name="packager-config" value="cfg/packager/CISebcdic.xml"="True" />
<server class="org.jpos.q2.iso.QServer" logger="Q2" name="gwmip-server-7003" realm="bnet-server-8000">
<attr name="port" type="java.lang.Integer">7003</attr>
<channel class="org.jpos.iso.channel.NACChannel"
packager="org.jpos.iso.packager.GenericPackager"
type="server"
logger="Q2"
>
<property name="packager-config" value="cfg/packager/CISebcdic.xml" debug="True" />
<property name="timeout" value="180000"/>
</channel>
<request-listener class="org.jpos.iso.IncomingListener" logger="Q2" realm="incoming-request-listener">
<property name="queue" value="GWMIPTXNMGR" />
<property name="ctx.DESTINATION" value="jPOS-AUTORESPONDER" />
</request-listener>
</server>
Please help,
Thank in advance,
totally lost below is the one which I am trying, the issue is with
protected ISOPackager getDynamicPackager(byte[] header, byte[]
message)
I am getting the error as below
<log realm="gwmip-server-5281.server.session/10.157.152.95:26215" at="2020-04-18T00:52:22.018">
<session-error>
<exception name="unexpected exception">
java.io.IOException: unexpected exception
at org.jpos.iso.BaseChannel.receive(BaseChannel.java:787)
at org.jpos.iso.ISOServer$Session.run(ISOServer.java:344)
at org.jpos.util.ThreadPool$PooledThread.run(ThreadPool.java:76)
Caused by: java.lang.NullPointerException
at java.nio.HeapByteBuffer.<init>(HeapByteBuffer.java:70)
at java.nio.ByteBuffer.wrap(ByteBuffer.java:373)
at org.jpos.iso.channel.GWMipChannel.getDynamicPackager(GWMipChannel.java:73)
at org.jpos.iso.BaseChannel.receive(BaseChannel.java:743)
... 2 more
Updated Custom Channel
package org.jpos.iso.channel;
import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.iso.ISOException;
import org.jpos.iso.ISOMsg;
import org.jpos.iso.ISOPackager;
import org.jpos.iso.packager.GenericPackager;
import org.jpos.iso.packager.MadaPackager;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
public class GWMipChannel extends NACChannel {
byte[] madaHeader = {(byte) 0x00, (byte) 0x18, (byte) 0xd6, (byte) 0xf3, (byte) 0xf6, (byte) 0xf8, (byte) 0xf7};
byte[] nitmxHeader = {(byte) 0x00, (byte) 0x18, (byte) 0xd6, (byte) 0xf3, (byte) 0xf7, (byte) 0xf3, (byte) 0xf2};
byte[] response_mada_header = {(byte) 0x00, (byte) 0x31, (byte) 0xc9, (byte) 0xf3, (byte) 0xf6, (byte) 0xf8, (byte) 0xf7};
#Override
protected byte[] readHeader(int hLen) throws IOException {
byte[] header = new byte[25];
serverIn.readFully(header, 0, 25);
boolean mada_header = ByteBuffer.wrap(header, 0, 7).equals(ByteBuffer.wrap(madaHeader));
boolean nitmx_header = ByteBuffer.wrap(header, 0, 7).equals(ByteBuffer.wrap(nitmxHeader));
boolean rsp_mada_header = ByteBuffer.wrap(header, 0, 7).equals(ByteBuffer.wrap(response_mada_header, 0, 7));
ISOPackager packager;
if (mada_header) {
return header;
} else if (nitmx_header) {
header = Arrays.copyOf(header,47 );
serverIn.readFully(header, 25, 22);
return header;
} else if (rsp_mada_header) {
//serverIn.readFully(header, 0, 50);
header = Arrays.copyOf(header, 48);
serverIn.readFully(header, 0, 48);
return header;
}
return header;
}
#Override
protected void sendMessageHeader(ISOMsg m, int len) throws IOException {
byte[] header = m.getHeader();
//assume header is the one to send, and already has 22 or 44 length
//or you can check
serverOut.write(header);
}
#Override
protected ISOPackager getDynamicPackager(byte[] header, byte[] message) {
boolean mada_header = ByteBuffer.wrap(header, 0, 7).equals(ByteBuffer.wrap(madaHeader,0,7));
boolean nitmx_header = ByteBuffer.wrap(header, 0, 7).equals(ByteBuffer.wrap(nitmxHeader,0,7));
ISOPackager packager = null;
if(mada_header) {
try {
packager = new GenericPackager("cfg/packager/CISebcidic_mada.xml");
} catch (ISOException e) {
e.printStackTrace();
}
}
else if (nitmx_header) {
try {
packager = new GenericPackager("cfg/packager/AS2805_NITMX.xml");
} catch (ISOException e) {
e.printStackTrace();
}
} else {
Exception e = new ISOException();
e.printStackTrace();
//return some default or throw some runtime exception exception
}
return packager;
}
}
the server configuration file is
<server class="org.jpos.q2.iso.QServer" logger="Q2" name="gwmip-server-5281" realm="gwmip-server-5281">
<attr name="port" type="java.lang.Integer">5281</attr>
<channel class="org.jpos.iso.channel.GWMipChannel"
packager="org.jpos.iso.packager.GenericPackager"
type="server"
logger="Q2"
header="0018D6F3F7F3F2000000000000000000000000000000000002"
>
<property name="packager-config" value="cfg/packager/AS2805_NITMX.xml" debug="True" />
<property name="timeout" value="180000"/>
</channel>
<request-listener class="org.jpos.iso.IncomingListener" logger="Q2" realm="incoming-request-listener">
<property name="queue" value="GWMIPTXNMGR" />
<property name="ctx.DESTINATION" value="gwmip-AUTORESPONDER" />
</request-listener>
</server>

In your Channel custom channel implementation you can override the getDynamicPackager(byte[] header, byte[] message)
//instantiate each as required in constructor for instance or you can make them configurable through setConfiguration
protected ISOPackager packager22, packager44;
protected byte[] len22HeaderPrefix, len44HeaderPrefix;
protected int headerPrefixLen = 4; //4 according to your other question
protected getDynamicPackager(byte[] header, byte[] message) {
if (Arrays.equals(header, len22HeaderPrefix, 0, prefixLen)) {
return packager22;
} else if (Arrays.equals(header, len44HeaderPrefix, 0, headerPrefixLen)) {
return packager44;
} else {
//return some default or throw some runtime exception exception
}
}

Related

Reply messages timing out - Spring AMQP

In our application around 1% of the transactions times out daily.
We clearly can see in the log that messages is being processed and sent back using reply-to mechanism but client not receiving the message and wait till the timeout then throws an error.
I'm unable to identify the issue that why these messages getting lost occasionally. Any help much appreciated.
Clint -> Rabbit -> Server -> Rabbit -> Client (timeout - 1% of transactions)
log lines:
client:
2021-11-28 23:03:43.317 WARN <sending msg to vhost <IP> with correlationId: 5cc5c40b-7193-1cf9-c66f-7c5d72901bdf , message properties correlationId: 10976>
Server:
2021-11-28 23:03:43.318 WARN <Received msg with correlationId: 5cc5c40b-7193-1cf9-c66f-7c5d72901bdf , message properties correlationId: 10976>
Server:
2021-11-28 23:03:57.830 WARN <sending msg to vhost <IP> with correlationId: 112f5771-93b9-6f8f-a353-b9a79bdd1438 , message properties correlationId: 10976>
client:
2021-11-28 23:04:43.317 ERROR - <An exception has occured: No reply received from 'assign' with arguments '[]' - perhaps a timeout in the template? org.springframework.remoting.RemoteProxyFailureException: No reply received from 'assign' with arguments '[]' - perhaps a timeout in the template?>
Client and Listener configuration:
<!-- client config -->
<rabbit:queue id="application.queue" name="API.request.queue"
declared-by="rabbitAdminConnectionFactory1"/>
<rabbit:direct-exchange name="#{ API +'.RequestDirectExchange'}" id="requestDirectExchange"
declared-by="rabbitAdminConnectionFactory1">
<rabbit:bindings>
<rabbit:binding queue="application.queue" key="API"/>
</rabbit:bindings>
</rabbit:direct-exchange>
<bean id="amqpTemplate" class="com.api.APIRabbitTemplate">
<property name="connectionFactory" ref="rabbitConnectionFactory"/>
<property name="routingKey" value="API"/>
<property name="exchange" value="#{ API +'.RequestDirectExchange'}"/>
<property name="queue" value="API.request.queue"/>
<property name="messageConverter" ref="rmessageConverter"/>
<property name="replyTimeout" value="60000"/>
<property name="receiveTimeout" value="60000"/>
<property name="retryTemplate" ref="retryTemplate"/>
</bean>
<!-- server listener config -->
<bean id="remotingAmqpTemplate1" class="com.API.APIRabbitTemplate">
<property name="connectionFactory" ref="rabbitConnectionFactory"/>
<property name="messageConverter" ref="rmessageConverter"/>
<property name="queue" value="$app{appName}.request.queue"/>
<property name="retryTemplate" ref="retryTemplate1"/>
</bean>
<bean id="amqpRemotingListener1" class="com.API.remoting.amqpRemotingListener">
<property name="amqpTemplate" ref="remotingAmqpTemplate1"/>
</bean>
<rabbit:listener-container connection-factory="rabbitConnectionFactory" auto-startup="false"
requeue-rejected="false" task-executor="taskExecutor"
concurrency="6" max-concurrency="6"
acknowledge="auto" prefetch="1"
transaction-size="1" channel-transacted="true">
<rabbit:listener ref="amqpRemotingListener1" queue-names="API.request.queue"
admin="rabbitAdminConnectionFactory1"/>
</rabbit:listener-container>
<rabbit:connection-factory id="rabbitConnectionFactory"
thread-factory="tfCommon1"
connection-factory="clientConnectionFactory"
addresses="$amqp{connectionURL1}"
username="$amqp{username}"
password="$amqp{password}"
channel-cache-size="$amqp{listeners.session.cache.size}"/>
<util:properties id="spring.amqp.global.properties">
<prop key="smlc.missing.queues.fatal">false</prop>
</util:properties>
EDIT:
public class ARabbitTemplate extends RabbitTemplate {
protected static Logger log = Logger.getLogger( AARabbitTemplate.class );
#Override
public <T> T execute(ChannelCallback<T> action) {
try {
return super.execute(action);
}catch (AmqpException ex){
//log
throw ex;
}
}
#Override
public void doSend(Channel channel, String exchange, String routingKey, Message message,
boolean mandatory, CorrelationData correlationData) throws Exception {
try {
super.doSend(channel, exchange, routingKey, message, mandatory, correlationData);
if(channel != null && channel.getConnection() != null && channel.getConnection().getAddress() != null) {
String IP = channel.getConnection().getAddress().getHostAddress();
String correlationId = message.getMessageProperties().getHeaders().get(MessagingConsts.CORRELATION_ID);
String message_CorrelationId = message.getMessageProperties().getCorrelationId();
log("sending msg to vhost " + IP + " with correlationId: " + correlationId + " , message properties correlationId: " + message_CorrelationId);
}
} catch (Exception ex) {
log.error("got exception while sending msg to ampq "+ ex.getLocalizedMessage());
throw ex;
}
}
#Override
public <R, S> boolean receiveAndReply(String queueName, ReceiveAndReplyCallback<R, S> callback,
ReplyToAddressCallback<S> replyToAddressCallback) throws AmqpException {
try {
return super.receiveAndReply(queueName, callback, replyToAddressCallback);
} catch (AmqpException amqpEx) {
throw amqpEx;
}
}
#Override
public Message receive() throws AmqpException {
try {
return super.receive();
} catch (AmqpException amqpEx) {
throw amqpEx;
}
}
#Override
public void onMessage(Message message) {
super.onMessage(message);
}
}
public class AAmqpRemotingListener implements MessageListener {
private AmqpTemplate amqpTemplate;
#Autowired
private MessageConverter messageConverter;
#Override
public void onMessage(Message message) {
String correlationId = message.getMessageProperties().getHeaders().get(MessagingConsts.CORRELATION_ID);
String message_CorrelationId = message.getMessageProperties().getCorrelationId();
log("Received msg with correlationId: " + correlationId + " , message properties correlationId: " + message_CorrelationId);
//rest of the code
//exporter.onMessage(message);
}
}
//Message Converter
public class SpringAmqpMessageConverter implements MessageConverter {
#Autowired
protected ObjectSerializer serializer;
#Override
public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {
byte[] data = serializer.writeObject(object);
// build message
MessageBuilder messageBuilder = MessageBuilder.withBody(data);
String correlationId = context.getCorrelationId();
if (StringUtils.isEmpty(correlationId)) {
correlationId = (new UUID(ThreadLocalRandom.current().nextLong(), ThreadLocalRandom.current().nextLong())).toString();
}
messageBuilder.setHeader(MessagingConsts.CORRELATION_ID, correlationId);
messageBuilder.copyHeaders(messageProperties.getHeaders());
messageBuilder.setCorrelationIdIfAbsent(correlationId);
messageBuilder.setDeliveryModeIfAbsentOrDefault(MessageDeliveryMode.NON_PERSISTENT);
Message message = messageBuilder.build();
return message;
}
#Override
public Object fromMessage(Message message) throws MessageConversionException {
//
}
}

Spring Integration IMAP - cannot get the body of the message

Here is my context.xml - it is working as expected, I can fetch email received to my gmail account
But the main problem is that I tried many solution to get the body of the email but I failed the getContent() give null pointer exception with all method found.
Anyone has the issue and know how to solve it?
<util:properties id="javaMailProperties">
<prop key="mail.imap.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop>
<prop key="mail.imap.socketFactory.fallback">false</prop>
<prop key="mail.store.protocol">imaps</prop>
<prop key="mail.debug">false</prop>
</util:properties>
<!-- In case you didn't notice: should-delete-messages="false" will make it not delete your emails from the inbox, and should-mark-messages-as-read="true" will mark them as read. -->
<int-mail:inbound-channel-adapter
id="imapAdapter"
store-uri="${imap.uri}"
channel="recieveEmailChannel"
should-delete-messages="false"
should-mark-messages-as-read="true"
auto-startup="true"
java-mail-properties="javaMailProperties">
<int:poller fixed-delay="${imap.poolerSecondsDelay}" time-unit="SECONDS" />
</int-mail:inbound-channel-adapter>
<int:channel id="recieveEmailChannel">
<int:interceptors>
<int:wire-tap channel="logger" />
</int:interceptors>
</int:channel>
<int:logging-channel-adapter id="logger"
level="DEBUG" />
<int:service-activator
input-channel="recieveEmailChannel" ref="emailReceiverService"
method="receive" />
<bean id="emailReceiverService"
class="com.dtp.integration.EmailReceiverService">
</bean>
</beans>
Here is the code I tried
public void receive(Message<?> message) throws MessagingException, IOException {
MimeMessage mimeMessage = (MimeMessage) message.getPayload();
mimeMessage.getAllHeaderLines();
String messageContent = getTextFromMessage(mimeMessage);
}
private String getTextFromMessage(MimeMessage message) throws MessagingException, IOException {
String result = "";
if (message.isMimeType("text/plain")) {
result = message.getContent().toString();
} else if (message.isMimeType("multipart/*")) {
MimeMultipart mimeMultipart = (MimeMultipart) message.getContent();
result = getTextFromMimeMultipart(mimeMultipart);
}
return result;
}
private String getTextFromMimeMultipart(
MimeMultipart mimeMultipart) throws MessagingException, IOException{
String result = "";
int count = mimeMultipart.getCount();
for (int i = 0; i < count; i++) {
BodyPart bodyPart = mimeMultipart.getBodyPart(i);
if (bodyPart.isMimeType("text/plain")) {
result = result + "\n" + bodyPart.getContent();
break; // without break same text appears twice in my tests
} else if (bodyPart.isMimeType("text/html")) {
String html = (String) bodyPart.getContent();
result = result + "\n" + org.jsoup.Jsoup.parse(html).text();
} else if (bodyPart.getContent() instanceof MimeMultipart){
result = result + getTextFromMimeMultipart((MimeMultipart)bodyPart.getContent());
}
}
return result;
}
thank you all for your contribution, the problem was with content of the message payload coming always null to the service activator bean, at the end I found that I should put this simple line on my inbound adapter configuration simple-content="true"

DefaultMessageListenerContainer not receiving all messages from ActiveMQ queue

ActiveMQ/Spring experts,
I am running into a very strange problem with ActiveMQ and DefaultMessageListenerContainer/SimpleMessageListenerContainer combination. We have a web application built using Spring (we are at 4.x). One of the transactions is related to processing a bulk upload of files and each file has a number of lines. Each line will become a message for processing.
A publisher publishes each line as a message to a persistent Queue. When examined through the ActiveMQ console we can see the message in the queue. To the same queue, we have a group of listeners configured using DefaultMessageListenerContainer(DMLC)/SimpleMessageListenerContainer(SMLC) (tried both) for consuming messages.
When the publisher publishes 100 messages sometimes only 99 or 98 messages are delivered but the rest are stuck in queue. The configuration is as follows:
ActiveMQ broker is running in standalone mode not networked or embedded in WildFly.
In the Spring application, we tried both DMLC and SMLC but both ran into this issue. Tried simpleMQConnectionFactory as well as PooledConnectionFactory and both times ran into same problem.
Tried setting the prefetch limit to "1" on the PooledConnectionFactory and ran into the same problem. Spring SMLC is throwing an exception when set to "0".
Max concurrent consumers is set to 50
When messages are stuck, if we restart WildFly the remaining messages in the queue are delivered to the consumers.
We are not using transacted sessions rather set the acknowledgModeName = "CLIENT_ACKNOWLEDGE"
We initialize the queue using a spring bean and use that for initializing the SMLC or DMLC
I am running out of options to try at this. If you share your experiences in this regard it is highly appreciated. This application is in production and the problem happens almost every other day sometimes multiple times in a day.
private void publishDMRMessage(DmrDTO p_dmrDTO, long jobID, int numDMRs) {
//Create a DMR message for each of the unique keys and publish it to
try {
DMRImportMessage message = new DMRImportMessage();
message.setDmrDTO(p_dmrDTO);
message.setDmrKey(p_dmrDTO.toString());
message.setDmrImportJobID(new Long(jobID));
message.setTask(Command.INITIALIZE_DMR_FORM);
message.setNumDMRForms(new Long(numDMRs));
sender.sendMessage(message);
} catch (Exception e) {
System.out.println(" JMS Exception = " + e.getMessage());
e.printStackTrace();
}
}
public class DMRMessageListener implements MessageListener {
private DMRImportManager manager;
private JMSMessageSender sender;
private DmrFormInitService formService;
private ProcessDMRValidationMessage validateService;
private ImportDmrService dmrService;
private static final Logger log = Logger.getLogger(DMRMessageListener.class);
public ImportDmrService getDmrService() {
return dmrService;
}
public void setDmrService(ImportDmrService dmrService) {
this.dmrService = dmrService;
}
public ProcessDMRValidationMessage getValidateService() {
return validateService;
}
public void setValidateService(ProcessDMRValidationMessage validateService) {
this.validateService = validateService;
}
public DmrFormInitService getFormService() {
return formService;
}
public void setFormService(DmrFormInitService formService) {
this.formService = formService;
}
public JMSMessageSender getSender() {
return sender;
}
public void setSender(JMSMessageSender sender) {
this.sender = sender;
}
public DMRImportManager getManager() {
return manager;
}
public void setManager(DMRImportManager manager) {
this.manager = manager;
}
public void onMessage(Message message) {
if (message instanceof ObjectMessage) {
try {
ObjectMessage objectMessage = (ObjectMessage) message;
DMRImportMessage dmrMessage = (DMRImportMessage)objectMessage.getObject();
log.info("============= Message Received =========================");
log.info("Message Type = " + dmrMessage.getTask() + " for JOB ID = " + dmrMessage.getDmrImportJobID());
log.info("Message Received === DMR ID = " + dmrMessage.getDmrID());
log.info("Message Received === DMR key = " + dmrMessage.getDmrKey());
log.info("============= Message Received =========================");
//Process the message
processDMRMessage(dmrMessage);
DMRProcessingStatus status = manager.updateStatus(dmrMessage);
if (status.isStatus()) {
log.info(" One stage is complete, the next stage should start for JOB ID = " + dmrMessage.getDmrImportJobID());
publishMessageForNextStepOfProcessing(dmrMessage, status);
}
}
catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException(ex);
}
}
else {
log.error(" ***** Received an invalid message -- NOT AN Object message so cannot be processed and will result in stuck jobs **** ");
throw new IllegalArgumentException("Message must be of type ObjectMessage");
}
//Send the next message in the chain
}
/**
* It will examine the message content and based on the message type it will invoke the appropriate
* service.
*
* #param dmrMessage DMRImportMessage
*/
private void processDMRMessage(DMRImportMessage dmrMessage) {
if (dmrMessage.getTask() == Command.INITIALIZE_DMR_FORM) {
Map<String, String> dmrInitResults = formService.initDmrForm(dmrMessage.getDmrDTO());
//Indicate in message that this DMR Key is not in ICIS
if (dmrInitResults != null) {
if (StringUtils.equalsIgnoreCase(dmrInitResults.get("wsUnscheduleDmrError"), "truee")) {
log.info("DMR Key is not in ICIS: " + dmrMessage.getDmrDTO().toString());
dmrMessage.setDmrKeyInICIS(false);
} else if (StringUtils.equalsIgnoreCase(dmrInitResults.get("wsDBDown"), "truee")) {
log.error("Web Service call failed for DMR Key: " + dmrMessage.getDmrDTO().toString());
}
}
}
try {
if (dmrMessage.getTask() == Command.IMPORT_DMR_PARAMETER) {
//Process the Parameter line
ParameterProcessingStatus status = dmrService.processLine(dmrMessage.getDmrImportJobID(), dmrMessage.getDmrParameterSubmission(), new Integer(dmrMessage.getLineNumber()), dmrMessage.getDmrKeysNotInICIS());
System.out.println("LINE = " + dmrMessage.getLineNumber() + " Status = " + status.isStatus());
dmrMessage.setProcessingStatus(status.isStatus());
dmrMessage.setDmrID(status.getDmrID());
dmrMessage.setDmrComment(status.getDmrComment());
return;
}
} catch(Exception e) {
log.error("An exception occurred during processing of line " + dmrMessage.getLineNumber() + " in job " + dmrMessage.getDmrImportJobID());
e.printStackTrace();
dmrMessage.setProcessingStatus(false);
dmrMessage.setDmrID(0L);
}
try {
if (dmrMessage.getTask() == Command.END_DMR_PARAMETER_IMPORT) {
//Process the Parameter line
//ParameterProcessingStatus status = dmrService.processLine(dmrMessage.getDmrImportJobID(), dmrMessage.getDmrParameterSubmission(), 100);
dmrMessage.setProcessingStatus(true);
dmrMessage.setDmrID(0L);
return;
}
} catch(Exception e) {
e.printStackTrace();
dmrMessage.setProcessingStatus(false);
dmrMessage.setDmrID(0L);
}
try {
if (dmrMessage.getTask() == Command.POST_PROCESS_DMR) {
//Validate DMRs
validateService.validateDMR(dmrMessage);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void publishMessageForNextStepOfProcessing(DMRImportMessage dmrMessage, DMRProcessingStatus status) throws JMSException {
log.info(" =========== Publish a message for next step of processing for JOB ID = " + dmrMessage.getDmrImportJobID());
if (dmrMessage.getTask() == Command.INITIALIZE_DMR_FORM) {
//Start the DMR Parameter Processing
sender.sendDMRControlMessage(this.createControlMessage(ProcessPhase.START_PARAMETER_PROCESSING, dmrMessage.getDmrImportJobID(), status.getDmrKeysNotInICIS()));
return;
}
if ((dmrMessage.getTask() == Command.IMPORT_DMR_PARAMETER)
|| (dmrMessage.getTask() == Command.END_DMR_PARAMETER_IMPORT)) {
//Start the DMR Validation Process
dmrService.postProcessParameters(dmrMessage.getDmrImportJobID(), status.getSuccessfulLines(), status.getErroredLines());
DMRImportControlMessage message = this.createControlMessage(ProcessPhase.START_DMR_VALIDATION, dmrMessage.getDmrImportJobID());
message.setDmrIDsWithComments(status.getDmrIDsWithComments());
sender.sendDMRControlMessage(message);
return;
}
if (dmrMessage.getTask() == Command.POST_PROCESS_DMR) {
//Start the next DMR import process
sender.sendDMRControlMessage(this.createControlMessage(ProcessPhase.START_DMR_FORM_INIT, dmrMessage.getDmrImportJobID()));
return;
}
log.info(" =========== End Publish a message for next step of processing for JOB ID = " + dmrMessage.getDmrImportJobID());
}
private DMRImportControlMessage createControlMessage(DMRImportControlMessage.ProcessPhase phase, Long jobID) {
return createControlMessage(phase, jobID, null);
}
private DMRImportControlMessage createControlMessage(DMRImportControlMessage.ProcessPhase p_phase, Long p_jobID, Set<DmrDTO> p_dmrDTOs) {
DMRImportControlMessage message = new DMRImportControlMessage();
message.setDmrImportJobID(p_jobID);
message.setPhase(p_phase);
if (p_dmrDTOs != null) {
message.setDmrKeysNotInICIS(p_dmrDTOs);
}
return message;
}
//Bean Configs.
<bean id="prefetchPolicy" class="org.apache.activemq.ActiveMQPrefetchPolicy">
<property name="queuePrefetch" value="0"/>
</bean>
<bean id="jmsFactoryPub" class="org.apache.activemq.ActiveMQConnectionFactory">
<constructor-arg index="0" value="tcp://localhost:61616" />
</bean>
<bean id="jmsFactoryReceive" class="org.apache.activemq.ActiveMQConnectionFactory">
<constructor-arg index="0" value="tcp://localhost:61616" />
<property name="prefetchPolicy" ref="prefetchPolicy" />
</bean>
<bean id="jmsFactoryControlMsg" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL">
<value>tcp://localhost:61616</value>
</property>
</bean>
<bean id="dmrQueue"
class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="DMRQueue" />
</bean>
<bean id="dmrControlQueue"
class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="DMRControlQueue" />
</bean>
<bean id="jmsQueueTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsFactoryPub" />
</bean>
<bean id="jmsQueueTemplateControlMsg" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsFactoryControlMsg" />
</bean>
<bean id="messageCreator" class="net.exchangenetwork.netdmr.service.DMRMessageCreator">
</bean>
<bean id="dmrMessageListener" class="net.exchangenetwork.netdmr.service.DMRMessageListener">
<property name="manager" ref="dmrImportManager"/>
<property name="sender" ref="messagePublisher"/>
<property name="formService" ref="dmrFormInit"/>
<property name="validateService" ref="dmrValidator"/>
<property name="dmrService" ref="importDmrService"/>
</bean>
<bean id="messageSender" class="net.exchangenetwork.netdmr.service.JMSMessageSender">
<property name="jmsTemplate" ref="jmsQueueTemplate" />
<property name="sendQueue" ref="dmrQueue" />
<property name="creator" ref="messageCreator" />
</bean>
<bean id="messagePublisher" class="net.exchangenetwork.netdmr.service.JMSMessageSender">
<property name="jmsTemplate" ref="jmsQueueTemplateControlMsg" />
<property name="sendQueue" ref="dmrControlQueue" />
<property name="creator" ref="messageCreator" />
</bean>
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsFactoryReceive"/>
<!-- this is the queue we will listen on -->
<property name="destination" ref="dmrQueue" />
<property name="messageListener" ref="dmrMessageListener"/>
<property name="concurrentConsumers" value="60"/>
<property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE"/>
<property name="errorHandler" ref="jmsErrorHandler"/>
<property name="exceptionListener" ref="jmsExceptionHandler"/>
<property name="receiveTimeout" value="0"/>
</bean>

method in spring to read txt file

I am having a requirement wherein I need to read the contents of a text file through spring framework. For this purpose I made a method in my service implementation class as below-
public String readFile(File file)
This method will take the file name as input and read the file.
I was writing the code in XML for spring as below-
<bean id="fstream" class="java.io.FileInputStream">
<constructor-arg value="C:/text.txt" />
</bean>
<bean id="in" class="java.io.DataInputStream">
<constructor-arg ref="fstream"/>
</bean>
<bean id="isr" class="java.io.InputStreamReader">
<constructor-arg ref="in"/>
</bean>
<bean id="br" class="java.io.BufferedReader">
<constructor-arg ref="isr"/>
</bean>
Following code goes in my method-
public String readFile(File file)
{
String line = null;
String content = "";
try
{
ApplicationContext context = new ClassPathXmlApplicationContext("FileDBJob.xml");
BufferedReader br = (BufferedReader) context.getBean("br");
while((line = br.readLine())!=null)
content = content.concat(line);
}
catch (Exception e)
{
e.printStackTrace();
}
return content;
}
But here the issue is that i need to hardcode the file name in XML, so there is no use of file parameter.
Kindly help in finding the solution. As I am new to spring and trying my hands with it so it may be possible that I am missing something. Any help would be of great help.
Don't inject the streams and readers, that's not really how Spring is intended to be used. I'd inject the file itself:
public class MyFileReader {
private File file;
public String readFile() {
StringBuilder builder = new StringBuilder();
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(getFile()));
String line = null;
while ((line = reader.readLine()) != null)
builder.append(line);
} catch (IOException e) {
e.printStackTrace();
} finally {
closeQuietly(reader);
}
return builder.toString();
}
private void closeQuietly(Closeable c) {
if (c != null) {
try {
c.close();
} catch (IOException ignored) {}
}
}
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
}
}
Then your bean def looks like this:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:app.properties"/>
</bean>
<bean class="com.myapp.MyFileReader">
<property name="file" value="${filePath}" />
</bean>
All that is left is to create your app.properties file with the correct info. You can also set the value by invoking the app with a -DfilePath=/foo/bar/whatever.txt
I have tested this code its working.....
Try to implement....you have to copy paste schedular.xml file in ur proj configuration folder(where applicationContext.xml file in ur application and it has to be
contextConfigLocation
WEB-INF/config/*.xml
in ur web.xml file).
Then configure SvhedularTask bean in ur service classes xml file....it will trigger for every minute.
////SCHEDULARTASK.JAVA//////
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Date;
import javax.servlet.ServletContext;
import org.springframework.web.context.ServletContextAware;
/**
* The Class SchedulerTask.
*/
public class SchedulerTask implements ServletContextAware{
private ServletContext servletContext;
#Override
public void setServletContext(ServletContext arg0) {
// TODO Auto-generated method stub
this.servletContext = arg0;
}
public void unZipProcess() throws IOException{
System.out.println(servletContext);
File folder = new File("C:/Users/rerrabelli/Desktop/test");
File[] listOfFiles = folder.listFiles();
if (listOfFiles != null){
for (int i = 0; i < listOfFiles.length; i++) {
if (listOfFiles[i].isFile()) {
if (listOfFiles[i].getName().endsWith(".txt")) {
File file = new File("C:/Users/rerrabelli/Desktop/test" + File.separator
+ listOfFiles[i].getName());
long millisec = file.lastModified();
Date dt = new Date(millisec);
long difference = new Date().getTime()-dt.getTime();
System.out.println((difference/1000)/60);
if(((difference/1000)/60)<1){
FileInputStream fin = new FileInputStream(
file);
ByteArrayOutputStream tmp = new ByteArrayOutputStream();
byte b;
while ((b = (byte) fin.read()) != -1) {
tmp.write(b);
}
byte[] customerData = tmp.toByteArray();
String data = new String(customerData);
System.out.println(data);
servletContext.setAttribute(file.getName(), data);
}
}
}
}
}
System.out.println(servletContext.getAttribute("test.txt"));
}
}
//////APPLICATION CONTEXT.xml/////////
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<bean id="schedulerTask" class="com.altimetrik.simreg.service.impl.SchedulerTask">
</bean>
</beans>
======================
SCHEDULAR.XML
===========
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd" >
<beans>
<import resource="applicationContext.xml"/>
<bean id="schedulerTask1"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject"> <ref bean="schedulerTask" /> </property>
<property name="targetMethod"> <value>unZipProcess</value> </property>
<property name="concurrent"> <value>false</value> </property>
</bean>
<bean id="UnzipTrigger"
class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail"> <ref bean="schedulerTask1" /> </property>
<property name="cronExpression"> <value>0 0/1 * * * ?</value> </property>
</bean>
<bean
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<!-- Add triggers here-->
<ref bean="UnzipTrigger" />
</list>
</property>
</bean>
</beans>

Spring integeration - error-channel handling issues

I am new in Spring Integeration.I have one requirement Using spring integeration
read a txt file (from Source folder)
do some validation
if validation is success -write into sucess file (in sucess folder)
If the validation is fail -write into failure file (in error folder)
if the file format is incorrect means I have to move that file into error folder(Ex excepted columns is 2 but in my file contain columns is 1)
My config file is like this
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:si="http://www.springframework.org/schema/integration"
xmlns:file="http://www.springframework.org/schema/integration/file"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
http://www.springframework.org/schema/integration/file
http://www.springframework.org/schema/integration/file/spring-integration-file-1.0.xsd">
<bean id="checkCSVReader"
class="com.check.wrapper">
<property name="pzMapXML" value="classpath:sampleFileFormat.xml" />
</bean>
<bean id="checkTrasnFomer"
class="com.check.checkTransfomer">
<property name="wrapper" ref="checkCSVReader" />
</bean>
<bean id="fileErrorProcessor"
class="com.check.ErrorChannelWriter">
</bean>
<bean id="listToStringTrans"
class="com.check.ListToStringTransfomer"></bean>
<bean id="validation"
class="com.check.Validation"/>
<file:inbound-channel-adapter directory="file://D:\check\soruce" prevent-duplicates="false"
auto-create-directory="true" channel="readChannel" >
<si:poller id="Poller">
<si:interval-trigger interval="10000" />
</si:poller>
</file:inbound-channel-adapter>
<si:channel id="readChannel" />
<si:chain input-channel="readChannel" output-channel="processChannel">
<si:header-enricher error-channel="errorFile" />
<file:file-to-string-transformer />
<si:transformer ref="checkTrasnFomer" method="transform" />
<si:service-activator ref="validation"
method="validate" />
</si:chain>
<si:channel id="processChannel" />
<si:transformer ref="listToStringTrans" method="transformList"
input-channel="processChannel" output-channel="finalOut" />
<si:channel id="finalOut" />
<file:outbound-channel-adapter id="checkSuccFileOutBound"
auto-create-directory="true" delete-source-files="true"
directory="file://D:\check\success" channel="finalOut">
</file:outbound-channel-adapter>
<si:channel id="errorFile" />
<si:transformer ref="fileErrorProcessor"
input-channel="errorFile" output-channel="errorChannel" method="transformError" />
<file:outbound-channel-adapter id="errorChannel"
directory="file://D:\check\error" delete-source-files="true"
/>
<si:channel id="checkFileErr" />
</beans>
my checkFlatPackCVSParserWrapper class is
public class checkFlatPackCVSParserWrapper {
private static final Log LOG = LogFactory.getLog("checkFlatPackCVSParserWrapper");
private Resource pzMapXML;
private char delimiter = ',';
private char qualifier = '"';
private boolean ignoreFirstRecord = false;
public Resource getPzMapXML() {
return pzMapXML;
}
public void setPzMapXML(Resource pzMapXML) {
this.pzMapXML = pzMapXML;
}
public char getDelimiter() {
return delimiter;
}
public void setDelimiter(char delimiter) {
this.delimiter = delimiter;
}
public char getQualifier() {
return qualifier;
}
public void setQualifier(char qualifier) {
this.qualifier = qualifier;
}
public boolean isIgnoreFirstRecord() {
return ignoreFirstRecord;
}
public void setIgnoreFirstRecord(boolean ignoreFirstRecord) {
this.ignoreFirstRecord = ignoreFirstRecord;
}
public Parser getParser(String csv) {
if(LOG.isDebugEnabled())
LOG.debug("getParser: " + csv);
Parser result = null;
try {
result = DefaultParserFactory.getInstance().newDelimitedParser(
pzMapXML.getInputStream(), //xml column mapping
new ByteArrayInputStream(csv.getBytes()), //txt file to parse
delimiter, //delimiter
qualifier, //text qualfier
ignoreFirstRecord);
}catch (Exception e) {
if(LOG.isDebugEnabled())
LOG.debug("Unable to read file: " + e );
throw new RuntimeException("File Parse exception");
}
return result;
}
}
sampleFileFormat.xml is
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE PZMAP SYSTEM "flatpack.dtd" >
<PZMAP>
<COLUMN name="FIRSTNAME" />
<COLUMN name="LASTNAME" />
</PZMAP>
and checkTransfomer is
public class checkTransfomer {
private static final Log LOG = LogFactory.getLog(checkTransfomer.class);
private CheckFlatPackCVSParserWrapper wrapper;
public String transform(String csv) {
Parser parser = wrapper.getParser(csv);
if(LOG.isDebugEnabled()) {
LOG.debug("Parser is: " + parser);
}
DataSet ds = parser.parse();
ArrayList<Check> list = new ArrayList<Check>();
while(ds.next()) {
Check check= new Check();
check.setFirstName(ds.getString("FIRSTNAME"));
check.setLastName(ds.getString("LASTNAME"));
if(LOG.isDebugEnabled()) {
LOG.debug("Bean value is: " + bean);
}
list.add(bean);
}
if(LOG.isDebugEnabled()) {
LOG.debug("Records fetched is: " + list.size());
}
return list.toString();
}
public CheckFlatPackCVSParserWrapper getWrapper() {
return wrapper;
}
public void setWrapper(CheckFlatPackCVSParserWrapper wrapper) {
this.wrapper = wrapper;
}
And my ErrorChannelWriter is
public class ErrorChannelWriter {
public static final Log LOG = LogFactory.getLog(ErrorChannelWriter.class);
public Message<?> transformError(ErrorMessage errorMessage) {
if (LOG.isDebugEnabled()) {
LOG.debug("Transforming errorMessage is: " + errorMessage);
}
return ((MessagingException) errorMessage.getPayload())
.getFailedMessage();
}
}
and my validagtion class is
com.check.Validation
public class Validation
{
void validation(CheckCheck)
{
if(Check.getFirstName().equals("maya"))
{
throw new RuntimeException("Name Already exist");
}
}
}
and my ListToStringTransfomer is
public class ListToStringTransfomer {
private static final Log LOG=LogFactory.getLog(ListToStringTransfomer.class);
public String transformList(List<IssueAppBean> list) {
return list.toString();
}
}
and my file containing one fields instead of two fields
> maya
here my file format is wrong, so record is moving to error folder.but there is no error message. how can i add error message(TOO FEW COLUMNS WANTED: 2 GOT: 1) when my file format is incorrect.
my requirement is in my error file should contaion
maya -TOO FEW COLUMNS WANTED: 2 GOT: 1 or(Any error message )
please give me any solution
I don't think that you should go through the error channel to solve this requirement. The main reason for this is that invalid input in this case is an expected scenario. The errorChannel is the channel that Spring Integration sends messages to if an unexpected exception happens in an endpoint.
If you add some header to the message if a validation failed, you can route based on that header and also record the failure message there. You can then send your error message to a log file or whatever on your own.

Resources