You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by no...@apache.org on 2010/06/16 16:07:36 UTC

svn commit: r955234 - in /james/server/trunk: ./ core-function/src/main/java/org/apache/james/dnsserver/ core-function/src/test/java/org/apache/james/dnsserver/ core-function/src/test/resources/org/apache/james/dnsserver/ dnsserver/ dnsserver/src/ dnss...

Author: norman
Date: Wed Jun 16 14:07:35 2010
New Revision: 955234

URL: http://svn.apache.org/viewvc?rev=955234&view=rev
Log:
move the DNSServer stuff to an extra bundle to simplify deployment in an OSGI Container
Let the spring-common module act as a fragment so we can use the lifecycle beans in an OSGI Container (using spring-dm) (JAMES-835)

Added:
    james/server/trunk/dnsserver/   (with props)
    james/server/trunk/dnsserver/pom.xml
    james/server/trunk/dnsserver/src/
    james/server/trunk/dnsserver/src/main/
    james/server/trunk/dnsserver/src/main/java/
    james/server/trunk/dnsserver/src/main/java/org/
    james/server/trunk/dnsserver/src/main/java/org/apache/
    james/server/trunk/dnsserver/src/main/java/org/apache/james/
    james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/
    james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/DNSServer.java   (with props)
    james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/DNSServerMBean.java   (with props)
    james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/package.html   (with props)
    james/server/trunk/dnsserver/src/main/resources/
    james/server/trunk/dnsserver/src/main/resources/META-INF/
    james/server/trunk/dnsserver/src/main/resources/META-INF/spring/
    james/server/trunk/dnsserver/src/main/resources/META-INF/spring/dns-osgi.xml
    james/server/trunk/dnsserver/src/main/resources/META-INF/spring/dns.xml
    james/server/trunk/dnsserver/src/main/resources/dnsserver.xml
    james/server/trunk/dnsserver/src/test/
    james/server/trunk/dnsserver/src/test/java/
    james/server/trunk/dnsserver/src/test/java/org/
    james/server/trunk/dnsserver/src/test/java/org/apache/
    james/server/trunk/dnsserver/src/test/java/org/apache/james/
    james/server/trunk/dnsserver/src/test/java/org/apache/james/dnsserver/
    james/server/trunk/dnsserver/src/test/java/org/apache/james/dnsserver/DNSServerTest.java   (with props)
    james/server/trunk/dnsserver/src/test/resources/
    james/server/trunk/dnsserver/src/test/resources/org/
    james/server/trunk/dnsserver/src/test/resources/org/apache/
    james/server/trunk/dnsserver/src/test/resources/org/apache/james/
    james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/
    james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/brandilyncollins.com.zone   (with props)
    james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/dnstest.com.zone
    james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/pippo.com.zone   (with props)
    james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/test-zone.com.zone   (with props)
    james/server/trunk/spring-common/src/main/java/org/apache/james/container/spring/lifecycle/OsgiSpringConfigurationRegistry.java
    james/server/trunk/spring-common/src/main/java/org/apache/james/container/spring/lifecycle/OsgiSpringLogRegistry.java
    james/server/trunk/spring-common/src/main/resources/
    james/server/trunk/spring-common/src/main/resources/META-INF/
    james/server/trunk/spring-common/src/main/resources/META-INF/spring/
    james/server/trunk/spring-common/src/main/resources/META-INF/spring/extender/
    james/server/trunk/spring-common/src/main/resources/META-INF/spring/extender/extender.xml
Removed:
    james/server/trunk/core-function/src/main/java/org/apache/james/dnsserver/
    james/server/trunk/core-function/src/test/java/org/apache/james/dnsserver/
    james/server/trunk/core-function/src/test/resources/org/apache/james/dnsserver/
Modified:
    james/server/trunk/pom.xml
    james/server/trunk/spring-common/pom.xml
    james/server/trunk/spring-common/src/main/java/org/apache/james/container/spring/lifecycle/SpringConfigurationRegistry.java
    james/server/trunk/spring-common/src/main/java/org/apache/james/container/spring/lifecycle/SpringLogRegistry.java
    james/server/trunk/spring-deployment/pom.xml

Propchange: james/server/trunk/dnsserver/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Wed Jun 16 14:07:35 2010
@@ -0,0 +1 @@
+target

Added: james/server/trunk/dnsserver/pom.xml
URL: http://svn.apache.org/viewvc/james/server/trunk/dnsserver/pom.xml?rev=955234&view=auto
==============================================================================
--- james/server/trunk/dnsserver/pom.xml (added)
+++ james/server/trunk/dnsserver/pom.xml Wed Jun 16 14:07:35 2010
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="ISO-8859-15"?>
+<!--
+  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.    
+-->
+<project>
+  <parent>
+    <artifactId>james-server</artifactId>
+    <groupId>org.apache.james</groupId>
+    <version>3.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.apache.james</groupId>
+  <artifactId>james-server-dnsserver</artifactId>
+  <name>Apache JAMES Server DNSSerice Implementation</name>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile> 
+            <manifest>
+              <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+              <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+            </manifest>
+          </archive>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-jar-plugin</artifactId>
+         <configuration>       
+           <archive>
+            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile> 
+            <manifest>
+              <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+              <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+            </manifest>
+          </archive>
+        </configuration>       
+        <executions>
+          <execution>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>bundle-manifest</id>
+            <phase>process-classes</phase>
+            <goals>
+              <goal>manifest</goal>
+            </goals>
+          </execution>
+        </executions>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Export-Package>org.apache.james.api.dnsservice.*</Export-Package>
+            <Embed-Dependency>*;scope=runtime</Embed-Dependency>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.james</groupId>
+      <artifactId>apache-mailet</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.james</groupId>
+      <artifactId>james-server-domain-api</artifactId>
+    </dependency>
+     <dependency>
+      <groupId>org.apache.james</groupId>
+      <artifactId>james-server-core-api</artifactId>
+    </dependency> 
+    <dependency>
+      <groupId>${javax.mail.groupId}</groupId>
+      <artifactId>${javax.mail.artifactId}</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-configuration</groupId>
+      <artifactId>commons-configuration</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-logging</groupId>
+      <artifactId>commons-logging</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>dnsjava</groupId>
+      <artifactId>dnsjava</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>

