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

net.grinder.communication.ServerReceiver Maven / Gradle / Ivy

The newest version!
// Copyright (C) 2000 - 2012 Philip Aston
// All rights reserved.
//
// This file is part of The Grinder software distribution. Refer to
// the file LICENSE which is part of The Grinder distribution for
// licensing details. The Grinder distribution is available on the
// Internet at http://grinder.sourceforge.net/
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS 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 THE
// COPYRIGHT HOLDERS OR 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.

package net.grinder.communication;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.concurrent.ExecutorService;

import net.grinder.common.UncheckedInterruptedException;
import net.grinder.communication.ResourcePool.Reservation;
import net.grinder.util.thread.ExecutorFactory;
import net.grinder.util.thread.InterruptibleRunnable;
import net.grinder.util.thread.InterruptibleRunnableAdapter;


/**
 * Manages the receipt of messages from many clients.
 *
 * @author Philip Aston
 */
public final class ServerReceiver implements Receiver {

  private final MessageQueue m_messageQueue = new MessageQueue(true);
  private final ExecutorService m_executor =
    ExecutorFactory.createCachedThreadPool("ServerReceiver");

  /**
   * Registers a new {@link Acceptor} from which the ServerReceiver
   * should process messages. Actively polls connections of the given types for
   * messages, de-serialises them, and queues them for retrieval using
   * {@link #waitForMessage()}.
   *
   * 

* A single {@code ServerReceiver} can listen to messages from multiple * {@link Acceptor}s. You can register the same {@link Acceptor} with * multiple {@code ServerReceiver}s, but then there is no way of * controlling which receiver will receive messages from a given * {@link Acceptor}. *

* * @param acceptor * Acceptor. * @param connectionTypes * Type of connections to listen for. * @param numberOfThreads * How many threads to dedicate to processing the Acceptor. The * threads this method spawns just read, deserialise, and queue. Set * {@code numberOfThreads} to the number of concurrent streams * you expect to be able to read. * @param idleThreadPollDelay * Time in milliseconds that an idle thread should sleep if there are * no sockets to process. * @param inactiveClientTimeOut * How long before we consider a client connection that presents no * data to be inactive. * * @exception CommunicationException * If this {@code ServerReceiver} has been shutdown. */ public void receiveFrom(Acceptor acceptor, ConnectionType[] connectionTypes, int numberOfThreads, final long idleThreadPollDelay, final long inactiveClientTimeOut) throws CommunicationException { if (connectionTypes.length == 0) { // Nothing to do. return; } final ResourcePool[] acceptedSocketSets = new ResourcePool[connectionTypes.length]; for (int i = 0; i < connectionTypes.length; ++i) { acceptedSocketSets[i] = acceptor.getSocketSet(connectionTypes[i]); } synchronized (this) { m_messageQueue.checkIfShutdown(); for (int i = 0; i < numberOfThreads; ++i) { m_executor.submit( new InterruptibleRunnableAdapter( new ServerReceiverRunnable( new CombinedResourcePool(acceptedSocketSets), idleThreadPollDelay, inactiveClientTimeOut))); } } } /** * Block until a message is available, or another thread has called * {@link #shutdown}. Typically called from a message dispatch loop. * *

Multiple threads can call this method, but only one thread * will receive a given message.

* * @return The message or {@code null} if shut down. * @throws CommunicationException If an error occurred receiving a message. */ public Message waitForMessage() throws CommunicationException { try { return m_messageQueue.dequeue(true); } catch (MessageQueue.ShutdownException e) { return null; } } /** * Shut down this receiver. */ public synchronized void shutdown() { m_messageQueue.shutdown(); m_executor.shutdownNow(); } /** * Obtain reservations from multiple ResourcePools. Delegates reserveNext() * to the next resource pool. * * @author Philip Aston */ private static final class CombinedResourcePool { private final ResourcePool[] m_resourcePools; // Guarded by m_resourcePools. private int m_next; CombinedResourcePool(ResourcePool[] resourcePools) { assert resourcePools.length > 0; m_resourcePools = resourcePools; } public Reservation reserveNext() { int i = 0; int start; synchronized (m_resourcePools) { start = ++m_next; } while (true) { final Reservation reservation = m_resourcePools[(start + i) % m_resourcePools.length].reserveNext(); if (!reservation.isSentinel() || i == m_resourcePools.length - 1) { return reservation; } ++i; } } } private final class ServerReceiverRunnable implements InterruptibleRunnable { private final CombinedResourcePool m_sockets; private final long m_delay; private final long m_inactiveClientTimeOut; private ServerReceiverRunnable(CombinedResourcePool sockets, long delay, long inactiveClientTimeOut) { m_sockets = sockets; m_delay = delay; m_inactiveClientTimeOut = inactiveClientTimeOut; } public void interruptibleRun() { try { // Did we do some work on the last pass? boolean idle = false; while (true) { final Reservation reservation = m_sockets.reserveNext(); boolean holdReservation = false; try { if (reservation.isSentinel()) { if (idle) { Thread.sleep(m_delay); } idle = true; } else { final IdleAwareSocketWrapper socketWrapper = (IdleAwareSocketWrapper)reservation.getResource(); // We don't need to synchronise access to the SocketWrapper // stream; access is protected through the socket set and only we // hold the reservation. if (socketWrapper.hasData(m_inactiveClientTimeOut)) { idle = false; final ObjectInputStream objectStream = new ObjectInputStream(socketWrapper.getInputStream()); final Message message = (Message)objectStream.readObject(); if (message instanceof CloseCommunicationMessage) { reservation.close(); continue; } if (message instanceof AddressAwareMessage) { final AddressAwareMessage addressAware = (AddressAwareMessage)message; addressAware.setAddress(socketWrapper.getAddress()); } if (message instanceof MessageRequiringResponse) { final MessageRequiringResponse messageRequiringResponse = (MessageRequiringResponse)message; messageRequiringResponse.setResponder( new SenderWithReservation( new StreamSender(socketWrapper.getOutputStream()), reservation)); m_messageQueue.queue(message); // Whatever handles the MessageExpectingResponse takes // responsibility for the reservation. holdReservation = true; } else { m_messageQueue.queue(message); } } } } catch (CommunicationException e) { reservation.close(); m_messageQueue.queue(e); } catch (IOException e) { reservation.close(); UncheckedInterruptedException.ioException(e); m_messageQueue.queue(e); } catch (ClassNotFoundException e) { reservation.close(); m_messageQueue.queue(e); } catch (InterruptedException e) { reservation.close(); throw new UncheckedInterruptedException(e); } finally { if (!holdReservation) { reservation.free(); } } } } catch (MessageQueue.ShutdownException e) { // We've been shutdown, exit this thread. } finally { // Ensure we're shutdown. shutdown(); } } } /** * SenderWithReservation. A one-shot use Sender, only accessed through a * {@link MessageRequiringResponse} wrapper which ensures send() is called at * most once. * *

* If no {@link MessageRequiringResponse#sendResponse(Message)} is not called, * the reservation will not be freed. No way to avoid this - we're handing off * responsibility to respond to a stream. *

* */ private static final class SenderWithReservation implements Sender { private final Sender m_delegateSender; private final Reservation m_reservation; private SenderWithReservation(Sender delegateSender, Reservation reservation) { m_delegateSender = delegateSender; m_reservation = reservation; } public void send(Message message) throws CommunicationException { try { m_delegateSender.send(message); } finally { shutdown(); } } public void shutdown() { // We rely on our ResponseSender's single call to send to ensure we don't // free the reservation multiple times. m_reservation.free(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy