2 * Copyright (c) 2002-2007 Sun Micro//Systems, Inc. All rights reserved.
4 * The Sun Project JXTA(TM) Software License
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
16 * 3. The end-user documentation included with the redistribution, if any, must
17 * include the following acknowledgment: "This product includes software
18 * developed by Sun Microsystems, Inc. for JXTA(TM) technology."
19 * Alternately, this acknowledgment may appear in the software itself, if
20 * and wherever such third-party acknowledgments normally appear.
22 * 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must
23 * not be used to endorse or promote products derived from this software
24 * without prior written permission. For written permission, please contact
25 * Project JXTA at http://www.jxta.org.
27 * 5. Products derived from this software may not be called "JXTA", nor may
28 * "JXTA" appear in their name, without prior written permission of Sun.
30 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
32 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SUN
33 * MICROSYSTEMS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
34 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
36 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
37 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
39 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 * JXTA is a registered trademark of Sun Microsystems, Inc. in the United
42 * States and other countries.
44 * Please see the license information page at :
45 * <http://www.jxta.org/project/www/license.html> for instructions on use of
46 * the license in source files.
48 * ====================================================================
50 * This software consists of voluntary contributions made by many individuals
51 * on behalf of Project JXTA. For more information on Project JXTA, please see
52 * http://www.jxta.org.
54 * This license is based on the BSD license adopted by the Apache Foundation.
56 package net.jxta.impl.rendezvous.rpv;
58 import java.io.IOException;
60 import net.jxta.endpoint.EndpointService;
61 import net.jxta.endpoint.Message;
62 import net.jxta.endpoint.Messenger;
63 import net.jxta.endpoint.OutgoingMessageEvent;
64 import net.jxta.endpoint.OutgoingMessageEventListener;
65 import net.jxta.impl.util.TimeUtils;
66 import net.jxta.protocol.RdvAdvertisement;
68 import java.util.logging.Level;
69 import net.jxta.logging.Logging;
70 import java.util.logging.Logger;
73 * An element of the PeerView.
75 * <p/>The destination address (peerID) is part of PeerViewDestination, which implements the
76 * comparable interface. That makes it possible to sort and create ordered lists of
77 * PeerViewElements, and to search these lists while knowing only a destination address.
79 public final class PeerViewElement extends PeerViewDestination implements OutgoingMessageEventListener {
84 private final static transient Logger LOG = Logger.getLogger(PeerViewElement.class.getName());
87 * EndpointService that this PeerViewElement must use.
89 private final EndpointService endpoint;
92 * Absolute time in milliseconds at which this element was created.
94 private final long created;
97 * Absolute time in milliseconds at which this element was created.
99 private long lastUpdate = 0;
102 * The encapsulated RdvAdvertisement for the Peer this instance
105 private RdvAdvertisement radv = null;
108 * True is the remote peer is known to be alive, false otherwise.
109 * It is always alive at birth. It may die soon after and we want to
110 * generate an event in that case.
112 private boolean alive = true;
115 * If true then we are not accepting new messages until something unclogs.
117 private volatile boolean throttling = false;
120 * PeerView that owns this PeerViewElement.
122 private PeerView peerview = null;
125 * A cached Messenger for sending to the destination peer.
127 private Messenger cachedMessenger = null;
130 * Initialize from a RdvAdvertisement.
132 * @param endpoint The endpoint service.
133 * @param radv the RdvAdvertisement from which to initialize
135 PeerViewElement(EndpointService endpoint, RdvAdvertisement radv) {
137 super(radv.getPeerID());
139 this.endpoint = endpoint;
142 created = TimeUtils.timeNow();
143 lastUpdate = created;
149 * A simple implementation for debugging. Do not attempt to parse this value!
152 public String toString() {
153 StringBuilder asString = new StringBuilder();
155 asString.append('\"');
156 asString.append(radv.getName());
157 asString.append('\"');
158 asString.append(alive ? " A " : " a ");
159 asString.append(isInPeerView() ? " P " : " p ");
160 asString.append(throttling ? " T " : " t ");
161 asString.append(" [");
162 asString.append(TimeUtils.toRelativeTimeMillis(TimeUtils.timeNow(), created) / TimeUtils.ASECOND);
163 asString.append("/");
164 asString.append(TimeUtils.toRelativeTimeMillis(TimeUtils.timeNow(), lastUpdate) / TimeUtils.ASECOND);
165 asString.append("]");
166 return asString.toString();
172 public void messageSendSucceeded(OutgoingMessageEvent e) {
174 // As far as we know, connectivity is fine.
175 setAlive(true, true);
183 public void messageSendFailed(OutgoingMessageEvent e) {
185 // As far as we know, connectivity is down.
186 // Except if failure is null; then it's just a queue overflow.
188 if (null != e.getFailure()) {
189 setAlive(false, true);
192 throttling = (e.getFailure() == null);
196 * Return <code>true</code> if the remote peer is known to be alive,
197 * <code>false</code> otherwise.
199 * @return Return <code>true</code> if the remote peer is known to be
200 * alive, <code>false</code> otherwise.
202 public boolean isAlive() {
207 * Update the connection status based upon the result of the last message
210 * <p/>We track the current dead-alive state and If we're in a peerview
211 * notify it of the transitions from alive to dead.
213 * @param live The known liveness of our connection to this peer.
214 * @param doNotify {@code true} will cause failure notifications to be sent.
215 * {@code false} makes notifications the caller's responsibility.
216 * @return {@code true} if a failure notification needs to be sent otherwise
219 boolean setAlive(boolean live, boolean doNotify) {
222 synchronized (this) {
223 mustNotify = alive && !live;
227 // Since we do this out of sync, it is in theory possible that our alive
228 // status has already changed. It is rare but will only cause a little
229 // shake. So leave the sync behind, it causes a deadlock.
230 if (mustNotify && doNotify) {
231 PeerView temp = peerview;
234 temp.notifyFailure(this, true);
241 boolean isInPeerView() {
242 return (null != peerview);
248 synchronized void setPeerView(PeerView pv) {
249 if ((null != peerview) && (null != pv)) {
250 throw new IllegalStateException("Element already in " + peerview);
257 * Return the time in absolute milliseconds at which we last updated this peer.
259 long getLastUpdateTime() {
264 * Sets the time in absolute milliseconds at which we last updated this peer.
266 void setLastUpdateTime(long last) {
271 * Send a message to the peer which is represented by the current
274 * @param msg the message to send
276 * @param serviceName the service name on the destination peer to
277 * which the message will be demultiplexed
279 * @param serviceParam the service param on the destination peer
280 * to which the message will be demultiplexed
282 * @return true if the message was successfully handed off to the
283 * endpoint for delivery, false otherwise
285 public boolean sendMessage(Message msg, String serviceName, String serviceParam) {
288 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
289 LOG.warning("Declining to send -- throttling on " + this);
294 Messenger sendVia = getCachedMessenger();
296 if (null == sendVia) {
297 // There is nothing really we can do.
298 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
299 LOG.warning("Could not get messenger for " + getPeerID());
302 OutgoingMessageEvent event = new OutgoingMessageEvent(msg,
303 new IOException("Couldn't get messenger for " + getPeerID()));
305 messageSendFailed(event);
309 sendVia.sendMessage(msg, serviceName, serviceParam, this);
315 * Get the encapsulated Peer Advertisement.
317 * @return the Advertisement of the Peer represented by this
320 public RdvAdvertisement getRdvAdvertisement() {
325 * Set the encapsulated Peer Advertisement.
327 * @param adv is the RdvAdvertisement to be set.
328 * @return RdvAdvertisement the old Advertisement of the Peer represented by this
331 RdvAdvertisement setRdvAdvertisement(RdvAdvertisement adv) {
333 if (!radv.getPeerID().equals(adv.getPeerID())) {
334 if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
335 LOG.severe("adv refers to a different peer");
338 throw new IllegalArgumentException("adv refers to a different peer");
341 RdvAdvertisement old = radv;
345 setLastUpdateTime(TimeUtils.timeNow());
351 * Return a messenger suitable for sending to this peer.
353 * @return a messenger to this PVE peer or if {@code null} if peer is
356 private Messenger getCachedMessenger() {
358 boolean mustNotify = false;
360 synchronized (this) {
361 if ((null == cachedMessenger) || ((cachedMessenger.getState() & Messenger.USABLE) == 0)) {
362 cachedMessenger = null;
364 if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
365 LOG.fine("Getting cached Messenger for " + radv.getName());
368 cachedMessenger = endpoint.getMessengerImmediate(getDestAddress(), radv.getRouteAdv());
370 if (null == cachedMessenger) {
371 mustNotify = setAlive(false, false);
372 } else if ((cachedMessenger.getState() & Messenger.RESOLVED) != 0) {
373 mustNotify = setAlive(true, false);
378 // Since we do this out of sync, it is in theory possible that our alive
379 // status has already changed. It is rare but will only cause a little
380 // shake. So leave the sync behind, it causes a deadlock.
382 PeerView temp = peerview;
385 temp.notifyFailure(this, true);
389 return cachedMessenger;