KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > james > core > AbstractJamesService


1 /***********************************************************************
2  * Copyright (c) 2000-2004 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.core;
19
20 import java.io.*;
21 import java.net.*;
22
23 import org.apache.avalon.framework.logger.*;
24 import org.apache.avalon.framework.component.*;
25 import org.apache.avalon.framework.configuration.*;
26 import org.apache.avalon.framework.activity.*;
27
28 import org.apache.avalon.excalibur.thread.ThreadPool;
29 import org.apache.avalon.cornerstone.services.threads.ThreadManager;
30
31 import org.apache.avalon.cornerstone.services.connection.AbstractHandlerFactory;
32 import org.apache.avalon.cornerstone.services.connection.ConnectionHandler;
33 import org.apache.avalon.cornerstone.services.connection.ConnectionHandlerFactory;
34 import org.apache.avalon.cornerstone.services.sockets.ServerSocketFactory;
35 import org.apache.avalon.cornerstone.services.sockets.SocketManager;
36
37 import org.apache.james.services.JamesConnectionManager;
38 import org.apache.james.util.watchdog.ThreadPerWatchdogFactory;
39 import org.apache.james.util.watchdog.Watchdog;
40 import org.apache.james.util.watchdog.WatchdogFactory;
41
42 /**
43  * Server which creates connection handlers. All new James service must
44  * inherit from this abstract implementation.
45  *
46  */

