/* * Copyright (c) 2002-2007 Sun Microsystems, Inc. All rights reserved. * * The Sun Project JXTA(TM) Software License * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The end-user documentation included with the redistribution, if any, must * include the following acknowledgment: "This product includes software * developed by Sun Microsystems, Inc. for JXTA(TM) technology." * Alternately, this acknowledgment may appear in the software itself, if * and wherever such third-party acknowledgments normally appear. * * 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must * not be used to endorse or promote products derived from this software * without prior written permission. For written permission, please contact * Project JXTA at http://www.jxta.org. * * 5. Products derived from this software may not be called "JXTA", nor may * "JXTA" appear in their name, without prior written permission of Sun. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SUN * MICROSYSTEMS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * JXTA is a registered trademark of Sun Microsystems, Inc. in the United * States and other countries. * * Please see the license information page at : * <http://www.jxta.org/project/www/license.html> for instructions on use of * the license in source files. * * ==================================================================== * * This software consists of voluntary contributions made by many individuals * on behalf of Project JXTA. For more information on Project JXTA, please see * http://www.jxta.org. * * This license is based on the BSD license adopted by the Apache Foundation. */ package net.jxta.impl.rendezvous.limited; import net.jxta.document.MimeMediaType; import net.jxta.document.XMLDocument; import net.jxta.endpoint.Message; import net.jxta.endpoint.MessageElement; import net.jxta.endpoint.TextDocumentMessageElement; import net.jxta.impl.protocol.LimitedRangeRdvMsg; import net.jxta.impl.rendezvous.RdvWalker; import net.jxta.impl.rendezvous.rpv.PeerViewElement; import net.jxta.logging.Logging; import net.jxta.peer.PeerID; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; /** * The Limited Range Walker is designed to be used by Rendezvous Peer in * order to propagate a message amongst them. A target destination peer * is used in order to send the message to a primary peer. Then, depending * on the TTL, the message is duplicated into two messages, each of them * being sent in opposite "directions" of the RPV. */ public class LimitedRangeWalker implements RdvWalker { /** * Logger */ private final static transient Logger LOG = Logger.getLogger(LimitedRangeWalker.class.getName()); /** * The walk we are associated with. */ private final LimitedRangeWalk walk; /** * Constructor * * @param walk The walk we will be associated with. */ public LimitedRangeWalker(LimitedRangeWalk walk) { this.walk = walk; } /** * {@inheritDoc} */ public void stop() { } /** * {@inheritDoc} * <p/> * Sends message on : * <pre> * "LR-Greeter"<groupid>/<walkSvc><walkParam> * </pre> * <p/> * XXX bondolo 20060720 This method will currently fail to walk the * message to the DOWN peer if there is a failure sending the message to * the UP peer. Perhaps it would be better to ignore any errors from * sending to either peer? */ private void walkMessage(Message msg, LimitedRangeRdvMsg rdvMsg) throws IOException { LimitedRangeRdvMsg.WalkDirection dir = rdvMsg.getDirection(); if ((dir == LimitedRangeRdvMsg.WalkDirection.BOTH) || (dir == LimitedRangeRdvMsg.WalkDirection.UP)) { PeerViewElement upPeer = walk.getPeerView().getUpPeer(); if ((upPeer != null) && upPeer.isAlive()) { Message newMsg = msg.clone(); if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("Walking " + newMsg + " [UP] to " + upPeer); } rdvMsg.setDirection(LimitedRangeRdvMsg.WalkDirection.UP); updateRdvMessage(newMsg, rdvMsg); upPeer.sendMessage(newMsg, walk.getWalkServiceName(), walk.getWalkServiceParam()); } } if ((dir == LimitedRangeRdvMsg.WalkDirection.BOTH) || (dir == LimitedRangeRdvMsg.WalkDirection.DOWN)) { PeerViewElement downPeer = walk.getPeerView().getDownPeer(); if ((downPeer != null) && downPeer.isAlive()) { Message newMsg = msg.clone(); if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("Walking " + newMsg + " [DOWN] to " + downPeer); } rdvMsg.setDirection(LimitedRangeRdvMsg.WalkDirection.DOWN); updateRdvMessage(newMsg, rdvMsg); downPeer.sendMessage(newMsg, walk.getWalkServiceName(), walk.getWalkServiceParam()); } } } /** * {@inheritDoc} */ public void walkMessage(PeerID destination, Message msg, String srcSvcName, String srcSvcParam, int ttl) throws IOException { if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("Walking " + msg + " to " + srcSvcName + "/" + srcSvcParam); } // Check if there is already a Rdv Message LimitedRangeRdvMsg rdvMsg = LimitedRangeWalk.getRdvMessage(msg); if (rdvMsg == null) { if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("Creating new Walk Header for " + msg + " with TTL=" + ttl); } // Create a new one. rdvMsg = new LimitedRangeRdvMsg(); rdvMsg.setTTL(Integer.MAX_VALUE); // will be reduced. rdvMsg.setDirection(LimitedRangeRdvMsg.WalkDirection.BOTH); rdvMsg.setSrcPeerID(walk.getPeerGroup().getPeerID()); rdvMsg.setSrcSvcName(srcSvcName); rdvMsg.setSrcSvcParams(srcSvcParam); } else { // decrement TTL before walk. rdvMsg.setTTL(rdvMsg.getTTL() - 1); } int useTTL = Math.min(ttl, rdvMsg.getTTL()); useTTL = Math.min(useTTL, walk.getPeerView().getView().size() + 1); rdvMsg.setTTL(useTTL); if (useTTL <= 0) { if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("LimitedRangeWalker was not able to send " + msg + " : No TTL remaining"); } return; } // Forward the message according to the direction set in the Rdv Message. if (null != destination) { Message tmp = msg.clone(); updateRdvMessage(tmp, rdvMsg); sendToPeer(destination, walk.getWalkServiceName(), walk.getWalkServiceParam(), tmp); } else { walkMessage(msg, rdvMsg); } } /** * Replace the old version of the rdvMsg * * @param msg The message to be updated. * @param rdvMsg The LimitedRangeRdvMsg which will update the message. * @return the updated message */ private Message updateRdvMessage(Message msg, LimitedRangeRdvMsg rdvMsg) { XMLDocument asDoc = (XMLDocument) rdvMsg.getDocument(MimeMediaType.XMLUTF8); MessageElement el = new TextDocumentMessageElement(LimitedRangeWalk.ELEMENTNAME, asDoc, null); msg.replaceMessageElement("jxta", el); return msg; } /** * Sends the provided message to the specified peer. The peer must be a * current member of the PeerView. * * @param dest The destination peer. * @param svcName The destinations service. * @param svcParam The destination service params. * @param msg The message to send. * @throws IOException Thrown for problems sending the message. */ private void sendToPeer(PeerID dest, String svcName, String svcParam, Message msg) throws IOException { PeerViewElement pve = walk.getPeerView().getPeerViewElement(dest); if (null == pve) { throw new IOException("LimitedRangeWalker was not able to send " + msg + " : no pve"); } if (!pve.sendMessage(msg, svcName, svcParam)) { throw new IOException("LimitedRangeWalker was not able to send " + msg + " : send failed"); } } }