Added: james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/DNSServer.java
URL: http://svn.apache.org/viewvc/james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/DNSServer.java?rev=955234&view=auto
==============================================================================
--- james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/DNSServer.java (added)
+++ james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/DNSServer.java Wed Jun 16 14:07:35 2010
@@ -0,0 +1,602 @@
+/****************************************************************
+ * 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.dnsserver;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.logging.Log;
+import org.apache.james.api.dnsservice.DNSService;
+import org.apache.james.api.dnsservice.TemporaryResolutionException;
+import org.apache.james.lifecycle.Configurable;
+import org.apache.james.lifecycle.LogEnabled;
+import org.apache.mailet.HostAddress;
+import org.xbill.DNS.ARecord;
+import org.xbill.DNS.Cache;
+import org.xbill.DNS.Credibility;
+import org.xbill.DNS.DClass;
+import org.xbill.DNS.ExtendedResolver;
+import org.xbill.DNS.Lookup;
+import org.xbill.DNS.MXRecord;
+import org.xbill.DNS.Name;
+import org.xbill.DNS.PTRRecord;
+import org.xbill.DNS.Record;
+import org.xbill.DNS.Resolver;
+import org.xbill.DNS.ResolverConfig;
+import org.xbill.DNS.ReverseMap;
+import org.xbill.DNS.TXTRecord;
+import org.xbill.DNS.TextParseException;
+import org.xbill.DNS.Type;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+
+/**
+ * Provides DNS client functionality to services running
+ * inside James
+ */
+public class DNSServer implements DNSService, DNSServerMBean, LogEnabled, Configurable {
+
+    /**
+     * A resolver instance used to retrieve DNS records.  This
+     * is a reference to a third party library object.
+     */
+    protected Resolver resolver;
+
+    /**
+     * A TTL cache of results received from the DNS server.  This
+     * is a reference to a third party library object.
+     */
+    protected Cache cache;
+
+    /**
+     * Maximum number of RR to cache.
+     */
+
+    private int maxCacheSize = 50000;
+
+    /**
+     * Whether the DNS response is required to be authoritative
+     */
+    private int dnsCredibility;
+    
+    /**
+     * The DNS servers to be used by this service
+     */
+    private List<String> dnsServers = new ArrayList<String>();
+    
+    /**
+     * The search paths to be used
+     */
+    private Name[] searchPaths = null;
+
+    /**
+     * The MX Comparator used in the MX sort.
+     */
+    private Comparator<MXRecord> mxComparator = new MXRecordComparator();
+
+    /**
+     * If true than the DNS server will return only a single IP per each MX record
+     * when looking up SMTPServers
+     */
+    private boolean singleIPPerMX;
+    
+    /**
+     * If true register this service as the default resolver/cache for DNSJava static
+     * calls
+     */
+    private boolean setAsDNSJavaDefault;
+    
+    private String localHostName;
+    
+    private String localCanonicalHostName;
+    
+    private String localAddress;
+    
+    private HierarchicalConfiguration configuration;
+    
+    private Log logger;
+    
+    
+    public void setLog(Log logger) {
+        this.logger = logger;
+    }
+    
+    public void configure(HierarchicalConfiguration configuration) throws ConfigurationException{
+
+        final boolean autodiscover =
+            configuration.getBoolean( "autodiscover", true );
+
+        List<Name> sPaths = new ArrayList<Name>();
+        if (autodiscover) {
+            logger.info("Autodiscovery is enabled - trying to discover your system's DNS Servers");
+            String[] serversArray = ResolverConfig.getCurrentConfig().servers();
+            if (serversArray != null) {
+                for ( int i = 0; i < serversArray.length; i++ ) {
+                    dnsServers.add(serversArray[ i ]);
+                    logger.info("Adding autodiscovered server " + serversArray[i]);
+                }
+            }
+            Name[] systemSearchPath = ResolverConfig.getCurrentConfig().searchPath();
+            if (systemSearchPath != null && systemSearchPath.length > 0) {
+                sPaths.addAll(Arrays.asList(systemSearchPath));
+            }
+            if (logger.isInfoEnabled()) {
+                for (Iterator<Name> i = sPaths.iterator(); i.hasNext();) {
+                    Name searchPath = i.next();
+                    logger.info("Adding autodiscovered search path " + searchPath.toString());
+                }
+            }
+        }
+
+        singleIPPerMX = configuration.getBoolean( "singleIPperMX", false ); 
+
+        setAsDNSJavaDefault = configuration.getBoolean( "setAsDNSJavaDefault" ,true );
+        
+        // Get the DNS servers that this service will use for lookups
+        final List<String> serversConfigurations = configuration.getList( "servers.server" );
+      
+
+        for ( int i = 0; i < serversConfigurations.size(); i++ ) {
+            dnsServers.add( serversConfigurations.get(i) );
+        }
+
+        // Get the DNS servers that this service will use for lookups
+        final List<String> searchPathsConfiguration = configuration.getList( "searchpaths.searchpath" );
+
+        for ( int i = 0; i < searchPathsConfiguration.size(); i++ ) {
+            try {
+                sPaths.add( Name.fromString(searchPathsConfiguration.get(i)) );
+            } catch (TextParseException e) {
+                throw new ConfigurationException("Unable to parse searchpath host: "+searchPathsConfiguration.get(i),e);
+            }
+        }
+        
+        searchPaths = (Name[]) sPaths.toArray(new Name[0]);
+
+        if (dnsServers.isEmpty()) {
+            logger.info("No DNS servers have been specified or found by autodiscovery - adding 127.0.0.1");
+            dnsServers.add("127.0.0.1");
+        }
+
+        final boolean authoritative =
+           configuration.getBoolean( "authoritative" , false);
+        // TODO: Check to see if the credibility field is being used correctly.  From the
+        //      docs I don't think so
+        dnsCredibility = authoritative ? Credibility.AUTH_ANSWER : Credibility.NONAUTH_ANSWER;
+
+        maxCacheSize = (int)configuration.getLong( "maxcachesize",maxCacheSize );
+    }
+    
+    
+    
+
+    @PostConstruct
+    public void init()
+        throws Exception {
+        logger.debug("DNSService init...");        
+
+        // If no DNS servers were configured, default to local host
+        if (dnsServers.isEmpty()) {
+            try {
+                dnsServers.add( InetAddress.getLocalHost().getHostName() );
+            } catch ( UnknownHostException ue ) {
+                dnsServers.add( "127.0.0.1" );
+            }
+        }
+
+        //Create the extended resolver...
+        final String[] serversArray = (String[])dnsServers.toArray(new String[0]);
+
+        if (logger.isInfoEnabled()) {
+            for(int c = 0; c < serversArray.length; c++) {
+                logger.info("DNS Server is: " + serversArray[c]);
+            }
+        }
+
+        try {
+            resolver = new ExtendedResolver( serversArray );
+        } catch (UnknownHostException uhe) {
+            logger.error("DNS service could not be initialized.  The DNS servers specified are not recognized hosts.", uhe);
+            throw uhe;
+        }
+
+        cache = new Cache (DClass.IN);
+        cache.setMaxEntries(maxCacheSize);
+        
+        if (setAsDNSJavaDefault) {
+            Lookup.setDefaultResolver(resolver);
+            Lookup.setDefaultCache(cache, DClass.IN);
+            Lookup.setDefaultSearchPath(searchPaths);
+            logger.info("Registered cache, resolver and search paths as DNSJava defaults");
+        }
+        
+        // Cache the local hostname and local address. This is needed because 
+        // the following issues:
+        // JAMES-787
+        // JAMES-302
+        InetAddress addr = getLocalHost();
+        localCanonicalHostName = addr.getCanonicalHostName();
+        localHostName = addr.getHostName();
+        localAddress = addr.getHostAddress();
+        
+        logger.debug("DNSService ...init end");
+    }
+
+    /**
+     * <p>Return the list of DNS servers in use by this service</p>
+     *
+     * @return an array of DNS server names
+     */
+    public String[] getDNSServers() {
+        return (String[])dnsServers.toArray(new String[0]);
+    }
+
+    /**
+     * <p>Return the list of DNS servers in use by this service</p>
+     *
+     * @return an array of DNS server names
+     */
+    public Name[] getSearchPaths() {
+        return searchPaths;
+    }
+
+    
+    /**
+     * <p>Return a prioritized unmodifiable list of MX records
+     * obtained from the server.</p>
+     *
+     * @param hostname domain name to look up
+     *
+     * @return a list of MX records corresponding to this mail domain
+     * @throws TemporaryResolutionException get thrown on temporary problems
+     */
+    private List<String> findMXRecordsRaw(String hostname) throws TemporaryResolutionException {
+        Record answers[] = lookup(hostname, Type.MX, "MX");
+        List<String> servers = new ArrayList<String>();
+        if (answers == null) {
+            return servers;
+        }
+
+        MXRecord mxAnswers[] = new MXRecord[answers.length];
+        for (int i = 0; i < answers.length; i++) {
+            mxAnswers[i] = (MXRecord)answers[i];
+        }
+
+        Arrays.sort(mxAnswers, mxComparator);
+
+        for (int i = 0; i < mxAnswers.length; i++) {
+            servers.add(mxAnswers[i].getTarget ().toString ());
+            logger.debug(new StringBuffer("Found MX record ").append(mxAnswers[i].getTarget ().toString ()).toString());
+        }
+        return servers;
+    }
+    
+    /**
+     * @see org.apache.james.api.dnsservice.DNSService#findMXRecords(String)
+     */
+    public Collection<String> findMXRecords(String hostname) throws TemporaryResolutionException {
+        List<String> servers = new ArrayList<String>();
+        try {
+            servers = findMXRecordsRaw(hostname);
+            return Collections.unmodifiableCollection(servers);
+        } finally {
+            //If we found no results, we'll add the original domain name if
+            //it's a valid DNS entry
+            if (servers.size () == 0) {
+                StringBuffer logBuffer =
+                    new StringBuffer(128)
+                            .append("Couldn't resolve MX records for domain ")
+                            .append(hostname)
+                            .append(".");
+                logger.info(logBuffer.toString());
+                try {
+                    getByName(hostname);
+                    servers.add(hostname);
+                } catch (UnknownHostException uhe) {
+                    // The original domain name is not a valid host,
+                    // so we can't add it to the server list.  In this
+                    // case we return an empty list of servers
+                    logBuffer = new StringBuffer(128)
+                              .append("Couldn't resolve IP address for host ")
+                              .append(hostname)
+                              .append(".");
+                    logger.error(logBuffer.toString());
+                }
+            }
+        }
+    }
+
+    /**
+     * Looks up DNS records of the specified type for the specified name.
+     *
+     * This method is a public wrapper for the private implementation
+     * method
+     *
+     * @param namestr the name of the host to be looked up
+     * @param type the type of record desired
+     * @param typeDesc the description of the record type, for debugging purpose
+     */
+    protected Record[] lookup(String namestr, int type, String typeDesc) throws TemporaryResolutionException {
+        // Name name = null;
+        try {
+            // name = Name.fromString(namestr, Name.root);
+            Lookup l = new Lookup(namestr, type);
+            
+            l.setCache(cache);
+            l.setResolver(resolver);
+            l.setCredibility(dnsCredibility);
+            l.setSearchPath(searchPaths);
+            Record[] r = l.run();
+            
+            try {
+                if (l.getResult() == Lookup.TRY_AGAIN) {
+                    throw new TemporaryResolutionException(
+                            "DNSService is temporary not reachable");
+                } else {
+                    return r;
+                }
+            } catch (IllegalStateException ise) {
+                // This is okay, because it mimics the original behaviour
+                // TODO find out if it's a bug in DNSJava 
+                logger.debug("Error determining result ", ise);
+                throw new TemporaryResolutionException(
+                        "DNSService is temporary not reachable");
+            }
+            
+            // return rawDNSLookup(name, false, type, typeDesc);
+        } catch (TextParseException tpe) {
+            // TODO: Figure out how to handle this correctly.
+            logger.error("Couldn't parse name " + namestr, tpe);
+            return null;
+        }
+    }
+    
+    protected Record[] lookupNoException(String namestr, int type, String typeDesc) {
+        try {
+            return lookup(namestr, type, typeDesc);
+        } catch (TemporaryResolutionException e) {
+            return null;
+        }
+    }
+    
+    /* RFC 2821 section 5 requires that we sort the MX records by their
+     * preference, and introduce a randomization.  This Comparator does
+     * comparisons as normal unless the values are equal, in which case
+     * it "tosses a coin", randomly speaking.
+     *
+     * This way MX record w/preference 0 appears before MX record
+     * w/preference 1, but a bunch of MX records with the same preference
+     * would appear in different orders each time.
+     *
+     * Reminder for maintainers: the return value on a Comparator can
+     * be counter-intuitive for those who aren't used to the old C
+     * strcmp function:
+     *
+     * < 0 ==> a < b
+     * = 0 ==> a = b
+     * > 0 ==> a > b
+     */
+    private static class MXRecordComparator implements Comparator<MXRecord> {
+        private final static Random random = new Random();
+        public int compare (MXRecord a, MXRecord b) {
+            int pa = a.getPriority();
+            int pb = b.getPriority();
+            return (pa == pb) ? (512 - random.nextInt(1024)) : pa - pb;
+        }
+    }
+
+    /**
+     * @see org.apache.james.api.dnsservice.DNSService#getSMTPHostAddresses(String)
+     */
+    public Iterator<HostAddress> getSMTPHostAddresses(final String domainName) throws TemporaryResolutionException {
+        return new Iterator<HostAddress>() {
+            private Iterator<String> mxHosts = findMXRecords(domainName).iterator();
+            private Iterator<HostAddress> addresses = null;
+
+            public boolean hasNext() {
+                /* Make sure that when next() is called, that we can
+                 * provide a HostAddress.  This means that we need to
+                 * have an inner iterator, and verify that it has
+                 * addresses.  We could, for example, run into a
+                 * situation where the next mxHost didn't have any valid
+                 * addresses.
+                 */
+                if ((addresses == null || !addresses.hasNext()) && mxHosts.hasNext()) do {
+                    final String nextHostname = (String)mxHosts.next();
+                    InetAddress[] addrs = null;
+                    try {
+                        if (singleIPPerMX) {
+                            addrs = new InetAddress[] {getByName(nextHostname)};
+                        } else {
+                            addrs = getAllByName(nextHostname);
+                        }
+                    } catch (UnknownHostException uhe) {
+                        // this should never happen, since we just got
+                        // this host from mxHosts, which should have
+                        // already done this check.
+                        StringBuffer logBuffer = new StringBuffer(128)
+                                                 .append("Couldn't resolve IP address for discovered host ")
+                                                 .append(nextHostname)
+                                                 .append(".");
+                        logger.error(logBuffer.toString());
+                    }
+                    final InetAddress[] ipAddresses = addrs;
+
+                    addresses = new Iterator<HostAddress>() {
+                        int i = 0;
+
+                        public boolean hasNext() {
+                            return ipAddresses != null && i < ipAddresses.length;
+                        }
+
+                        public HostAddress next() {
+                            return new org.apache.mailet.HostAddress(nextHostname, "smtp://" + ipAddresses[i++].getHostAddress());
+                        }
+
+                        public void remove() {
+                            throw new UnsupportedOperationException ("remove not supported by this iterator");
+                        }
+                    };
+                } while (!addresses.hasNext() && mxHosts.hasNext());
+
+                return addresses != null && addresses.hasNext();
+            }
+
+            public HostAddress next() {
+                return addresses != null ? addresses.next() : null;
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException ("remove not supported by this iterator");
+            }
+        };
+    }
+
+    /* java.net.InetAddress.get[All]ByName(String) allows an IP literal
+     * to be passed, and will recognize it even with a trailing '.'.
+     * However, org.xbill.DNS.Address does not recognize an IP literal
+     * with a trailing '.' character.  The problem is that when we
+     * lookup an MX record for some domains, we may find an IP address,
+     * which will have had the trailing '.' appended by the time we get
+     * it back from dnsjava.  An MX record is not allowed to have an IP
+     * address as the right-hand-side, but there are still plenty of
+     * such records on the Internet.  Since java.net.InetAddress can
+     * handle them, for the time being we've decided to support them.
+     *
+     * These methods are NOT intended for use outside of James, and are
+     * NOT declared by the org.apache.james.services.DNSServer.  This is
+     * currently a stopgap measure to be revisited for the next release.
+     */
+
+    private static String allowIPLiteral(String host) {
+        if ((host.charAt(host.length() - 1) == '.')) {
+            String possible_ip_literal = host.substring(0, host.length() - 1);
+            if (org.xbill.DNS.Address.isDottedQuad(possible_ip_literal)) {
+                host = possible_ip_literal;
+            }
+        }
+        return host;
+    }
+
+    /**
+     * @see org.apache.james.api.dnsservice.DNSService#getByName(String)
+     */
+    public InetAddress getByName(String host) throws UnknownHostException {
+        String name = allowIPLiteral(host);
+         
+        try {
+            // Check if its local
+            if (name.equalsIgnoreCase(localHostName) || name.equalsIgnoreCase(localCanonicalHostName) ||name.equals(localAddress)) {
+                return getLocalHost();
+            }
+            
+            return org.xbill.DNS.Address.getByAddress(name);
+        } catch (UnknownHostException e) {
+            Record[] records = lookupNoException(name, Type.A, "A");
+
+            if (records != null && records.length >= 1) {
+                ARecord a = (ARecord) records[0];
+                return InetAddress.getByAddress(name, a.getAddress().getAddress());
+            } else throw e;
+        }
+    }
+
+    /**
+     * @see org.apache.james.api.dnsservice.DNSService#getAllByName(String)
+     */
+    public InetAddress[] getAllByName(String host) throws UnknownHostException {
+        String name = allowIPLiteral(host);
+        try {
+            // Check if its local
+            if (name.equalsIgnoreCase(localHostName) || name.equalsIgnoreCase(localCanonicalHostName) ||name.equals(localAddress)) {
+                return new InetAddress[] {getLocalHost()};
+            }
+            
+            InetAddress addr = org.xbill.DNS.Address.getByAddress(name);
+            return new InetAddress[] {addr};
+        } catch (UnknownHostException e) {
+            Record[] records = lookupNoException(name, Type.A, "A");
+            
+            if (records != null && records.length >= 1) {
+                InetAddress [] addrs = new InetAddress[records.length];
+                for (int i = 0; i < records.length; i++) {
+                    ARecord a = (ARecord) records[i];
+                    addrs[i] = InetAddress.getByAddress(name, a.getAddress().getAddress());
+                }
+                return addrs;
+            } else throw e;
+        }
+    }
+    
+    /**
+     * @see org.apache.james.api.dnsservice.DNSService#findTXTRecords(String)
+     */
+    public Collection<String> findTXTRecords(String hostname){
+        List<String> txtR = new ArrayList<String>();
+        Record[] records = lookupNoException(hostname, Type.TXT, "TXT");
+    
+        if (records != null) {
+           for (int i = 0; i < records.length; i++) {
+               TXTRecord txt = (TXTRecord) records[i];
+               txtR.add(txt.rdataToString());
+           }
+        
+        }
+        return txtR;
+    }
+
+    /**
+     * @see org.apache.james.api.dnsservice.DNSService#getHostName(java.net.InetAddress)
+     */
+    public String getHostName(InetAddress addr){
+        String result = null;
+        Name name = ReverseMap.fromAddress(addr);
+        Record[] records = lookupNoException(name.toString(), Type.PTR, "PTR");
+
+        if (records == null) {
+            result = addr.getHostAddress();
+        } else {
+            PTRRecord ptr = (PTRRecord) records[0];
+            result = ptr.getTarget().toString();
+        }
+        return result;
+    }
+
+    /**
+     * @see org.apache.james.api.dnsservice.DNSService#getLocalHost()
+     */
+    public InetAddress getLocalHost() throws UnknownHostException {
+        return InetAddress.getLocalHost();
+    }
+
+}

