View Javadoc

1   /************************************************************************
2    * Copyright (c) 2000-2006 The Apache Software Foundation.             *
3    * All rights reserved.                                                *
4    * ------------------------------------------------------------------- *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you *
6    * may not use this file except in compliance with the License. You    *
7    * may obtain a copy of the License at:                                *
8    *                                                                     *
9    *     http://www.apache.org/licenses/LICENSE-2.0                      *
10   *                                                                     *
11   * Unless required by applicable law or agreed to in writing, software *
12   * distributed under the License is distributed on an "AS IS" BASIS,   *
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
14   * implied.  See the License for the specific language governing       *
15   * permissions and limitations under the License.                      *
16   ***********************************************************************/
17  
18  package org.apache.james.util.connection;
19  import java.net.ServerSocket;
20  import java.util.HashMap;
21  import org.apache.excalibur.thread.ThreadPool;
22  import org.apache.avalon.cornerstone.services.connection.ConnectionHandlerFactory;
23  import org.apache.james.services.JamesConnectionManager;
24  import org.apache.avalon.cornerstone.services.threads.ThreadManager;
25  import org.apache.avalon.framework.service.ServiceException;
26  import org.apache.avalon.framework.service.ServiceManager;
27  import org.apache.avalon.framework.service.Serviceable;
28  import org.apache.avalon.framework.configuration.Configurable;
29  import org.apache.avalon.framework.configuration.Configuration;
30  import org.apache.avalon.framework.configuration.ConfigurationException;
31  import org.apache.avalon.framework.container.ContainerUtil;
32  import org.apache.avalon.framework.activity.Disposable;
33  import org.apache.avalon.framework.logger.AbstractLogEnabled;
34  /***
35   * An implementation of ConnectionManager that supports configurable
36   * idle timeouts and a configurable value for the maximum number of
37   * client connections to a particular port.
38   *
39   */
40  public class SimpleConnectionManager
41      extends AbstractLogEnabled
42      implements JamesConnectionManager, Serviceable, Configurable, Disposable {
43      /***
44       * The default value for client socket idle timeouts.  The
45       * Java default is 0, meaning no timeout.  That's dangerous
46       * for a connection handler like this one, because it can
47       * easily lead to consumption of network resources.  So we
48       * allow users to configure the system to allow no timeout,
49       * but if no timeout is specified in the configuration, we
50       * use a timeout of 5 minutes.
51       */
52      private static final int DEFAULT_SOCKET_TIMEOUT = 5 * 60 * 1000;
53      /***
54       * The default value for the maximum number of allowed client
55       * connections.
56       */
57      private static final int DEFAULT_MAX_CONNECTIONS = 30;
58      /***
59       * The map of connection name / server connections managed by this connection
60       * manager.
61       */
62      private final HashMap connectionMap = new HashMap();
63      /***
64       * The idle timeout for the individual sockets spawed from the server socket.
65       */
66      protected int timeout = 0;
67      /***
68       * The maximum number of client connections allowed per server connection.
69       */
70      protected int maxOpenConn = 0;
71      /***
72       * The ThreadManager component that is used to provide a default thread pool.
73       */
74      private ThreadManager threadManager;
75      /***
76       * Whether the SimpleConnectionManager has been disposed.
77       */
78      private volatile boolean disposed = false;
79      /***
80       * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
81       */
82      public void configure(final Configuration configuration) throws ConfigurationException {
83          timeout = configuration.getChild("idle-timeout").getValueAsInteger(DEFAULT_SOCKET_TIMEOUT);
84          maxOpenConn = configuration.getChild("max-connections").getValueAsInteger(DEFAULT_MAX_CONNECTIONS);
85          if (timeout < 0) {
86              StringBuffer exceptionBuffer =
87                  new StringBuffer(128).append("Specified socket timeout value of ").append(timeout).append(
88                      " is not a legal value.");
89              throw new ConfigurationException(exceptionBuffer.toString());
90          }
91          if (maxOpenConn < 0) {
92              StringBuffer exceptionBuffer =
93                  new StringBuffer(128).append("Specified maximum number of open connections of ").append(
94                      maxOpenConn).append(
95                      " is not a legal value.");
96              throw new ConfigurationException(exceptionBuffer.toString());
97          }
98          if (getLogger().isDebugEnabled()) {
99              getLogger().debug(
100                 "Connection timeout is " + (timeout == 0 ? "unlimited" : Long.toString(timeout)));
101             getLogger().debug(
102                 "The maximum number of simultaneously open connections is "
103                     + (maxOpenConn == 0 ? "unlimited" : Integer.toString(maxOpenConn)));
104         }
105     }
106     /***
107      * @see org.apache.avalon.framework.service.Serviceable#service(ServiceManager)
108      */
109     public void service(ServiceManager componentManager) throws ServiceException {
110         threadManager = (ThreadManager)componentManager.lookup(ThreadManager.ROLE);
111     }
112     /***
113      * Disconnects all the underlying ServerConnections
114      */
115     public void dispose() {
116         disposed = true;
117         if (getLogger().isDebugEnabled()) {
118             getLogger().debug("Starting SimpleConnectionManager dispose...");
119         }
120         final String[] names = (String[])connectionMap.keySet().toArray(new String[0]);
121         for (int i = 0; i < names.length; i++) {
122             try {
123                 if (getLogger().isDebugEnabled()) {
124                     getLogger().debug("Disconnecting ServerConnection " + names[i]);
125                 }
126                 disconnect(names[i], true);
127             } catch (final Exception e) {
128                 getLogger().warn("Error disconnecting " + names[i], e);
129             }
130         }
131         if (getLogger().isDebugEnabled()) {
132             getLogger().debug("Finishing SimpleConnectionManager dispose...");
133         }
134     }
135     /***
136      * Start managing a connection.
137      * Management involves accepting connections and farming them out to threads
138      * from pool to be handled.
139      *
140      * @param name the name of connection
141      * @param socket the ServerSocket from which to
142      * @param handlerFactory the factory from which to acquire handlers
143      * @param threadPool the thread pool to use
144      * @param maxOpenConnections the maximum number of open connections allowed for this server socket.
145      * @exception Exception if an error occurs
146      */
147     public void connect(
148         String name,
149         ServerSocket socket,
150         ConnectionHandlerFactory handlerFactory,
151         ThreadPool threadPool,
152         int maxOpenConnections)
153         throws Exception {
154         if (disposed) {
155             throw new IllegalStateException("Connection manager has already been shutdown.");
156         }
157         if (null != connectionMap.get(name)) {
158             throw new IllegalArgumentException("Connection already exists with name " + name);
159         }
160         if (maxOpenConnections < 0) {
161             throw new IllegalArgumentException("The maximum number of client connections per server socket cannot be less that zero.");
162         }
163         ServerConnection runner =
164             new ServerConnection(socket, handlerFactory, threadPool, timeout, maxOpenConnections);
165         setupLogger(runner);
166         ContainerUtil.initialize(runner);
167         connectionMap.put(name, runner);
168         threadPool.execute(runner);
169     }
170     /***
171      * Start managing a connection.
172      * Management involves accepting connections and farming them out to threads
173      * from pool to be handled.
174      *
175      * @param name the name of connection
176      * @param socket the ServerSocket from which to
177      * @param handlerFactory the factory from which to acquire handlers
178      * @param threadPool the thread pool to use
179      * @exception Exception if an error occurs
180      */
181     public void connect(
182         String name,
183         ServerSocket socket,
184         ConnectionHandlerFactory handlerFactory,
185         ThreadPool threadPool)
186         throws Exception {
187         connect(name, socket, handlerFactory, threadPool, maxOpenConn);
188     }
189     /***
190      * Start managing a connection.
191      * This is similar to other connect method except that it uses default thread pool.
192      *
193      * @param name the name of connection
194      * @param socket the ServerSocket from which to
195      * @param handlerFactory the factory from which to acquire handlers
196      * @exception Exception if an error occurs
197      */
198     public void connect(String name, ServerSocket socket, ConnectionHandlerFactory handlerFactory)
199         throws Exception {
200         connect(name, socket, handlerFactory, threadManager.getDefaultThreadPool());
201     }
202     /***
203      * Start managing a connection.
204      * This is similar to other connect method except that it uses default thread pool.
205      *
206      * @param name the name of connection
207      * @param socket the ServerSocket from which to
208      * @param handlerFactory the factory from which to acquire handlers
209      * @param maxOpenConnections the maximum number of open connections allowed for this server socket.
210      * @exception Exception if an error occurs
211      */
212     public void connect(
213         String name,
214         ServerSocket socket,
215         ConnectionHandlerFactory handlerFactory,
216         int maxOpenConnections)
217         throws Exception {
218         connect(name, socket, handlerFactory, threadManager.getDefaultThreadPool(), maxOpenConnections);
219     }
220     /***
221      * This shuts down all handlers and socket, waiting for each to gracefully shutdown.
222      *
223      * @param name the name of connection
224      * @exception Exception if an error occurs
225      */
226     public void disconnect(final String name) throws Exception {
227         disconnect(name, false);
228     }
229     /***
230      * This shuts down a connection.
231      * If tearDown is true then it will forcefully the connection and try
232      * to return as soon as possible. Otherwise it will behave the same as
233      * void disconnect( String name );
234      *
235      * @param name the name of connection
236      * @param tearDown if true will forcefully tear down all handlers
237      * @exception Exception if an error occurs
238      */
239     public void disconnect(final String name, final boolean tearDown) throws Exception {
240         ServerConnection connection = (ServerConnection)connectionMap.remove(name);
241         if (null == connection) {
242             throw new IllegalArgumentException("No such connection with name " + name);
243         }
244         // TODO: deal with tear down parameter
245         connection.dispose();
246     }
247     /***
248      * Returns the default maximum number of open connections supported by this
249      * SimpleConnectionManager
250      *
251      * @return the maximum number of connections
252      */
253     public int getMaximumNumberOfOpenConnections() {
254         return maxOpenConn;
255     }
256 
257 }