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

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