Propchange: james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/DNSServer.java
------------------------------------------------------------------------------
    cvs2svn:cvs-rev = 1.9.4.20

Propchange: james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/DNSServer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/DNSServer.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/DNSServerMBean.java
URL: http://svn.apache.org/viewvc/james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/DNSServerMBean.java?rev=955234&view=auto
==============================================================================
--- james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/DNSServerMBean.java (added)
+++ james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/DNSServerMBean.java Wed Jun 16 14:07:35 2010
@@ -0,0 +1,35 @@
+/****************************************************************
+ * 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.dnsserver;
+
+/**
+ * An interface to expose James management functionality through JMX.
+ * 
+ * @phoenix:mx-topic name="DNSService"
+ */
+public interface DNSServerMBean {
+    /**
+    * @phoenix:mx-operation
+    * @phoenix:mx-description Returns the list of DNS servers
+    */    
+    public String[] getDNSServers();
+}

Propchange: james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/DNSServerMBean.java
------------------------------------------------------------------------------
    cvs2svn:cvs-rev = 1.1.2.2

Propchange: james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/DNSServerMBean.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/DNSServerMBean.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/package.html
URL: http://svn.apache.org/viewvc/james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/package.html?rev=955234&view=auto
==============================================================================
--- james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/package.html (added)
+++ james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/package.html Wed Jun 16 14:07:35 2010
@@ -0,0 +1,21 @@
+<!--
+  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.    
+-->
+<body>
+Provides classes implementing simple DNS facilities for James
+</body>

