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

ch.qos.logback.core.net.server.ConcurrentServerRunner Maven / Gradle / Ivy

There is a newer version: 2.12.15
Show newest version
/**
 * Logback: the reliable, generic, fast and flexible logging framework.
 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
 *
 * This program and the accompanying materials are dual-licensed under
 * either the terms of the Eclipse Public License v1.0 as published by
 * the Eclipse Foundation
 *
 *   or (per the licensee's choosing)
 *
 * under the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation.
 */
package ch.qos.logback.core.net.server;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import ch.qos.logback.core.spi.ContextAwareBase;

/**
 * A concurrent {@link ServerRunner}.
 * 

* An instance of this object is created with a {@link ServerListener} and * an {@link Executor}. On invocation of the {@link #start()} method, it * passes itself to the given {@code Executor} and returns immediately. On * invocation of its {@link #run()} method by the {@link Executor} it begins * accepting client connections via its {@code ServerListener}. As each * new {@link Client} is accepted, the client is configured with the * runner's {@link LoggingContext} and is then passed to the {@code * Executor} for concurrent execution of the client's service loop. *

* On invocation of the {@link #stop()} method, the runner closes the listener * and each of the connected clients (by invoking {@link Client#close()} * effectively interrupting any blocked I/O calls and causing these concurrent * subtasks to exit gracefully). This ensures that before the {@link #stop()} * method returns (1) all I/O resources have been released and (2) all * of the threads of the {@code Executor} are idle. * * @author Carl Harris */ public abstract class ConcurrentServerRunner extends ContextAwareBase implements Runnable, ServerRunner { private final Lock clientsLock = new ReentrantLock(); private final Collection clients = new ArrayList(); private final ServerListener listener; private final Executor executor; private boolean running; /** * Constructs a new server runner. * @param listener the listener from which the server will accept new * clients * @param executor a executor that will facilitate execution of the * listening and client-handling tasks; while any {@link Executor} * is allowed here, outside of unit testing the only reasonable choice * is a bounded thread pool of some kind. */ public ConcurrentServerRunner(ServerListener listener, Executor executor) { this.listener = listener; this.executor = executor; } /** * {@inheritDoc} */ public boolean isRunning() { return running; } protected void setRunning(boolean running) { this.running = running; } /** * {@inheritDoc} */ public void stop() throws IOException { listener.close(); accept(new ClientVisitor() { public void visit(T client) { client.close(); } }); } /** * {@inheritDoc} */ public void accept(ClientVisitor visitor) { Collection clients = copyClients(); for (T client : clients) { try { visitor.visit(client); } catch (RuntimeException ex) { addError(client + ": " + ex); } } } /** * Creates a copy of the collection of all clients that are presently * being tracked by the server. * @return collection of client objects */ private Collection copyClients() { clientsLock.lock(); try { Collection copy = new ArrayList(clients); return copy; } finally { clientsLock.unlock(); } } /** * {@inheritDoc} */ public void run() { setRunning(true); try { addInfo("listening on " + listener); while (!Thread.currentThread().isInterrupted()) { T client = listener.acceptClient(); if (!configureClient(client)) { addError(client + ": connection dropped"); client.close(); continue; } try { executor.execute(new ClientWrapper(client)); } catch (RejectedExecutionException ex) { addError(client + ": connection dropped"); client.close(); } } } catch (InterruptedException ex) { assert true; // ok... we'll shut down } catch (Exception ex) { addError("listener: " + ex); } setRunning(false); addInfo("shutting down"); listener.close(); } /** * Configures a connected client. *

* A subclass implements this method to perform any necessary configuration * of the client object before its {@link Client#run()} method is invoked. * * @param client the subject client * @return {@code true} if configuration was successful; if the return * value is {@code false} the client connection will be dropped */ protected abstract boolean configureClient(T client); /** * Adds a client to the collection of those being tracked by the server. * @param client the client to add */ private void addClient(T client) { clientsLock.lock(); try { clients.add(client); } finally { clientsLock.unlock(); } } /** * Removes a client from the collection of those being tracked by the server. * @param client the client to remote */ private void removeClient(T client) { clientsLock.lock(); try { clients.remove(client); } finally { clientsLock.unlock(); } } /** * A wrapper for a {@link Client} responsible for ensuring that client * tracking is performed properly. */ private class ClientWrapper implements Client { private final T delegate; public ClientWrapper(T client) { this.delegate = client; } public void run() { addClient(delegate); try { delegate.run(); } finally { removeClient(delegate); } } public void close() { delegate.close(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy