/*
* Copyright (c) 2004-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.endpoint;
import java.io.IOException;
import java.io.InterruptedIOException;
import net.jxta.util.AbstractSimpleSelectable;
import net.jxta.util.SimpleSelectable;
/**
* An AbstractMessenger is used to implement messengers (for example, by transport modules).
* It supplies the convenience, bw compatible, obvious, or otherwise rarely changed methods.
* Many method cannot be overloaded in order to ensure standard behavior.
* The rest is left to implementations.
*
* @see net.jxta.endpoint.EndpointService
* @see net.jxta.endpoint.EndpointAddress
* @see net.jxta.endpoint.Message
*/
public abstract class AbstractMessenger extends AbstractSimpleSelectable implements Messenger {
/**
* The default Maximum Transmission Unit.
*/
protected static final long DEFAULT_MTU = Long.parseLong(System.getProperty("net.jxta.MTU", "65536"));
/**
* The destination address of messages sent on this messenger.
*/
protected final EndpointAddress dstAddress;
private final MessengerStateListenerSet listeners = new MessengerStateListenerSet();
protected final MessengerStateListener distributingListener = new MessengerStateListener() {
public boolean messengerStateChanged(int newState) {
listeners.notifyNewState(newState);
return true;
}
};
/**
* Create a new abstract messenger.
* @param dest who messages should be addressed to
*/
public AbstractMessenger(EndpointAddress dest) {
dstAddress = dest;
}
/**
* {@inheritDoc}
* <p/>
* A simple implementation for debugging. Do not depend upon the format.
*/
@Override
public String toString() {
return super.toString() + " {" + dstAddress + "}";
}
/**
* {@inheritDoc}
*/
public final EndpointAddress getDestinationAddress() {
return dstAddress;
}
/**
* {@inheritDoc}
* <p/>It is not always enforced. At least this much can always be sent.
*/
public long getMTU() {
return DEFAULT_MTU;
}
/**
* {@inheritDoc}
* <p/>
* This is a minimal implementation. It may not detect closure
* initiated by the other side unless the messenger was actually used
* since. A more accurate (but not mandatory implementation) would
* actually go and check the underlying connection, if relevant...unless
* breakage initiated by the other side is actually reported asynchronously
* when it happens. Breakage detection from the other side need not
* be reported atomically with its occurrence. This not very important
* since we canonicalize transport messengers and so do not need to
* aggressively collect closed ones. When not used, messengers die by themselves.
*/
public boolean isClosed() {
return (getState() & USABLE) == 0;
}
/**
* {@inheritDoc}
*/
public final void flush() throws IOException {
int currentState = 0;
try {
currentState = waitState(IDLE, 0);
} catch (InterruptedException ie) {
InterruptedIOException iio = new InterruptedIOException("flush() interrupted");
iio.initCause(ie);
throw iio;
}
if ((currentState & (CLOSED | USABLE)) != 0) {
return;
}
throw new IOException("Messenger was unexpectedly closed.");
}
/**
* {@inheritDoc}
*/
public final boolean sendMessage(Message msg) throws IOException {
return sendMessage(msg, null, null);
}
/**
* {@inheritDoc}
*
*/
public void sendMessage(Message msg, String service, String serviceParam, OutgoingMessageEventListener listener) {
throw new UnsupportedOperationException("This legacy method is not supported by this messenger.");
}
/**
* {@inheritDoc}
*/
public final boolean sendMessage(Message msg, String rService, String rServiceParam) throws IOException {
// We have to retrieve the failure from the message and throw it if its an IOException, this is what the API
// says that this method does.
boolean ret = sendMessageN(msg, rService, rServiceParam);
Object failed = msg.getMessageProperty(Messenger.class);
if (failed == null) {
// huh ?
return ret;
}
if (failed == OutgoingMessageEvent.SUCCESS) {
return true;
}
Throwable throwable;
if (failed instanceof Throwable)
throwable = (Throwable) failed;
else throwable = ((OutgoingMessageEvent) failed).getFailure();
if (throwable == null) {
// Must be saturation, then. (No throw for that).
return false;
}
// Now see how we can manage to throw it.
if (throwable instanceof IOException) {
throw (IOException) throwable;
} else if (throwable instanceof RuntimeException) {
throw (RuntimeException) throwable;
} else if (throwable instanceof Error) {
throw (Error) throwable;
}
IOException failure = new IOException("Failure sending message");
failure.initCause(throwable);
throw failure;
}
/**
* {@inheritDoc}
*/
public final int waitState(int wantedStates, long timeout) throws InterruptedException {
// register the barrier first, in case the state changes concurrently while we do
// our initial interrogation
MessengerStateBarrier barrier = new MessengerStateBarrier(wantedStates);
addStateListener(barrier);
int currentState = getState();
if((currentState & wantedStates) != 0) {
barrier.expire();
return currentState;
}
// we are not currently in any of the states we want, so wait until we are
// notified that we are
int matchingState = barrier.awaitMatch(timeout);
barrier.expire();
if(matchingState != MessengerStateBarrier.NO_MATCH) {
return matchingState;
} else {
return getState();
}
}
public void addStateListener(MessengerStateListener listener) {
listeners.addStateListener(listener);
}
public void removeStateListener(MessengerStateListener listener) {
listeners.removeStateListener(listener);
}
/*
* SimpleSelectable implementation.
*/
/**
* Implements a default for all AbstractMessengers: mirror the event to our selectors. This is what is needed by all the
* known AbstractMessengers that register themselves somewhere. (That is ChannelMessengers).
* FIXME - jice@jxta.org 20040413: Not sure that this is the best default.
*
* @param changedObject Ignored.
*/
public void itemChanged(SimpleSelectable changedObject) {
notifyChange();
}
}