Propchange: james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/package.html
------------------------------------------------------------------------------
    cvs2svn:cvs-rev = 1.1

Propchange: james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/package.html
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: james/server/trunk/dnsserver/src/main/java/org/apache/james/dnsserver/package.html
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: james/server/trunk/dnsserver/src/main/resources/META-INF/spring/dns-osgi.xml
URL: http://svn.apache.org/viewvc/james/server/trunk/dnsserver/src/main/resources/META-INF/spring/dns-osgi.xml?rev=955234&view=auto
==============================================================================
--- james/server/trunk/dnsserver/src/main/resources/META-INF/spring/dns-osgi.xml (added)
+++ james/server/trunk/dnsserver/src/main/resources/META-INF/spring/dns-osgi.xml Wed Jun 16 14:07:35 2010
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans:beans                                                                            
+   xmlns="http://www.springframework.org/schema/osgi"                                   
+   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+   xmlns:beans="http://www.springframework.org/schema/beans"                     
+   xsi:schemaLocation="http://www.springframework.org/schema/osgi  
+       http://www.springframework.org/schema/osgi/spring-osgi.xsd
+       http://www.springframework.org/schema/beans   
+       http://www.springframework.org/schema/beans/spring-beans.xsd">                   
+
+    <!--  export the dnsserver as osgi service -->
+    <service id="dnsserver" ref="dnsserver"                                 
+       interface="org.apache.james.api.dnsservice.DNSService" />
+</beans:beans>   
\ No newline at end of file

