All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.oracle.coherence.patterns.messaging.Subscription Maven / Gradle / Ivy

There is a newer version: 13.0.1
Show newest version
/*
 * File: Subscription.java
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * The contents of this file are subject to the terms and conditions of 
 * the Common Development and Distribution License 1.0 (the "License").
 *
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the License by consulting the LICENSE.txt file
 * distributed with this file, or by consulting
 * or https://oss.oracle.com/licenses/CDDL
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file LICENSE.txt.
 *
 * MODIFICATIONS:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 */

package com.oracle.coherence.patterns.messaging;

import com.oracle.coherence.common.ranges.Ranges;

import com.tangosol.io.ExternalizableLite;

import com.tangosol.io.pof.PofReader;
import com.tangosol.io.pof.PofWriter;
import com.tangosol.io.pof.PortableObject;

import com.tangosol.util.ExternalizableHelper;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * A {@link Subscription} represents the current state of a
 * subscriber of {@link Message}s for a {@link Destination}.
 * 

* This class is abstract as there are many forms of {@link Subscription}s to * {@link Destination}s, each with their own particular semantics and * state management requirements. For example, a {@link Subscription} to * a {@link Topic} is very different from a {@link Subscription} to a {@link Queue}. *

* Copyright (c) 2008. All Rights Reserved. Oracle Corporation.
* Oracle is a registered trademark of Oracle Corporation and/or its affiliates. * * @author Brian Oliver */ @SuppressWarnings("serial") public abstract class Subscription implements ExternalizableLite, PortableObject { /** * All {@link Subscription}s have a {@link Status} indicating if they are current accepting messages. */ public enum Status { /** * When a {@link Subscription} is {@link #ENABLED} it may accept messages to consume. */ ENABLED, /** * When a {@link Subscription} is {@link #DISABLED} is won't accept messages to consume. */ DISABLED; } /** * The name of the Coherence Cache that will store {@link Subscription}s. */ public static final String CACHENAME = "coherence.messagingpattern.subscriptions"; /** * The {@link SubscriptionIdentifier} identifying the {@link Subscription}. */ private SubscriptionIdentifier subscriptionIdentifier; /** * This is a map used to keep track of the last * message sequence number received per partition. */ private HashMap lastMessageSequenceNumberMap = null; /** * The {@link Map} of {@link Ranges} that are visible to the {@link Subscription}. Each partition has its own map */ private MessageTracker visibleMessageTracker = null; /** * Number of messages delivered to subscriptions. */ private long numMessagesAcknowledged; /** * Number of messages received by subscriber. */ private long numMessagesReceived; /** * The {@link Status} of the {@link Subscription}. */ private Status status; /** * Required for {@link ExternalizableLite} and {@link PortableObject}. */ public Subscription() { } /** * Standard Constructor. * * @param subscriptionIdentifier subscription identifier */ public Subscription(SubscriptionIdentifier subscriptionIdentifier, Status status) { this.subscriptionIdentifier = subscriptionIdentifier; this.createVisibleMessageTracker(); this.numMessagesAcknowledged = 0; this.numMessagesReceived = 0; this.status = status; this.lastMessageSequenceNumberMap = new HashMap(); } /** * Get subscription name. * * @return subscription name */ public String getName() { return this.subscriptionIdentifier.toString(); } /** * Get the number of messages received. * * @return the number of messages received. */ public long getNumMessagesReceived() { return numMessagesReceived; } /** * Get the number of messages acknowledged. * * @return the number of messages acknowledged. */ public long getNumMessagesAcknowledged() { return numMessagesAcknowledged; } /** * Returns the {@link SubscriptionIdentifier} of the {@link Subscription}. * * @return SubscriptionIdentifier */ public SubscriptionIdentifier getIdentifier() { return subscriptionIdentifier; } /** * Determines the {@link Status} of the {@link Subscription} * * @return The {@link Status} */ public Status getStatus() { return status; } /** * Sets the {@link Status} of the {@link Subscription} to {@link Status#ENABLED}. */ public void enable() { this.status = Status.ENABLED; } /** * Sets the {@link Status} of the {@link Subscription} to {@link Status#DISABLED}. */ public void disable() { this.status = Status.DISABLED; } /** * Check if the subscription has visible messages. * * @return true if the subscription has visible messages. */ public boolean hasVisibleMessages() { return visibleMessageTracker.size() != 0; } /** * Get the number of visible messages. * * @return the number of visible messages. */ public long getNumMessages() { return visibleMessageTracker.size(); } /** * Get the tracker of visible messages. * * @return the tracker of visible messages. */ public MessageTracker getVisibleMessageTracker() { return visibleMessageTracker; } /** * Rollback the subscription. This is implemented by super classes but cannot be abstract since * push replication instantiates the Subscription class and never performs rollback. */ public void rollback() { } /** * Create the visible message tracker. */ protected void createVisibleMessageTracker() { this.visibleMessageTracker = new DefaultMessageTracker("VisibleMessageTracker"); } /** * Get the last sequence number of message received for a specific partition * * @param partitionId partition id of the message partition * @return Long the last sequence number of the message received for a given partition * or zero if no messages were received for that partition. */ public Long getLastMessageSequenceNumber(Integer partitionId) { Long lastMsgSeqNum; if (lastMessageSequenceNumberMap.containsKey(partitionId)) { lastMsgSeqNum = this.lastMessageSequenceNumberMap.get(partitionId); } else { lastMsgSeqNum = Long.valueOf(0); } return lastMsgSeqNum; } /** * Accept the messages in the {@link MessageTracker} and add them to * deliver message list. * * @param messageTracker message tracker * * @return Returns true if the {@link Subscription} has accepted the messages, false * otherwise. */ public boolean onAcceptMessage(MessageTracker messageTracker) { // we should only accept a message if the subscription is enabled if (status == Status.ENABLED) { Iterator iter = messageTracker.iterator(); while (iter.hasNext()) { MessageIdentifier messageIdentifier = iter.next(); acceptOneMessage(messageIdentifier, true); } return true; } else { return false; } } /** * Add a message to the deliver message list. Ignore the message if it has already been added. * * @param messageIdentifier message identifier * @param forceAccept Always accept the message, don't check if it a duplicate. This is used for re-delivered messages. * * @return Returns true if the {@link Subscription} has accepted the message, false * otherwise. */ public Boolean acceptOneMessage(MessageIdentifier messageIdentifier, Boolean forceAccept) { // we should only accept a message if the subscription is enabled if (status == Status.ENABLED) { Integer partitionId = Integer.valueOf(messageIdentifier.getPartitionId()); if (forceAccept.booleanValue()) { visibleMessageTracker.add(messageIdentifier); } else { if (lastMessageSequenceNumberMap.containsKey(partitionId)) { long lastMsgSeqNum = this.lastMessageSequenceNumberMap.get(partitionId).longValue(); if (messageIdentifier.getMessageSequenceNumber() <= lastMsgSeqNum) { return Boolean.FALSE; // duplicate message, ignore it. } } lastMessageSequenceNumberMap.put(partitionId, new Long(messageIdentifier.getMessageSequenceNumber())); visibleMessageTracker.add(messageIdentifier); } numMessagesReceived++; return true; } else { return false; } } /** * Rollback an accepted message so that it can be accepted by this subscription again. This is needed * by queue subscription rollback. We only keep the last sequence number, not the history so simply set the * last sequence number to one less than the input sequence number so that it can be re-accepted. * * @param messageIdentifier message identifier */ protected void rollbackAcceptedMessage(MessageIdentifier messageIdentifier) { Integer partitionId = Integer.valueOf(messageIdentifier.getPartitionId()); long newSeqNum = messageIdentifier.getMessageSequenceNumber(); if (lastMessageSequenceNumberMap.containsKey(partitionId)) { numMessagesReceived--; if (newSeqNum == 1) { lastMessageSequenceNumberMap.remove(partitionId); } else { long lastMsgSeqNum = this.lastMessageSequenceNumberMap.get(partitionId).longValue(); if (newSeqNum >= lastMsgSeqNum) { lastMessageSequenceNumberMap.put(partitionId, new Long(newSeqNum - 1)); } } } } /** * Returns the next {@link Message} id that should be delivered * to the {@link Subscription}. * * @return MessageIdentifier */ public MessageIdentifier nextMessageToDeliver() { MessageIdentifier messageIdentifier; Iterator iter = getVisibleMessageTracker().iterator(); if (iter.hasNext()) { messageIdentifier = iter.next(); } else { messageIdentifier = MessageIdentifier.getNullIdentifier(); } return messageIdentifier; } /** * Handles when a {@link MessageTracker} of visible {@link Message}s for this * {@link Subscription} have been acknowledged. *

* Once the {@link Message}s have been acknowledged, they are no longer visible * to this {@link Subscription}. * * @param messageTracker A {@link MessageTracker} of {@link Message} ids to acknowledge. */ public void onAcknowledgeMessages(MessageTracker messageTracker) { numMessagesAcknowledged += messageTracker.size(); getVisibleMessageTracker().removeAll(messageTracker); } /** * Acknowledge all {@link Message}s that have been delivered. * * @return MessageTracker containing message identifiers that have been acknowledged. */ public MessageTracker onAcknowledgeAllMessages() { numMessagesAcknowledged += getVisibleMessageTracker().size(); MessageTracker outTracker = visibleMessageTracker; visibleMessageTracker = new DefaultMessageTracker(); return outTracker; } /** * {@inheritDoc} */ public void readExternal(DataInput in) throws IOException { subscriptionIdentifier = (SubscriptionIdentifier) ExternalizableHelper.readExternalizableLite(in); visibleMessageTracker = (DefaultMessageTracker) ExternalizableHelper.readObject(in); numMessagesReceived = ExternalizableHelper.readLong(in); numMessagesAcknowledged = ExternalizableHelper.readLong(in); lastMessageSequenceNumberMap = new HashMap(); ClassLoader loader = getClass().getClassLoader(); ExternalizableHelper.readMap(in, lastMessageSequenceNumberMap, loader); status = Status.valueOf(ExternalizableHelper.readSafeUTF(in)); } /** * {@inheritDoc} */ public void writeExternal(DataOutput out) throws IOException { ExternalizableHelper.writeExternalizableLite(out, subscriptionIdentifier); ExternalizableHelper.writeObject(out, visibleMessageTracker); ExternalizableHelper.writeLong(out, numMessagesReceived); ExternalizableHelper.writeLong(out, numMessagesAcknowledged); ExternalizableHelper.writeMap(out, lastMessageSequenceNumberMap); ExternalizableHelper.writeSafeUTF(out, status.name()); } /** * {@inheritDoc} */ public void readExternal(PofReader reader) throws IOException { subscriptionIdentifier = (SubscriptionIdentifier) reader.readObject(0); visibleMessageTracker = (DefaultMessageTracker) reader.readObject(1); numMessagesReceived = reader.readLong(2); numMessagesAcknowledged = reader.readLong(3); lastMessageSequenceNumberMap = new HashMap(); reader.readMap(4, lastMessageSequenceNumberMap); status = Status.valueOf(reader.readString(5)); } /** * {@inheritDoc} */ public void writeExternal(PofWriter writer) throws IOException { writer.writeObject(0, subscriptionIdentifier); writer.writeObject(1, visibleMessageTracker); writer.writeLong(2, numMessagesReceived); writer.writeLong(3, numMessagesAcknowledged); writer.writeMap(4, lastMessageSequenceNumberMap); writer.writeString(5, status.name()); } /** * {@inheritDoc} */ public String toString() { return String.format("Subscription{subscriptionIdentifier=%s, status=%s, visibleMessageTracker=%s}", subscriptionIdentifier, status, visibleMessageTracker); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy