View Javadoc

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