View Javadoc

1   /*
2    * Copyright (C) The Spice Group. All rights reserved.
3    *
4    * This software is published under the terms of the Spice
5    * Software License version 1.1, a copy of which has been included
6    * with this distribution in the LICENSE.txt file.
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 }