fr.dyade.aaa.agent.MessageVector Maven / Gradle / Ivy
Show all versions of a3-rt Show documentation
/*
* Copyright (C) 2004 - 2023 ScalAgent Distributed Technologies
*
* 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.1 of the License, or 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.
*
* Initial developer(s): ScalAgent Distributed Technologies
* Contributor(s):
*/
package fr.dyade.aaa.agent;
import java.util.Enumeration;
import java.util.Hashtable;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import fr.dyade.aaa.common.Debug;
import fr.dyade.aaa.common.EmptyQueueException;
/**
* Class MessageVector
represents a persistent vector of
* Message (source and target agent identifier, notification).
* As messages have a relatively short life span, then the messages
* are kept in main memory. If possible, the list is backed by a persistent
* image on the disk for reliability needs. In this case, we can use
* SoftReference to avoid memory overflow.
* The stamp information in Message is used to restore the queue from
* persistent storage at initialization time, so there is no longer need
* to save MessageVector
object state.
*/
final class MessageVector implements MessageQueue {
private Logger logmon = null;
private String logmsg = null;
private long cpt1, cpt2;
/**
* The array buffer into which the Message
objects are stored
* in memory. The capacity of this array buffer is at least large enough to
* contain all the messages of the MessageVector
.
* Messages are stored in a circular way, first one in data[first]
* through data[(first+count-1)%length]. Any other array elements
* are null.
*/
private Object data[];
/** The index of the first message in the circular buffer. */
private int first;
/**
* The number of messages in this MessageVector object. Components
* data[first] through data[(first+count-1)%length] are the
* actual items.
*/
private int count;
/** The number of validated message in this MessageQueue. */
private int validated;
private boolean persistent;
MessageVector(String name, boolean persistent) {
logmon = Debug.getLogger(getClass().getName() + '.' + name);
logmsg = name + ".MessageVector: ";
if (msgTypesTracking)
counters = new Hashtable<>();
this.persistent = persistent;
data = new Object[50];
first = 0;
count = 0;
validated = 0;
}
/**
* Insert a message in the queue, it should only be used during initialization for
* restoring the queue state.
*
* This algorithm takes into account the reinitialization of the stamp and makes it
* possible to suitably sort messages having been created before this reset.
*
* @param item the message to be pushed onto this queue.
* @param comparator the MessageComparator interface of MessageConsumer.
*/
public synchronized void insert(Message item, MessageComparator comparator) {
if (Debug.debug && logmon.isLoggable(BasicLevel.DEBUG))
logmon.log(BasicLevel.DEBUG, logmsg + "insert(" + item + ")");
int i = 0;
for (; i " + item);
return item;
}
/**
* Looks at the message at the top of this queue without removing
* it from the queue. It should never be used during a transaction
* to avoid dead-lock problems. It waits until a message is available
* or the specified amount of time has elapsed.
*
* @param timeout the maximum time to wait in milliseconds.
* @return the message at the top of this queue.
* @exception InterruptedException if another thread has interrupted the
* current thread.
* @exception IllegalArgumentException if the value of timeout is negative.
*/
public synchronized Message get(long timeout) throws InterruptedException {
if (Debug.debug && logmon.isLoggable(BasicLevel.DEBUG)) {
logmon.log(BasicLevel.DEBUG, logmsg + "get(" + timeout + ")");
cpt1 += 1; cpt2 += validated;
if ((cpt1 & 0xFFFFL) == 0L) {
logmon.log(BasicLevel.DEBUG, logmsg + (cpt2/cpt1) + '/' + validated);
}
}
Message item = null;
if ((validated == 0) && (timeout > 0)) wait(timeout);
if (validated > 0) item = getMessageAt(0);
if (Debug.debug && logmon.isLoggable(BasicLevel.DEBUG))
logmon.log(BasicLevel.DEBUG, logmsg + "get() -> " + item);
return item;
}
/**
* Looks at the first message of this queue where the destination server
* is the specified one.
* The message is not removed from the queue. It should never be used during
* a transaction to avoid dead-lock problems.
*
* @param to the unique server id.
* @return the corresponding message or null if none .
*/
public synchronized Message getMessageTo(short to) {
if (Debug.debug && logmon.isLoggable(BasicLevel.DEBUG)) {
logmon.log(BasicLevel.DEBUG, logmsg + "getFrom(" + to + ")");
cpt1 += 1; cpt2 += validated;
if ((cpt1 & 0xFFFFL) == 0L) {
logmon.log(BasicLevel.DEBUG, logmsg + (cpt2/cpt1) + '/' + validated);
}
}
Message item = null;
for (int i=0; i " + item);
return item;
}
/**
* Removes the specified message from the queue if exists.
*
* @param msg the message to remove.
*/
synchronized void removeMessage(Message msg) {
if (Debug.debug && logmon.isLoggable(BasicLevel.DEBUG))
logmon.log(BasicLevel.DEBUG,
logmsg + "removeMessage #" + msg.getStamp());
for (int i = 0; i " + i);
removeMessageAt(i);
validated -= 1;
return;
}
}
logmon.log(BasicLevel.ERROR,
logmsg + "removeMessage #" + msg.getStamp() + " not found");
return;
}
/**
* Removes all messages with a stamp less than the specified one.
* Be careful with the use of this method, in particular it does not
* take in account the multiples incoming nodes.
*
* @param stamp the barrier stamp.
* @return the number of removed messages.
*/
synchronized int remove(int stamp) {
if (validated == 0) return 0;
if (Debug.debug && logmon.isLoggable(BasicLevel.DEBUG))
logmon.log(BasicLevel.DEBUG, logmsg + "remove #" + stamp);
int i = 0;
for (; i" +i);
return i;
}
/**
* Removes the first messages with a timestamp less than the specified one.
* Be careful with the use of this method, in particular it does not take in
* account the multiples incoming nodes.
*
* @param currentTimeMillis the timestamp
* @return the first messages with a timestamp less than the specified one.
*/
synchronized Message removeExpired(long currentTimeMillis) {
if (validated == 0) return null;
if (Debug.debug && logmon.isLoggable(BasicLevel.DEBUG))
logmon.log(BasicLevel.DEBUG,
logmsg + "removeExpired - " + currentTimeMillis);
for (int i = 0; i 0) &&
(currentTimeMillis >= msg.not.expiration)) {
removeMessageAt(i);
validated -= 1;
if (Debug.debug && logmon.isLoggable(BasicLevel.DEBUG))
logmon.log(BasicLevel.DEBUG, logmsg + "remove #" + msg.getStamp());
return msg;
}
}
return null;
}
/**
* Inserts the specified message to this MessageVector
at
* the specified index. Each component in this vector with an index greater
* or equal to the specified index is shifted upward.
*
* @param item the message to be pushed onto this queue.
* @param index where to insert the new message.
*/
private void insertMessageAt(Message item, int index) {
if (Debug.debug && logmon.isLoggable(BasicLevel.DEBUG))
logmon.log(BasicLevel.DEBUG,
logmsg + "insertMessageAt(" + item + ", " + index + ")");
if (count == data.length) {
Object newData[] = new Object[data.length *2];
if ((first + count) < data.length) {
System.arraycopy(data, first, newData, 0, count);
} else {
int j = data.length - first;
System.arraycopy(data, first, newData, 0, j);
System.arraycopy(data, 0, newData, j, count - j);
}
first = 0;
data = newData;
}
if (index != count) {
try {
int srcPos = (first + index)%data.length;
int destPos = (first + index + 1)%data.length;
int copyLength = count - index;
int lastDestPos = (destPos + copyLength - 1)%data.length;
if (srcPos > lastDestPos) {
Object lastElement = data[data.length - 1];
int copyLength1 = data.length - srcPos -1;
if (copyLength1 > 0) {
System.arraycopy(data, srcPos, data, destPos, copyLength1);
}
int copyLength2 = copyLength - (copyLength1 + 1);
if (copyLength2 > 0) {
System.arraycopy(data, 0, data, 1, copyLength2);
}
data[0] = lastElement;
} else {
System.arraycopy(data, srcPos, data, destPos, copyLength);
}
} catch (ArrayIndexOutOfBoundsException exc) {
throw new RuntimeException("insertMessageAt '" + index + "': " + this, exc);
}
}
if (persistent)
data[(first + index)%data.length] = new MessageSoftRef(item);
else
data[(first + index)%data.length] = item;
count += 1;
if (Debug.debug && logmon.isLoggable(BasicLevel.DEBUG))
logmon.log(BasicLevel.DEBUG,
logmsg + "insertMessageAt() -> " + this);
}
/**
* Returns the message at the specified index.
*
* @param index the index of the message.
* @return The message at the top of this queue.
*/
private Message getMessageAt(int index) {
if (Debug.debug && logmon.isLoggable(BasicLevel.DEBUG))
logmon.log(BasicLevel.DEBUG, logmsg + "getMessageAt(" + index + ")");
int idx = (first + index)%data.length;
if (data[idx] == null) throw new RuntimeException("Null element in: " + this);
if (persistent) {
return ((MessageSoftRef) data[idx]).loadMessage();
}
return (Message) data[idx];
}
/**
* Deletes the message at the specified index.
*
* @param index the index of the message to remove.
*/
private void removeMessageAt(int index) {
if (index == 0) {
// It is the first element, just move the start of the list.
data[first] = null; /* let gc do its work */
first = (first +1)%data.length;
} else if (index == (count -1)) {
// It is the last element, just move the end of the list.
data[(first + index) %data.length] = null; /* let gc do its work */
} else if ((first + index) < data.length) {
// Moves the start of the box to the empty 'box'
System.arraycopy(data, first,
data, first +1, index);
// Erase the old first 'box'
data[first] = null; /* let gc do its work */
// Move the first ptr +1, and decrease counter
first = (first +1)%data.length;
} else {
// Moves the end of the vector -1 to the empty 'box'
System.arraycopy(data, (first + index)%data.length +1,
data, (first + index)%data.length, count - index -1);
// Erase the old last 'box'
data[(first + count -1)%data.length] = null; /* let gc do its work */
}
// Decrease the counter
count -= 1;
// If there is no more element, moves the empty list to the beginning of
// the vector.
if (count == 0) first = 0;
if (Debug.debug && logmon.isLoggable(BasicLevel.DEBUG))
logmon.log(BasicLevel.DEBUG,
logmsg + "removeMessageAt(" + index + ") -> " + this);
}
/**
* Returns the number of messages in this vector.
*
* @return the number of messages in this vector.
*/
public int size() {
return count;
}
/**
* Returns a string representation of this MessageVector
* object. Be careful we scan the vector without synchronization, so the
* result can be incoherent.
*
* @return A string representation of this object.
*/
public String toString() {
StringBuffer strbuf = new StringBuffer();
strbuf.append('(').append(super.toString());
strbuf.append(",first=").append(first);
strbuf.append(",count=").append(count);
strbuf.append(",validated=").append(validated).append(",(");
for (int i=0; i
* This property can be fixed either from java
launching command or
* a3servers.xml configuration file.
*/
public static final String MSG_TYPES_TRACKING = "fr.dyade.aaa.agent.MsgTypesTracking";
/**
* True if the tracking of the distribution of messages type is allowed.
*/
private static final boolean msgTypesTracking = AgentServer.getBoolean(MSG_TYPES_TRACKING);
static class Counter {
int total = 1;
int live = 1;
}
Hashtable counters = null;
private void inc(Notification not) {
if (not == null) return;
Counter counter = counters.get(not.getClass());
if (counter == null) {
counters.put(not.getClass(), new Counter());
} else {
counter.total += 1;
counter.live += 1;
}
}
private void dec(Notification not) {
if (not == null) return;
Counter counter = counters.get(not.getClass());
counter.live -= 1;
}
/**
* Returns a report about the distribution of messages type in queue.
*/
public String report() {
StringBuffer strbuf = new StringBuffer();
strbuf.append("waiting=").append(size()).append('\n');
if ((size() != 0) && (counters != null)) {
for (Enumeration e = counters.keys(); e.hasMoreElements();){
Class clazz = e.nextElement();
Counter counter = counters.get(clazz);
if (counter != null)
strbuf.append(clazz.getName()).append('=').append(counter.live).append('/').append(counter.total).append('\n');
}
}
return strbuf.toString();
}
}