/*
* ChainBuilder ESB
* Visual Enterprise Integration
*
* Copyright (C) 2006 Bostech Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
* $Id: ConsumerProcessor.java 1206 2006-09-23 03:51:32Z elu $
*/
package com.bostechcorp.cbesb.runtime.component.jms.processors;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import javax.jbi.JBIException;
import javax.jbi.messaging.MessageExchange;
import javax.jbi.messaging.NormalizedMessage;
import javax.jms.BytesMessage;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.TextMessage;
import javax.resource.spi.work.Work;
import javax.resource.spi.work.WorkException;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.bostechcorp.cbesb.common.runtime.CbesbException;
import com.bostechcorp.cbesb.common.runtime.ConfigurationException;
import com.bostechcorp.cbesb.common.runtime.ResourcesConnectionException;
import com.bostechcorp.cbesb.common.util.ErrorUtil;
import com.bostechcorp.cbesb.runtime.ccsl.jbi.messaging.CbConsumerProcessor;
import com.bostechcorp.cbesb.runtime.ccsl.jbi.messaging.StatusConstants;
import com.bostechcorp.cbesb.runtime.ccsl.lib.ExternalInput;
import com.bostechcorp.cbesb.runtime.ccsl.messages.FaultMessage.FaultMessageTypes;
import com.bostechcorp.cbesb.runtime.ccsl.nmhandler.FaultHandler;
import com.bostechcorp.cbesb.runtime.ccsl.nmhandler.SourceHelper;
import com.bostechcorp.cbesb.runtime.component.jms.JMSEndpoint;
import com.bostechcorp.cbesb.runtime.component.jms.JMSMarshaler;
import com.bostechcorp.cbesb.runtime.component.util.wsdl.WsdlMepConstants;
import com.bostechcorp.cbesb.runtime.jms.JMSConsumerHandler;
import com.bostechcorp.cbesb.runtime.jms.JMSHandler;
public class ConsumerProcessor extends CbConsumerProcessor {
protected final transient Log logger = LogFactory.getLog(getClass());
protected AtomicBoolean running = new AtomicBoolean(false);
//TODO Eric Lu : please pay attention to the thread-safety issue here for using any attributes
private JMSHandler jmsHandler;
private JMSEndpoint endpoint;
// private MessageExchange exchange;
// added in JMS 1.2
Message jmsInMessage = null;
private FaultMessageTypes faultType = null;
//Important: rename retrysLeft -> retrysCount, we want it compare to endpoint.getMaxRetryCount() directly,
//So we can support use change MaxRetryCount property of endpoint dynamically by Admin Console
protected AtomicInteger retrysCount = new AtomicInteger(0);
// protected long retryInterval = 0;
private Work pollWork = null;
// since version 1.2b
private boolean recoverableInRetryFault=false;
// private boolean needsReply;
//
public ConsumerProcessor(JMSEndpoint endpoint) {
super(endpoint);
this.endpoint = endpoint;
}
protected void doStart() throws ConfigurationException,
ResourcesConnectionException, JMSException, WorkException,
InterruptedException {
// try {
String initialContextFactory = null;
String providerUrl = null;
String connectionFactoryName = null;
String userName = null;
String password = null;
if (endpoint.getJndiInitialContextFactory() != null)
initialContextFactory = endpoint.getJndiInitialContextFactory();
if (endpoint.getJndiProviderUrl() != null)
providerUrl = endpoint.getJndiProviderUrl();
if (endpoint.getJndiConnectionFactoryName() != null)
connectionFactoryName = endpoint.getJndiConnectionFactoryName();
logger.debug("defaultMep:"+endpoint.getDefaultMep());
if (!endpoint.getDefaultMep().equals(WsdlMepConstants.IN_ONLY)) {
// creating a tranactional/nonTransactional handler
logger.info("Creating transactional JMS Handler");
jmsHandler = new JMSConsumerHandler(initialContextFactory, providerUrl,
connectionFactoryName, userName, password, endpoint
.getDestinationStyle(), endpoint
.getTargetDestinationName(), endpoint
.getReplyDestinationName(), endpoint
.isTransactional(),endpoint.getDLQ());
// jmsHandler.setExceptionListener(this);
//retry relate to transaction.
if (endpoint.isTransactional() && endpoint.isRetry()) // getting retry settings
{
// synchronized(retrysCount)
// {retrysCount.set(endpoint.getMaxRetryCount());
// retrysCount.notify();
// }
//retryInterval = endpoint.getRetryInterval();
logger.debug("Retry mode enabled. "+endpoint.getMaxRetryCount()+" retry's count with interval of "+endpoint.getRetryInterval()+" ms");
}else
{
logger.debug("Retry mode Disabled.");
}
} else// Inonly Mep: non transactional handler
{
//Transaction not supported for InOnly MEP
logger.info("Creating nontransactional JMS Handler");
jmsHandler = new JMSConsumerHandler(initialContextFactory, providerUrl,
connectionFactoryName, userName, password, endpoint
.getDestinationStyle(), endpoint
.getTargetDestinationName(), endpoint
.getReplyDestinationName(), false,endpoint.getDLQ());
}
synchronized (running) {
synchronized(retrysCount){
pollWork = new Work() {
public void release() {}
public void run()
{ConsumerProcessor.this.poll();}
};
endpoint.getServiceUnit().getComponent().getWorkManager()
.startWork(pollWork);
running.wait();
retrysCount.wait();
}
}
}
protected void doStop(boolean retryStopped) throws Exception {
logger.debug("try to stop.........");
if(jmsHandler!=null) {
logger.debug("try to role back and close jmshandler");
jmsHandler.rollback();
jmsHandler.close();
}
logger.debug("try to set running to false");
synchronized(retrysCount)
{
retrysCount.incrementAndGet(); // decreasing retrys left
retrysCount.notify();
}
synchronized(running) {
running.set(false);
running.notify();
}
logger.debug("setted running to false");
logger.debug("stop done");
if(retryStopped)
endpoint.setState(StatusConstants.STATUS_RETRY_STOPPRD);
else
endpoint.setState(StatusConstants.STATUS_DOWN);
}
/**
* transacted pool
*/
protected void poll() {
synchronized (running) {
running.set(true);
running.notify();
}
try {
while (running.get()) {
logger.info("Consumer polling message from queue.");
jmsInMessage = null;
// just in case the selector is used, then we filter the recived
// messages based on selector
if (endpoint.isEnableQuery()) {
jmsInMessage = jmsHandler.receiveFiltered(endpoint
.getQueryExpression(), false,true);
} else {
//jmsInMessage = jmsHandler.receive();
jmsInMessage = jmsHandler.receiveFiltered("", false,true);
}
if (jmsInMessage != null) {
debugJmsMessageProperties(jmsInMessage); // Print properties
ExternalInput input = jmsMessageToExternalInput();
while (input.hasMoreData()) {
// exchange = null;
long startProcessTime=System.currentTimeMillis();
faultType = null;// forget any previous error
logger.debug("POLL() BEFORE PROCESS(INPUT)");
process(input); // this does a callback to transform()
logger.debug("POLL() AFTER PROCESS(INPUT)");
if (faultType != null){
//Recoverable fault, already roleback in doProcessFault() method
if(faultType.equals(FaultMessageTypes.RECOVERABLE)){
synchronized (pollWork){
pollWork.wait(endpoint.getRetryInterval());
}
break;
}
//UnRecoverable falut, already commit in DoProcessFault() method
//else continue;
}
//No fault
else {
jmsHandler.commit();
if(!endpoint.getState().equals(StatusConstants.STATUS_UP))
endpoint.setState(StatusConstants.STATUS_UP);
}
endpoint.sendMessageProcessedNotification(System.currentTimeMillis()-startProcessTime);
}
}
if(jmsHandler.isClosed()) {
logger.info("jmsHandler closed");
break;
}
}
} catch (Exception e) {
ErrorUtil.printError("Exception in poll(): ", e);
} finally {
synchronized (running) {
running.set(false);
running.notify();
logger.info("Polling Stoped; \"running\" flag set to False");
}
}
}
private ExternalInput jmsMessageToExternalInput()
throws Exception {
String charset = endpoint.getCharset();
String readStyle = endpoint.getReadStyle();
String recType = endpoint.getRecordType();
int rpm = Integer.parseInt(endpoint.getRecordsPerMessage());
ExternalInput input = null;
if (jmsInMessage instanceof TextMessage) {
TextMessage tm = (TextMessage) jmsInMessage;
input = new ExternalInput(new StringReader(tm.getText()), charset,
readStyle, recType, rpm);
} else if (jmsInMessage instanceof BytesMessage) {
BytesMessage bm = (BytesMessage) jmsInMessage;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[2048];
for (int l = 0; (l = bm.readBytes(buf)) > 0;)
baos.write(buf, 0, l);
input = new ExternalInput(new ByteArrayInputStream(baos
.toByteArray()), charset, readStyle, recType, rpm);
} else
throw new Exception("unsupported JMS message type " + jmsInMessage);
return input;
}
public void transform(Object data, MessageExchange me) throws Exception {
super.transform(data, me);
// getting all properties from jmsInMessage and setting them to
// NMExchange
JMSMarshaler.addMetatataToNMExchange(me, jmsInMessage);
// exchange = me;
}
@Override
protected void doProcessFault(NormalizedMessage nm, String fault)
throws JMSException, InterruptedException, ConfigurationException,Exception {
// /if there is any kind of fault
// message then just put the JMSMessage back in the queue
recoverableInRetryFault=false;
FaultHandler fh = new FaultHandler(fault);
logger.info("Consumer endpoint '" + endpoint.getEndpoint()
+ "' reports error: " + fh.getMessage());
if (fh.getEndpointString() != null)
logger.info("The error was occured at the endpoint: "
+ fh.getEndpointString());
logger.debug("Fault handler: isRecoverable="+fh.isRecoverable());
if (fh.isRecoverable()) {
faultType = FaultMessageTypes.RECOVERABLE;
jmsHandler.rollback();
// send notification
endpoint.sendJMSTransactionRollBackNotification();
logger
.info("JMS Rollback in doProcessFault, fault is Recoverable");
if (endpoint.isRetry()) { // if in retry mode
logger.debug("JMS Is in retry Mode: retryInterval="
+ endpoint.getRetryInterval() + "; Retry's count=" + retrysCount.get());
//Thread.sleep(retryInterval); // wait a while,
synchronized(retrysCount)
{
retrysCount.incrementAndGet(); // decreasing retrys left
retrysCount.notify();
}
// stop message pooling
if (retrysCount.get() >=endpoint.getMaxRetryCount()){
logger.info("retry count exceeded");
doStop(true);
//send notification
logger.debug("send notification :retry count exceeded");
endpoint.sendJMSRetryCountExceededNotification();
logger.debug("set state:retry stooped");
}else
{
recoverableInRetryFault=true;
endpoint.setState(StatusConstants.STATUS_RETRY_STARTED);
}
} else // if in noRetry mode and recoverable fault
// then just stop the component and roll back
{
doStop(false);
}
} else {
faultType = FaultMessageTypes.UNRECOVERABLE;
logger.debug("before write to DLQ");
jmsHandler.writeToDLQ(jmsInMessage);
logger.debug("after write to DLQ");
// send notification
endpoint.sendJMSDLQNotification();
logger.debug("after sent DLQ notification");
jmsHandler.commit();
logger.info("JMS Commit in doProcessFault, Fault is Urecoverable");
}
}
/*
* Process out situation
*
* @see com.bostechcorp.cbesb.runtime.ccsl.jbi.messaging.BaseConsumerProcessor#doProcessOut(javax.jbi.messaging.NormalizedMessage,
* java.lang.String)
*/
@Override
protected void doProcessOut(NormalizedMessage nm, String s,
MessageExchange exchange) throws JMSException,
ConfigurationException, TransformerConfigurationException,
IOException, TransformerException {
String charset = endpoint.getCharset();
//TODO: does reply in consumer need to check savaMetadata
Message jmsReply = JMSMarshaler.normalizedToJMS(exchange
.getMessage("out"), exchange, endpoint.getWriteStyle(),
charset, jmsHandler.getSession(),false);
// getting all properties from mExchange and setting them to the
// jms message
// JMSMarshaler.addMetatataToJms(jmsReply, exchange);
// sending the message to reply queue
jmsHandler.reply(jmsInMessage, jmsReply);
// Process the Out message and write it into the reply queue.
}
/* (non-Javadoc)
* @see com.bostechcorp.cbesb.runtime.ccsl.jbi.messaging.CbConsumerProcessor#doProcessFault(javax.jbi.messaging.NormalizedMessage)
*/
@Override
protected String doProcessFault(NormalizedMessage nm) throws JBIException,
CbesbException {
//needs to be overwritten because of the bug 474
String fault = null;
if (nm != null) {
fault = SourceHelper.createString(nm.getContent());
}
try {
doProcessFault(nm, fault);
//since version 1.2b
if (recoverableInRetryFault) return "retry";
//
} catch (Exception e) {
throw CbesbException.create(e);
}
return fault;
}
/**
* Print out at DEBUG level, jms message properties
* @param msg
* @throws JMSException
*/
protected void debugJmsMessageProperties(Message msg) throws JMSException
{
Enumeration enumeration=msg.getPropertyNames();
String result="JMS Message Properies:\n";
while (enumeration.hasMoreElements())
{
String propName =(String)enumeration.nextElement();
Object propValue = msg.getObjectProperty(propName);
result+=("["+propName+"]"+"="+propValue)+"\n";
}
result+=("JMS Message Properies:(endprint)--->")+"\n";
logger.debug(result);
}
}
|