47 public abstract class AbstractJamesService extends AbstractHandlerFactory
48     implements Component, Composable, Configurable,
49                Disposable, Initializable, ConnectionHandlerFactory {
50
51     /**
52      * The default value for the connection timeout.
53      */

54     protected static final int DEFAULT_TIMEOUT = 5* 60 * 1000;
55
56     /**
57      * The name of the parameter defining the connection timeout.
58      */

59     protected static final String JavaDoc TIMEOUT_NAME = "connectiontimeout";
60
61     /**
62      * The default value for the connection backlog.
63      */

64     protected static final int DEFAULT_BACKLOG = 5;
65
66     /**
67      * The name of the parameter defining the connection backlog.
68      */

69     protected static final String JavaDoc BACKLOG_NAME = "connectionBacklog";
70
71     /**
72      * The name of the parameter defining the service hello name.
73      */

74     public static final String JavaDoc HELLO_NAME = "helloName";
75
76     /**
77      * The ConnectionManager that spawns and manages service connections.
78      */

79     private JamesConnectionManager connectionManager;
80
81     /**
82      * The name of the thread group to be used by this service for
83      * generating connections
84      */

85     protected String JavaDoc threadGroup;
86
87     /**
88      * The thread pool used by this service that holds the threads
89      * that service the client connections.
90      */

91     protected ThreadPool threadPool = null;
92
93     /**
94      * The server socket type used to generate connections for this server.
95      */

96     protected String JavaDoc serverSocketType = "plain";
97
98     /**
99      * The port on which this service will be made available.
100      */

101     protected int port = -1;
102
103     /**
104      * Network interface to which the service will bind. If not set,
105      * the server binds to all available interfaces.
106      */

107     protected InetAddress bindTo = null;
108
109     /*
110      * The server socket associated with this service
111      */

112     protected ServerSocket serverSocket;
113
114     /**
115      * The name of the connection used by this service. We need to
116      * track this so we can tell the ConnectionManager which service
117      * to disconnect upon shutdown.
118      */

119     protected String JavaDoc connectionName;
120
121     /**
122      * The maximum number of connections allowed for this service.
123      */

124     protected Integer JavaDoc connectionLimit;
125
126     /**
127      * The connection idle timeout. Used primarily to prevent server
128      * problems from hanging a connection.
129      */

130     protected int timeout;
131
132     /**
133      * The connection backlog.
134      */

135     protected int backlog;
136
137     /**
138      * The hello name for the service.
139      */

140     protected String JavaDoc helloName;
141
142     /**
143      * The component manager used by this service.
144      */

145     private ComponentManager compMgr;
146
147     /**
148      * Whether this service is enabled.
149      */

150     private volatile boolean enabled;
151
152     /**
153      * @see org.apache.avalon.framework.component.Composable#compose(ComponentManager)
154      */

155     public void compose(ComponentManager comp) throws ComponentException {
156         super.compose(comp);
157         compMgr = comp;
158         connectionManager = (JamesConnectionManager) compMgr.lookup(JamesConnectionManager.ROLE);
159     }
160
161     /**
162      * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
163      */

164     public void configure(Configuration conf) throws ConfigurationException {
165         enabled = conf.getAttributeAsBoolean("enabled", true);
166         if (!enabled) {
167           getLogger().info(getServiceType() + " disabled by configuration");
168           return;
169         }
170
171         Configuration handlerConfiguration = conf.getChild("handler");
172
173         // Send the handler subconfiguration to the super class. This
174
// ensures that the handler config is passed to the handlers.
175
//
176
// TODO: This should be rationalized. The handler element of the
177
// server configuration doesn't really make a whole lot of
178
// sense. We should modify the config to get rid of it.
179
// Keeping it for now to maintain backwards compatibility.
180
super.configure(handlerConfiguration);
181
182         port = conf.getChild("port").getValueAsInteger(getDefaultPort());
183
184         Configuration serverSocketTypeConf = conf.getChild("serverSocketType", false);
185         String JavaDoc confSocketType = null;
186         if (serverSocketTypeConf != null ) {
187             confSocketType = serverSocketTypeConf.getValue();
188         }
189
190         if (confSocketType == null) {
191             // Only load the useTLS parameter if a specific socket type has not
192
// been specified. This maintains backwards compatibility while
193
// allowing us to have more complex (i.e. multiple SSL configuration)
194
// deployments
195
final boolean useTLS = conf.getChild("useTLS").getValueAsBoolean(isDefaultTLSEnabled());
196             if (useTLS) {
197               serverSocketType = "ssl";
198             }
199         } else {
200             serverSocketType = confSocketType;
201         }
202
203         StringBuffer JavaDoc infoBuffer;
204         threadGroup = conf.getChild("threadGroup").getValue(null);
205         if (threadGroup != null) {
206             infoBuffer =
207                 new StringBuffer JavaDoc(64)
208                         .append(getServiceType())
209                         .append(" uses thread group: ")
210                         .append(threadGroup);
211             getLogger().info(infoBuffer.toString());
212         }
213         else {
214             getLogger().info(getServiceType() + " uses default thread group.");
215         }
216
217         try {
218             final String JavaDoc bindAddress = conf.getChild("bind").getValue(null);
219             if( null != bindAddress ) {
220                 bindTo = InetAddress.getByName(bindAddress);
221                 infoBuffer =
222                     new StringBuffer JavaDoc(64)
223                             .append(getServiceType())
224                             .append(" bound to: ")
225                             .append(bindTo);
226                 getLogger().info(infoBuffer.toString());
227             }
228         }
229         catch( final UnknownHostException unhe ) {
230             throw new ConfigurationException( "Malformed bind parameter in configuration of service " + getServiceType(), unhe );
231         }
232
233         String JavaDoc hostName = null;
234         try {
235             hostName = InetAddress.getLocalHost().getHostName();
236         } catch (UnknownHostException ue) {
237             hostName = "localhost";
238         }
239
240         infoBuffer =
241             new StringBuffer JavaDoc(64)
242                     .append(getServiceType())
243                     .append(" is running on: ")
244                     .append(hostName);
245         getLogger().info(infoBuffer.toString());
246
247         Configuration helloConf = handlerConfiguration.getChild(HELLO_NAME);
248         boolean autodetect = helloConf.getAttributeAsBoolean("autodetect", true);
249         if (autodetect) {
250             helloName = hostName;
251         } else {
252             helloName = helloConf.getValue("localhost");
253         }
254         infoBuffer =
255             new StringBuffer JavaDoc(64)
256                     .append(getServiceType())
257                     .append(" handler hello name is: ")
258                     .append(helloName);
259         getLogger().info(infoBuffer.toString());
260
261         timeout = handlerConfiguration.getChild(TIMEOUT_NAME).getValueAsInteger(DEFAULT_TIMEOUT);
262
263         infoBuffer =
264             new StringBuffer JavaDoc(64)
265                     .append(getServiceType())
266                     .append(" handler connection timeout is: ")
267                     .append(timeout);
268         getLogger().info(infoBuffer.toString());
269
270         backlog = conf.getChild(BACKLOG_NAME).getValueAsInteger(DEFAULT_BACKLOG);
271
272         infoBuffer =
273                     new StringBuffer JavaDoc(64)
274                     .append(getServiceType())
275                     .append(" connection backlog is: ")
276                     .append(backlog);
277         getLogger().info(infoBuffer.toString());
278
279         final String JavaDoc location = "generated:" + getServiceType();
280
281         if (connectionManager instanceof JamesConnectionManager) {
282             String JavaDoc connectionLimitString = conf.getChild("connectionLimit").getValue(null);
283             if (connectionLimitString != null) {
284                 try {
285                     connectionLimit = new Integer JavaDoc(connectionLimitString);
286                 } catch (NumberFormatException JavaDoc nfe) {
287                     getLogger().error("Connection limit value is not properly formatted.", nfe);
288                 }
289                 if (connectionLimit.intValue() < 0) {
290                     getLogger().error("Connection limit value cannot be less than zero.");
291                     throw new ConfigurationException("Connection limit value cannot be less than zero.");
292                 }
293             } else {
294                 connectionLimit = new Integer JavaDoc(((JamesConnectionManager)connectionManager).getMaximumNumberOfOpenConnections());
295             }
296             infoBuffer = new StringBuffer JavaDoc(128)
297                 .append(getServiceType())
298                 .append(" will allow a maximum of ")
299                 .append(connectionLimit.intValue())
300                 .append(" connections.");
301             getLogger().info(infoBuffer.toString());
302         }
303     }
304
305     /**
306      * @see org.apache.avalon.framework.activity.Initializable#initialize()
307      */

308     public void initialize() throws Exception JavaDoc {
309         if (!isEnabled()) {
310             getLogger().info(getServiceType() + " Disabled");
311             System.out.println(getServiceType() + " Disabled");
312             return;
313         }
314         getLogger().debug(getServiceType() + " init...");
315
316         SocketManager socketManager = (SocketManager) compMgr.lookup(SocketManager.ROLE);
317
318         ThreadManager threadManager = (ThreadManager) compMgr.lookup(ThreadManager.ROLE);
319
320         if (threadGroup != null) {
321             threadPool = threadManager.getThreadPool(threadGroup);
322         } else {
323             threadPool = threadManager.getDefaultThreadPool();
324         }
325
326         ServerSocketFactory factory = socketManager.getServerSocketFactory(serverSocketType);
327         ServerSocket serverSocket = factory.createServerSocket(port, backlog, bindTo);
328     
329         if (null == connectionName) {
330             final StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
331             sb.append(serverSocketType);
332             sb.append(':');
333             sb.append(port);
334     
335             if (null != bindTo) {
336                 sb.append('/');
337                 sb.append(bindTo);
338             }
339             connectionName = sb.toString();
340         }
341
342         if ((connectionLimit != null) &&
343             (connectionManager instanceof JamesConnectionManager)) {
344             if (null != threadPool) {
345                 ((JamesConnectionManager)connectionManager).connect(connectionName, serverSocket, this, threadPool, connectionLimit.intValue());
346             }
347             else {
348                 ((JamesConnectionManager)connectionManager).connect(connectionName, serverSocket, this, connectionLimit.intValue()); // default pool
349
}
350         } else {
351             if (null != threadPool) {
352                 connectionManager.connect(connectionName, serverSocket, this, threadPool);
353             }
354             else {
355                 connectionManager.connect(connectionName, serverSocket, this); // default pool
356
}
357         }
358
359         getLogger().debug(getServiceType() + " ...init end");
360
361         StringBuffer JavaDoc logBuffer =
362             new StringBuffer JavaDoc(64)
363                 .append(getServiceType())
364                 .append(" started ")
365                 .append(connectionName);
366         String JavaDoc logString = logBuffer.toString();
367         System.out.println(logString);
368         getLogger().info(logString);
369     }
370
371     /**
372      * @see org.apache.avalon.framework.activity.Disposable#dispose()
373      */

374     public void dispose() {
375
376         if (!isEnabled()) {
377             return;
378         }
379         StringBuffer JavaDoc infoBuffer =
380             new StringBuffer JavaDoc(64)
381                     .append(getServiceType())
382                     .append(" dispose... ")
383                     .append(connectionName);
384         getLogger().debug(infoBuffer.toString());
385
386         try {
387             connectionManager.disconnect(connectionName, true);
388         } catch (final Exception JavaDoc e) {
389             StringBuffer JavaDoc warnBuffer =
390                 new StringBuffer JavaDoc(64)
391                         .append("Error disconnecting ")
392                         .append(getServiceType())
393                         .append(": ");
394             getLogger().warn(warnBuffer.toString(), e);
395         }
396
397         compMgr = null;
398
399         connectionManager = null;
400         threadPool = null;
401
402         // This is needed to make sure sockets are promptly closed on Windows 2000
403
// TODO: Check this - shouldn't need to explicitly gc to force socket closure
404
System.gc();
405     
406         getLogger().debug(getServiceType() + " ...dispose end");
407     }
408
409     /**
410      * This constructs the WatchdogFactory that will be used to guard
411      * against runaway or stuck behavior. Should only be called once
412      * by a subclass in its initialize() method.
413      *
414      * @return the WatchdogFactory to be employed by subclasses.
415      */

416     protected WatchdogFactory getWatchdogFactory() {
417         WatchdogFactory theWatchdogFactory = null;
418         theWatchdogFactory = new ThreadPerWatchdogFactory(threadPool, timeout);
419         if (theWatchdogFactory instanceof LogEnabled) {
420             ((LogEnabled)theWatchdogFactory).enableLogging(getLogger());
421         }
422         return theWatchdogFactory;
423      }
424
425
426     /**
427      * Describes whether this service is enabled by configuration.
428      *
429      * @return is the service enabled.
430      */

431     public final boolean isEnabled() {
432         return enabled;
433     }
434     /**
435      * Overide this method to create actual instance of connection handler.
436      *
437      * @return the new ConnectionHandler
438      * @exception Exception if an error occurs
439      */

440     protected abstract ConnectionHandler newHandler()
441         throws Exception JavaDoc;
442
443     /**
444      * Get the default port for this server type.
445      *
446      * It is strongly recommended that subclasses of this class
447      * override this method to specify the default port for their
448      * specific server type.
449      *
450      * @return the default port
451      */

452      protected int getDefaultPort() {
453         return 0;
454      }
455
456     /**
457      * Get whether TLS is enabled for this server's socket by default.
458      *
459      * @return the default port
460      */

461      protected boolean isDefaultTLSEnabled() {
462         return false;
463      }
464
465     /**
466      * This method returns the type of service provided by this server.
467      * This should be invariant over the life of the class.
468      *
469      * Subclasses may override this implementation. This implementation
470      * parses the complete class name and returns the undecorated class
471      * name.
472      *
473      * @return description of this server
474      */

475     public String JavaDoc getServiceType() {
476         String JavaDoc name = getClass().getName();
477         int p = name.lastIndexOf(".");
478         if (p > 0 && p < name.length() - 2) {
479             name = name.substring(p + 1);
480         }
481         return name;
482     }
483     
484     /**
485     * Returns the port that the service is bound to
486     *
487     * @return int The port number
488     */

489     public int getPort() {
490         return port;
491     }
492     
493     /**
494     * Returns the address if the network interface the socket is bound to
495     *
496     * @return String The network interface name
497     */

498     public String JavaDoc getNetworkInterface() {
499         if (bindTo == null) {
500             return "All";
501         } else {
502             return bindTo.getHostAddress();
503         }
504     }
505     
506     /**
507     * Returns the server socket type, plain or SSL
508     *
509     * @return String The scoekt type, plain or SSL
510     */

511     public String JavaDoc getSocketType() {
512         return serverSocketType;
513     }
514 }
515
516
Popular Tags