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.smtpserver;
21  
22  import org.apache.avalon.framework.activity.Initializable;
23  import org.apache.avalon.framework.configuration.Configurable;
24  import org.apache.avalon.framework.configuration.Configuration;
25  import org.apache.avalon.framework.configuration.ConfigurationException;
26  import org.apache.avalon.framework.configuration.DefaultConfiguration;
27  import org.apache.avalon.framework.container.ContainerUtil;
28  import org.apache.avalon.framework.logger.AbstractLogEnabled;
29  import org.apache.avalon.framework.service.ServiceException;
30  import org.apache.avalon.framework.service.ServiceManager;
31  import org.apache.avalon.framework.service.Serviceable;
32  import org.apache.james.smtpserver.core.CoreCmdHandlerLoader;
33  import org.apache.james.smtpserver.core.SendMailHandler;
34  import org.apache.james.smtpserver.core.UnknownCmdHandler;
35  import org.apache.james.smtpserver.core.filter.CoreFilterCmdHandlerLoader;
36  
37  import java.util.ArrayList;
38  import java.util.Collection;
39  import java.util.Enumeration;
40  import java.util.HashMap;
41  import java.util.Iterator;
42  import java.util.List;
43  import java.util.Locale;
44  import java.util.Map;
45  import java.util.Properties;
46  
47  /**
48    * The SMTPHandlerChain is per service object providing access
49    * ConnectHandlers, Commandhandlers and message handlers
50    */
51  public class SMTPHandlerChain extends AbstractLogEnabled implements Configurable, Serviceable, Initializable {
52  
53      private HashMap commandHandlerMap = new HashMap();
54      private ArrayList messageHandlers = new ArrayList();
55      private ArrayList connectHandlers = new ArrayList();
56  
57      private final CommandHandler unknownHandler = new UnknownCmdHandler();
58      private ServiceManager serviceManager;
59      
60      private final static String[] mandatoryCommands = { "MAIL" , "RCPT", "DATA"};
61  
62      public void service(ServiceManager arg0) throws ServiceException {
63          serviceManager = arg0;
64      }
65  
66      /**
67       * loads the various handlers from the configuration
68       * 
69       * @param configuration
70       *            configuration under handlerchain node
71       */
72      public void configure(Configuration configuration)
73              throws ConfigurationException {
74          addToMap(UnknownCmdHandler.UNKNOWN_COMMAND, unknownHandler);
75          if (configuration == null
76                  || configuration.getChildren("handler") == null
77                  || configuration.getChildren("handler").length == 0) {
78              configuration = new DefaultConfiguration("handlerchain");
79              Properties cmds = new Properties();
80              cmds.setProperty("Default CoreCmdFilterHandlerLoader",
81                      CoreFilterCmdHandlerLoader.class.getName());
82              cmds.setProperty("Default CoreCmdHandlerLoader", CoreCmdHandlerLoader.class
83                      .getName());
84              cmds.setProperty("Default SendMailHandler", SendMailHandler.class
85                      .getName());
86              Enumeration e = cmds.keys();
87              while (e.hasMoreElements()) {
88                  String cmdName = (String) e.nextElement();
89                  String className = cmds.getProperty(cmdName);
90                  ((DefaultConfiguration) configuration).addChild(addHandler(
91                          cmdName, className));
92              }
93          }
94          if (configuration != null) {
95              Configuration[] children = configuration.getChildren("handler");
96              ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
97  
98              // load the BaseFilterCmdHandler
99              loadClass(classLoader, CoreFilterCmdHandlerLoader.class.getName(),
100                     addHandler(null, CoreFilterCmdHandlerLoader.class.getName()));
101 
102             // load the configured handlers
103             if (children != null) {
104                 for (int i = 0; i < children.length; i++) {
105                     String className = children[i].getAttribute("class");
106                     if (className != null) {
107 
108                         // ignore base handlers.
109                         if (!className.equals(CoreFilterCmdHandlerLoader.class
110                                 .getName())
111                                 && !className.equals(CoreCmdHandlerLoader.class
112                                         .getName())
113                                 && !className.equals(SendMailHandler.class
114                                         .getName())) {
115 
116                             // load the handler
117                             loadClass(classLoader, className, children[i]);
118                         }
119                     }
120                 }
121 
122                 // load the BaseCmdHandler and SendMailHandler
123                 loadClass(classLoader, CoreCmdHandlerLoader.class.getName(),
124                         addHandler(null, CoreCmdHandlerLoader.class.getName()));
125                 loadClass(classLoader, SendMailHandler.class.getName(),
126                         addHandler(null, SendMailHandler.class.getName()));
127             }
128         }
129 
130         // the size must be greater than 1 because we added UnknownCmdHandler to
131         // the map
132 
133         if (commandHandlerMap.size() < 2) {
134             if (getLogger().isErrorEnabled()) {
135                 getLogger().error("No commandhandlers configured");
136             }
137             throw new ConfigurationException("No commandhandlers configured");
138         } else {
139             boolean found = true;
140             for (int i = 0; i < mandatoryCommands.length; i++) {
141                 if (!commandHandlerMap.containsKey(mandatoryCommands[i])) {
142                     if (getLogger().isErrorEnabled()) {
143                         getLogger().error(
144                                 "No commandhandlers configured for the command:"
145                                         + mandatoryCommands[i]);
146                     }
147                     found = false;
148                     break;
149                 }
150             }
151 
152             if (!found) {
153                 throw new ConfigurationException(
154                         "No commandhandlers configured for mandatory commands");
155             }
156 
157             if (messageHandlers.size() == 0) {
158                 if (getLogger().isErrorEnabled()) {
159                     getLogger()
160                             .error(
161                                     "No messageHandler configured. Check that SendMailHandler is configured in the SMTPHandlerChain");
162                 }
163                 throw new ConfigurationException("No messageHandler configured");
164             }
165 
166         }
167 
168     }
169     
170     /**
171      * @see org.apache.avalon.framework.activity.Initializable#initialize()
172      */
173     public void initialize() throws Exception {
174         Iterator h = commandHandlerMap.keySet().iterator();
175     
176         while(h.hasNext()) {
177             List handlers = (List) commandHandlerMap.get(h.next());
178             Iterator h2 = handlers.iterator();
179             while (h2.hasNext()) {
180               ContainerUtil.initialize(h2.next());
181             }
182         }
183     }
184 
185     /**
186      * Load and add the classes to the handler map
187      * 
188      * @param classLoader The classLoader to use
189      * @param className The class name 
190      * @param config The configuration 
191      * @throws ConfigurationException Get thrown on error
192      */
193     private void loadClass(ClassLoader classLoader, String className,
194             Configuration config) throws ConfigurationException {
195         try {
196             Object handler = classLoader.loadClass(className).newInstance();
197 
198             // enable logging
199             ContainerUtil.enableLogging(handler, getLogger());
200 
201             // servicing the handler
202             ContainerUtil.service(handler, serviceManager);
203 
204             // configure the handler
205             ContainerUtil.configure(handler, config);
206 
207             // if it is a connect handler add it to list of connect handlers
208             if (handler instanceof ConnectHandler) {
209                 connectHandlers.add(handler);
210                 if (getLogger().isInfoEnabled()) {
211                     getLogger().info("Added ConnectHandler: " + className);
212                 }
213             }
214 
215             // if it is a commands handler add it to the map with key as command
216             // name
217             if (handler instanceof CommandsHandler) {
218                 Map c = ((CommandsHandler) handler).getCommands();
219 
220                 Iterator cmdKeys = c.keySet().iterator();
221 
222                 while (cmdKeys.hasNext()) {
223                     String commandName = cmdKeys.next().toString();
224                     String cName = c.get(commandName).toString();
225 
226                     DefaultConfiguration cmdConf = new DefaultConfiguration(
227                             "handler");
228                     cmdConf.setAttribute("command", commandName);
229                     cmdConf.setAttribute("class", cName);
230 
231                     loadClass(classLoader, cName, cmdConf);
232                 }
233 
234             }
235 
236             // if it is a command handler add it to the map with key as command
237             // name
238             if (handler instanceof CommandHandler) {
239                 String commandName = config.getAttribute("command");
240                 String cmds[] = commandName.split(",");
241                 Collection implCmds = ((CommandHandler) handler).getImplCommands();
242 
243                 for (int i = 0; i < cmds.length; i++) {
244                     commandName = cmds[i].trim().toUpperCase(Locale.US);
245 
246                     // Check if the commandHandler implement the configured command
247                     if (implCmds.contains(commandName)) {
248                         addToMap(commandName, (CommandHandler) handler);
249                         if (getLogger().isInfoEnabled()) {
250                             getLogger().info(
251                                     "Added Commandhandler: " + className);
252                         }
253                     } else {
254                         // The Configured command is not implemented. Throw an exception
255                         throw new ConfigurationException("Commandhandler "
256                                 + className + " not implement the command "
257                                 + commandName);
258                     }
259 
260                 }
261 
262             }
263 
264             // if it is a message handler add it to list of message handlers
265             if (handler instanceof MessageHandler) {
266                 messageHandlers.add(handler);
267                 if (getLogger().isInfoEnabled()) {
268                     getLogger().info("Added MessageHandler: " + className);
269                 }
270             }
271         } catch (ClassNotFoundException ex) {
272             if (getLogger().isErrorEnabled()) {
273                 getLogger().error("Failed to add Commandhandler: " + className,
274                         ex);
275             }
276             throw new ConfigurationException("Failed to add Commandhandler: "
277                     + className, ex);
278         } catch (IllegalAccessException ex) {
279             if (getLogger().isErrorEnabled()) {
280                 getLogger().error("Failed to add Commandhandler: " + className,
281                         ex);
282             }
283             throw new ConfigurationException("Failed to add Commandhandler: "
284                     + className, ex);
285         } catch (InstantiationException ex) {
286             if (getLogger().isErrorEnabled()) {
287                 getLogger().error("Failed to add Commandhandler: " + className,
288                         ex);
289             }
290             throw new ConfigurationException("Failed to add Commandhandler: "
291                     + className, ex);
292         } catch (ServiceException e) {
293             if (getLogger().isErrorEnabled()) {
294                 getLogger().error(
295                         "Failed to service Commandhandler: " + className, e);
296             }
297             throw new ConfigurationException("Failed to add Commandhandler: "
298                     + className, e);
299         }
300     }
301 
302     /**
303      * Return a DefaultConfiguration build on the given command name and classname
304      * 
305      * @param cmdName The command name
306      * @param className The class name
307      * @return DefaultConfiguration
308      */
309     private DefaultConfiguration addHandler(String cmdName, String className) {
310         DefaultConfiguration cmdConf = new DefaultConfiguration("handler");
311         cmdConf.setAttribute("command",cmdName);
312         cmdConf.setAttribute("class",className);
313         return cmdConf;
314     }
315 
316     /**
317      * Add it to map (key as command name, value is an array list of commandhandlers)
318      *
319      * @param commandName the command name which will be key
320      * @param cmdHandler The commandhandler object
321      */
322     private void addToMap(String commandName, CommandHandler cmdHandler) {
323         ArrayList handlers = (ArrayList)commandHandlerMap.get(commandName);
324         if(handlers == null) {
325             handlers = new ArrayList();
326             commandHandlerMap.put(commandName, handlers);
327         }
328         handlers.add(cmdHandler);
329     }
330 
331     /**
332      * Returns all the configured commandhandlers for the specified command
333      *
334      * @param command the command name which will be key
335      * @return List of commandhandlers
336      */
337     List getCommandHandlers(String command) {
338         if (command == null) {
339             return null;
340         }
341         if (getLogger().isDebugEnabled()) {
342             getLogger().debug("Lookup command handler for command: " + command);
343         }
344         List handlers =  (List)commandHandlerMap.get(command);
345         if(handlers == null) {
346             handlers = (List)commandHandlerMap.get(UnknownCmdHandler.UNKNOWN_COMMAND);
347         }
348 
349         return handlers;
350     }
351 
352     /**
353      * Returns all the configured message handlers
354      *
355      * @return List of message handlers
356      */
357     List getMessageHandlers() {
358         return messageHandlers;
359     }
360 
361     /**
362      * Returns all the configured connect handlers
363      *
364      * @return List of connect handlers
365      */
366     List getConnectHandlers() {
367         return connectHandlers;
368     }
369 
370 }