/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.cxf.ws.rm; import java.util.Date; import java.util.logging.Level; import java.util.logging.Logger; import javax.xml.datatype.Duration; import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.jaxb.DatatypeFactory; import org.apache.cxf.ws.addressing.ContextUtils; import org.apache.cxf.ws.addressing.EndpointReferenceType; import org.apache.cxf.ws.rm.manager.SequenceTerminationPolicyType; import org.apache.cxf.ws.rm.v200702.Expires; import org.apache.cxf.ws.rm.v200702.Identifier; import org.apache.cxf.ws.rm.v200702.SequenceAcknowledgement; import org.apache.cxf.ws.rm.v200702.SequenceAcknowledgement.AcknowledgementRange; // TODO: handle lastMessage public class SourceSequence extends AbstractSequence { private static final Logger LOG = LogUtils.getL7dLogger(SourceSequence.class); private Date expires; private Source source; private long currentMessageNumber; private boolean lastMessage; private Identifier offeringId; private EndpointReferenceType target; public SourceSequence(Identifier i, ProtocolVariation pv) { this(i, null, null, pv); } public SourceSequence(Identifier i, Date e, Identifier oi, ProtocolVariation pv) { this(i, e, oi, 0, false, pv); } public SourceSequence(Identifier i, Date e, Identifier oi, long cmn, boolean lm, ProtocolVariation pv) { super(i, pv); expires = e; offeringId = oi; currentMessageNumber = cmn; lastMessage = lm; acknowledgement = new SequenceAcknowledgement(); acknowledgement.setIdentifier(id); } /** * @return the message number assigned to the most recent outgoing * application message. */ public long getCurrentMessageNr() { return currentMessageNumber; } /** * @return true if the last message had been sent for this sequence. */ public boolean isLastMessage() { return lastMessage; } /** * @return the identifier of the sequence that was created on behalf of the * CreateSequence request that included this sequence as an offer */ public Identifier getOfferingSequenceIdentifier() { return offeringId; } /** * @return the identifier of the rm source */ public String getEndpointIdentifier() { return source.getName(); } /** * @return the expiry data of this sequence */ public Date getExpires() { return expires; } /** * Returns true if this sequence was constructed from an offer for an * inbound sequence includes in the CreateSequenceRequest in response to * which the sequence with the specified identifier was created. * * @param id the sequence identifier * @return true if the sequence was constructed from an offer. */ public boolean offeredBy(Identifier sid) { return null != offeringId && offeringId.getValue().equals(sid.getValue()); } /** * Returns true if any messages other than the number supplied are waiting for acknowledgment. * * @param num message number to check * @return true if all messages have been acknowledged. */ public boolean needAcknowledge(long num) { if (currentMessageNumber != num) { return true; } if (acknowledgement.getAcknowledgementRange().size() == 0) { return false; } if (acknowledgement.getAcknowledgementRange().size() == 1) { AcknowledgementRange r = acknowledgement.getAcknowledgementRange().get(0); return r.getLower().longValue() != 1 || r.getUpper().longValue() < (num - 1); } return true; } /** * Returns true if a last message had been sent for this sequence and if all * messages for this sequence have been acknowledged. * * @return true if all messages have been acknowledged. */ public boolean allAcknowledged() { if (!lastMessage) { return false; } if (acknowledgement.getAcknowledgementRange().size() == 1) { AcknowledgementRange r = acknowledgement.getAcknowledgementRange().get(0); return r.getLower().longValue() == 1 && r.getUpper().longValue() == currentMessageNumber; } return false; } /** * Used by the RM source to cache received acknowledgements for this * sequence. * * @param acknowledgement an acknowledgement for this sequence */ public void setAcknowledged(SequenceAcknowledgement a) throws RMException { acknowledgement = a; source.getManager().getRetransmissionQueue().purgeAcknowledged(this); if (allAcknowledged()) { if (null == target || RMUtils.getAddressingConstants().getAnonymousURI().equals( target.getAddress().getValue())) { LOG.log(Level.WARNING, "STANDALONE_ANON_TERMINATE_SEQUENCE_MSG"); // keep the sequence and let RMOutInterceptor remove it after building the TS message // if we remove the sequence here, RMOutInterceptor should check for a null sequence } else { RMEndpoint rme = source.getReliableEndpoint(); Proxy proxy = rme.getProxy(); // REVIST for rm 1.1, provide an option to how the close and terminate messages are sent if (ProtocolVariation.RM11WSA200508 == getProtocol()) { proxy.lastMessage(this); } proxy.terminate(this); source.removeSequence(this); } } } /** * Returns the source associated with this source sequence. * * @return the source. */ public Source getSource() { return source; } void setSource(Source s) { source = s; } void setLastMessage(boolean lm) { lastMessage = lm; } /** * Returns true if the sequence is expired. * * @return true if the sequence is expired. */ boolean isExpired() { return expires == null ? false : new Date().after(expires); } public void setExpires(Expires ex) { Duration d = null; expires = null; if (null != ex) { d = ex.getValue(); } if (null != d && !d.equals(DatatypeFactory.PT0S)) { Date now = new Date(); expires = new Date(now.getTime() + ex.getValue().getTimeInMillis(now)); } } /** * Returns the next message number and increases the message number. * * @return the next message number. */ long nextMessageNumber() { return nextMessageNumber(null, 0, false); } /** * Returns the next message number and increases the message number. The * parameters, if not null, indicate that this message is being sent as a * response to the message with the specified message number in the sequence * specified by the by the identifier, and are used to decide if this * message should be the last in this sequence. * * @return the next message number. */ public long nextMessageNumber(Identifier inSeqId, long inMsgNumber, boolean last) { assert !lastMessage; long result = 0; synchronized (this) { currentMessageNumber++; if (last) { lastMessage = true; } else { checkLastMessage(inSeqId, inMsgNumber); } result = currentMessageNumber; } return result; } SequenceAcknowledgement getAcknowledgement() { return acknowledgement; } /** * The target for the sequence is the first non-anonymous address that a * message is sent to as part of this sequence. It is subsequently used for * as the target of out-of-band protocol messages related to that sequence * that originate from the sequnce source (i.e. TerminateSequence and * LastMessage, but not AckRequested or SequenceAcknowledgement as these are * orignate from the sequence destination). * * @param to */ synchronized void setTarget(EndpointReferenceType to) { if (target == null && !ContextUtils.isGenericAddress(to)) { target = to; } } synchronized EndpointReferenceType getTarget() { return target; } /** * Checks if the current message should be the last message in this sequence * and if so sets the lastMessageNumber property. */ private void checkLastMessage(Identifier inSeqId, long inMsgNumber) { // check if this is a response to a message that was is the last message // in the sequence // that included this sequence as an offer if (null != inSeqId && 0 != inMsgNumber) { Destination destination = source.getReliableEndpoint().getDestination(); DestinationSequence inSeq = null; if (null != destination) { inSeq = destination.getSequence(inSeqId); } if (null != inSeq && offeredBy(inSeqId) && inMsgNumber == inSeq.getLastMessageNumber()) { lastMessage = true; } } if (!lastMessage) { SequenceTerminationPolicyType stp = source.getManager().getSourcePolicy() .getSequenceTerminationPolicy(); assert null != stp; if ((stp.getMaxLength() != 0 && stp.getMaxLength() <= currentMessageNumber) || (stp.getMaxRanges() > 0 && acknowledgement.getAcknowledgementRange().size() >= stp .getMaxRanges()) || (stp.getMaxUnacknowledged() > 0 && source.getManager().getRetransmissionQueue() .countUnacknowledged(this) >= stp.getMaxUnacknowledged())) { lastMessage = true; } } if (LOG.isLoggable(Level.FINE) && lastMessage) { LOG.fine(currentMessageNumber + " should be the last message in this sequence."); } } }