Added: james/server/trunk/dnsserver/src/main/resources/META-INF/spring/dns.xml
URL: http://svn.apache.org/viewvc/james/server/trunk/dnsserver/src/main/resources/META-INF/spring/dns.xml?rev=955234&view=auto
==============================================================================
--- james/server/trunk/dnsserver/src/main/resources/META-INF/spring/dns.xml (added)
+++ james/server/trunk/dnsserver/src/main/resources/META-INF/spring/dns.xml Wed Jun 16 14:07:35 2010
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+    <!--
+        ! 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. !
+    -->
+
+
+<beans xmlns="http://www.springframework.org/schema/beans" 
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:camel="http://camel.apache.org/schema/spring"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+       
+    <bean class="org.apache.james.container.spring.lifecycle.CommonsConfigurableBeanPostProcessor">
+        <property name="configurationRegistry" ref="configurationRegistry" />
+        <property name="order" value="1" />
+    </bean>
+
+    <bean id="configurationRegistry" class="org.apache.james.container.spring.lifecycle.OsgiSpringConfigurationRegistry"/>
+    
+    <bean class="org.apache.james.container.spring.lifecycle.LogEnabledBeanPostProcessor">
+        <property name="logRegistry" ref="logRegistry" />
+        <property name="order" value="0" />
+    </bean>
+
+    <bean id="logRegistry" class="org.apache.james.container.spring.lifecycle.OsgiSpringLogRegistry"/>
+    
+    <bean class= "org.springframework.context.annotation.CommonAnnotationBeanPostProcessor">
+        <property name="order" value="3" />
+    </bean>
+    
+    <bean id="dnsserver" class="org.apache.james.dnsserver.DNSServer" />
+
+
+</beans>
\ No newline at end of file

Added: james/server/trunk/dnsserver/src/main/resources/dnsserver.xml
URL: http://svn.apache.org/viewvc/james/server/trunk/dnsserver/src/main/resources/dnsserver.xml?rev=955234&view=auto
==============================================================================
--- james/server/trunk/dnsserver/src/main/resources/dnsserver.xml (added)
+++ james/server/trunk/dnsserver/src/main/resources/dnsserver.xml Wed Jun 16 14:07:35 2010
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<!--
+  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.                                           
+ -->
+<!-- DNS Server Block -->
+<!-- -->
+<!-- Specifies DNS Server information for use by various components inside -->
+<!-- James. -->
+<!-- -->
+<!-- If autodiscover is true, James will attempt to autodiscover the DNS servers configured on your underlying system.-->
+<!-- Currently, this works if the OS has a unix-like /etc/resolv.conf,-->
+<!-- or the system is Windows based with ipconfig or winipcfg.-->
+<!-- -->
+<!-- If no DNS servers are found and you have not specified any below, 127.0.0.1 will be used-->
+<!-- If you use autodiscover and add DNS servers manually a combination of all the dns servers will be used  -->
+<!--  -->
+<!-- Information includes a list of DNS Servers to be used by James.  These are -->
+<!-- specified by the server elements, each of which is a child element of the -->
+<!-- servers element.  Each server element is the IP address of a single DNS server. -->
+<!-- The servers element can have multiple server children. -->
+<dnsserver>
+    <servers>
+        <!--Enter ip address of your DNS server, one IP address per server -->
+        <!-- element. -->
+        <!--
+        <server>127.0.0.1</server>
+        -->
+    </servers>
+    <!-- Change autodiscover to false if you would like to turn off autodiscovery -->
+    <!-- and set the DNS servers manually in the <servers> section -->
+    <autodiscover>true</autodiscover>
+    <authoritative>false</authoritative>
+
+    <!-- Maximum number of entries to maintain in the DNS cache -->
+    <maxcachesize>50000</maxcachesize>
+      
+    <!-- Uncomment this if you want James to try a single server for each -->
+    <!-- multihomed mx host. -->
+    <!--
+    <singleIPperMX> true </singleIPperMX>
+    -->
+</dnsserver>
+   
\ No newline at end of file

Added: james/server/trunk/dnsserver/src/test/java/org/apache/james/dnsserver/DNSServerTest.java
URL: http://svn.apache.org/viewvc/james/server/trunk/dnsserver/src/test/java/org/apache/james/dnsserver/DNSServerTest.java?rev=955234&view=auto
==============================================================================
--- james/server/trunk/dnsserver/src/test/java/org/apache/james/dnsserver/DNSServerTest.java (added)
+++ james/server/trunk/dnsserver/src/test/java/org/apache/james/dnsserver/DNSServerTest.java Wed Jun 16 14:07:35 2010
@@ -0,0 +1,254 @@
+/****************************************************************
+ * 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.dnsserver;
+
+import org.apache.commons.configuration.DefaultConfigurationBuilder;
+import org.apache.commons.logging.impl.SimpleLog;
+import org.apache.mailet.HostAddress;
+import org.xbill.DNS.Cache;
+import org.xbill.DNS.DClass;
+import org.xbill.DNS.Lookup;
+import org.xbill.DNS.Message;
+import org.xbill.DNS.Name;
+import org.xbill.DNS.RRset;
+import org.xbill.DNS.Record;
+import org.xbill.DNS.Resolver;
+import org.xbill.DNS.SOARecord;
+import org.xbill.DNS.SetResponse;
+import org.xbill.DNS.Zone;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+
+public class DNSServerTest extends TestCase {
+
+    private TestableDNSServer dnsServer;
+    private Cache defaultCache;
+    private Resolver defaultResolver;
+    private Name[] defaultSearchPaths;
+    
+    public void testNoMX() throws Exception {
+        dnsServer.setResolver(null);
+        dnsServer.setCache(new ZoneCache("dnstest.com."));
+        //a.setSearchPath(new String[] { "searchdomain.com." });
+        Collection<String> records = dnsServer.findMXRecords("nomx.dnstest.com.");
+        assertEquals(1, records.size());
+        assertEquals("nomx.dnstest.com.", records.iterator()
+                .next());
+    }
+    
+    public void testBadMX() throws Exception {
+        dnsServer.setResolver(null);
+        dnsServer.setCache(new ZoneCache("dnstest.com."));
+        //a.setSearchPath(new String[] { "searchdomain.com." });
+        Collection<String> records = dnsServer.findMXRecords("badmx.dnstest.com.");
+        assertEquals(1, records.size());
+        assertEquals("badhost.dnstest.com.", records.iterator()
+                .next());
+        Iterator<HostAddress> it = dnsServer.getSMTPHostAddresses("badmx.dnstest.com.");
+        assertFalse(it.hasNext());
+    }
+    
+    public void testINARecords() throws Exception {
+        // Zone z = loadZone("pippo.com.");
+        dnsServer.setResolver(null);
+        dnsServer.setCache(new ZoneCache("pippo.com."));
+        // dnsServer.setLookupper(new ZoneLookupper(z));
+        Collection<String> records = dnsServer.findMXRecords("www.pippo.com.");
+        assertEquals(1, records.size());
+        assertEquals("pippo.com.inbound.mxlogic.net.", records.iterator()
+                .next());
+    }
+
+    public void testMXCatches() throws Exception {
+        // Zone z = loadZone("test-zone.com.");
+        dnsServer.setResolver(null);
+        dnsServer.setCache(new ZoneCache("test-zone.com."));
+        // dnsServer.setLookupper(new ZoneLookupper(z));
+        Collection<String> res = dnsServer.findMXRecords("test-zone.com.");
+        try {
+            res.add(new String());
+            fail("MX Collection should not be modifiable");
+        } catch (UnsupportedOperationException e) {
+        }
+        assertEquals(1,res.size());
+        assertEquals("mail.test-zone.com.",res.iterator().next());
+    }
+
+    public void testCNAMEasMXrecords() throws Exception {
+        // Zone z = loadZone("brandilyncollins.com.");
+        dnsServer.setResolver(null);
+        dnsServer.setCache(new ZoneCache("brandilyncollins.com."));
+        // dnsServer.setLookupper(new ZoneLookupper(z));
+        Iterator<HostAddress> records = dnsServer.getSMTPHostAddresses("brandilyncollins.com.");
+        assertEquals(true, records.hasNext());
+    }
+
+    protected void setUp() throws Exception {
+        dnsServer = new TestableDNSServer();
+        DefaultConfigurationBuilder db = new DefaultConfigurationBuilder();
+
+        db.load(new ByteArrayInputStream("<dnsserver><autodiscover>true</autodiscover><authoritative>false</authoritative></dnsserver>".getBytes()));
+        dnsServer.setLog(new SimpleLog("MockLog"));
+        dnsServer.configure(db);
+        dnsServer.init();
+        
+        
+        defaultCache = Lookup.getDefaultCache(DClass.IN);
+        defaultResolver = Lookup.getDefaultResolver();
+        defaultSearchPaths = Lookup.getDefaultSearchPath();
+        Lookup.setDefaultCache(null, DClass.IN);
+        Lookup.setDefaultResolver(null);
+        Lookup.setDefaultSearchPath(new Name[] {});
+    }
+
+    protected void tearDown() throws Exception {
+        dnsServer.setCache(null);
+        dnsServer = null;
+        Lookup.setDefaultCache(defaultCache, DClass.IN);
+        Lookup.setDefaultResolver(defaultResolver);
+        Lookup.setDefaultSearchPath(defaultSearchPaths);
+    }
+
+    private Zone loadZone(String zoneName) throws IOException {
+        String zoneFilename = zoneName + "zone";
+        URL zoneResource = getClass().getResource(zoneFilename);
+        assertNotNull("test resource for zone could not be loaded: " + zoneFilename, zoneResource);
+        String zoneFile = zoneResource.getFile();
+        Zone zone = new Zone(Name.fromString(zoneName),zoneFile);
+        return zone;
+    }
+
+    private final class ZoneCache extends Cache {
+
+        Zone z = null;
+        
+        public ZoneCache(String string) throws IOException {
+            z = loadZone(string);
+        }
+
+        public SetResponse addMessage(Message arg0) {
+            throw new UnsupportedOperationException("ZoneCache is a mock used only for testing purpose");
+        }
+
+        public synchronized void addNegative(Name arg0, int arg1, SOARecord arg2, int arg3) {
+            throw new UnsupportedOperationException("ZoneCache is a mock used only for testing purpose");
+        }
+
+        public synchronized void addRecord(Record arg0, int arg1, Object arg2) {
+            throw new UnsupportedOperationException("ZoneCache is a mock used only for testing purpose");
+        }
+
+        public synchronized void addRRset(RRset arg0, int arg1) {
+            throw new UnsupportedOperationException("ZoneCache is a mock used only for testing purpose");
+        }
+
+        public synchronized void clearCache() {
+            throw new UnsupportedOperationException("ZoneCache is a mock used only for testing purpose");
+        }
+
+        public RRset[] findAnyRecords(Name arg0, int arg1) {
+            throw new UnsupportedOperationException("ZoneCache is a mock used only for testing purpose");
+        }
+
+        public RRset[] findRecords(Name arg0, int arg1) {
+            throw new UnsupportedOperationException("ZoneCache is a mock used only for testing purpose");
+        }
+
+        public void flushName(Name arg0) {
+            throw new UnsupportedOperationException("ZoneCache is a mock used only for testing purpose");
+        }
+
+        public void flushSet(Name arg0, int arg1) {
+            throw new UnsupportedOperationException("ZoneCache is a mock used only for testing purpose");
+        }
+
+        public int getDClass() {
+            throw new UnsupportedOperationException("ZoneCache is a mock used only for testing purpose");
+        }
+
+        public int getMaxCache() {
+            throw new UnsupportedOperationException("ZoneCache is a mock used only for testing purpose");
+        }
+
+        public int getMaxEntries() {
+            throw new UnsupportedOperationException("ZoneCache is a mock used only for testing purpose");
+        }
+
+        public int getMaxNCache() {
+            throw new UnsupportedOperationException("ZoneCache is a mock used only for testing purpose");
+        }
+
+        public int getSize() {
+            throw new UnsupportedOperationException("ZoneCache is a mock used only for testing purpose");
+        }
+
+        protected synchronized SetResponse lookup(Name arg0, int arg1, int arg2) {
+            throw new UnsupportedOperationException("ZoneCache is a mock used only for testing purpose");
+        }
+
+        public SetResponse lookupRecords(Name arg0, int arg1, int arg2) {
+            System.out.println("Cache.lookupRecords "+arg0+","+arg1+","+arg2);
+            return z.findRecords(arg0, arg1);
+            //return super.lookupRecords(arg0, arg1, arg2);
+        }
+
+        public void setCleanInterval(int arg0) {
+            throw new UnsupportedOperationException("ZoneCache is a mock used only for testing purpose");
+        }
+
+        public void setMaxCache(int arg0) {
+            throw new UnsupportedOperationException("ZoneCache is a mock used only for testing purpose");
+        }
+
+        public void setMaxEntries(int arg0) {
+            throw new UnsupportedOperationException("ZoneCache is a mock used only for testing purpose");
+        }
+
+        public void setMaxNCache(int arg0) {
+            throw new UnsupportedOperationException("ZoneCache is a mock used only for testing purpose");
+        }
+    }
+
+    private final class TestableDNSServer extends DNSServer {
+        
+        public void setResolver(Resolver r) {
+            resolver = r;
+        }
+
+        public Resolver getResolver() {
+            return resolver;
+        }
+        
+        public void setCache(Cache c) {
+            cache = c;
+        }
+        
+        public Cache getCache() {
+            return cache;
+        }
+    }
+
+}

Propchange: james/server/trunk/dnsserver/src/test/java/org/apache/james/dnsserver/DNSServerTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/brandilyncollins.com.zone
URL: http://svn.apache.org/viewvc/james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/brandilyncollins.com.zone?rev=955234&view=auto
==============================================================================
--- james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/brandilyncollins.com.zone (added)
+++ james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/brandilyncollins.com.zone Wed Jun 16 14:07:35 2010
@@ -0,0 +1,5 @@
+brandilyncollins.com.		14440	IN	SOA	ns1.hyperdrivedns.net. admin.hyperdrivedns.com. 2004121207 14400 7200 3600000 86400
+brandilyncollins.com.		14400	IN	MX	0 mail.brandilyncollins.com.
+brandilyncollins.com.		14400	IN	NS	ns1.hyperdrivedns.net.
+mail.brandilyncollins.com.	14400	IN	CNAME	mail2.brandilyncollins.com.
+mail2.brandilyncollins.com.	14400	IN	A	201.202.203.204

Propchange: james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/brandilyncollins.com.zone
------------------------------------------------------------------------------
    svn:eol-style = native

Added: james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/dnstest.com.zone
URL: http://svn.apache.org/viewvc/james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/dnstest.com.zone?rev=955234&view=auto
==============================================================================
--- james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/dnstest.com.zone (added)
+++ james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/dnstest.com.zone Wed Jun 16 14:07:35 2010
@@ -0,0 +1,14 @@
+dnstest.com.		14440	IN	SOA	ns1.hyperdrivedns.net. admin.hyperdrivedns.com. 2004121207 14400 7200 3600000 86400
+dnstest.com.		14400	IN	MX	0 dnstest.com.inbound.mxlogic.net.
+dnstest.com.		14400	IN	NS	ns1.hyperdrivedns.net.
+dnstest.com.		14400	IN	A	204.11.236.232
+mail.dnstest.com.		14400	IN	A	216.183.119.114
+hk.dnstest.com.		14400	IN	A	204.11.236.232
+www.dnstest.com.		14400	IN	CNAME	dnstest.com.
+nomx.dnstest.com.   14400 IN  A  204.12.234.32
+badmx.dnstest.com.	14400 IN  MX  0 badhost.dnstest.com.
+localhost.dnstest.com.	14400	IN	A	127.0.0.1
+www.pluto.dnstest.com.	14400	IN	A	204.11.236.232
+pluto.dnstest.com.	14400	IN	A	204.11.236.232
+www.hk.dnstest.com.	14400	IN	A	204.11.236.232
+ftp.dnstest.com.		14400	IN	A	204.11.236.232

Added: james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/pippo.com.zone
URL: http://svn.apache.org/viewvc/james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/pippo.com.zone?rev=955234&view=auto
==============================================================================
--- james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/pippo.com.zone (added)
+++ james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/pippo.com.zone Wed Jun 16 14:07:35 2010
@@ -0,0 +1,12 @@
+pippo.com.		14440	IN	SOA	ns1.hyperdrivedns.net. admin.hyperdrivedns.com. 2004121207 14400 7200 3600000 86400
+pippo.com.		14400	IN	MX	0 pippo.com.inbound.mxlogic.net.
+pippo.com.		14400	IN	NS	ns1.hyperdrivedns.net.
+pippo.com.		14400	IN	A	204.11.236.232
+mail.pippo.com.		14400	IN	A	216.183.119.114
+hk.pippo.com.		14400	IN	A	204.11.236.232
+www.pippo.com.		14400	IN	CNAME	pippo.com.
+localhost.pippo.com.	14400	IN	A	127.0.0.1
+www.pluto.pippo.com.	14400	IN	A	204.11.236.232
+pluto.pippo.com.	14400	IN	A	204.11.236.232
+www.hk.pippo.com.	14400	IN	A	204.11.236.232
+ftp.pippo.com.		14400	IN	A	204.11.236.232

Propchange: james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/pippo.com.zone
------------------------------------------------------------------------------
    svn:eol-style = native

Added: james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/test-zone.com.zone
URL: http://svn.apache.org/viewvc/james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/test-zone.com.zone?rev=955234&view=auto
==============================================================================
--- james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/test-zone.com.zone (added)
+++ james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/test-zone.com.zone Wed Jun 16 14:07:35 2010
@@ -0,0 +1,5 @@
+test-zone.com.		14440	IN	SOA	ns1.hyperdrivedns.net. admin.hyperdrivedns.com. 2004121207 14400 7200 3600000 86400
+test-zone.com.		14400	IN	CNAME	alt.test-zone.com.
+alt.test-zone.com.	14400	IN	MX	10	mail.test-zone.com.
+test-zone.com.		14400	IN	NS	ns1.hyperdrivedns.net.
+mail.test-zone.com.	14400	IN	A	201.202.203.204

Propchange: james/server/trunk/dnsserver/src/test/resources/org/apache/james/dnsserver/test-zone.com.zone
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: james/server/trunk/pom.xml
URL: http://svn.apache.org/viewvc/james/server/trunk/pom.xml?rev=955234&r1=955233&r2=955234&view=diff
==============================================================================
--- james/server/trunk/pom.xml (original)
+++ james/server/trunk/pom.xml Wed Jun 16 14:07:35 2010
@@ -53,6 +53,7 @@
     <module>spring-deployment</module>
     <module>netty-socket</module>
     <module>spring-common</module>
+    <module>dnsserver</module>
   </modules>
   <ciManagement>
     <system>hudson</system>
@@ -208,6 +209,11 @@
     <dependencies>
     <dependency>
       <groupId>org.apache.james</groupId>
+      <artifactId>james-server-dnsserver</artifactId>
+      <version>${pom.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.james</groupId>
       <artifactId>james-server-spring-common</artifactId>
       <version>${pom.version}</version>
     </dependency>

Modified: james/server/trunk/spring-common/pom.xml
URL: http://svn.apache.org/viewvc/james/server/trunk/spring-common/pom.xml?rev=955234&r1=955233&r2=955234&view=diff
==============================================================================
--- james/server/trunk/spring-common/pom.xml (original)
+++ james/server/trunk/spring-common/pom.xml Wed Jun 16 14:07:35 2010
@@ -30,19 +30,6 @@
   <build>
     <plugins>
       <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile> 
-            <manifest>
-              <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
-              <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
-            </manifest>
-          </archive>
-        </configuration>
-      </plugin>
-      <plugin>
         <artifactId>maven-jar-plugin</artifactId>
          <configuration>       
            <archive>
@@ -51,11 +38,15 @@
               <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
               <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
             </manifest>
+            <manifestEntries>
+              <Fragment-Host>org.springframework.osgi.extender</Fragment-Host>
+           </manifestEntries>
           </archive>
         </configuration>       
         <executions>
           <execution>
             <goals>
+              <goal>jar</goal>
               <goal>test-jar</goal>
             </goals>
           </execution>

Added: james/server/trunk/spring-common/src/main/java/org/apache/james/container/spring/lifecycle/OsgiSpringConfigurationRegistry.java
URL: http://svn.apache.org/viewvc/james/server/trunk/spring-common/src/main/java/org/apache/james/container/spring/lifecycle/OsgiSpringConfigurationRegistry.java?rev=955234&view=auto
==============================================================================
--- james/server/trunk/spring-common/src/main/java/org/apache/james/container/spring/lifecycle/OsgiSpringConfigurationRegistry.java (added)
+++ james/server/trunk/spring-common/src/main/java/org/apache/james/container/spring/lifecycle/OsgiSpringConfigurationRegistry.java Wed Jun 16 14:07:35 2010
@@ -0,0 +1,32 @@
+/****************************************************************
+ * 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.container.spring.lifecycle;
+
+public class OsgiSpringConfigurationRegistry extends SpringConfigurationRegistry{
+
+	/**
+	 * Load configuration from classpath.
+	 * 
+	 * TODO: We should allow to specify a different config location via osgi configuration service
+	 */
+	protected String getConfigPrefix() {
+		return "classpath:";
+	}
+
+}

Added: james/server/trunk/spring-common/src/main/java/org/apache/james/container/spring/lifecycle/OsgiSpringLogRegistry.java
URL: http://svn.apache.org/viewvc/james/server/trunk/spring-common/src/main/java/org/apache/james/container/spring/lifecycle/OsgiSpringLogRegistry.java?rev=955234&view=auto
==============================================================================
--- james/server/trunk/spring-common/src/main/java/org/apache/james/container/spring/lifecycle/OsgiSpringLogRegistry.java (added)
+++ james/server/trunk/spring-common/src/main/java/org/apache/james/container/spring/lifecycle/OsgiSpringLogRegistry.java Wed Jun 16 14:07:35 2010
@@ -0,0 +1,34 @@
+/****************************************************************
+ * 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.container.spring.lifecycle;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class OsgiSpringLogRegistry extends SpringLogRegistry{
+
+	/**
+	 * Use {@link LogFactory} to create a Log
+	 */
+	protected Log createLog(String loggerName) {
+		return LogFactory.getLog(loggerName);
+	}
+
+	
+}

Modified: james/server/trunk/spring-common/src/main/java/org/apache/james/container/spring/lifecycle/SpringConfigurationRegistry.java
URL: http://svn.apache.org/viewvc/james/server/trunk/spring-common/src/main/java/org/apache/james/container/spring/lifecycle/SpringConfigurationRegistry.java?rev=955234&r1=955233&r2=955234&view=diff
==============================================================================
--- james/server/trunk/spring-common/src/main/java/org/apache/james/container/spring/lifecycle/SpringConfigurationRegistry.java (original)
+++ james/server/trunk/spring-common/src/main/java/org/apache/james/container/spring/lifecycle/SpringConfigurationRegistry.java Wed Jun 16 14:07:35 2010
@@ -66,6 +66,12 @@ public class SpringConfigurationRegistry
 	}
 
 
