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 java.util.ArrayList;
23  import java.util.Enumeration;
24  import java.util.List;
25  import java.util.HashMap;
26  import java.util.Locale;
27  import java.util.Properties;
28  
29  import org.apache.avalon.framework.configuration.Configurable;
30  import org.apache.avalon.framework.configuration.Configuration;
31  import org.apache.avalon.framework.configuration.ConfigurationException;
32  import org.apache.avalon.framework.configuration.DefaultConfiguration;
33  import org.apache.avalon.framework.container.ContainerUtil;
34  import org.apache.avalon.framework.logger.AbstractLogEnabled;
35  import org.apache.avalon.framework.logger.LogEnabled;
36  import org.apache.avalon.framework.service.ServiceException;
37  import org.apache.avalon.framework.service.ServiceManager;
38  import org.apache.avalon.framework.service.Serviceable;
39  
40  /***
41    * The SMTPHandlerChain is per service object providing access
42    * ConnectHandlers, Commandhandlers and message handlers
43    */
44  public class SMTPHandlerChain extends AbstractLogEnabled implements Configurable, Serviceable {
45  
46      private HashMap commandHandlerMap = new HashMap();
47      private ArrayList messageHandlers = new ArrayList();
48      private ArrayList connectHandlers = new ArrayList();
49  
50      private final CommandHandler unknownHandler = new UnknownCmdHandler();
51      private ServiceManager serviceManager;
52  
53      private final static String[] mandatoryCommands = { "MAIL" , "RCPT", "DATA"};
54  
55      public void service(ServiceManager arg0) throws ServiceException {
56          serviceManager = arg0;
57      }
58  
59  
60      /***
61       * loads the various handlers from the configuration
62       * @param configuration configuration under handlerchain node
63       */
64      public void configure(Configuration configuration) throws  ConfigurationException {
65          addToMap(UnknownCmdHandler.UNKNOWN_COMMAND, unknownHandler);
66          if(configuration == null || configuration.getChildren("handler") == null || configuration.getChildren("handler").length == 0) {
67              configuration = new DefaultConfiguration("handlerchain");
68              Properties cmds = new Properties();
69              cmds.setProperty("AUTH",AuthCmdHandler.class.getName());
70              cmds.setProperty("DATA",DataCmdHandler.class.getName());
71              cmds.setProperty("EHLO",EhloCmdHandler.class.getName());
72              cmds.setProperty("EXPN",ExpnCmdHandler.class.getName());
73              cmds.setProperty("HELO",HeloCmdHandler.class.getName());
74              cmds.setProperty("HELP",HelpCmdHandler.class.getName());
75              cmds.setProperty("MAIL",MailCmdHandler.class.getName());
76              cmds.setProperty("NOOP",NoopCmdHandler.class.getName());
77              cmds.setProperty("QUIT",QuitCmdHandler.class.getName());
78              cmds.setProperty("RCPT" ,RcptCmdHandler.class.getName());
79              cmds.setProperty("RSET",RsetCmdHandler.class.getName());
80              cmds.setProperty("VRFY",VrfyCmdHandler.class.getName());
81              cmds.setProperty("Default SendMailHandler",SendMailHandler.class.getName());
82              Enumeration e = cmds.keys();
83              while (e.hasMoreElements()) {
84                  String cmdName = (String) e.nextElement();
85                  String className = cmds.getProperty(cmdName);
86                  DefaultConfiguration cmdConf = new DefaultConfiguration("handler");
87                  cmdConf.setAttribute("command",cmdName);
88                  cmdConf.setAttribute("class",className);
89                  ((DefaultConfiguration) configuration).addChild(cmdConf);
90              }
91          }
92          if(configuration != null) {
93              Configuration[] children = configuration.getChildren("handler");
94              if ( children != null ) {
95                  ClassLoader classLoader = getClass().getClassLoader();
96                  for ( int i = 0 ; i < children.length ; i++ ) {
97                      String className = children[i].getAttribute("class");
98                      if(className != null) {
99                          //load the handler
100                         try {
101                             Object handler = classLoader.loadClass(className).newInstance();
102 
103                             //enable logging
104                             if (handler instanceof LogEnabled) {
105                                 ((LogEnabled)handler).enableLogging(getLogger());
106                             }
107 
108                             //servicing the handler
109                             ContainerUtil.service(handler,serviceManager);
110 
111                             //configure the handler
112                             ContainerUtil.configure(handler,children[i]);
113 
114                             //if it is a connect handler add it to list of connect handlers
115                             if(handler instanceof ConnectHandler) {
116                                 connectHandlers.add((ConnectHandler)handler);
117                                 if (getLogger().isInfoEnabled()) {
118                                     getLogger().info("Added ConnectHandler: " + className);
119                                 }
120                             }
121 
122                             //if it is a command handler add it to the map with key as command name
123                             if(handler instanceof CommandHandler) {
124                                 String commandName = children[i].getAttribute("command");
125                                 commandName = commandName.toUpperCase(Locale.US);
126                                 addToMap(commandName, (CommandHandler)handler);
127                                 if (getLogger().isInfoEnabled()) {
128                                     getLogger().info("Added Commandhandler: " + className);
129                                 }
130 
131                             }
132 
133                             //if it is a message handler add it to list of message handlers
134                             if(handler instanceof MessageHandler) {
135                                 messageHandlers.add((MessageHandler)handler);
136                                 if (getLogger().isInfoEnabled()) {
137                                     getLogger().info("Added MessageHandler: " + className);
138                                 }
139                             }
140 
141                         } catch (ClassNotFoundException ex) {
142                            if (getLogger().isErrorEnabled()) {
143                                getLogger().error("Failed to add Commandhandler: " + className,ex);
144                            }
145                            throw new ConfigurationException("Failed to add Commandhandler: " + className,ex);
146                         } catch (IllegalAccessException ex) {
147                            if (getLogger().isErrorEnabled()) {
148                                getLogger().error("Failed to add Commandhandler: " + className,ex);
149                            }
150                            throw new ConfigurationException("Failed to add Commandhandler: " + className,ex);
151                         } catch (InstantiationException ex) {
152                            if (getLogger().isErrorEnabled()) {
153                                getLogger().error("Failed to add Commandhandler: " + className,ex);
154                            }
155                            throw new ConfigurationException("Failed to add Commandhandler: " + className,ex);
156                         } catch (ServiceException e) {
157                             if (getLogger().isErrorEnabled()) {
158                                 getLogger().error("Failed to service Commandhandler: " + className,e);
159                             }
160                             throw new ConfigurationException("Failed to add Commandhandler: " + className,e);
161                         }
162                     }
163                 }
164             }
165         }
166 
167         //the size must be greater than 1 because we added UnknownCmdHandler to the map
168         if(commandHandlerMap.size() < 2) {
169             if (getLogger().isErrorEnabled()) {
170                 getLogger().error("No commandhandlers configured");
171             }
172             throw new ConfigurationException("No commandhandlers configured");
173         } else {
174             boolean found = true;
175             for (int i = 0; i < mandatoryCommands.length; i++) {
176                 if(!commandHandlerMap.containsKey(mandatoryCommands[i])) {
177                     if (getLogger().isErrorEnabled()) {
178                         getLogger().error("No commandhandlers configured for the command:" + mandatoryCommands[i]);
179                     }
180                     found = false;
181                     break;
182                 }
183             }
184 
185             if(!found) {
186                 throw new ConfigurationException("No commandhandlers configured for mandatory commands");
187             }
188             
189             if (messageHandlers.size() == 0) {
190                 if (getLogger().isErrorEnabled()) {
191                     getLogger().error("No messageHandler configured. Check that SendMailHandler is configured in the SMTPHandlerChain");
192                 }
193                 throw new ConfigurationException("No messageHandler configured");
194             }
195 
196         }
197     }
198 
199     /***
200      * Add it to map (key as command name, value is an array list of commandhandlers)
201      *
202      * @param commandName the command name which will be key
203      * @param cmdHandler The commandhandler object
204      */
205     private void addToMap(String commandName, CommandHandler cmdHandler) {
206         ArrayList handlers = (ArrayList)commandHandlerMap.get(commandName);
207         if(handlers == null) {
208             handlers = new ArrayList();
209             commandHandlerMap.put(commandName, handlers);
210         }
211         handlers.add(cmdHandler);
212     }
213 
214     /***
215      * Returns all the configured commandhandlers for the specified command
216      *
217      * @param commandName the command name which will be key
218      * @return List of commandhandlers
219      */
220     List getCommandHandlers(String command) {
221         if (command == null) {
222             return null;
223         }
224         if (getLogger().isDebugEnabled()) {
225             getLogger().debug("Lookup command handler for command: " + command);
226         }
227         List handlers =  (List)commandHandlerMap.get(command);
228         if(handlers == null) {
229             handlers = (List)commandHandlerMap.get(UnknownCmdHandler.UNKNOWN_COMMAND);
230         }
231 
232         return handlers;
233     }
234 
235     /***
236      * Returns all the configured message handlers
237      *
238      * @return List of message handlers
239      */
240     List getMessageHandlers() {
241         return messageHandlers;
242     }
243 
244     /***
245      * Returns all the configured connect handlers
246      *
247      * @return List of connect handlers
248      */
249     List getConnectHandlers() {
250         return connectHandlers;
251     }
252 
253 }