Author: bago Date: Wed Oct 14 17:13:48 2009 New Revision: 825198 URL: http://svn.apache.org/viewvc?rev=825198&view=rev Log: a sample refactoring using composition instead of inheritance (JAMES-930)
Added: james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPServerComposed.java (with props) james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/SMTPServerComposedTest.java (with props) Added: james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPServerComposed.java URL: http://svn.apache.org/viewvc/james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPServerComposed.java?rev=825198&view=auto ============================================================================== --- james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPServerComposed.java (added) +++ james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPServerComposed.java Wed Oct 14 17:13:48 2009 @@ -0,0 +1,400 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + + + +package org.apache.james.smtpserver; + +import javax.annotation.Resource; + +import org.apache.avalon.framework.activity.Initializable; +import org.apache.avalon.framework.configuration.Configurable; +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.avalon.framework.configuration.ConfigurationException; +import org.apache.avalon.framework.logger.AbstractLogEnabled; +import org.apache.avalon.framework.service.DefaultServiceManager; +import org.apache.avalon.framework.service.ServiceException; +import org.apache.avalon.framework.service.ServiceManager; +import org.apache.avalon.framework.service.Serviceable; +import org.apache.commons.logging.impl.AvalonLogger; +import org.apache.james.Constants; +import org.apache.james.api.dnsservice.DNSService; +import org.apache.james.api.dnsservice.util.NetMatcher; +import org.apache.james.api.kernel.LoaderService; +import org.apache.james.services.MailServer; +import org.apache.james.socket.AvalonProtocolServer; +import org.apache.james.socket.api.ProtocolHandler; +import org.apache.james.socket.api.ProtocolHandlerFactory; +import org.apache.james.socket.api.ProtocolServer; +import org.apache.james.socket.configuration.JamesConfiguration; +import org.apache.mailet.MailetContext; + +/** + * This is an test refactoring for SMTPServer where the avalon socket server + * is a dependency and this class do not depend on it. + * + * <p>Accepts SMTP connections on a server socket and dispatches them to SMTPHandlers.</p> + * + * <p>Also responsible for loading and parsing SMTP specific configuration.</p> + * + * @version 1.1.0, 06/02/2001 + */ +/* + * IMPORTANT: SMTPServer extends AbstractJamesService. If you implement ANY + * lifecycle methods, you MUST call super.<method> as well. + */ +public class SMTPServerComposed extends AbstractLogEnabled implements ProtocolHandlerFactory, Serviceable, Configurable, Initializable { + + + /** + * The handler chain - SMTPhandlers can lookup handlerchain to obtain + * Command handlers , Message handlers and connection handlers + * Constructed during initialisation to allow dependency injection. + */ + private SMTPHandlerChain handlerChain; + + /** + * The mailet context - we access it here to set the hello name for the Mailet API + */ + private MailetContext mailetcontext; + + /** + * The internal mail server service. + */ + private MailServer mailServer; + + /** Loads instances */ + private LoaderService loader; + + /** Cached configuration data for handler */ + private Configuration handlerConfiguration; + + /** + * The DNSService + */ + private DNSService dnsService = null; + + /** + * Whether authentication is required to use + * this SMTP server. + */ + private final static int AUTH_DISABLED = 0; + private final static int AUTH_REQUIRED = 1; + private final static int AUTH_ANNOUNCE = 2; + private int authRequired = AUTH_DISABLED; + + /** + * Whether the server needs helo to be send first + */ + private boolean heloEhloEnforcement = false; + + /** + * SMTPGreeting to use + */ + private String smtpGreeting = null; + + /** + * This is a Network Matcher that should be configured to contain + * authorized networks that bypass SMTP AUTH requirements. + */ + private NetMatcher authorizedNetworks = null; + + /** + * The maximum message size allowed by this SMTP server. The default + * value, 0, means no limit. + */ + private long maxMessageSize = 0; + + /** + * The number of bytes to read before resetting + * the connection timeout timer. Defaults to + * 20 KB. + */ + private int lengthReset = 20 * 1024; + + /** + * The configuration data to be passed to the handler + */ + private SMTPConfiguration theConfigData + = new SMTPHandlerConfigurationDataImpl(); + + private boolean addressBracketsEnforcement = true; + + private ProtocolServer protocolServer; + + private AvalonProtocolServer avalonProtocolServer; + + /** + * Gets the current instance loader. + * @return the loader + */ + public final LoaderService getLoader() { + return loader; + } + + /** + * Sets the loader to be used for instances. + * @param loader the loader to set, not null + */ + @Resource(name="org.apache.james.LoaderService") + public final void setLoader(LoaderService loader) { + this.loader = loader; + } + + /** + * @see org.apache.avalon.framework.service.Serviceable#service(ServiceManager) + */ + public void service( final ServiceManager manager ) throws ServiceException { + mailetcontext = (MailetContext) manager.lookup("org.apache.mailet.MailetContext"); + mailServer = (MailServer) manager.lookup(MailServer.ROLE); + dnsService = (DNSService) manager.lookup(DNSService.ROLE); + + // initialize the avalonProtocolServer passing "this" as the ProtocolHandlerFactory service + DefaultServiceManager sm = new DefaultServiceManager(manager); + sm.put(ProtocolHandlerFactory.ROLE, (ProtocolHandlerFactory) this); + avalonProtocolServer = new AvalonProtocolServer(); + avalonProtocolServer.enableLogging(getLogger()); + avalonProtocolServer.service(sm); + } + + /** + * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration) + */ + public void configure(final Configuration configuration) throws ConfigurationException { + avalonProtocolServer.configure(configuration); + + if (avalonProtocolServer.isEnabled()) { + + handlerConfiguration = configuration.getChild("handler"); + String authRequiredString = handlerConfiguration.getChild("authRequired").getValue("false").trim().toLowerCase(); + if (authRequiredString.equals("true")) authRequired = AUTH_REQUIRED; + else if (authRequiredString.equals("announce")) authRequired = AUTH_ANNOUNCE; + else authRequired = AUTH_DISABLED; + if (authRequired != AUTH_DISABLED) { + getLogger().info("This SMTP server requires authentication."); + } else { + getLogger().info("This SMTP server does not require authentication."); + } + + String authorizedAddresses = handlerConfiguration.getChild("authorizedAddresses").getValue(null); + if (authRequired == AUTH_DISABLED && authorizedAddresses == null) { + /* if SMTP AUTH is not requred then we will use + * authorizedAddresses to determine whether or not to + * relay e-mail. Therefore if SMTP AUTH is not + * required, we will not relay e-mail unless the + * sending IP address is authorized. + * + * Since this is a change in behavior for James v2, + * create a default authorizedAddresses network of + * 0.0.0.0/0, which matches all possible addresses, thus + * preserving the current behavior. + * + * James v3 should require the <authorizedAddresses> + * element. + */ + authorizedAddresses = "0.0.0.0/0.0.0.0"; + } + + if (authorizedAddresses != null) { + java.util.StringTokenizer st = new java.util.StringTokenizer(authorizedAddresses, ", ", false); + java.util.Collection<String> networks = new java.util.ArrayList<String>(); + while (st.hasMoreTokens()) { + String addr = st.nextToken(); + networks.add(addr); + } + authorizedNetworks = new NetMatcher(networks, dnsService); + } + + if (authorizedNetworks != null) { + getLogger().info("Authorized addresses: " + authorizedNetworks.toString()); + } + + // get the message size limit from the conf file and multiply + // by 1024, to put it in bytes + maxMessageSize = handlerConfiguration.getChild( "maxmessagesize" ).getValueAsLong( maxMessageSize ) * 1024; + if (maxMessageSize > 0) { + getLogger().info("The maximum allowed message size is " + maxMessageSize + " bytes."); + } else { + getLogger().info("No maximum message size is enforced for this server."); + } + // How many bytes to read before updating the timer that data is being transfered + lengthReset = configuration.getChild("lengthReset").getValueAsInteger(lengthReset); + if (lengthReset <= 0) { + throw new ConfigurationException("The configured value for the idle timeout reset, " + lengthReset + ", is not valid."); + } + if (getLogger().isInfoEnabled()) { + getLogger().info("The idle timeout will be reset every " + lengthReset + " bytes."); + } + + heloEhloEnforcement = handlerConfiguration.getChild("heloEhloEnforcement").getValueAsBoolean(true); + + if (authRequiredString.equals("true")) authRequired = AUTH_REQUIRED; + + // get the smtpGreeting + smtpGreeting = handlerConfiguration.getChild("smtpGreeting").getValue(null); + + addressBracketsEnforcement = handlerConfiguration.getChild("addressBracketsEnforcement").getValueAsBoolean(true); + } + } + + public void initialize() throws Exception { + // this initialize the protocol server that wil call back our init(ProtocolServer) method + avalonProtocolServer.initialize(); + + } + + private void prepareHandlerChain() throws Exception { + handlerChain = loader.load(SMTPHandlerChain.class); + + //set the logger + handlerChain.setLog(new AvalonLogger(getLogger())); + + //read from the XML configuration and create and configure each of the handlers + handlerChain.configure(new JamesConfiguration(handlerConfiguration.getChild("handlerchain"))); + } + + /** + * @see org.apache.james.core.AbstractProtocolServer#getDefaultPort() + */ + public int getDefaultPort() { + return 25; + } + + /** + * @see org.apache.james.core.AbstractProtocolServer#getServiceType() + */ + public String getServiceType() { + return "SMTP Service"; + } + + /** + * A class to provide SMTP handler configuration to the handlers + */ + private class SMTPHandlerConfigurationDataImpl implements SMTPConfiguration { + + /** + * @see org.apache.james.smtpserver.SMTPConfiguration#getHelloName() + */ + public String getHelloName() { + if (SMTPServerComposed.this.avalonProtocolServer.getHelloName() == null) { + return SMTPServerComposed.this.mailServer.getHelloName(); + } else { + return SMTPServerComposed.this.avalonProtocolServer.getHelloName(); + } + } + + /** + * @see org.apache.james.smtpserver.SMTPConfiguration#getResetLength() + */ + public int getResetLength() { + return SMTPServerComposed.this.lengthReset; + } + + /** + * @see org.apache.james.smtpserver.SMTPConfiguration#getMaxMessageSize() + */ + public long getMaxMessageSize() { + return SMTPServerComposed.this.maxMessageSize; + } + + /** + * @see org.apache.james.smtpserver.SMTPConfiguration#isAuthSupported(String) + */ + public boolean isRelayingAllowed(String remoteIP) { + boolean relayingAllowed = false; + if (authorizedNetworks != null) { + relayingAllowed = SMTPServerComposed.this.authorizedNetworks.matchInetNetwork(remoteIP); + } + return relayingAllowed; + } + + /** + * @see org.apache.james.smtpserver.SMTPConfiguration#useHeloEhloEnforcement() + */ + public boolean useHeloEhloEnforcement() { + return SMTPServerComposed.this.heloEhloEnforcement; + } + + + /** + * @see org.apache.james.smtpserver.SMTPConfiguration#getSMTPGreeting() + */ + public String getSMTPGreeting() { + return SMTPServerComposed.this.smtpGreeting; + } + + /** + * @see org.apache.james.smtpserver.SMTPConfiguration#useAddressBracketsEnforcement() + */ + public boolean useAddressBracketsEnforcement() { + return SMTPServerComposed.this.addressBracketsEnforcement; + } + + public boolean isAuthRequired(String remoteIP) { + if (SMTPServerComposed.this.authRequired == AUTH_ANNOUNCE) return true; + boolean authRequired = SMTPServerComposed.this.authRequired != AUTH_DISABLED; + if (authorizedNetworks != null) { + authRequired = authRequired && !SMTPServerComposed.this.authorizedNetworks.matchInetNetwork(remoteIP); + } + return authRequired; + } + + public boolean isStartTLSSupported() { + return SMTPServerComposed.this.avalonProtocolServer.useStartTLS(); + } + + //TODO: IF we create here an interface to get DNSServer + // we should access it from the SMTPHandlers + + } + + /** + * @see org.apache.james.socket.AbstractProtocolServer#newProtocolHandlerInstance() + */ + public ProtocolHandler newProtocolHandlerInstance() { + final SMTPHandler theHandler = new SMTPHandler(handlerChain, theConfigData); + return theHandler; + } + + + public void init() throws Exception { + // complete the initialization + + } + + public void prepare(ProtocolServer server) throws Exception { + // in this case the protocolServer has been instantiated by us, + // so this will equal to avalonProtocolServer. + this.protocolServer = server; + + String hello = (String) mailetcontext.getAttribute(Constants.HELLO_NAME); + + if (protocolServer.isEnabled()) { + // TODO Remove this in next not backwards compatible release! + if (hello == null) mailetcontext.setAttribute(Constants.HELLO_NAME, protocolServer.getHelloName()); + } else { + // TODO Remove this in next not backwards compatible release! + if (hello == null) mailetcontext.setAttribute(Constants.HELLO_NAME, "localhost"); + } + + + prepareHandlerChain(); + } + +} Propchange: james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPServerComposed.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: james/server/trunk/smtpserver-function/src/main/java/org/apache/james/smtpserver/SMTPServerComposed.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/SMTPServerComposedTest.java URL: http://svn.apache.org/viewvc/james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/SMTPServerComposedTest.java?rev=825198&view=auto ============================================================================== --- james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/SMTPServerComposedTest.java (added) +++ james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/SMTPServerComposedTest.java Wed Oct 14 17:13:48 2009 @@ -0,0 +1,44 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.smtpserver; + +import org.apache.avalon.framework.container.ContainerUtil; +import org.apache.james.test.mock.avalon.MockLogger; + +public class SMTPServerComposedTest extends SMTPServerTest { + + private SMTPServerComposed m_smtpServer; + + protected void setUp() throws Exception { + m_smtpServer = new SMTPServerComposed(); + ContainerUtil.enableLogging(m_smtpServer,new MockLogger()); + m_serviceManager = setUpServiceManager(); + ContainerUtil.service(m_smtpServer, m_serviceManager); + m_smtpServer.setLoader(m_serviceManager); + m_testConfiguration = new SMTPTestConfiguration(m_smtpListenerPort); + } + + protected void finishSetUp(SMTPTestConfiguration testConfiguration) throws Exception { + testConfiguration.init(); + ContainerUtil.configure(m_smtpServer, testConfiguration); + m_mailServer.setMaxMessageSizeBytes(m_testConfiguration.getMaxMessageSize()*1024); + ContainerUtil.initialize(m_smtpServer); + } +} Propchange: james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/SMTPServerComposedTest.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: james/server/trunk/smtpserver-function/src/test/java/org/apache/james/smtpserver/SMTPServerComposedTest.java ------------------------------------------------------------------------------ svn:mime-type = text/plain --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org