001: /*
002: * Copyright (C) The Spice Group. All rights reserved.
003: *
004: * This software is published under the terms of the Spice
005: * Software License version 1.1, a copy of which has been included
006: * with this distribution in the LICENSE.txt file.
007: */
008: package org.codehaus.spice.netserve.connection.impl;
009:
010: import java.net.ServerSocket;
011: import java.util.Hashtable;
012: import java.util.Map;
013: import org.codehaus.spice.netserve.connection.RequestHandler;
014: import org.codehaus.spice.netserve.connection.SocketAcceptorManager;
015: import org.codehaus.spice.netserve.connection.impl.AcceptorConfig;
016: import org.codehaus.spice.netserve.connection.impl.AcceptorMonitor;
017: import org.codehaus.spice.netserve.connection.impl.ConnectionAcceptor;
018:
019: /**
020: * Default implementation of SocketAcceptorManager that uses
021: * a thread per acceptor approach.
022: *
023: * <p>Note that on some OS/JVM combinations <tt>soTimeout</tt> must
024: * be set to non-0 value or else the ServerSocket will never get out
025: * of accept() system call and we wont be able to shutdown the server
026: * socket properly. However it can introduce performance problems if
027: * constantly timing out. </p>
028: *
029: * @author Peter Donald
030: * @author Mauro Talevi
031: * @version $Revision: 1.3 $ $Date: 2004/03/21 23:42:59 $
032: */
033: public class DefaultAcceptorManager implements SocketAcceptorManager {
034: /**
035: * The map of name->acceptor.
036: */
037: private final Map m_acceptors = new Hashtable();
038:
039: /**
040: * The monitor that receives notifications of Connection events
041: */
042: private AcceptorMonitor m_monitor = NullAcceptorMonitor.MONITOR;
043:
044: /**
045: * Value that we are to set SO_TIMEOUT to if the user
046: * has not already set the timeout. Defaults to 1000 (1s timeout).
047: */
048: private int m_soTimeout = 1000;
049:
050: /**
051: * Set to the number of milliseconds that we will wait
052: * for a connection to shutdown gracefully. Defaults to 0
053: * which indicates indefinite wait.
054: */
055: private int m_shutdownTimeout;
056:
057: /**
058: * Set the AcceptorMonitor that receives events when changes occur.
059: *
060: * @param monitor the AcceptorMonitor that receives events when
061: * changes occur.
062: */
063: public void setMonitor(final AcceptorMonitor monitor) {
064: m_monitor = monitor;
065: }
066:
067: /**
068: * Set the value that we are to set SO_TIMEOUT to if the user
069: * has not already set the timeout. Defaults to 1000 (1s timeout).
070: *
071: * @param soTimeout the timeout value
072: */
073: public void setSoTimeout(final int soTimeout) {
074: m_soTimeout = soTimeout;
075: }
076:
077: /**
078: * Set timeout for shutting down handlers.
079: * The timeout defaults to 0 which means wait indefinetly.
080: *
081: * @param shutdownTimeout the timeout
082: */
083: public void setShutdownTimeout(final int shutdownTimeout) {
084: m_shutdownTimeout = shutdownTimeout;
085: }
086:
087: /**
088: * Return the shutdownTimeout.
089: *
090: * @return the shutdownTimeout
091: */
092: protected int getShutdownTimeout() {
093: return m_shutdownTimeout;
094: }
095:
096: /**
097: * Dispose the ConnectionManager which involves shutting down all
098: * the connected acceptors.
099: */
100: public void shutdownAcceptors() {
101: final String[] names;
102: synchronized (m_acceptors) {
103: names = (String[]) m_acceptors.keySet().toArray(
104: new String[0]);
105: }
106: for (int i = 0; i < names.length; i++) {
107: disconnect(names[i]);
108: }
109: }
110:
111: /**
112: * Start accepting connections from a socket and passing connections
113: * to specified handler.
114: *
115: * @param name the name of connection. This serves as a key used to
116: * shutdown acceptor.
117: * @param socket the ServerSocket from which connections are accepted
118: * @throws java.lang.Exception if unable to initiate connection management. This could
119: * be due to the key already being used for another acceptor,
120: * the serversocket being closed, the handler being null etc.
121: */
122: public void connect(final String name, final ServerSocket socket,
123: final RequestHandler handler) throws Exception {
124: if (null == name) {
125: throw new NullPointerException("name");
126: }
127: if (null == socket) {
128: throw new NullPointerException("socket");
129: }
130: if (null == handler) {
131: throw new NullPointerException("handler");
132: }
133:
134: if (0 == socket.getSoTimeout()) {
135: socket.setSoTimeout(m_soTimeout);
136: }
137:
138: final ConnectionAcceptor acceptor;
139: synchronized (m_acceptors) {
140: if (isConnected(name)) {
141: final String message = "Connection already exists with name "
142: + name;
143: throw new IllegalArgumentException(message);
144: }
145:
146: final AcceptorConfig config = new AcceptorConfig(name,
147: socket, handler);
148: acceptor = new ConnectionAcceptor(config, getMonitor());
149: m_acceptors.put(name, acceptor);
150: }
151:
152: final Thread thread = new Thread(acceptor, "Acceptor[" + name
153: + "]");
154: thread.start();
155: while (!acceptor.hasStarted()) {
156: Thread.sleep(5);
157: }
158: }
159:
160: /**
161: * Return true if acceptor with specified name exists.
162: *
163: * @param name the name
164: * @return true if acceptor with specified name exists.
165: */
166: public boolean isConnected(final String name) {
167: return m_acceptors.containsKey(name);
168: }
169:
170: /**
171: * This shuts down the acceptor and the associated ServerSocket.
172: *
173: * @param name the name of connection
174: * @throws java.lang.IllegalArgumentException if no connection with specified name
175: */
176: public void disconnect(final String name) {
177: final ConnectionAcceptor acceptor = (ConnectionAcceptor) m_acceptors
178: .remove(name);
179: if (null == acceptor) {
180: final String message = "No connection with name " + name;
181: throw new IllegalArgumentException(message);
182: }
183:
184: acceptor.close(getShutdownTimeout());
185: }
186:
187: /**
188: * Return the monitor used by manager.
189: *
190: * @return the monitor used by manager.
191: */
192: protected AcceptorMonitor getMonitor() {
193: return m_monitor;
194: }
195: }
|