1
2
3
4
5
6
7
8 package org.codehaus.spice.netserve.connection.impl;
9
10 import java.net.ServerSocket;
11 import java.util.Hashtable;
12 import java.util.Map;
13 import org.codehaus.spice.netserve.connection.RequestHandler;
14 import org.codehaus.spice.netserve.connection.SocketAcceptorManager;
15 import org.codehaus.spice.netserve.connection.impl.AcceptorConfig;
16 import org.codehaus.spice.netserve.connection.impl.AcceptorMonitor;
17 import org.codehaus.spice.netserve.connection.impl.ConnectionAcceptor;
18
19 /***
20 * Default implementation of SocketAcceptorManager that uses
21 * a thread per acceptor approach.
22 *
23 * <p>Note that on some OS/JVM combinations <tt>soTimeout</tt> must
24 * be set to non-0 value or else the ServerSocket will never get out
25 * of accept() system call and we wont be able to shutdown the server
26 * socket properly. However it can introduce performance problems if
27 * constantly timing out. </p>
28 *
29 * @author Peter Donald
30 * @author Mauro Talevi
31 * @version $Revision: 1.3 $ $Date: 2004/03/21 23:42:59 $
32 */
33 public class DefaultAcceptorManager
34 implements SocketAcceptorManager
35 {
36 /***
37 * The map of name->acceptor.
38 */
39 private final Map m_acceptors = new Hashtable();
40
41 /***
42 * The monitor that receives notifications of Connection events
43 */
44 private AcceptorMonitor m_monitor = NullAcceptorMonitor.MONITOR;
45
46 /***
47 * Value that we are to set SO_TIMEOUT to if the user
48 * has not already set the timeout. Defaults to 1000 (1s timeout).
49 */
50 private int m_soTimeout = 1000;
51
52 /***
53 * Set to the number of milliseconds that we will wait
54 * for a connection to shutdown gracefully. Defaults to 0
55 * which indicates indefinite wait.
56 */
57 private int m_shutdownTimeout;
58
59 /***
60 * Set the AcceptorMonitor that receives events when changes occur.
61 *
62 * @param monitor the AcceptorMonitor that receives events when
63 * changes occur.
64 */
65 public void setMonitor( final AcceptorMonitor monitor )
66 {
67 m_monitor = monitor;
68 }
69
70 /***
71 * Set the value that we are to set SO_TIMEOUT to if the user
72 * has not already set the timeout. Defaults to 1000 (1s timeout).
73 *
74 * @param soTimeout the timeout value
75 */
76 public void setSoTimeout( final int soTimeout )
77 {
78 m_soTimeout = soTimeout;
79 }
80
81 /***
82 * Set timeout for shutting down handlers.
83 * The timeout defaults to 0 which means wait indefinetly.
84 *
85 * @param shutdownTimeout the timeout
86 */
87 public void setShutdownTimeout( final int shutdownTimeout )
88 {
89 m_shutdownTimeout = shutdownTimeout;
90 }
91
92 /***
93 * Return the shutdownTimeout.
94 *
95 * @return the shutdownTimeout
96 */
97 protected int getShutdownTimeout()
98 {
99 return m_shutdownTimeout;
100 }
101
102 /***
103 * Dispose the ConnectionManager which involves shutting down all
104 * the connected acceptors.
105 */
106 public void shutdownAcceptors()
107 {
108 final String[] names;
109 synchronized( m_acceptors )
110 {
111 names = (String[])m_acceptors.keySet().toArray( new String[ 0 ] );
112 }
113 for( int i = 0; i < names.length; i++ )
114 {
115 disconnect( names[ i ] );
116 }
117 }
118
119 /***
120 * Start accepting connections from a socket and passing connections
121 * to specified handler.
122 *
123 * @param name the name of connection. This serves as a key used to
124 * shutdown acceptor.
125 * @param socket the ServerSocket from which connections are accepted
126 * @throws java.lang.Exception if unable to initiate connection management. This could
127 * be due to the key already being used for another acceptor,
128 * the serversocket being closed, the handler being null etc.
129 */
130 public void connect( final String name,
131 final ServerSocket socket,
132 final RequestHandler handler )
133 throws Exception
134 {
135 if( null == name )
136 {
137 throw new NullPointerException( "name" );
138 }
139 if( null == socket )
140 {
141 throw new NullPointerException( "socket" );
142 }
143 if( null == handler )
144 {
145 throw new NullPointerException( "handler" );
146 }
147
148 if( 0 == socket.getSoTimeout() )
149 {
150 socket.setSoTimeout( m_soTimeout );
151 }
152
153 final ConnectionAcceptor acceptor;
154 synchronized( m_acceptors )
155 {
156 if( isConnected( name ) )
157 {
158 final String message =
159 "Connection already exists with name " + name;
160 throw new IllegalArgumentException( message );
161 }
162
163 final AcceptorConfig config = new AcceptorConfig( name, socket, handler );
164 acceptor = new ConnectionAcceptor( config, getMonitor() );
165 m_acceptors.put( name, acceptor );
166 }
167
168 final Thread thread =
169 new Thread( acceptor, "Acceptor[" + name + "]" );
170 thread.start();
171 while( !acceptor.hasStarted() )
172 {
173 Thread.sleep( 5 );
174 }
175 }
176
177 /***
178 * Return true if acceptor with specified name exists.
179 *
180 * @param name the name
181 * @return true if acceptor with specified name exists.
182 */
183 public boolean isConnected( final String name )
184 {
185 return m_acceptors.containsKey( name );
186 }
187
188 /***
189 * This shuts down the acceptor and the associated ServerSocket.
190 *
191 * @param name the name of connection
192 * @throws java.lang.IllegalArgumentException if no connection with specified name
193 */
194 public void disconnect( final String name )
195 {
196 final ConnectionAcceptor acceptor =
197 (ConnectionAcceptor)m_acceptors.remove( name );
198 if( null == acceptor )
199 {
200 final String message = "No connection with name " + name;
201 throw new IllegalArgumentException( message );
202 }
203
204 acceptor.close( getShutdownTimeout() );
205 }
206
207 /***
208 * Return the monitor used by manager.
209 *
210 * @return the monitor used by manager.
211 */
212 protected AcceptorMonitor getMonitor()
213 {
214 return m_monitor;
215 }
216 }