/* * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file * except in compliance with the License. * * You can obtain a copy of the License at * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== */ package org.identityconnectors.framework.server.impl; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.identityconnectors.common.logging.Log; import org.identityconnectors.framework.common.exceptions.ConnectorException; import org.identityconnectors.framework.server.ConnectorServer; class ConnectionListener extends CCLWatchThread { /** * This is the size of our internal queue. For now I have this relatively * small because I want the OS to manage the connect queue coming in. That * way it can properly turn away excessive requests */ private final static int INTERNAL_QUEUE_SIZE = 2; private static final Log LOG = Log.getLog(ConnectionListener.class); /** * The server object that we are using */ private final ConnectorServer connectorServer; /** * The server socket. This must be bound at the time of creation. */ private final ServerSocket socket; /** * Pool of executors */ private final ExecutorService threadPool; /** * Set to indicated we need to start shutting down */ private boolean stopped = false; /** * Creates the listener thread * * @param server * The server object * @param socket * The socket (should already be bound) */ public ConnectionListener(ConnectorServer server, ServerSocket socket) { super("ConnectionListener"); connectorServer = server; this.socket = socket; // idle time timeout threadPool = new ThreadPoolExecutor(server.getMinWorkers(), server.getMaxWorkers(), 30, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(INTERNAL_QUEUE_SIZE, true), // fair new CCLWatchThreadFactory()); } @Override public void run() { while (!isStopped()) { try { Socket connection = socket.accept(); ConnectionProcessor processor = new ConnectionProcessor(connectorServer, connection); // this really sucks - ideally, execute would block // if the queue is full. now we have to do a busy wait // the effect is that eventually our socket's accept // queue will fill up and start rejecting requests // at the connection (which is what we want) while (true) { try { threadPool.execute(processor); break; } catch (RejectedExecutionException e) { try { Thread.sleep(100); } catch (Exception e2) { /* ignore */ } } } } catch (Throwable e) { // log the error unless it's because we've stopped if (!isStopped() || !(e instanceof SocketException)) { LOG.error(e, "Error processing request"); } // wait a second before trying again if (!isStopped()) { try { Thread.sleep(1000); } catch (Exception e2) { /* ignore */ } } } } } private synchronized void markStopped() { stopped = true; } private synchronized boolean isStopped() { return stopped; } public void shutdown() { if (Thread.currentThread() == this) { throw new IllegalArgumentException("Shutdown may not be called from this thread"); } if (!isStopped()) { try { // set the stopped flag so we no its a normal // shutdown and don't log the SocketException markStopped(); // close the socket - this causes accept to throw an exception socket.close(); // wait for the main listener thread to die so we don't // get any new requests join(); // wait for all in-progress requests to finish threadPool.shutdown(); } catch (Exception e) { throw ConnectorException.wrap(e); } } } }