
org.objectweb.dream.protocol.messagePassing.ReliableMessagePassingProtocolImpl 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): Matthieu Leclercq
* Contributor(s):
*/
package org.objectweb.dream.protocol.messagePassing;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import org.objectweb.dream.IOPushException;
import org.objectweb.dream.PushException;
import org.objectweb.dream.control.activity.Util;
import org.objectweb.dream.control.activity.manager.TaskManager;
import org.objectweb.dream.control.activity.task.AbstractTask;
import org.objectweb.dream.control.activity.task.TaskController;
import org.objectweb.dream.dreamannotation.DreamComponent;
import org.objectweb.dream.dreamannotation.DreamMonolog;
import org.objectweb.dream.message.Message;
import org.objectweb.dream.message.MessageManagerType;
import org.objectweb.dream.protocol.ExportException;
import org.objectweb.dream.protocol.ExportIdentifier;
import org.objectweb.dream.protocol.ExportIdentifierChunk;
import org.objectweb.dream.protocol.IncomingPush;
import org.objectweb.dream.protocol.InvalidExportIdentifierException;
import org.objectweb.dream.protocol.Protocol;
import org.objectweb.dream.protocol.messagePassing.overChannel.MessagePassingOverChannelImpl;
import org.objectweb.dream.util.Dream;
import org.objectweb.dream.util.Error;
import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.fraclet.annotation.annotations.Interface;
import org.objectweb.fractal.fraclet.annotation.annotations.Provides;
import org.objectweb.fractal.fraclet.annotation.annotations.Requires;
import org.objectweb.fractal.fraclet.annotation.annotations.Service;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
/**
* Implementation of a reliable bus protocol. This implementation manage a
* retransmission list for every destinations known as down. A
* destination is known as down if an {@link IOPushException} is caught while
* pushing a message to it. A periodic task is in charge of trying to retransmit
* failed messages.
* Since a destination is down only if an exception is caught, this protocol
* should be used over the {@link UDPProtocolItf} to make it reliable. But, it
* may be used over {@link MessagePassingOverChannelImpl} protocol.
*/
@DreamComponent(controllerDesc = "activeDreamUnstoppablePrimitive")
@Provides(interfaces = { @Interface(name = Protocol.ITF_NAME, signature = ReliableMessagePassingProtocol.class) })
public class ReliableMessagePassingProtocolImpl implements ReliableMessagePassingProtocol {
private static final String DEFAULT_FROM_CHUNK_NAME = "RMPP-from";
private static final boolean DEFENSIVE_CHECKS = true;
// ------------------------------------------------------------------------
// ---
// Client interfaces
// ------------------------------------------------------------------------
// ---
@Requires(name = "lower-protocol")
protected MessagePassingProtocol lowerLevelProtocolItf;
@Requires(name = "message-manager")
protected MessageManagerType messageManagerItf;
@Requires(name = "task-manager")
protected TaskManager taskManagerItf;
// ------------------------------------------------------------------------
// --
// Services interfaces
// ------------------------------------------------------------------------
// --
/**
* Component reference
*/
@Service
Component weaveableC;
/**
* Logger of the component
*/
@DreamMonolog()
protected Logger logger;
// ------------------------------------------------------------------------
// ---
// Implementation of the MessagePassingProtocol interface
// ------------------------------------------------------------------------
// ---
/**
* @see MessagePassingProtocol#export(IncomingPush, Map)
*/
public MessagePassingOutgoingPush export(IncomingPush incomingPushItf, Map hints)
throws ExportException {
Session session;
Object o = (hints == null) ? null : hints.get(FROM_CHUNK_NAME);
if (o != null && !(o instanceof String)) {
throw new ExportException("Invalid 'from-chunk-name' hint value, must be a String");
}
String fromChunkName = (String) o;
boolean removeFromChunk;
if (fromChunkName == null) {
fromChunkName = DEFAULT_FROM_CHUNK_NAME;
removeFromChunk = true;
if (hints == null) {
hints = new HashMap();
}
hints.put(FROM_CHUNK_NAME, fromChunkName);
} else {
removeFromChunk = false;
}
session = new Session(incomingPushItf, fromChunkName, removeFromChunk);
final MessagePassingOutgoingPush lowerPush = this.lowerLevelProtocolItf.export(session, hints);
if (removeFromChunk) {
hints.remove(FROM_CHUNK_NAME);
}
session.initialize(lowerPush);
long period = DEFAULT_RETRANSMIT_PERIOD;
o = hints.get(RETRANSMIT_PERIOD_HINT_KEY);
if (o != null) {
if (!(o instanceof Number)) {
throw new ExportException(
"Invalid retransmission period in hint map, must be a Number");
}
period = ((Number) o).longValue();
}
final Map taskHints = new HashMap();
taskHints.put("period", new Long(period));
try {
Util.addTask(this.weaveableC, session.retransmitTask, taskHints);
} catch (final Exception e) {
throw new ExportException("Unable to register retransmission task : " + e.getMessage());
}
return session;
}
/**
* @see MessagePassingProtocol#createExportIdentifier(Map,
* ExportIdentifier[])
*/
public ExportIdentifier createExportIdentifier(Map info, ExportIdentifier[] next)
throws InvalidExportIdentifierException {
return this.lowerLevelProtocolItf.createExportIdentifier(info, next);
}
// ------------------------------------------------------------------------
// ---
// Inner classes
// ------------------------------------------------------------------------
// ---
private class Session implements MessagePassingOutgoingPush, IncomingPush {
private final IncomingPush upperIncomingPush;
private boolean initialized = false;
private boolean closed = false;
private ExportIdentifier localId;
private MessagePassingOutgoingPush lowerOutgoingPush;
private final Map destinationStates = new HashMap();
private final RetransmitTask retransmitTask = new RetransmitTask();
private final String fromChunkName;
private final boolean removeFromChunk;
protected Session(IncomingPush upperIncomingPush, String fromChunkName,
boolean removeFromChunk) {
this.fromChunkName = fromChunkName;
this.removeFromChunk = removeFromChunk;
this.upperIncomingPush = upperIncomingPush;
}
protected synchronized void initialize(MessagePassingOutgoingPush lowerOutgoingPush) {
this.lowerOutgoingPush = lowerOutgoingPush;
this.localId = lowerOutgoingPush.getLocalExportIdentifier();
this.initialized = true;
this.notifyAll();
}
// --------------------------------------------------------------------
// -----
// Implementation of the MessagePassingOutgoingPush interface
// --------------------------------------------------------------------
// -----
/**
* @see MessagePassingOutgoingPush#getLocalExportIdentifier()
*/
public ExportIdentifier getLocalExportIdentifier() {
return this.localId;
}
/**
* @see MessagePassingOutgoingPush#outgoingPush(Message,
* ExportIdentifier)
*/
public void outgoingPush(Message message, ExportIdentifier to)
throws InvalidExportIdentifierException, IOPushException {
DestinationState state;
synchronized (this) {
if (this.closed) {
throw new IOPushException("Session is closed");
}
state = this.destinationStates.get(to);
if (state == null) {
state = new DestinationState(to);
this.destinationStates.put(to, state);
}
}
try {
state.transmit(message);
} catch (final InvalidExportIdentifierException e) {
synchronized (this) {
this.destinationStates.remove(to);
}
}
}
/**
* @see MessagePassingOutgoingPush#outgoingClose(IncomingPush)
*/
public synchronized void outgoingClose(IncomingPush incomingPush) throws IOException {
this.lowerOutgoingPush.outgoingClose(this);
try {
final TaskController taskController = Dream.getTaskController(ReliableMessagePassingProtocolImpl.this.weaveableC);
taskController.removeTask(this.retransmitTask);
} catch (final Exception e) {
Error.bug(ReliableMessagePassingProtocolImpl.this.logger, e);
}
final Iterator iterator = this.destinationStates.values().iterator();
while (iterator.hasNext()) {
iterator.next().deletedMessages();
}
this.closed = true;
}
// --------------------------------------------------------------------
// -----
// Implementation of the IncomingPush interface
// --------------------------------------------------------------------
// -----
/**
* @see IncomingPush#incomingPush(Message)
*/
public void incomingPush(Message message) throws PushException {
synchronized (this) {
while (!this.initialized) {
try {
this.wait();
} catch (final InterruptedException e) {
throw new PushException(
"Interrupted while waiting for the session to be initialized", e);
}
}
}
ExportIdentifierChunk chunk = ReliableMessagePassingProtocolImpl.this.messageManagerItf.getChunk(message, this.fromChunkName);
if (this.removeFromChunk && chunk != null) {
chunk = ReliableMessagePassingProtocolImpl.this.messageManagerItf.removeChunk(message, this.fromChunkName);
}
if (chunk == null) {
ReliableMessagePassingProtocolImpl.this.logger.log(BasicLevel.WARN, "Can't find from chunk in incoming message.");
this.upperIncomingPush.incomingPush(message);
} else {
this.upperIncomingPush.incomingPush(message);
final DestinationState state = this.destinationStates.get(chunk.getExportIdentifier());
if (state != null) {
try {
state.retransmit();
} catch (final InvalidExportIdentifierException e) {
ReliableMessagePassingProtocolImpl.this.logger.log(BasicLevel.WARN,
"Caught InvalidExportIdentifierException during "
+ "retransmission. Messages with this destination will be "
+ "discarded");
synchronized (Session.this) {
this.destinationStates.remove(state.remoteId);
}
state.deletedMessages();
}
}
}
}
/**
* @see IncomingPush#incomingClosed(Object, Exception)
*/
public synchronized void incomingClosed(Object outgoingPush, Exception exception) {
// never called on message passing protocol
Error.bug(ReliableMessagePassingProtocolImpl.this.logger);
}
protected class DestinationState {
protected boolean started = true;
protected ExportIdentifier remoteId;
protected LinkedList waitingList;
protected DestinationState(ExportIdentifier remoteId) {
this.remoteId = remoteId;
}
private void add(Message message) {
if (this.waitingList == null) {
this.waitingList = new LinkedList();
}
this.waitingList.addLast(message);
}
private void readd(Message message) {
if (this.waitingList == null) {
this.waitingList = new LinkedList();
}
this.waitingList.addFirst(message);
}
private Message get() {
if (this.waitingList == null || this.waitingList.isEmpty()) {
return null;
}
return this.waitingList.removeFirst();
}
protected synchronized void transmit(Message message)
throws InvalidExportIdentifierException {
if (DEFENSIVE_CHECKS && this.started && this.waitingList != null && !this.waitingList.isEmpty()) {
Error.bug(ReliableMessagePassingProtocolImpl.this.logger);
}
if (!this.started) {
this.add(message);
return;
}
try {
Session.this.lowerOutgoingPush.outgoingPush(message, this.remoteId);
} catch (final PushException e) {
this.started = false;
this.add(message);
return;
}
}
protected synchronized void retransmit() throws InvalidExportIdentifierException {
this.started = true;
Message m = this.get();
while (m != null) {
try {
Session.this.lowerOutgoingPush.outgoingPush(m, this.remoteId);
} catch (final PushException e) {
this.started = false;
this.readd(m);
break;
}
m = this.get();
}
}
protected synchronized void deletedMessages() {
Message m = this.get();
while (m != null) {
ReliableMessagePassingProtocolImpl.this.messageManagerItf.deleteMessage(m);
m = this.get();
}
}
}
protected class RetransmitTask extends AbstractTask {
public RetransmitTask() {
super("Retransmit task");
}
public Object execute(Object hints) throws InterruptedException {
Iterator iterator;
synchronized (Session.this) {
iterator = new ArrayList(Session.this.destinationStates.values())
.iterator();
}
while (iterator.hasNext()) {
final DestinationState state = iterator.next();
try {
state.retransmit();
} catch (final InvalidExportIdentifierException e) {
ReliableMessagePassingProtocolImpl.this.logger.log(BasicLevel.WARN,
"Caught InvalidExportIdentifierException during "
+ "retransmission. Messages with this destination will be "
+ "discarded");
synchronized (Session.this) {
Session.this.destinationStates.remove(state.remoteId);
}
state.deletedMessages();
}
}
return EXECUTE_AGAIN;
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy