/*
* Copyright 1999-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
*/
package org.apache.sandesha2.msgprocessors;
import java.util.Iterator;
import java.util.List;
import javax.xml.namespace.QName;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axiom.soap.SOAPHeader;
import org.apache.axis2.AxisFault;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.description.AxisOperation;
import org.apache.axis2.wsdl.WSDLConstants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.sandesha2.RMMsgContext;
import org.apache.sandesha2.Sandesha2Constants;
import org.apache.sandesha2.SandeshaException;
import org.apache.sandesha2.i18n.SandeshaMessageHelper;
import org.apache.sandesha2.i18n.SandeshaMessageKeys;
import org.apache.sandesha2.polling.PollingManager;
import org.apache.sandesha2.security.SecurityManager;
import org.apache.sandesha2.security.SecurityToken;
import org.apache.sandesha2.storage.StorageManager;
import org.apache.sandesha2.storage.beanmanagers.RMDBeanMgr;
import org.apache.sandesha2.storage.beanmanagers.SenderBeanMgr;
import org.apache.sandesha2.storage.beans.RMDBean;
import org.apache.sandesha2.storage.beans.RMSBean;
import org.apache.sandesha2.storage.beans.SenderBean;
import org.apache.sandesha2.util.FaultManager;
import org.apache.sandesha2.util.Range;
import org.apache.sandesha2.util.RangeString;
import org.apache.sandesha2.util.SandeshaUtil;
import org.apache.sandesha2.util.TerminateManager;
import org.apache.sandesha2.wsrm.AcknowledgementRange;
import org.apache.sandesha2.wsrm.SequenceAcknowledgement;
/**
* Responsible for processing acknowledgement headers on incoming messages.
*/
public class AcknowledgementProcessor {
private static final Log log = LogFactory.getLog(AcknowledgementProcessor.class);
/**
* @param message
* @throws AxisFault
*/
public void processAckHeaders(RMMsgContext message) throws AxisFault {
if (log.isDebugEnabled())
log.debug("Enter: AcknowledgementProcessor::processAckHeaders");
SOAPEnvelope envelope = message.getMessageContext().getEnvelope();
SOAPHeader header = envelope.getHeader();
if(header!=null)
{
for(int i = 0; i < Sandesha2Constants.SPEC_NS_URIS.length; i++) {
QName headerName = new QName(Sandesha2Constants.SPEC_NS_URIS[i], Sandesha2Constants.WSRM_COMMON.SEQUENCE_ACK);
Iterator acks = header.getChildrenWithName(headerName);
while(acks.hasNext()) {
OMElement ack = (OMElement) acks.next();
SequenceAcknowledgement seqAck = new SequenceAcknowledgement(headerName.getNamespaceURI());
seqAck.fromOMElement(ack);
processAckHeader(message, ack, seqAck);
}
}
}
if (log.isDebugEnabled())
log.debug("Exit: AcknowledgementProcessor::processAckHeaders");
}
/**
* @param rmMsgCtx
* @param soapHeader
* @param sequenceAck
* @throws AxisFault
*/
private void processAckHeader(RMMsgContext rmMsgCtx, OMElement soapHeader, SequenceAcknowledgement sequenceAck)
throws AxisFault {
if (log.isDebugEnabled())
log.debug("Enter: AcknowledgementProcessor::processAckHeader " + soapHeader);
boolean piggybackedAck = !(rmMsgCtx.getMessageType()==Sandesha2Constants.MessageTypes.ACK);
MessageContext msgCtx = rmMsgCtx.getMessageContext();
ConfigurationContext configCtx = msgCtx.getConfigurationContext();
StorageManager storageManager = SandeshaUtil.getSandeshaStorageManager(configCtx, configCtx
.getAxisConfiguration());
SenderBeanMgr retransmitterMgr = storageManager.getSenderBeanMgr();
String outSequenceId = sequenceAck.getIdentifier().getIdentifier();
RMSBean rmsBean = SandeshaUtil.getRMSBeanFromSequenceId(storageManager, outSequenceId);
if (outSequenceId == null || "".equals(outSequenceId)) {
String message = SandeshaMessageHelper.getMessage(SandeshaMessageKeys.outSeqIDIsNull);
log.debug(message);
throw new SandeshaException(message);
}
if (FaultManager.checkForUnknownSequence(rmMsgCtx, outSequenceId, storageManager, piggybackedAck)) {
if (log.isDebugEnabled())
log.debug("Exit: AcknowledgementProcessor::processAckHeader, Unknown sequence");
return;
}
if (FaultManager.checkForSequenceTerminated(rmMsgCtx, outSequenceId, rmsBean, piggybackedAck)) {
if (log.isDebugEnabled())
log.debug("Exit: AcknowledgementProcessor::processAckHeader, Sequence terminated");
return;
}
// Check that the sender of this Ack holds the correct token
String internalSequenceId = rmsBean.getInternalSequenceID();
if(rmsBean.getSecurityTokenData() != null) {
SecurityManager secManager = SandeshaUtil.getSecurityManager(configCtx);
SecurityToken token = secManager.recoverSecurityToken(rmsBean.getSecurityTokenData());
secManager.checkProofOfPossession(token, soapHeader, msgCtx);
}
if(log.isDebugEnabled()) log.debug("Got Ack for RM Sequence: " + outSequenceId + ", internalSeqId: " + internalSequenceId);
Iterator ackRangeIterator = sequenceAck.getAcknowledgementRanges().iterator();
if (FaultManager.checkForUnknownSequence(rmMsgCtx, outSequenceId, storageManager, piggybackedAck)) {
if (log.isDebugEnabled())
log.debug("Exit: AcknowledgementProcessor::processAckHeader, Unknown sequence ");
return;
}
if (FaultManager.checkForInvalidAcknowledgement(rmMsgCtx, sequenceAck, storageManager, rmsBean, piggybackedAck)) {
if (log.isDebugEnabled())
log.debug("Exit: AcknowledgementProcessor::processAckHeader, Invalid Ack range ");
return;
}
String replyToAddress = rmsBean.getReplyToEPR();
EndpointReference replyTo = new EndpointReference (replyToAddress);
boolean anonReplyTo = replyTo.hasAnonymousAddress();
String rmVersion = rmMsgCtx.getRMSpecVersion();
// Compare the clientCompletedMessages with the range we just got, to work out if there
// is any new information in this ack message
RangeString completedMessages = rmsBean.getClientCompletedMessages();
long numberOfNewMessagesAcked = 0;
while(ackRangeIterator.hasNext()) {
AcknowledgementRange ackRange = (AcknowledgementRange) ackRangeIterator.next();
long lower = ackRange.getLowerValue();
long upper = ackRange.getUpperValue();
Range ackedRange = new Range(lower, upper);
// Quick check to see if the whole range is already covered
if(!completedMessages.isRangeCompleted(ackedRange)) {
//we now know that this range is complete so we update it. This should aggregate the
//ranges together and tell us which numbers are newly acked
Range[] newRanges = completedMessages.addRange(ackedRange).getRanges();
// We now take each newly acked message in turn and see if we need to update a sender bean
for (int rangeIndex=0; rangeIndex < newRanges.length; rangeIndex++) {
//now work on each newly acked message in this range
for(long messageNo = newRanges[rangeIndex].lowerValue; messageNo<=newRanges[rangeIndex].upperValue; messageNo++){
numberOfNewMessagesAcked++;
SenderBean matcher = new SenderBean();
matcher.setSequenceID(outSequenceId);
matcher.setMessageNumber(messageNo);
List retransmitterBeans = retransmitterMgr.find(matcher);
if (! retransmitterBeans.isEmpty()){
Iterator retransmitterBeansItr = retransmitterBeans.iterator();
while (retransmitterBeansItr.hasNext()) {
SenderBean retransmitterBean = (SenderBean) retransmitterBeansItr.next();
if (retransmitterBean != null) {
// Check we haven't got an Ack for a message that hasn't been sent yet !
if (retransmitterBean.getSentCount() == 0) {
FaultManager.makeInvalidAcknowledgementFault(rmMsgCtx, sequenceAck, ackRange,
storageManager, piggybackedAck);
if (log.isDebugEnabled())
log.debug("Exit: AcknowledgementProcessor::processAckHeader, Invalid Ack");
return;
}
String storageKey = retransmitterBean.getMessageContextRefKey();
boolean syncResponseNeeded = false;
if (Sandesha2Constants.SPEC_VERSIONS.v1_0.equals(rmVersion) && anonReplyTo) {
MessageContext applicationMessage = storageManager.retrieveMessageContext(storageKey, configCtx);
AxisOperation operation = applicationMessage.getAxisOperation();
if(operation!= null) {
int mep = operation.getAxisSpecificMEPConstant();
syncResponseNeeded = (mep == WSDLConstants.MEP_CONSTANT_OUT_IN);
}
}
if (!syncResponseNeeded) {
// removing the application message from the storage.
retransmitterMgr.delete(retransmitterBean.getMessageID());
storageManager.removeMessageContext(storageKey);
}
}
}
}//end of if (!retransmitterBeans.isEmpty()){
}//end for
}//end for
} //end while
}
// updating the last activated time of the sequence.
rmsBean.setLastActivatedTime(System.currentTimeMillis());
//adding a MakeConnection for the response sequence if needed.
if (rmsBean.getOfferedSequence() != null) {
RMDBeanMgr rMDBeanMgr = storageManager.getRMDBeanMgr();
RMDBean rMDBean = rMDBeanMgr.retrieve(outSequenceId);
if (rMDBean!=null && rMDBean.isPollingMode()) {
PollingManager manager = storageManager.getPollingManager();
if(manager != null) manager.schedulePollingRequest(rMDBean.getSequenceID(), false);
}
}
// We overwrite the previous client completed message ranges with the
// latest view, but only if it is an update i.e. contained a new
// ack range (which is because we do not previous acks arriving late
// to break us)
if (numberOfNewMessagesAcked>0) {
rmsBean.setClientCompletedMessages(completedMessages);
}
// Update the RMSBean
storageManager.getRMSBeanMgr().update(rmsBean);
// Try and terminate the sequence
if (!rmsBean.isAvoidAutoTermination())
TerminateManager.checkAndTerminate(rmMsgCtx.getConfigurationContext(), storageManager, rmsBean);
if (log.isDebugEnabled())
log.debug("Exit: AcknowledgementProcessor::processAckHeader");
}
}