/* * Copyright 1999-2006 University of Chicago * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.dcache.ftp.client.dc; import java.io.IOException; import java.util.Enumeration; import java.util.Hashtable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Represents a set of open sockets that are being cached for subsequent transfers. * CheckIn() a socket to add it to the pool. Other threads can use it. CheckOut() a socket to mark it busy; it will remain in the pool but noone * else can check it out. Finally, you can remove a socket from the pool, in which case the pool will remove it from all its references. */ public class SocketPool { private static final Logger logger = LoggerFactory.getLogger(SocketPool.class); protected final Hashtable allSockets = new Hashtable(); protected final Hashtable freeSockets = new Hashtable(); protected final Hashtable busySockets = new Hashtable(); /** * Constructor for SocketPool. */ public SocketPool() { } /** * add socketBox to the pool. Depending on its state, it will be added to free or busy sockets. */ public synchronized void add(SocketBox sb) { int status = ((ManagedSocketBox) sb).getStatus(); if (allSockets.containsKey(sb)) { throw new IllegalArgumentException("This socket already exists in the socket pool."); } allSockets.put(sb, sb); if (status == ManagedSocketBox.FREE) { if (freeSockets.containsKey(sb)) { throw new IllegalArgumentException("This socket already exists in the pool of free sockets."); } logger.debug("adding a free socket"); freeSockets.put(sb, sb); } else { if (busySockets.containsKey(sb)) { throw new IllegalArgumentException("This socket already exists in the pool of busy sockets."); } logger.debug("adding a busy socket"); busySockets.put(sb, sb); } } /** * remove socketBox from the pool, remove all references to it */ public synchronized void remove(SocketBox sb) { int status = ((ManagedSocketBox) sb).getStatus(); if (!allSockets.containsKey(sb)) { throw new IllegalArgumentException("This socket does not seem to exist in the socket pool."); } allSockets.remove(sb); if (status == ManagedSocketBox.FREE) { if (!freeSockets.containsKey(sb)) { throw new IllegalArgumentException( "This socket is marked free, but does not exist in the pool of free sockets."); } freeSockets.remove(sb); } else { if (!busySockets.containsKey(sb)) { throw new IllegalArgumentException( "This socket is marked busy, but does not exist in the pool of busy sockets."); } busySockets.remove(sb); } } /** * checks out the next free socket and returns it, or returns null if there aren't any. * Before calling this method, the socket needs to be first add()ed to the pool. */ public synchronized SocketBox checkOut() { Enumeration e = freeSockets.keys(); if (e.hasMoreElements()) { SocketBox sb = (SocketBox) e.nextElement(); if (busySockets.containsKey(sb)) { throw new IllegalArgumentException( "This socket is marked free, but already exists in the pool of busy sockets."); } ((ManagedSocketBox) sb).setStatus(ManagedSocketBox.BUSY); freeSockets.remove(sb); busySockets.put(sb, sb); return sb; } else { return null; } } /** * Before calling this method, the socket needs to be first add()ed to the pool and checked out. Note: checking in a * socket that is not reusable will cause its removal from the pool. */ public synchronized void checkIn(SocketBox sb) { if (((ManagedSocketBox) sb).getStatus() != ManagedSocketBox.BUSY) { throw new IllegalArgumentException("The socket is already marked free, cannot check it in twice."); } if (!busySockets.containsKey(sb)) { throw new IllegalArgumentException("This socket does not exist in the pool of busy sockets."); } if (freeSockets.containsKey(sb)) { throw new IllegalArgumentException("This socket already exists in the pool of free sockets."); } if (!((ManagedSocketBox) sb).isReusable()) { throw new IllegalArgumentException("This socket is not reusable; cannot check in."); } ((ManagedSocketBox) sb).setStatus(ManagedSocketBox.FREE); busySockets.remove(sb); freeSockets.put(sb, sb); } /** * @return number of all cached sockets */ public int count() { return allSockets.size(); } /** * @return number of free sockets */ public int countFree() { return freeSockets.size(); } /** * @return number of busy sockets */ public int countBusy() { return busySockets.size(); } /** * @return true if there is at least 1 free socket */ public boolean hasFree() { return (countFree() > 0); } /** * Apply the suplied callback to all socketBoxes. */ public synchronized void applyToAll(SocketOperator op) throws Exception { Enumeration keys = allSockets.keys(); while (keys.hasMoreElements()) { SocketBox myBox = (SocketBox) keys.nextElement(); op.operate(myBox); } } /** * Forcibly close all sockets, and remove them from the pool. */ public synchronized void flush() throws IOException { Enumeration keys = allSockets.keys(); // close all sockets before removing them while (keys.hasMoreElements()) { SocketBox myBox = (SocketBox) keys.nextElement(); if (myBox != null) { myBox.setSocket(null); } } allSockets.clear(); freeSockets.clear(); busySockets.clear(); } }