package net.jxta.util; import java.net.SocketException; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import net.jxta.logging.Logging; /** * A server pipe listener which stores a queue of incoming connections, to be * synchronously pulled off the queue at a pace determined by the client. If more * connections are received than the specified backlog, new connections beyond that * point will be discarded until connections are accepted from the head of the queue */ public class QueuingServerPipeAcceptor implements ServerPipeAcceptListener { private BlockingQueue<JxtaBiDiPipe> pendingAcceptance; private long defaultTimeout; private static final Logger LOG = Logger.getLogger(QueuingServerPipeAcceptor.class.getName()); /** * @param backlog the maximum number of connections to queue for acceptance. * @param defaultTimeout the default timeout of the {@link #accept()} method, in * milliseconds. If this value is 0, then the timeout will be effectively indefinite. * If the value is less than zero, then no timeout will be applied, i.e. accept will * return immediately). */ public QueuingServerPipeAcceptor(int backlog, long defaultTimeout) { pendingAcceptance = new ArrayBlockingQueue<JxtaBiDiPipe>(backlog); setTimeout(defaultTimeout); } public void pipeAccepted(JxtaBiDiPipe pipe) { if(!pendingAcceptance.offer(pipe) && Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "pending queue full, discarding incoming pipe {0} from {1}", new Object[] { pipe.getPipeAdvertisement().getPipeID(), pipe.getRemotePeerAdvertisement().getPeerID() }); } } /** * Accept a queued incoming pipe, or wait up to the specified timeout for a pipe * to be established. timeout <= 0 is equivalent to saying "do not wait". * * @return the accepted pipe, or null if no new pipe arrived before the timeout. */ public JxtaBiDiPipe accept(long timeout, TimeUnit timeoutUnit) throws InterruptedException { return pendingAcceptance.poll(timeout, TimeUnit.MILLISECONDS); } /** * @return the accepted pipe, or null if no new pipe arrived before the timeout. */ public JxtaBiDiPipe accept() throws InterruptedException { return accept(defaultTimeout, TimeUnit.MILLISECONDS); } /** * Variant of accept which throws a SocketException if no pipe is received within the timeout. * This exists to be compatible with the original interface of {@link JxtaServerPipe#accept()}. */ public JxtaBiDiPipe acceptBackwardsCompatible() throws SocketException { try { JxtaBiDiPipe pipe = accept(); if(pipe == null) { throw new SocketException("No pipe received within timeout"); } return pipe; } catch (InterruptedException e) { SocketException s = new SocketException("Interrupted while waiting for new incoming pipe"); s.initCause(e); throw s; } } public void serverPipeClosed() { pendingAcceptance.clear(); } /** * Sets the default timeout value (in milliseconds) that is used for the * {@link #accept()} method. * * @param timeout */ public void setTimeout(long timeout) { if(timeout == 0) { this.defaultTimeout = Long.MAX_VALUE; } this.defaultTimeout = timeout; } public void setTimeoutBackwardsCompatible(int timeout) { if (timeout < 0) { throw new IllegalArgumentException("Negative timeout values are not allowed."); } else if(timeout == 0) { this.defaultTimeout = Long.MAX_VALUE; } else { this.defaultTimeout = timeout; } } /** * Returns the default timeout value (in milliseconds) that is used for the * {@link #accept()} method. */ public long getTimeout() { return defaultTimeout; } /** * Variant of getTimeout which matches the original behaviour of {@link JxtaServerPipe#getPipeTimeout()}. * If the timeout value is greater than {@link Integer#MAX_VALUE} then 0 is returned, indicating an * infinite timeout. */ public int getTimeoutBackwardsCompatible() { if(defaultTimeout > Integer.MAX_VALUE) { return 0; } return (int)defaultTimeout; } }