+	/**
+	 * Return the configuration prefix to load the config. In this case its
+	 * file://conf/
+	 * 
+	 * @return prefix
+	 */
 	protected String getConfigPrefix() {
 		return "file://conf/";
 	}
@@ -82,7 +88,9 @@ public class SpringConfigurationRegistry
     private XMLConfiguration getConfig(Resource r) throws ConfigurationException, IOException {
         XMLConfiguration config = new XMLConfiguration();
         config.setDelimiterParsingDisabled(true);
-        config.load(r.getFile());
+        
+        // Use InputStream so we are not bound to File implementations of the config
+        config.load(r.getInputStream());
         return config;
     }
 

Modified: james/server/trunk/spring-common/src/main/java/org/apache/james/container/spring/lifecycle/SpringLogRegistry.java
URL: http://svn.apache.org/viewvc/james/server/trunk/spring-common/src/main/java/org/apache/james/container/spring/lifecycle/SpringLogRegistry.java?rev=955234&r1=955233&r2=955234&view=diff
==============================================================================
--- james/server/trunk/spring-common/src/main/java/org/apache/james/container/spring/lifecycle/SpringLogRegistry.java (original)
+++ james/server/trunk/spring-common/src/main/java/org/apache/james/container/spring/lifecycle/SpringLogRegistry.java Wed Jun 16 14:07:35 2010
@@ -47,10 +47,20 @@ public class SpringLogRegistry implement
 	    if (log != null) {
 	        return log;
 	    } else {
-	        return new Log4JLogger(PREFIX + componentname);
+	        return createLog(PREFIX + componentname);
 	    }
 	}
 	
+	/**
+	 * Use {@link Log4JLogger} to create the Log
+	 * 
+	 * @param loggerName
+	 * @return log
+	 */
+	protected Log createLog(String loggerName) {
+		return new Log4JLogger(loggerName);
+	}
+	
 	/*
 	 * (non-Javadoc)
 	 * @see org.apache.james.container.spring.Registry#registerForComponent(java.lang.String, java.lang.Object)

Added: james/server/trunk/spring-common/src/main/resources/META-INF/spring/extender/extender.xml
URL: http://svn.apache.org/viewvc/james/server/trunk/spring-common/src/main/resources/META-INF/spring/extender/extender.xml?rev=955234&view=auto
==============================================================================
--- james/server/trunk/spring-common/src/main/resources/META-INF/spring/extender/extender.xml (added)
+++ james/server/trunk/spring-common/src/main/resources/META-INF/spring/extender/extender.xml Wed Jun 16 14:07:35 2010
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+    <!--
+        ! 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. !
+    -->
+
+
+<beans xmlns="http://www.springframework.org/schema/beans" 
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util" 
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">
+          
+    <util:properties id="extenderProperties"> <prop key="process.annotations">true</prop> </util:properties>
+     <!--     
+    <bean class="org.apache.james.container.spring.lifecycle.CommonsConfigurableBeanPostProcessor">
+        <property name="configurationRegistry" ref="configurationRegistry" />
+        <property name="order" value="1" />
+    </bean>
+
+    <bean id="configurationRegistry" class="org.apache.james.container.spring.lifecycle.SpringConfigurationRegistry">
+
+        <property name="configurationMappings">
+            <map>
+                <entry key="mailboxmanager" value="imapserver" />
+                <entry key="mailetcontext" value="James"/>
+                <entry key="smtpProtocolHandlerChain" value="smtpserver"/>
+                <entry key="pop3ProtocolHandlerChain" value="pop3server"/>
+                <entry key="remoteProtocolHandlerChain" value="remotemanager"/>
+                <entry key="spool" value="spoolmanager"/>
+                <entry key="mailserver" value="James"/>	
+            </map>
+        </property>
+
+    </bean>
+
+    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+        <property name = "location" value="file://conf/database.properties"/>
+    </bean>
+
+
+    <bean class="org.apache.james.container.spring.lifecycle.LogEnabledBeanPostProcessor">
+        <property name="logRegistry" ref="logRegistry" />
+        <property name="order" value="0" />
+    </bean>
+
+    <bean id="logRegistry" class="org.apache.james.container.spring.lifecycle.SpringLogRegistry">
+            <property name="logMappings">
+            <map>
+                <entry key="smtpProtocolHandlerChain" value="smtpserver"/>
+                <entry key="pop3ProtocolHandlerChain" value="pop3server"/>
+                <entry key="remoteProtocolHandlerChain" value="remoteManager"/>
+                <entry key="spool" value="spoolmanager"/>
+                <entry key="mailserver" value="James"/>
+                <entry key="poster" value="James"/>
+            </map>
+        </property>
+    </bean>
+
+    <bean class= "org.springframework.context.annotation.CommonAnnotationBeanPostProcessor">
+        <property name="order" value="3" />
+    </bean>
+    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
+    -->
+</beans>
\ No newline at end of file

Modified: james/server/trunk/spring-deployment/pom.xml
URL: http://svn.apache.org/viewvc/james/server/trunk/spring-deployment/pom.xml?rev=955234&r1=955233&r2=955234&view=diff
==============================================================================
--- james/server/trunk/spring-deployment/pom.xml (original)
+++ james/server/trunk/spring-deployment/pom.xml Wed Jun 16 14:07:35 2010
@@ -403,7 +403,11 @@
       <groupId>log4j</groupId>
       <artifactId>log4j</artifactId>
     </dependency>
-
+    <dependency>
+      <groupId>org.apache.james</groupId>
+      <artifactId>james-server-dnsserver</artifactId>
+      <scope>runtime</scope>
+    </dependency>
     <dependency>
       <groupId>org.apache.james</groupId>
       <artifactId>james-server-netty-socket</artifactId>



---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org