
org.objectweb.dream.queue.buffer.BufferMatching Maven / Gradle / Ivy
/**
* Dream
* Copyright (C) 2003-2004 INRIA Rhone-Alpes
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Contact: [email protected]
*
* Initial developer(s): Romain Lenglet
* Contributor(s):
*/
package org.objectweb.dream.queue.buffer;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.objectweb.dream.message.Message;
import org.objectweb.dream.queue.MessageMatcher;
import org.objectweb.dream.dreamannotation.DreamComponent;
import org.objectweb.fractal.fraclet.annotation.annotations.Requires;
/**
* A buffer in which stored messages are available to {@link #get() get}
* or {@link #remove() remove} only if they are {@link
* MessageMatcher#acceptMessage(Message) accepted} by the {@link MessageMatcher
* message matcher} bound to this component. If the availability criteria of
* the message matcher are modified while {@link #get()} calls and/or
* {@link #remove()} calls are blocked waiting for available messages, and the
* message matcher criteria modification would let them return messages, the
* blocked calls can be unblocked by calling {@link #tryGet()} after the message
* matcher has been modified.
*/
@DreamComponent(controllerDesc = "dreamUnstoppablePrimitive")
public class BufferMatching extends AbstractBuffer
{
// FIXME: how to get or remove unmatched messages?
// FIXME: if we don't do anything, the unmatched messages remain in the
// buffer infinitely and may overflow the buffer!
// FIXME: refactor the commented 'remove' method
// ------------------------------------------------------------------------
// Client interface
// ------------------------------------------------------------------------
/**
* The message matcher that is used to select the messages that can be
* returned by calls to {@link #get()}, {@link #tryGet()}, {@link #remove()},
* {@link #tryRemove()} and {@link #removeAll()}. This is the client
* interface named "message-matcher".
*/
@Requires(name = MessageMatcher.ITF_NAME)
protected MessageMatcher messageMatcherItf;
// ------------------------------------------------------------------------
// Fields
// ------------------------------------------------------------------------
/**
* The messages stored in this buffer, in adding order (FIFO).
*/
protected LinkedList messages = new LinkedList();
/**
* Messages that have matched {@link #messageMatcherItf} and that are ready to
* be returned. The messages are in adding order (FIFO).
*/
protected LinkedList messagesToBeReturned = new LinkedList();
/**
* The number of calls that are waiting to remove matching messages.
*/
protected int removersCount = 0;
/**
* The number of calls that are waiting to get (not remove) matching messages.
*/
protected int gettersCount = 0;
// ---------------------------------------------------------------------------
// Implementation of the AbstractBuffer methods.
// ---------------------------------------------------------------------------
protected void doAdd(Message message)
{
messages.add(message);
incrementStoredMessagesCount(1);
searchMessagesToBeReturned();
}
// /**
// * @see BufferRemove#remove()
// */
// public Message remove() throws InterruptedException
// {
// synchronized (lock)
// {
// removersCount++;
// Message message = null;
// try
// {
// message = super.remove();
// }
// catch (InterruptedException e)
// {
// throw e;
// }
// finally
// {
// removersCount--;
// }
// return message;
// }
// }
protected Message doRemove()
{
Message message = messagesToBeReturned.remove(0);
incrementStoredMessagesCount(-1);
incrementAvailableMessagesCount(-1);
return message;
}
protected Message doRemoveAll()
{
Message aggregatedMessage = null;
/* First, remove all already available messages. */
Iterator messagesIterator = messagesToBeReturned.iterator();
while (messagesIterator.hasNext())
{
Message message = (Message) messagesIterator.next();
if (aggregatedMessage == null)
{
aggregatedMessage = messageManagerItf.createMessage();
}
messageManagerItf.addSubMessage(aggregatedMessage, message);
messagesIterator.remove();
}
/* Now look into stored messages for messages that may now match. */
messagesIterator = messages.iterator();
while (messagesIterator.hasNext())
{
Message message = (Message) messagesIterator.next();
if (messageMatcherItf.acceptMessage(message))
{
if (aggregatedMessage == null)
{
aggregatedMessage = messageManagerItf.createMessage();
}
messageManagerItf.addSubMessage(aggregatedMessage, message);
messagesIterator.remove();
}
}
availableMessagesCount = 0;
storedMessagesCount = messages.size();
return aggregatedMessage;
}
/**
* @see BufferRemove#get()
*/
public Message get() throws InterruptedException
{
synchronized (lock)
{
gettersCount++;
Message message = null;
try
{
message = super.get();
}
catch (InterruptedException e)
{
throw e;
}
finally
{
gettersCount--;
}
return message;
}
}
protected Message doGet()
{
Message message = messagesToBeReturned.getFirst();
/*
* Notify any remover that may be waiting for the message that we are going
* to return, so that it can remove it.
*/
searchMessagesToBeReturned();
return message;
}
public boolean hasAvailableMessage()
{
/*
* Retry an iteration over the messages, in case the matching criteria have
* changed. Maybe messages will be matched this time.
*/
searchMessagesToBeReturned();
return super.hasAvailableMessage();
}
// ---------------------------------------------------------------------------
// Utility methods.
// ---------------------------------------------------------------------------
/**
* If there are waiting removers (i.e., if {@link #removersCount}
* > 0
* or if {@link #gettersCount} > 0
), iterate over
* {@link #messages the received messages}, and test each message by calling
* {@link MessageMatcher#acceptMessage(Message)} using
* {@link #messageMatcherItf the bound matcher}, until at most enough matching
* messages for waiting removers and getters have been found. The matching
* messages are put in the same order into {@link #messagesToBeReturned},
* then removers are notified. The {@link #lock lock} has been acquired when
* this method is called.
*/
protected void searchMessagesToBeReturned()
{
/* Local variable copy of attribute, for optimization. */
int localRemoversCount = removersCount;
/* We need at least one message for all getters. */
boolean thereAreGetters = (gettersCount > 0);
/*
* If there are no waiting getters or removers, no need to search for
* messages.
*/
if ((!thereAreGetters) || (localRemoversCount < 1))
{
return;
}
/* Local variable copy of attribute, for optimization. */
List localMessagesToBeReturned = messagesToBeReturned;
int messagesToMatchCount = (thereAreGetters ? 1 : 0) + localRemoversCount
- localMessagesToBeReturned.size();
/*
* If there are already enough messages selected for all waiting removers,
* do nothing. Simply notify them. Otherwise, iterate over the list of
* received messages, and select enough matching messages.
*/
if (messagesToMatchCount > 0)
{
/* Local variable copy of attribute, for optimization. */
MessageMatcher localMessageMatcher = messageMatcherItf;
Iterator messagesIterator = messages.iterator();
while ((messagesToMatchCount > 0) && messagesIterator.hasNext())
{
Message message = messagesIterator.next();
/*
* If the message matches, transfer it from the list of received
* messages to the list of messages ready to be returned.
*/
if (localMessageMatcher.acceptMessage(message))
{
messagesToMatchCount--;
localMessagesToBeReturned.add(message);
messagesIterator.remove();
incrementAvailableMessagesCount(1);
}
}
}
/*
* Notify waiting removers if there are messages ready to be returned.
*/
if (!localMessagesToBeReturned.isEmpty())
{
lock.notifyAll();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy