if(lock != null && !lock.ownsLock(workId, this)) {
if (log.isDebugEnabled()) log.debug("Exit: SenderWorker::run, another worker holds the lock");
return;
}
Transaction transaction = null;
try {
StorageManager storageManager = SandeshaUtil.getSandeshaStorageManager(configurationContext, configurationContext.getAxisConfiguration());
SenderBeanMgr senderBeanMgr = storageManager.getSenderBeanMgr();
transaction = storageManager.getTransaction();
String key = senderBean.getMessageContextRefKey();
MessageContext msgCtx = null;
RMMsgContext rmMsgCtx = null;
if(messageToSend != null) {
msgCtx = messageToSend.getMessageContext();
rmMsgCtx = messageToSend;
} else {
msgCtx = storageManager.retrieveMessageContext(key, configurationContext);
if (msgCtx == null) {
// This sender bean has already been processed
if(transaction != null && transaction.isActive()) transaction.commit();
transaction = null;
return;
}
rmMsgCtx = MsgInitializer.initializeMessage(msgCtx);
}
// sender will not send the message if following property is
// set and not true.
// But it will set if it is not set (null)
// This is used to make sure that the mesage get passed the
// Sandesha2TransportSender.
String qualifiedForSending = (String) msgCtx.getProperty(Sandesha2Constants.QUALIFIED_FOR_SENDING);
if (qualifiedForSending != null && !qualifiedForSending.equals(Sandesha2Constants.VALUE_TRUE)) {
if (log.isDebugEnabled())
log.debug("Exit: SenderWorker::run, !qualified for sending");
if(transaction != null && transaction.isActive()) {
transaction.commit();
transaction = null;
}
return;
}
if (msgCtx == null) {
if (log.isDebugEnabled())
log.debug(SandeshaMessageHelper.getMessage(SandeshaMessageKeys.sendHasUnavailableMsgEntry));
if(transaction != null && transaction.isActive()) {
transaction.commit();
transaction = null;
}
return;
}
// operation is the lowest level Sandesha2 should be attached
ArrayList<Integer> msgsNotToSend = SandeshaUtil.getPropertyBean(msgCtx.getAxisOperation()).getMsgTypesToDrop();
if (msgsNotToSend != null && msgsNotToSend.contains(new Integer(rmMsgCtx.getMessageType()))) {
if (log.isDebugEnabled())
log.debug("Exit: SenderWorker::run, message type to be dropped " + rmMsgCtx.getMessageType());
if(transaction != null && transaction.isActive()) {
transaction.commit();
transaction = null;
}
return;
}
// If we are sending to the anonymous URI then we _must_ have a transport waiting,
// or the message can't go anywhere. If there is nothing here then we leave the
// message in the sender queue, and a MakeConnection (or a retransmitted request)
// will hopefully pick it up soon.
Boolean makeConnection = (Boolean) msgCtx.getProperty(Sandesha2Constants.MAKE_CONNECTION_RESPONSE);
EndpointReference toEPR = msgCtx.getTo();
MessageContext inMsg = null;
OperationContext op = msgCtx.getOperationContext();
RequestResponseTransport t = (RequestResponseTransport) msgCtx.getProperty(RequestResponseTransport.TRANSPORT_CONTROL);
if (t==null) {
if (op != null)
inMsg = op.getMessageContext(WSDLConstants.MESSAGE_LABEL_IN_VALUE);
if (inMsg != null)
t = (RequestResponseTransport) inMsg.getProperty(RequestResponseTransport.TRANSPORT_CONTROL);
}
// If we are anonymous, and this is not a makeConnection, then we must have a transport waiting
if((toEPR==null || toEPR.hasAnonymousAddress()) &&
(makeConnection == null || !makeConnection.booleanValue()) &&
(t == null || !t.getStatus().equals(RequestResponseTransportStatus.WAITING))) {
// Mark this sender bean so that we know that the transport is unavailable, if the
// bean is still stored.
SenderBean bean = senderBeanMgr.retrieve(senderBean.getMessageID());
if(bean != null && bean.isTransportAvailable()) {
bean.setTransportAvailable(false);
senderBeanMgr.update(bean);
}
// Commit the update
if(transaction != null && transaction.isActive()) transaction.commit();
transaction = null;
if (log.isDebugEnabled())
log.debug("Exit: SenderWorker::run, no response transport for anonymous message");
return;
}
int messageType = senderBean.getMessageType();
if (isAckPiggybackableMsgType(messageType)) {
// Commit the update
if(transaction != null && transaction.isActive()) transaction.commit();
transaction = storageManager.getTransaction();
// Piggyback ack messages based on the 'To' address of the message
AcknowledgementManager.piggybackAcksIfPresent(rmMsgCtx, storageManager);
}
if (transaction != null && transaction.isActive())
transaction.commit();
transaction = storageManager.getTransaction();
/*Removing the sending of AckRequested msgs as has performance impact.
It could be rewritten to send AckRequested headers infrequantly and
hence be less of a performance impact. Functionally it
may be required to interop with other implementations but until
the problem occurs it's best not to do it at all and keep performance
as optimal as possible
//If this is an application msg we need to add an ackRequest to the header
if(messageType == Sandesha2Constants.MessageTypes.APPLICATION){
//Add an ackRequest
RMSBean rmsBean = SandeshaUtil.getRMSBeanFromSequenceId(storageManager, senderBean.getSequenceID());
RMMsgCreator.addAckRequest(rmMsgCtx, senderBean.getSequenceID(), rmsBean);
if (transaction != null && transaction.isActive())
transaction.commit();
transaction = storageManager.getTransaction();
} */
//if this is a sync RM exchange protocol we always have to add an ack
boolean ackPresent = false;
Iterator it = rmMsgCtx.getSequenceAcknowledgements();
if (it.hasNext())
ackPresent = true;
if (!ackPresent && toEPR.hasAnonymousAddress()
&& rmMsgCtx.getMessageContext().isServerSide()
&&
(messageType==Sandesha2Constants.MessageTypes.APPLICATION ||
messageType==Sandesha2Constants.MessageTypes.UNKNOWN ||
messageType==Sandesha2Constants.MessageTypes.LAST_MESSAGE)) {
String inboundSequenceId = senderBean.getInboundSequenceId();
if (inboundSequenceId==null)
throw new SandeshaException ("InboundSequenceID is not set for the sequence:" + senderBean.getSequenceID());
RMDBean incomingSequenceBean = SandeshaUtil.getRMDBeanFromSequenceId(storageManager, inboundSequenceId);
if (incomingSequenceBean!=null)
RMMsgCreator.addAckMessage(rmMsgCtx, inboundSequenceId, incomingSequenceBean, false, true);
}
if (transaction != null && transaction.isActive())
transaction.commit();
transaction = storageManager.getTransaction();
senderBean = updateMessage(rmMsgCtx,senderBean,storageManager);
if (senderBean == null) {
if (log.isDebugEnabled())
log.debug("Exit: SenderWorker::run, !continueSending");
if(transaction != null && transaction.isActive()) {
transaction.rollback();
transaction = null;
}
return;
}
// Although not actually sent yet, update the send count to indicate an attempt
if (senderBean.isReSend()) {
senderBeanMgr.update(senderBean);
}
// sending the message
boolean successfullySent = false;
// No need to redecorate application messages ... only for rm protocol messages
if(Sandesha2Constants.MessageTypes.APPLICATION!=senderBean.getMessageType()){
//try to redecorate the EPR if necessary
if (log.isDebugEnabled())
log.debug("Redecorate EPR : " + msgCtx.getEnvelope().getHeader());
EndpointReference replyToEPR = msgCtx.getReplyTo();
if(replyToEPR!=null){
replyToEPR = SandeshaUtil.getEPRDecorator(msgCtx.getConfigurationContext()).decorateEndpointReference(replyToEPR);
msgCtx.setReplyTo(replyToEPR);
}
}
// have to commit the transaction before sending. This may
// get changed when WS-AT is available.
if(transaction != null) {
transaction.commit();
transaction = null;
}
boolean processResponseForFaults = false;
try {
InvocationResponse response = InvocationResponse.CONTINUE;
if(storageManager.requiresMessageSerialization()) {
if(msgCtx.isPaused()) {
if (log.isDebugEnabled())
log.debug("Resuming a send for message : " + msgCtx.getEnvelope().getHeader());
msgCtx.setPaused(false);
msgCtx.setProperty(MessageContext.TRANSPORT_NON_BLOCKING, Boolean.FALSE);
response = AxisEngine.resumeSend(msgCtx);
} else {
if (log.isDebugEnabled())
log.debug("Sending a message : " + msgCtx.getEnvelope().getHeader());
msgCtx.setProperty(MessageContext.TRANSPORT_NON_BLOCKING, Boolean.FALSE);
AxisEngine.send(msgCtx); // TODO check if this should return an invocation response
}
} else {
ArrayList<Handler> retransmittablePhases = (ArrayList<Handler>) msgCtx.getProperty(Sandesha2Constants.RETRANSMITTABLE_PHASES);
if (retransmittablePhases!=null) {
msgCtx.setExecutionChain(retransmittablePhases);
} else {
ArrayList<Handler> emptyExecutionChain = new ArrayList<Handler>();
msgCtx.setExecutionChain(emptyExecutionChain);
}
msgCtx.setCurrentHandlerIndex(0);
msgCtx.setCurrentPhaseIndex(0);
msgCtx.setPaused(false);
if (log.isDebugEnabled())
log.debug("Resuming a send for message : " + msgCtx.getEnvelope().getHeader());
msgCtx.setProperty(MessageContext.TRANSPORT_NON_BLOCKING, Boolean.FALSE);
response = AxisEngine.resumeSend(msgCtx);
}
if(log.isDebugEnabled()) log.debug("Engine resume returned " + response);
if(response != InvocationResponse.SUSPEND) {
if(t != null) {
if(log.isDebugEnabled()) log.debug("Signalling transport in " + t);
t.signalResponseReady();
}
}
successfullySent = true;
} catch (AxisFault e) {
//this is a possible SOAP 1.2 Fault. So letting it proceed.
processResponseForFaults = true;
recordError(e, rmMsgCtx, storageManager);
} catch (Exception e) {
String message = SandeshaMessageHelper.getMessage(
SandeshaMessageKeys.sendMsgError, e.toString());
if (log.isDebugEnabled())
log.debug(message, e);
recordError(e, rmMsgCtx, storageManager);
}
// Establish the transaction for post-send processing
transaction = storageManager.getTransaction();
// update or delete only if the object is still present.
SenderBean bean1 = senderBeanMgr
.retrieve(senderBean.getMessageID());
if (bean1 != null) {
if (senderBean.isReSend()) {
bean1.setTimeToSend(senderBean.getTimeToSend());
senderBeanMgr.update(bean1);
} else {
senderBeanMgr.delete(bean1.getMessageID());
// removing the message from the storage.
String messageStoredKey = bean1.getMessageContextRefKey();
storageManager.removeMessageContext(messageStoredKey);
}
}
// Commit the transaction to release the SenderBean
if (transaction!=null)
transaction.commit();
transaction = null;
if ((processResponseForFaults || successfullySent) && !msgCtx.isServerSide())
checkForSyncResponses(msgCtx);
if ((rmMsgCtx.getMessageType() == Sandesha2Constants.MessageTypes.TERMINATE_SEQ)
&&
(Sandesha2Constants.SPEC_2005_02.NS_URI.equals(rmMsgCtx.getRMNamespaceValue()))) {
try {
transaction = storageManager.getTransaction();
//terminate message sent using the SandeshaClient. Since the terminate message will simply get the
//InFlow of the reference message get called which could be zero sized (OutOnly operations).
// terminate sending side if this is the WSRM 1.0 spec.
// If the WSRM versoion is 1.1 termination will happen in the terminate sequence response message.
TerminateSequence terminateSequence = rmMsgCtx.getTerminateSequence();
String sequenceID = terminateSequence.getIdentifier().getIdentifier();
RMSBean rmsBean = SandeshaUtil.getRMSBeanFromSequenceId(storageManager, sequenceID);
TerminateManager.terminateSendingSide(rmsBean, storageManager, false, null);
if(transaction != null && transaction.isActive()) transaction.commit();
transaction = null;
} finally {
if(transaction != null && transaction.isActive()) {
transaction.rollback();
transaction = null;
}
}
}
} catch (Exception e) {
if (log.isDebugEnabled()) log.debug("Caught exception", e);
} finally {
if (lock!=null && workId!=null) {
lock.removeWork(workId);
}
if (transaction!=null && transaction.isActive()) {
try {
transaction.rollback();
} catch (SandeshaStorageException e) {
if (log.isWarnEnabled())
log.warn("Caught exception rolling back transaction", e);
}
}