http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/data/data-jdbc/src/main/java/org/apache/james/user/jdbc/AbstractJdbcUsersRepository.java
----------------------------------------------------------------------
diff --git 
a/server/data/data-jdbc/src/main/java/org/apache/james/user/jdbc/AbstractJdbcUsersRepository.java
 
b/server/data/data-jdbc/src/main/java/org/apache/james/user/jdbc/AbstractJdbcUsersRepository.java
index 8fc86c4..c6c02eb 100644
--- 
a/server/data/data-jdbc/src/main/java/org/apache/james/user/jdbc/AbstractJdbcUsersRepository.java
+++ 
b/server/data/data-jdbc/src/main/java/org/apache/james/user/jdbc/AbstractJdbcUsersRepository.java
@@ -96,25 +96,25 @@ import org.slf4j.LoggerFactory;
 public abstract class AbstractJdbcUsersRepository extends 
AbstractJamesUsersRepository {
     private static final Logger LOGGER = 
LoggerFactory.getLogger(AbstractJdbcUsersRepository.class);
 
-    protected Map<String, String> m_sqlParameters;
+    protected Map<String, String> sqlParameters;
 
-    private String m_sqlFileName;
+    private String sqlFileName;
 
-    private DataSource m_datasource;
+    private DataSource datasource;
 
     // Fetches all Users from the db.
-    private String m_getUsersSql;
+    private String getUsersSql;
 
     // This fetch a user by name, ensuring case-insensitive matching.
-    private String m_userByNameCaseInsensitiveSql;
+    private String userByNameCaseInsensitiveSql;
 
     // Insert, update and delete sql statements are not guaranteed
     // to be case-insensitive; this is handled in code.
-    private String m_insertUserSql;
+    private String insertUserSql;
 
-    private String m_updateUserSql;
+    private String updateUserSql;
 
-    private String m_deleteUserSql;
+    private String deleteUserSql;
 
     // The JDBCUtil helper class
     private JDBCUtil theJDBCUtil;
@@ -213,12 +213,12 @@ public abstract class AbstractJdbcUsersRepository extends 
AbstractJamesUsersRepo
     /**
      * Set the DataSourceSelector
      * 
-     * @param m_datasource
+     * @param datasource
      *            the DataSourceSelector
      */
     @Inject
-    public void setDatasource(DataSource m_datasource) {
-        this.m_datasource = m_datasource;
+    public void setDatasource(DataSource datasource) {
+        this.datasource = datasource;
     }
 
     /**
@@ -259,31 +259,31 @@ public abstract class AbstractJdbcUsersRepository extends 
AbstractJamesUsersRepo
             InputStream sqlFile;
 
             try {
-                sqlFile = fileSystem.getResource(m_sqlFileName);
+                sqlFile = fileSystem.getResource(sqlFileName);
             } catch (Exception e) {
                 LOGGER.error(e.getMessage(), e);
                 throw e;
             }
 
-            LOGGER.debug("Reading SQL resources from: {}, section {}.", 
m_sqlFileName, getClass().getName());
+            LOGGER.debug("Reading SQL resources from: {}, section {}.", 
sqlFileName, getClass().getName());
 
             SqlResources sqlStatements = new SqlResources();
-            sqlStatements.init(sqlFile, this.getClass().getName(), conn, 
m_sqlParameters);
+            sqlStatements.init(sqlFile, this.getClass().getName(), conn, 
sqlParameters);
 
             // Create the SQL Strings to use for this table.
             // Fetches all Users from the db.
-            m_getUsersSql = sqlStatements.getSqlString("select", true);
+            getUsersSql = sqlStatements.getSqlString("select", true);
 
             // Get a user by lowercase name. (optional)
             // If not provided, the entire list is iterated to find a user.
-            m_userByNameCaseInsensitiveSql = 
sqlStatements.getSqlString("selectByLowercaseName");
+            userByNameCaseInsensitiveSql = 
sqlStatements.getSqlString("selectByLowercaseName");
 
             // Insert, update and delete are not guaranteed to be
             // case-insensitive
             // Will always be called with correct case in username..
-            m_insertUserSql = sqlStatements.getSqlString("insert", true);
-            m_updateUserSql = sqlStatements.getSqlString("update", true);
-            m_deleteUserSql = sqlStatements.getSqlString("delete", true);
+            insertUserSql = sqlStatements.getSqlString("insert", true);
+            updateUserSql = sqlStatements.getSqlString("update", true);
+            deleteUserSql = sqlStatements.getSqlString("delete", true);
 
             // Creates a single table with "username" the Primary Key.
             String createUserTableSql = 
sqlStatements.getSqlString("createTable", true);
@@ -373,15 +373,15 @@ public abstract class AbstractJdbcUsersRepository extends 
AbstractJamesUsersRepo
         }
 
         // Build SqlParameters and get datasource name from URL parameters
-        m_sqlParameters = new HashMap<>();
+        sqlParameters = new HashMap<>();
         switch (urlParams.size()) {
         case 3:
-            m_sqlParameters.put("key", urlParams.get(2));
-            m_sqlParameters.put("table", urlParams.get(1));
+            sqlParameters.put("key", urlParams.get(2));
+            sqlParameters.put("table", urlParams.get(1));
             urlParams.get(0);
             break;
         case 2:
-            m_sqlParameters.put("table", urlParams.get(1));
+            sqlParameters.put("table", urlParams.get(1));
             urlParams.get(0);
             break;
         case 1:
@@ -391,10 +391,10 @@ public abstract class AbstractJdbcUsersRepository extends 
AbstractJamesUsersRepo
             throw new ConfigurationException("Malformed destinationURL - " + 
"Must be of the format \"db://<data-source>[/<table>[/<key>]]\".");
         }
 
-        LOGGER.debug("Parsed URL: table = '{}', key = '{}'", 
m_sqlParameters.get("table"), m_sqlParameters.get("key"));
+        LOGGER.debug("Parsed URL: table = '{}', key = '{}'", 
sqlParameters.get("table"), sqlParameters.get("key"));
 
         // Get the SQL file location
-        m_sqlFileName = configuration.getString("sqlFile", null);
+        sqlFileName = configuration.getString("sqlFile", null);
 
         // Get other sql parameters from the configuration object,
         // if any.
@@ -403,7 +403,7 @@ public abstract class AbstractJdbcUsersRepository extends 
AbstractJamesUsersRepo
             String rawName = paramIt.next();
             String paramName = 
paramIt.next().substring("sqlParameters.[@".length(), rawName.length() - 1);
             String paramValue = configuration.getString(rawName);
-            m_sqlParameters.put(paramName, paramValue);
+            sqlParameters.put(paramName, paramValue);
         }
     }
 
@@ -448,7 +448,7 @@ public abstract class AbstractJdbcUsersRepository extends 
AbstractJamesUsersRepo
         try {
             conn = openConnection();
             // Get a ResultSet containing all users.
-            getUsersStatement = conn.prepareStatement(m_getUsersSql);
+            getUsersStatement = conn.prepareStatement(getUsersSql);
             rsUsers = getUsersStatement.executeQuery();
 
             // Loop through and build a User for every row.
@@ -483,7 +483,7 @@ public abstract class AbstractJdbcUsersRepository extends 
AbstractJamesUsersRepo
         try {
             conn = openConnection();
             // Get a PreparedStatement for the insert.
-            addUserStatement = conn.prepareStatement(m_insertUserSql);
+            addUserStatement = conn.prepareStatement(insertUserSql);
 
             setUserForInsertStatement(user, addUserStatement);
 
@@ -513,7 +513,7 @@ public abstract class AbstractJdbcUsersRepository extends 
AbstractJamesUsersRepo
         // Delete from the database.
         try {
             conn = openConnection();
-            removeUserStatement = conn.prepareStatement(m_deleteUserSql);
+            removeUserStatement = conn.prepareStatement(deleteUserSql);
             removeUserStatement.setString(1, username);
             removeUserStatement.execute();
         } catch (SQLException sqlExc) {
@@ -538,7 +538,7 @@ public abstract class AbstractJdbcUsersRepository extends 
AbstractJamesUsersRepo
         // Update the database.
         try {
             conn = openConnection();
-            updateUserStatement = conn.prepareStatement(m_updateUserSql);
+            updateUserStatement = conn.prepareStatement(updateUserSql);
             setUserForUpdateStatement(user, updateUserStatement);
             updateUserStatement.execute();
         } catch (SQLException sqlExc) {
@@ -592,7 +592,7 @@ public abstract class AbstractJdbcUsersRepository extends 
AbstractJamesUsersRepo
     protected User getUserByName(String name, boolean ignoreCase) throws 
UsersRepositoryException {
         // See if this statement has been set, if not, use
         // simple superclass method.
-        if (m_userByNameCaseInsensitiveSql == null) {
+        if (userByNameCaseInsensitiveSql == null) {
             return getUserByNameIterating(name, ignoreCase);
         }
 
@@ -604,7 +604,7 @@ public abstract class AbstractJdbcUsersRepository extends 
AbstractJamesUsersRepo
         try {
             conn = openConnection();
             // Get a ResultSet containing all users.
-            String sql = m_userByNameCaseInsensitiveSql;
+            String sql = userByNameCaseInsensitiveSql;
             getUsersStatement = conn.prepareStatement(sql);
 
             getUsersStatement.setString(1, name.toLowerCase(Locale.US));
@@ -688,7 +688,7 @@ public abstract class AbstractJdbcUsersRepository extends 
AbstractJamesUsersRepo
      * @throws SQLException
      */
     private Connection openConnection() throws SQLException {
-        return m_datasource.getConnection();
+        return datasource.getConnection();
 
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/ReadOnlyLDAPUser.java
----------------------------------------------------------------------
diff --git 
a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/ReadOnlyLDAPUser.java
 
b/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/ReadOnlyLDAPUser.java
index 3f35362..9156016 100644
--- 
a/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/ReadOnlyLDAPUser.java
+++ 
b/server/data/data-ldap/src/main/java/org/apache/james/user/ldap/ReadOnlyLDAPUser.java
@@ -54,18 +54,18 @@ public class ReadOnlyLDAPUser implements User, Serializable 
{
      * <code>&quot;myorg.com&quot;</code>, the user's email address will be
      * <code>&quot;john.bold&#64;myorg.com&quot;</code>.
      */
-    private String _userName;
+    private String userName;
 
     /**
      * The distinguished name of the user-record in the LDAP directory.
      */
-    private String _userDN;
+    private String userDN;
 
     /**
      * The context for the LDAP server from which to retrieve the
      * user's details.
      */
-    private LdapContext _ldapContext = null;
+    private LdapContext ldapContext = null;
 
     /**
      * Creates a new instance of ReadOnlyLDAPUser.
@@ -95,9 +95,9 @@ public class ReadOnlyLDAPUser implements User, Serializable {
      */
     public ReadOnlyLDAPUser(String userName, String userDN, LdapContext 
ldapContext) {
         this();
-        _userName = userName;
-        _userDN = userDN;
-        _ldapContext = ldapContext;
+        this.userName = userName;
+        this.userDN = userDN;
+        this.ldapContext = ldapContext;
     }
 
     /**
@@ -108,7 +108,7 @@ public class ReadOnlyLDAPUser implements User, Serializable 
{
      * @return The user's identifier or name.
      */
     public String getUserName() {
-        return _userName;
+        return userName;
     }
 
     /**
@@ -138,10 +138,10 @@ public class ReadOnlyLDAPUser implements User, 
Serializable {
         boolean result = false;
         LdapContext ldapContext = null;
         try {
-            ldapContext = _ldapContext.newInstance(null);
+            ldapContext = this.ldapContext.newInstance(null);
             ldapContext.addToEnvironment(Context.SECURITY_AUTHENTICATION,
                     LdapConstants.SECURITY_AUTHENTICATION_SIMPLE);
-            ldapContext.addToEnvironment(Context.SECURITY_PRINCIPAL, _userDN);
+            ldapContext.addToEnvironment(Context.SECURITY_PRINCIPAL, userDN);
             ldapContext.addToEnvironment(Context.SECURITY_CREDENTIALS, 
password);
             ldapContext.reconnect(null);
             result = true;

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/data/data-library/src/main/java/org/apache/james/repository/file/AbstractFileRepository.java
----------------------------------------------------------------------
diff --git 
a/server/data/data-library/src/main/java/org/apache/james/repository/file/AbstractFileRepository.java
 
b/server/data/data-library/src/main/java/org/apache/james/repository/file/AbstractFileRepository.java
index bd8a81c..bd2dfa1 100644
--- 
a/server/data/data-library/src/main/java/org/apache/james/repository/file/AbstractFileRepository.java
+++ 
b/server/data/data-library/src/main/java/org/apache/james/repository/file/AbstractFileRepository.java
@@ -54,13 +54,13 @@ public abstract class AbstractFileRepository implements 
Repository, Configurable
 
     protected static final char[] HEX_DIGITS = new char[] { '0', '1', '2', 
'3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
 
-    protected String m_extension;
+    protected String extension;
 
-    protected String m_name;
+    protected String name;
 
-    protected FilenameFilter m_filter;
+    protected FilenameFilter filter;
 
-    protected File m_baseDirectory;
+    protected File baseDirectory;
 
     private FileSystem fileSystem;
 
@@ -85,37 +85,37 @@ public abstract class AbstractFileRepository implements 
Repository, Configurable
         File directory;
 
         try {
-            directory = m_baseDirectory.getCanonicalFile();
+            directory = baseDirectory.getCanonicalFile();
         } catch (IOException ioe) {
-            throw new ConfigurationException("Unable to form canonical 
representation of " + m_baseDirectory);
+            throw new ConfigurationException("Unable to form canonical 
representation of " + baseDirectory);
         }
 
-        m_name = "Repository";
-        String m_postfix = getExtensionDecorator();
-        m_extension = "." + m_name + m_postfix;
-        m_filter = new ExtensionFileFilter(m_extension);
+        name = "Repository";
+        String postfix = getExtensionDecorator();
+        extension = "." + name + postfix;
+        filter = new ExtensionFileFilter(extension);
         // m_filter = new 
NumberedRepositoryFileFilter(getExtensionDecorator());
 
         FileUtils.forceMkdir(directory);
 
-        LOGGER.info("{} opened in {}", getClass().getName(), m_baseDirectory);
+        LOGGER.info("{} opened in {}", getClass().getName(), baseDirectory);
 
         // We will look for all numbered repository files in this
         // directory and rename them to non-numbered repositories,
         // logging all the way.
 
-        FilenameFilter num_filter = new 
NumberedRepositoryFileFilter(getExtensionDecorator());
-        final String[] names = directory.list(num_filter);
+        FilenameFilter numFilter = new 
NumberedRepositoryFileFilter(getExtensionDecorator());
+        final String[] names = directory.list(numFilter);
 
         for (String origFilename : names) {
             // This needs to handle (skip over) the possible repository
             // numbers
-            int pos = origFilename.length() - m_postfix.length();
+            int pos = origFilename.length() - postfix.length();
             while (pos >= 1 && Character.isDigit(origFilename.charAt(pos - 
1))) {
                 pos--;
             }
-            pos -= ".".length() + m_name.length();
-            String newFilename = origFilename.substring(0, pos) + m_extension;
+            pos -= ".".length() + name.length();
+            String newFilename = origFilename.substring(0, pos) + extension;
 
             File origFile = new File(directory, origFilename);
             File newFile = new File(directory, newFilename);
@@ -145,7 +145,7 @@ public abstract class AbstractFileRepository implements 
Repository, Configurable
         }
 
         try {
-            m_baseDirectory = fileSystem.getFile(destination);
+            baseDirectory = fileSystem.getFile(destination);
         } catch (FileNotFoundException e) {
             throw new ConfigurationException("Unable to acces destination " + 
destination, e);
         }
@@ -177,7 +177,7 @@ public abstract class AbstractFileRepository implements 
Repository, Configurable
         child.setFileSystem(fileSystem);
 
         try {
-            child.setDestination(m_baseDirectory.getAbsolutePath() + 
File.pathSeparatorChar + childName + File.pathSeparator);
+            child.setDestination(baseDirectory.getAbsolutePath() + 
File.pathSeparatorChar + childName + File.pathSeparator);
         } catch (ConfigurationException ce) {
             throw new RuntimeException("Cannot set destination for child child 
" + "repository " + childName + " : " + ce);
         }
@@ -189,7 +189,7 @@ public abstract class AbstractFileRepository implements 
Repository, Configurable
         }
 
         if (DEBUG && LOGGER.isDebugEnabled()) {
-            LOGGER.debug("Child repository of " + m_name + " created in " + 
m_baseDirectory + File.pathSeparatorChar + childName + File.pathSeparator);
+            LOGGER.debug("Child repository of " + name + " created in " + 
baseDirectory + File.pathSeparatorChar + childName + File.pathSeparator);
         }
 
         return child;
@@ -282,8 +282,8 @@ public abstract class AbstractFileRepository implements 
Repository, Configurable
      * Returns the list of used keys.
      */
     public Iterator<String> list() {
-        final File storeDir = new File(m_baseDirectory.getAbsolutePath());
-        final String[] names = storeDir.list(m_filter);
+        final File storeDir = new File(baseDirectory.getAbsolutePath());
+        final String[] names = storeDir.list(filter);
 
         return Arrays.stream(names)
             .map(this::decode)
@@ -313,10 +313,10 @@ public abstract class AbstractFileRepository implements 
Repository, Configurable
         }
 
         StringBuilder result = new StringBuilder();
-        result.append(m_baseDirectory.getAbsolutePath());
+        result.append(baseDirectory.getAbsolutePath());
         result.append(File.separator);
         result.append(buffer);
-        result.append(m_extension);
+        result.append(extension);
         return result.toString();
     }
 
@@ -330,7 +330,7 @@ public abstract class AbstractFileRepository implements 
Repository, Configurable
      * @return key a String which can be used to retrieve the filename
      */
     protected String decode(String filename) {
-        filename = filename.substring(0, filename.length() - 
m_extension.length());
+        filename = filename.substring(0, filename.length() - 
extension.length());
         final int size = filename.length();
         final byte[] bytes = new byte[size >>> 1];
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/data/data-library/src/main/java/org/apache/james/repository/file/ClassLoaderObjectInputStream.java
----------------------------------------------------------------------
diff --git 
a/server/data/data-library/src/main/java/org/apache/james/repository/file/ClassLoaderObjectInputStream.java
 
b/server/data/data-library/src/main/java/org/apache/james/repository/file/ClassLoaderObjectInputStream.java
index c7c256c..4bda697 100644
--- 
a/server/data/data-library/src/main/java/org/apache/james/repository/file/ClassLoaderObjectInputStream.java
+++ 
b/server/data/data-library/src/main/java/org/apache/james/repository/file/ClassLoaderObjectInputStream.java
@@ -29,15 +29,15 @@ import java.io.ObjectStreamClass;
  * Avalon components that are juggling many classloaders.
  */
 public class ClassLoaderObjectInputStream extends ObjectInputStream {
-    private final ClassLoader m_classLoader;
+    private final ClassLoader classLoader;
 
     public ClassLoaderObjectInputStream(ClassLoader classLoader, InputStream 
inputStream) throws IOException {
         super(inputStream);
-        m_classLoader = classLoader;
+        this.classLoader = classLoader;
     }
 
     protected Class<?> resolveClass(ObjectStreamClass objectStreamClass) 
throws IOException, ClassNotFoundException {
-        final Class<?> clazz = Class.forName(objectStreamClass.getName(), 
false, m_classLoader);
+        final Class<?> clazz = Class.forName(objectStreamClass.getName(), 
false, classLoader);
 
         if (null != clazz) {
             return clazz; // the classloader knows of the class

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/data/data-library/src/main/java/org/apache/james/repository/file/ExtensionFileFilter.java
----------------------------------------------------------------------
diff --git 
a/server/data/data-library/src/main/java/org/apache/james/repository/file/ExtensionFileFilter.java
 
b/server/data/data-library/src/main/java/org/apache/james/repository/file/ExtensionFileFilter.java
index 2820a4a..c052c19 100644
--- 
a/server/data/data-library/src/main/java/org/apache/james/repository/file/ExtensionFileFilter.java
+++ 
b/server/data/data-library/src/main/java/org/apache/james/repository/file/ExtensionFileFilter.java
@@ -41,17 +41,17 @@ import java.util.Arrays;
  * </pre>
  */
 public class ExtensionFileFilter implements FilenameFilter {
-    private final String[] m_extensions;
+    private final String[] extensions;
 
     public ExtensionFileFilter(String[] extensions) {
-        m_extensions = extensions;
+        this.extensions = extensions;
     }
 
     public ExtensionFileFilter(String extension) {
-        m_extensions = new String[] { extension };
+        extensions = new String[] { extension };
     }
 
     public boolean accept(File file, String name) {
-        return Arrays.stream(m_extensions).anyMatch(name::endsWith);
+        return Arrays.stream(extensions).anyMatch(name::endsWith);
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/data/data-library/src/main/java/org/apache/james/rrt/lib/RecipientRewriteTableUtil.java
----------------------------------------------------------------------
diff --git 
a/server/data/data-library/src/main/java/org/apache/james/rrt/lib/RecipientRewriteTableUtil.java
 
b/server/data/data-library/src/main/java/org/apache/james/rrt/lib/RecipientRewriteTableUtil.java
index 19a5db6..75c4054 100644
--- 
a/server/data/data-library/src/main/java/org/apache/james/rrt/lib/RecipientRewriteTableUtil.java
+++ 
b/server/data/data-library/src/main/java/org/apache/james/rrt/lib/RecipientRewriteTableUtil.java
@@ -115,18 +115,18 @@ public class RecipientRewriteTableUtil {
      * @return the substituted string
      */
     private static String substituteSubString(String input, String find, 
String replace) {
-        int find_length = find.length();
-        int replace_length = replace.length();
+        int findLength = find.length();
+        int replaceLength = replace.length();
 
         StringBuilder output = new StringBuilder(input);
         int index = input.indexOf(find);
         int outputOffset = 0;
 
         while (index > -1) {
-            output.replace(index + outputOffset, index + outputOffset + 
find_length, replace);
-            outputOffset = outputOffset + (replace_length - find_length);
+            output.replace(index + outputOffset, index + outputOffset + 
findLength, replace);
+            outputOffset = outputOffset + (replaceLength - findLength);
 
-            index = input.indexOf(find, index + find_length);
+            index = input.indexOf(find, index + findLength);
         }
 
         String result = output.toString();

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/data/data-library/src/test/java/org/apache/james/domainlist/lib/AbstractDomainListTest.java
----------------------------------------------------------------------
diff --git 
a/server/data/data-library/src/test/java/org/apache/james/domainlist/lib/AbstractDomainListTest.java
 
b/server/data/data-library/src/test/java/org/apache/james/domainlist/lib/AbstractDomainListTest.java
index 03a0555..658c68c 100644
--- 
a/server/data/data-library/src/test/java/org/apache/james/domainlist/lib/AbstractDomainListTest.java
+++ 
b/server/data/data-library/src/test/java/org/apache/james/domainlist/lib/AbstractDomainListTest.java
@@ -39,12 +39,12 @@ public abstract class AbstractDomainListTest {
 
     private static final Logger LOGGER = 
LoggerFactory.getLogger(AbstractDomainListTest.class);
 
-    private final String DOMAIN_1 = "domain1.tld";
-    private final String DOMAIN_2 = "domain2.tld";
-    private final String DOMAIN_3 = "domain3.tld";
-    private final String DOMAIN_4 = "domain4.tld";
-    private final String DOMAIN_5 = "domain5.tld";
-    private final String DOMAIN_UPPER_5 = "Domain5.tld";
+    private static final String DOMAIN_1 = "domain1.tld";
+    private static final String DOMAIN_2 = "domain2.tld";
+    private static final String DOMAIN_3 = "domain3.tld";
+    private static final String DOMAIN_4 = "domain4.tld";
+    private static final String DOMAIN_5 = "domain5.tld";
+    private static final String DOMAIN_UPPER_5 = "Domain5.tld";
 
     private DomainList domainList;
 
@@ -117,21 +117,21 @@ public abstract class AbstractDomainListTest {
     }
 
     @Test
-    public void ContainsShouldReturnFalseWhenDomainIsRemoved() throws 
DomainListException {
+    public void containsShouldReturnFalseWhenDomainIsRemoved() throws 
DomainListException {
         domainList.addDomain(DOMAIN_1);
         domainList.removeDomain(DOMAIN_1);
         assertThat(domainList.containsDomain(DOMAIN_1)).isFalse();
     }
 
     @Test
-    public void RemoveShouldRemoveDomainsUsingUpperCases() throws 
DomainListException {
+    public void removeShouldRemoveDomainsUsingUpperCases() throws 
DomainListException {
         domainList.addDomain(DOMAIN_UPPER_5);
         domainList.removeDomain(DOMAIN_UPPER_5);
         assertThat(domainList.containsDomain(DOMAIN_UPPER_5)).isFalse();
     }
 
     @Test
-    public void RemoveShouldRemoveDomainsUsingLowerCases() throws 
DomainListException {
+    public void removeShouldRemoveDomainsUsingLowerCases() throws 
DomainListException {
         domainList.addDomain(DOMAIN_UPPER_5);
         domainList.removeDomain(DOMAIN_5);
         assertThat(domainList.containsDomain(DOMAIN_UPPER_5)).isFalse();

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/data/data-library/src/test/java/org/apache/james/mailrepository/mock/MockMailRepositoryStore.java
----------------------------------------------------------------------
diff --git 
a/server/data/data-library/src/test/java/org/apache/james/mailrepository/mock/MockMailRepositoryStore.java
 
b/server/data/data-library/src/test/java/org/apache/james/mailrepository/mock/MockMailRepositoryStore.java
index 707a2c9..6d16b4c 100644
--- 
a/server/data/data-library/src/test/java/org/apache/james/mailrepository/mock/MockMailRepositoryStore.java
+++ 
b/server/data/data-library/src/test/java/org/apache/james/mailrepository/mock/MockMailRepositoryStore.java
@@ -29,10 +29,10 @@ import 
org.apache.james.mailrepository.api.MailRepositoryStore;
 
 public class MockMailRepositoryStore implements MailRepositoryStore {
 
-    final Map<String, MailRepository> m_storedObjectMap = new HashMap<>();
+    final Map<String, MailRepository> storedObjectMap = new HashMap<>();
 
     public void add(String url, MailRepository obj) {
-        m_storedObjectMap.put(url, obj);
+        storedObjectMap.put(url, obj);
     }
 
     @Override
@@ -42,12 +42,12 @@ public class MockMailRepositoryStore implements 
MailRepositoryStore {
 
     private MailRepository get(String key) {
         System.out.println(key);
-        return m_storedObjectMap.get(key);
+        return storedObjectMap.get(key);
     }
 
     @Override
     public List<String> getUrls() {
-        return new ArrayList<>(m_storedObjectMap.keySet());
+        return new ArrayList<>(storedObjectMap.keySet());
     }
 
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/data/data-library/src/test/java/org/apache/james/user/lib/UsersRepositoryManagementTest.java
----------------------------------------------------------------------
diff --git 
a/server/data/data-library/src/test/java/org/apache/james/user/lib/UsersRepositoryManagementTest.java
 
b/server/data/data-library/src/test/java/org/apache/james/user/lib/UsersRepositoryManagementTest.java
index d3e85d8..7ffa5fa 100644
--- 
a/server/data/data-library/src/test/java/org/apache/james/user/lib/UsersRepositoryManagementTest.java
+++ 
b/server/data/data-library/src/test/java/org/apache/james/user/lib/UsersRepositoryManagementTest.java
@@ -35,44 +35,44 @@ import org.junit.Test;
  */
 public class UsersRepositoryManagementTest {
 
-    private InMemoryUsersRepository m_mockUsersRepository;
-    private UsersRepositoryManagement m_userManagement;
+    private InMemoryUsersRepository mockUsersRepository;
+    private UsersRepositoryManagement userManagement;
 
     @Before
     public void setUp() throws Exception {
-        m_mockUsersRepository = new InMemoryUsersRepository();
+        mockUsersRepository = new InMemoryUsersRepository();
 
-        m_userManagement = new UsersRepositoryManagement();
-        m_userManagement.setUsersRepository(m_mockUsersRepository);
+        userManagement = new UsersRepositoryManagement();
+        userManagement.setUsersRepository(mockUsersRepository);
     }
 
     @Test
     public void testUserCount() throws Exception {
-        assertEquals("no user yet", 0, m_userManagement.countUsers());
-        m_mockUsersRepository.addUser("testCount1", "testCount");
-        assertEquals("1 user", 1, m_userManagement.countUsers());
-        m_mockUsersRepository.addUser("testCount2", "testCount");
-        assertEquals("2 users", 2, m_userManagement.countUsers());
-        m_mockUsersRepository.removeUser("testCount1");
-        assertEquals("1 user", 1, m_userManagement.countUsers());
+        assertEquals("no user yet", 0, userManagement.countUsers());
+        mockUsersRepository.addUser("testCount1", "testCount");
+        assertEquals("1 user", 1, userManagement.countUsers());
+        mockUsersRepository.addUser("testCount2", "testCount");
+        assertEquals("2 users", 2, userManagement.countUsers());
+        mockUsersRepository.removeUser("testCount1");
+        assertEquals("1 user", 1, userManagement.countUsers());
     }
 
     @Test
     public void testAddUserAndVerify() throws Exception {
-        m_mockUsersRepository.addUser("testCount1", "testCount");
-        assertFalse("user not there", 
m_userManagement.verifyExists("testNotAdded"));
-        assertTrue("user is there", 
m_userManagement.verifyExists("testCount1"));
-        m_mockUsersRepository.removeUser("testCount1");
-        assertFalse("user not there", 
m_userManagement.verifyExists("testCount1"));
+        mockUsersRepository.addUser("testCount1", "testCount");
+        assertFalse("user not there", 
userManagement.verifyExists("testNotAdded"));
+        assertTrue("user is there", userManagement.verifyExists("testCount1"));
+        mockUsersRepository.removeUser("testCount1");
+        assertFalse("user not there", 
userManagement.verifyExists("testCount1"));
     }
 
     @Test
     public void testDelUser() throws Exception {
-        m_mockUsersRepository.addUser("testDel", "test");
-        assertFalse("user not there", 
m_userManagement.verifyExists("testNotDeletable"));
-        assertTrue("user is there", m_userManagement.verifyExists("testDel"));
-        m_mockUsersRepository.removeUser("testDel");
-        assertFalse("user no longer there", 
m_userManagement.verifyExists("testDel"));
+        mockUsersRepository.addUser("testDel", "test");
+        assertFalse("user not there", 
userManagement.verifyExists("testNotDeletable"));
+        assertTrue("user is there", userManagement.verifyExists("testDel"));
+        mockUsersRepository.removeUser("testDel");
+        assertFalse("user no longer there", 
userManagement.verifyExists("testDel"));
     }
 
     @Test
@@ -82,10 +82,10 @@ public class UsersRepositoryManagementTest {
         List<String> users = Arrays.asList(usersArray);
 
         for (String user : users) {
-            m_mockUsersRepository.addUser(user, "test");
+            mockUsersRepository.addUser(user, "test");
         }
 
-        String[] userNames = m_userManagement.listAllUsers();
+        String[] userNames = userManagement.listAllUsers();
         assertEquals("user count", users.size(), userNames.length);
 
         for (String user : userNames) {
@@ -98,22 +98,22 @@ public class UsersRepositoryManagementTest {
     @Test
     public void testSetPassword() throws Exception {
 
-        m_userManagement.addUser("testPwdUser", "pwd1");
+        userManagement.addUser("testPwdUser", "pwd1");
 
-        assertTrue("initial password", 
m_mockUsersRepository.test("testPwdUser", "pwd1"));
+        assertTrue("initial password", mockUsersRepository.test("testPwdUser", 
"pwd1"));
 
         // set empty pwd
-        m_userManagement.setPassword("testPwdUser", "");
-        assertTrue("password changed to empty", 
m_mockUsersRepository.test("testPwdUser", ""));
+        userManagement.setPassword("testPwdUser", "");
+        assertTrue("password changed to empty", 
mockUsersRepository.test("testPwdUser", ""));
 
         // change pwd
-        m_userManagement.setPassword("testPwdUser", "pwd2");
-        assertTrue("password not changed to pwd2", 
m_mockUsersRepository.test("testPwdUser", "pwd2"));
+        userManagement.setPassword("testPwdUser", "pwd2");
+        assertTrue("password not changed to pwd2", 
mockUsersRepository.test("testPwdUser", "pwd2"));
 
         // assure case sensitivity
-        m_userManagement.setPassword("testPwdUser", "pWD2");
-        assertFalse("password no longer pwd2", 
m_mockUsersRepository.test("testPwdUser", "pwd2"));
-        assertTrue("password changed to pWD2", 
m_mockUsersRepository.test("testPwdUser", "pWD2"));
+        userManagement.setPassword("testPwdUser", "pWD2");
+        assertFalse("password no longer pwd2", 
mockUsersRepository.test("testPwdUser", "pwd2"));
+        assertTrue("password changed to pWD2", 
mockUsersRepository.test("testPwdUser", "pWD2"));
 
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/data/data-library/src/test/java/org/apache/james/user/lib/mock/InMemoryUsersRepository.java
----------------------------------------------------------------------
diff --git 
a/server/data/data-library/src/test/java/org/apache/james/user/lib/mock/InMemoryUsersRepository.java
 
b/server/data/data-library/src/test/java/org/apache/james/user/lib/mock/InMemoryUsersRepository.java
index 0409af6..6053070 100644
--- 
a/server/data/data-library/src/test/java/org/apache/james/user/lib/mock/InMemoryUsersRepository.java
+++ 
b/server/data/data-library/src/test/java/org/apache/james/user/lib/mock/InMemoryUsersRepository.java
@@ -33,7 +33,7 @@ import org.apache.james.user.lib.model.DefaultUser;
 @SuppressWarnings("deprecation")
 public class InMemoryUsersRepository extends AbstractJamesUsersRepository {
 
-    private final HashMap<String, User> m_users = new HashMap<>();
+    private final HashMap<String, User> users = new HashMap<>();
     /**
      * force the repository to hold implementations of JamesUser interface,
      * instead of User JamesUser is _not_ required as of the UsersRepository
@@ -41,10 +41,10 @@ public class InMemoryUsersRepository extends 
AbstractJamesUsersRepository {
      * UsersRepository while at the same time expecting it to hold JamesUsers
      * (like in RemoteManagerHandler)
      */
-    private boolean m_forceUseJamesUser = false;
+    private boolean forceUseJamesUser = false;
 
     public void setForceUseJamesUser() {
-        m_forceUseJamesUser = true;
+        forceUseJamesUser = true;
     }
 
     @Override
@@ -52,29 +52,29 @@ public class InMemoryUsersRepository extends 
AbstractJamesUsersRepository {
         if (ignoreCase) {
             return getUserByNameCaseInsensitive(name);
         } else {
-            return m_users.get(name);
+            return users.get(name);
         }
     }
 
     public User getUserByNameCaseInsensitive(String name) {
-        return m_users.get(name.toLowerCase(Locale.US));
+        return users.get(name.toLowerCase(Locale.US));
     }
 
     public String getRealName(String name) {
         if (ignoreCase) {
-            return m_users.get(name.toLowerCase(Locale.US)) != null ? 
-                    m_users.get(name.toLowerCase(Locale.US)).getUserName() : 
null;
+            return users.get(name.toLowerCase(Locale.US)) != null ? 
+                    users.get(name.toLowerCase(Locale.US)).getUserName() : 
null;
         } else {
-            return m_users.get(name) != null ? name : null;
+            return users.get(name) != null ? name : null;
         }
     }
 
     @Override
     public void removeUser(String name) throws UsersRepositoryException {
-        if (!m_users.containsKey(name)) {
+        if (!users.containsKey(name)) {
             throw new UsersRepositoryException("No such user");
         } else {
-            m_users.remove(name);
+            users.remove(name);
         }
     }
 
@@ -83,7 +83,7 @@ public class InMemoryUsersRepository extends 
AbstractJamesUsersRepository {
         if (ignoreCase) {
             return containsCaseInsensitive(name);
         } else {
-            return m_users.containsKey(name);
+            return users.containsKey(name);
         }
     }
 
@@ -99,11 +99,11 @@ public class InMemoryUsersRepository extends 
AbstractJamesUsersRepository {
 
     @Override
     public int countUsers() throws UsersRepositoryException {
-        return m_users.size();
+        return users.size();
     }
 
     protected List<String> listUserNames() {
-        Iterator<User> users = m_users.values().iterator();
+        Iterator<User> users = this.users.values().iterator();
         List<String> userNames = new LinkedList<>();
         while (users.hasNext()) {
             User user = users.next();
@@ -120,19 +120,19 @@ public class InMemoryUsersRepository extends 
AbstractJamesUsersRepository {
 
     @Override
     protected void doAddUser(User user) throws UsersRepositoryException {
-        if (m_forceUseJamesUser && user instanceof DefaultUser) {
+        if (forceUseJamesUser && user instanceof DefaultUser) {
             DefaultUser aUser = (DefaultUser) user;
             user = new DefaultJamesUser(aUser.getUserName(), 
aUser.getHashedPassword(), aUser.getHashAlgorithm());
         }
 
         String key = user.getUserName();
-        m_users.put(key, user);
+        users.put(key, user);
     }
 
     @Override
     protected void doUpdateUser(User user) throws UsersRepositoryException {
-        if (m_users.containsKey(user.getUserName())) {
-            m_users.put(user.getUserName(), user);
+        if (users.containsKey(user.getUserName())) {
+            users.put(user.getUserName(), user);
         } else {
             throw new UsersRepositoryException("No such user");
         }

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/dns-service/dnsservice-dnsjava/src/main/java/org/apache/james/dnsservice/dnsjava/DNSJavaService.java
----------------------------------------------------------------------
diff --git 
a/server/dns-service/dnsservice-dnsjava/src/main/java/org/apache/james/dnsservice/dnsjava/DNSJavaService.java
 
b/server/dns-service/dnsservice-dnsjava/src/main/java/org/apache/james/dnsservice/dnsjava/DNSJavaService.java
index 1ff0e96..0230768 100644
--- 
a/server/dns-service/dnsservice-dnsjava/src/main/java/org/apache/james/dnsservice/dnsjava/DNSJavaService.java
+++ 
b/server/dns-service/dnsservice-dnsjava/src/main/java/org/apache/james/dnsservice/dnsjava/DNSJavaService.java
@@ -422,9 +422,9 @@ public class DNSJavaService implements DNSService, 
DNSServiceMBean, Configurable
 
     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;
+            String possibleIpLiteral = host.substring(0, host.length() - 1);
+            if (org.xbill.DNS.Address.isDottedQuad(possibleIpLiteral)) {
+                host = possibleIpLiteral;
             }
         }
         return host;

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/crypto/SMIMESignIntegrationTest.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/crypto/SMIMESignIntegrationTest.java
 
b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/crypto/SMIMESignIntegrationTest.java
index 3870876..3bf2eb6 100644
--- 
a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/crypto/SMIMESignIntegrationTest.java
+++ 
b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/crypto/SMIMESignIntegrationTest.java
@@ -126,7 +126,7 @@ public class SMIMESignIntegrationTest {
     }
 
     @Test
-    public void NonAuthenticatedMessagesShouldNotBeSigned() throws Exception {
+    public void nonAuthenticatedMessagesShouldNotBeSigned() throws Exception {
         messageSender.connect(LOCALHOST_IP, SMTP_SECURE_PORT)
             .sendMessage(FROM, RECIPIENT)
             .awaitSent(awaitOneMinute);

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RemoteDelivery.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RemoteDelivery.java
 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RemoteDelivery.java
index 43307e3..e75b510 100644
--- 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RemoteDelivery.java
+++ 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RemoteDelivery.java
@@ -38,10 +38,10 @@ import org.apache.james.queue.api.MailPrioritySupport;
 import org.apache.james.queue.api.MailQueue;
 import org.apache.james.queue.api.MailQueue.MailQueueException;
 import org.apache.james.queue.api.MailQueueFactory;
-import org.apache.james.transport.mailets.remoteDelivery.Bouncer;
-import org.apache.james.transport.mailets.remoteDelivery.DeliveryRunnable;
-import 
org.apache.james.transport.mailets.remoteDelivery.RemoteDeliveryConfiguration;
-import 
org.apache.james.transport.mailets.remoteDelivery.RemoteDeliverySocketFactory;
+import org.apache.james.transport.mailets.remote.delivery.Bouncer;
+import org.apache.james.transport.mailets.remote.delivery.DeliveryRunnable;
+import 
org.apache.james.transport.mailets.remote.delivery.RemoteDeliveryConfiguration;
+import 
org.apache.james.transport.mailets.remote.delivery.RemoteDeliverySocketFactory;
 import org.apache.mailet.Mail;
 import org.apache.mailet.base.GenericMailet;
 import org.slf4j.Logger;
@@ -122,7 +122,7 @@ import com.google.common.collect.HashMultimap;
 public class RemoteDelivery extends GenericMailet {
     private static final Logger LOGGER = 
LoggerFactory.getLogger(RemoteDelivery.class);
 
-    public enum THREAD_STATE {
+    public enum ThreadState {
         START_THREADS,
         DO_NOT_START_THREADS
     }
@@ -134,7 +134,7 @@ public class RemoteDelivery extends GenericMailet {
     private final MailQueueFactory queueFactory;
     private final MetricFactory metricFactory;
     private final AtomicBoolean isDestroyed;
-    private final THREAD_STATE startThreads;
+    private final ThreadState startThreads;
 
     private MailQueue queue;
     private RemoteDeliveryConfiguration configuration;
@@ -142,10 +142,10 @@ public class RemoteDelivery extends GenericMailet {
 
     @Inject
     public RemoteDelivery(DNSService dnsServer, DomainList domainList, 
MailQueueFactory queueFactory, MetricFactory metricFactory) {
-        this(dnsServer, domainList, queueFactory, metricFactory, 
THREAD_STATE.START_THREADS);
+        this(dnsServer, domainList, queueFactory, metricFactory, 
ThreadState.START_THREADS);
     }
 
-    public RemoteDelivery(DNSService dnsServer, DomainList domainList, 
MailQueueFactory queueFactory, MetricFactory metricFactory, THREAD_STATE 
startThreads) {
+    public RemoteDelivery(DNSService dnsServer, DomainList domainList, 
MailQueueFactory queueFactory, MetricFactory metricFactory, ThreadState 
startThreads) {
         this.dnsServer = dnsServer;
         this.domainList = domainList;
         this.queueFactory = queueFactory;
@@ -164,7 +164,7 @@ public class RemoteDelivery extends GenericMailet {
         } catch (UnknownHostException e) {
             LOGGER.error("Invalid bind setting ({}): ", 
configuration.getBindAddress(), e);
         }
-        if (startThreads == THREAD_STATE.START_THREADS) {
+        if (startThreads == ThreadState.START_THREADS) {
             initDeliveryThreads();
         }
     }
@@ -256,7 +256,7 @@ public class RemoteDelivery extends GenericMailet {
      */
     @Override
     public synchronized void destroy() {
-        if (startThreads == THREAD_STATE.START_THREADS) {
+        if (startThreads == ThreadState.START_THREADS) {
             isDestroyed.set(true);
             executor.shutdown();
             notifyAll();

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/RejectAction.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/RejectAction.java
 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/RejectAction.java
index 9087d3e..473221b 100644
--- 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/RejectAction.java
+++ 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/RejectAction.java
@@ -86,35 +86,35 @@ public class RejectAction implements MailAction {
         humanText.append("\r\n");
         humanText.append(anAction.getMessage());
 
-        String reporting_UA_name = null;
+        String reportingUAName = null;
         try {
-            reporting_UA_name = InetAddress.getLocalHost()
+            reportingUAName = InetAddress.getLocalHost()
                     .getCanonicalHostName();
         } catch (UnknownHostException ex) {
-            reporting_UA_name = "localhost";
+            reportingUAName = "localhost";
         }
 
-        String reporting_UA_product = context.getServerInfo();
+        String reportingUAProduct = context.getServerInfo();
 
         String[] originalRecipients = aMail.getMessage().getHeader(
                 "Original-Recipient");
-        String original_recipient = null;
+        String originalRecipient = null;
         if (null != originalRecipients && originalRecipients.length > 0) {
-            original_recipient = originalRecipients[0];
+            originalRecipient = originalRecipients[0];
         }
 
         MailAddress soleRecipient = ActionUtils.getSoleRecipient(aMail);
-        String final_recipient = soleRecipient.asString();
-        String original_message_id = aMail.getMessage().getMessageID();
+        String finalRecipient = soleRecipient.asString();
+        String originalMessageId = aMail.getMessage().getMessageID();
 
         Multipart multipart = MDN.builder()
             .humanReadableText(humanText.toString())
             .report(
                 MDNReport.builder()
-                    .reportingUserAgentField(reporting_UA_name, 
reporting_UA_product)
-                    .finalRecipientField(final_recipient)
-                    .originalRecipientField(original_recipient)
-                    .originalMessageIdField(original_message_id)
+                    .reportingUserAgentField(reportingUAName, 
reportingUAProduct)
+                    .finalRecipientField(finalRecipient)
+                    .originalRecipientField(originalRecipient)
+                    .originalMessageIdField(originalMessageId)
                     .dispositionField(Disposition.builder()
                         .actionMode(DispositionActionMode.Automatic)
                         .sendingMode(DispositionSendingMode.Automatic)

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/AddressesArrayToMailAddressListConverter.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/AddressesArrayToMailAddressListConverter.java
 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/AddressesArrayToMailAddressListConverter.java
new file mode 100644
index 0000000..7b10a18
--- /dev/null
+++ 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/AddressesArrayToMailAddressListConverter.java
@@ -0,0 +1,60 @@
+/****************************************************************
+ * 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.transport.mailets.remote.delivery;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+
+import javax.mail.Address;
+import javax.mail.internet.AddressException;
+
+import org.apache.james.core.MailAddress;
+import org.apache.james.util.OptionalUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.steveash.guavate.Guavate;
+import com.google.common.collect.ImmutableList;
+
+public class AddressesArrayToMailAddressListConverter {
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(AddressesArrayToMailAddressListConverter.class);
+
+    public static List<MailAddress> getAddressesAsMailAddress(Address[] 
addresses) {
+        if (addresses == null) {
+            return ImmutableList.of();
+        }
+        return Arrays.asList(addresses)
+            .stream()
+            .map(address -> toMailAddress(address))
+            .flatMap(OptionalUtils::toStream)
+            .collect(Guavate.toImmutableList());
+    }
+
+    private static Optional<MailAddress> toMailAddress(Address address) {
+        try {
+            return Optional.of(new MailAddress(address.toString()));
+        } catch (AddressException e) {
+            LOGGER.debug("Can't parse unsent address {}", address, e);
+            return Optional.empty();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/Bouncer.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/Bouncer.java
 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/Bouncer.java
new file mode 100644
index 0000000..0cff3cb
--- /dev/null
+++ 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/Bouncer.java
@@ -0,0 +1,146 @@
+/****************************************************************
+ * 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.transport.mailets.remote.delivery;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.ConnectException;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+
+import javax.mail.MessagingException;
+import javax.mail.SendFailedException;
+
+import org.apache.james.core.MailAddress;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailetContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Bouncer {
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(Bouncer.class);
+
+    public static final String DELIVERY_ERROR = "delivery-error";
+    private final RemoteDeliveryConfiguration configuration;
+    private final MailetContext mailetContext;
+
+    public Bouncer(RemoteDeliveryConfiguration configuration, MailetContext 
mailetContext) {
+        this.configuration = configuration;
+        this.mailetContext = mailetContext;
+    }
+
+    public void bounce(Mail mail, Exception ex) {
+        if (mail.getSender() == null) {
+            LOGGER.debug("Null Sender: no bounce will be generated for {}", 
mail.getName());
+        } else {
+            if (configuration.getBounceProcessor() != null) {
+                mail.setAttribute(DELIVERY_ERROR, getErrorMsg(ex));
+                try {
+                    mailetContext.sendMail(mail, 
configuration.getBounceProcessor());
+                } catch (MessagingException e) {
+                    LOGGER.warn("Exception re-inserting failed mail: ", e);
+                }
+            } else {
+                bounceWithMailetContext(mail, ex);
+            }
+        }
+    }
+
+
+    private void bounceWithMailetContext(Mail mail, Exception ex) {
+        LOGGER.debug("Sending failure message {}", mail.getName());
+        try {
+            mailetContext.bounce(mail, explanationText(mail, ex));
+        } catch (MessagingException me) {
+            LOGGER.warn("Encountered unexpected messaging exception while 
bouncing message", me);
+        } catch (Exception e) {
+            LOGGER.warn("Encountered unexpected exception while bouncing 
message", e);
+        }
+    }
+
+    public String explanationText(Mail mail, Exception ex) {
+        StringWriter sout = new StringWriter();
+        PrintWriter out = new PrintWriter(sout, true);
+        out.println("Hi. This is the James mail server at " + 
resolveMachineName() + ".");
+        out.println("I'm afraid I wasn't able to deliver your message to the 
following addresses.");
+        out.println("This is a permanent error; I've given up. Sorry it didn't 
work out. Below");
+        out.println("I include the list of recipients and the reason why I was 
unable to deliver");
+        out.println("your message.");
+        out.println();
+        for (MailAddress mailAddress : mail.getRecipients()) {
+            out.println(mailAddress);
+        }
+        if (ex instanceof MessagingException) {
+            if (((MessagingException) ex).getNextException() == null) {
+                out.println(sanitizeExceptionMessage(ex));
+            } else {
+                Exception ex1 = ((MessagingException) ex).getNextException();
+                if (ex1 instanceof SendFailedException) {
+                    out.println("Remote mail server told me: " + 
sanitizeExceptionMessage(ex1));
+                } else if (ex1 instanceof UnknownHostException) {
+                    out.println("Unknown host: " + 
sanitizeExceptionMessage(ex1));
+                    out.println("This could be a DNS server error, a typo, or 
a problem with the recipient's mail server.");
+                } else if (ex1 instanceof ConnectException) {
+                    // Already formatted as "Connection timed out: connect"
+                    out.println(sanitizeExceptionMessage(ex1));
+                } else if (ex1 instanceof SocketException) {
+                    out.println("Socket exception: " + 
sanitizeExceptionMessage(ex1));
+                } else {
+                    out.println(sanitizeExceptionMessage(ex1));
+                }
+            }
+        }
+        out.println();
+        return sout.toString();
+    }
+
+    private String sanitizeExceptionMessage(Exception e) {
+        if (e.getMessage() == null) {
+            return "null";
+        } else {
+            return e.getMessage().trim();
+        }
+    }
+
+    private String resolveMachineName() {
+        try {
+            return configuration.getHeloNameProvider().getHeloName();
+        } catch (Exception e) {
+            return "[address unknown]";
+        }
+    }
+
+    public String getErrorMsg(Exception ex) {
+        if (ex instanceof MessagingException) {
+            return getNestedExceptionMessage((MessagingException) ex);
+        } else {
+            return sanitizeExceptionMessage(ex);
+        }
+    }
+
+    public String getNestedExceptionMessage(MessagingException me) {
+        if (me.getNextException() == null) {
+            return sanitizeExceptionMessage(me);
+        } else {
+            Exception ex1 = me.getNextException();
+            return sanitizeExceptionMessage(ex1);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/Converter7Bit.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/Converter7Bit.java
 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/Converter7Bit.java
new file mode 100644
index 0000000..63b17f6
--- /dev/null
+++ 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/Converter7Bit.java
@@ -0,0 +1,65 @@
+/****************************************************************
+ * 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.transport.mailets.remote.delivery;
+
+import java.io.IOException;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMultipart;
+import javax.mail.internet.MimePart;
+
+import org.apache.mailet.MailetContext;
+
+public class Converter7Bit {
+
+    private final MailetContext mailetContext;
+
+    public Converter7Bit(MailetContext mailetContext) {
+        this.mailetContext = mailetContext;
+    }
+
+    public MimePart convertTo7Bit(MimePart part) throws MessagingException, 
IOException {
+        if (part.isMimeType("multipart/*")) {
+            MimeMultipart parts = (MimeMultipart) part.getContent();
+            int count = parts.getCount();
+            for (int i = 0; i < count; i++) {
+                convertTo7Bit((MimePart) parts.getBodyPart(i));
+            }
+        } else if ("8bit".equals(part.getEncoding())) {
+            // The content may already be in encoded the form (likely with mail
+            // created from a
+            // stream). In that case, just changing the encoding to
+            // quoted-printable will mangle
+            // the result when this is transmitted. We must first convert the
+            // content into its
+            // native format, set it back, and only THEN set the transfer
+            // encoding to force the
+            // content to be encoded appropriately.
+
+            // if the part doesn't contain text it will be base64 encoded.
+            String contentTransferEncoding = part.isMimeType("text/*") ? 
"quoted-printable" : "base64";
+            part.setContent(part.getContent(), part.getContentType());
+            part.setHeader("Content-Transfer-Encoding", 
contentTransferEncoding);
+            part.addHeader("X-MIME-Autoconverted", "from 8bit to " + 
contentTransferEncoding + " by " + mailetContext.getServerInfo());
+        }
+        return part;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/Delay.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/Delay.java
 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/Delay.java
new file mode 100644
index 0000000..af50fdd
--- /dev/null
+++ 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/Delay.java
@@ -0,0 +1,112 @@
+/****************************************************************
+ * 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.transport.mailets.remote.delivery;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import javax.mail.MessagingException;
+
+import org.apache.james.util.TimeConverter;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+
+public class Delay {
+    /**
+     * <p> The optional attempt is the number of tries this delay should be 
used (default = 1).
+     * The delayTime is parsed by {@link TimeConverter}</p>
+     *
+     * @param initString the string to initialize this Delay object from. It 
has the form "[attempt\*]delaytime[unit]"
+     */
+    public static Delay from(String initString) throws MessagingException {
+        if (Strings.isNullOrEmpty(initString)) {
+            throw new NumberFormatException("Null or Empty strings are not 
permitted");
+        }
+        List<String> parts = 
Splitter.on('*').trimResults().splitToList(initString);
+
+        if (parts.size() == 1) {
+            return new Delay(DEFAULT_ATTEMPTS, 
TimeConverter.getMilliSeconds(parts.get(0)));
+        }
+        if (parts.size() == 2) {
+            int attempts = Integer.parseInt(parts.get(0));
+            if (attempts < 0) {
+                throw new MessagingException("Number of attempts negative in " 
+ initString);
+            }
+            return new Delay(attempts, 
TimeConverter.getMilliSeconds(parts.get(1)));
+        }
+        throw new MessagingException(initString + " contains too much parts");
+    }
+
+    public static final long DEFAULT_DELAY_TIME = TimeUnit.HOURS.toMillis(6);
+    public static final int DEFAULT_ATTEMPTS = 1;
+
+    private final int attempts;
+    private final long delayTimeInMs;
+
+    public Delay() {
+        this(DEFAULT_ATTEMPTS, DEFAULT_DELAY_TIME);
+    }
+
+    @VisibleForTesting
+    Delay(int attempts, long delayTime) {
+        this.attempts = attempts;
+        this.delayTimeInMs = delayTime;
+    }
+
+    public long getDelayTimeInMs() {
+        return delayTimeInMs;
+    }
+
+    public int getAttempts() {
+        return attempts;
+    }
+
+    public List<Long> getExpendendDelays() {
+        return Repeat.repeat(delayTimeInMs, attempts);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("attempts", attempts)
+            .add("delayTime", delayTimeInMs)
+            .toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof Delay) {
+            Delay that = (Delay) o;
+
+            return Objects.equal(this.attempts, that.attempts)
+                && Objects.equal(this.delayTimeInMs, that.delayTimeInMs);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(attempts, delayTimeInMs);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/DelaysAndMaxRetry.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/DelaysAndMaxRetry.java
 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/DelaysAndMaxRetry.java
new file mode 100644
index 0000000..7936f3d
--- /dev/null
+++ 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/DelaysAndMaxRetry.java
@@ -0,0 +1,160 @@
+/****************************************************************
+ * 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.transport.mailets.remote.delivery;
+
+import java.util.List;
+
+import javax.mail.MessagingException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Objects;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class DelaysAndMaxRetry {
+
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(DelaysAndMaxRetry.class);
+
+    public static DelaysAndMaxRetry defaults() {
+        return new 
DelaysAndMaxRetry(RemoteDeliveryConfiguration.DEFAULT_MAX_RETRY, 
Repeat.repeat(new Delay(), RemoteDeliveryConfiguration.DEFAULT_MAX_RETRY));
+    }
+
+    public static DelaysAndMaxRetry from(int intendedMaxRetries, String 
delaysAsString) throws MessagingException {
+        List<Delay> delayTimesList = createDelayList(delaysAsString);
+        int totalAttempts = computeTotalAttempts(delayTimesList);
+        return getDelaysAndMaxRetry(intendedMaxRetries, totalAttempts, 
delayTimesList);
+    }
+
+    private static DelaysAndMaxRetry getDelaysAndMaxRetry(int 
intendedMaxRetries, int totalAttempts, List<Delay> delayTimesList) throws 
MessagingException {
+        if (totalAttempts > intendedMaxRetries) {
+            LOGGER.warn("Total number of delayTime attempts exceeds maxRetries 
specified. Increasing maxRetries from {} to {}", intendedMaxRetries, 
totalAttempts);
+            return new DelaysAndMaxRetry(totalAttempts, delayTimesList);
+        } else {
+            int extra = intendedMaxRetries - totalAttempts;
+            if (extra > 0) {
+                LOGGER.warn("maxRetries is larger than total number of 
attempts specified. Increasing last delayTime with {} attempts ", extra);
+                return addExtraAttemptToLastDelay(intendedMaxRetries, extra, 
delayTimesList);
+            }
+            return new DelaysAndMaxRetry(intendedMaxRetries, delayTimesList);
+        }
+    }
+
+    private static DelaysAndMaxRetry addExtraAttemptToLastDelay(int 
intendedMaxRetries, int extra, List<Delay> delayTimesList) throws 
MessagingException {
+        if (delayTimesList.size() != 0) {
+            Delay lastDelay = delayTimesList.get(delayTimesList.size() - 1);
+            LOGGER.warn("Delay of {} msecs is now attempted: {} times", 
lastDelay.getDelayTimeInMs(), lastDelay.getAttempts());
+            return new DelaysAndMaxRetry(intendedMaxRetries,
+                ImmutableList.copyOf(
+                    Iterables.concat(
+                        Iterables.limit(delayTimesList, delayTimesList.size() 
- 1),
+                        ImmutableList.of(new Delay(lastDelay.getAttempts() + 
extra, lastDelay.getDelayTimeInMs())))));
+        } else {
+            throw new MessagingException("No delaytimes, cannot continue");
+        }
+    }
+
+    private static List<Delay> createDelayList(String delaysAsString) {
+        if (delaysAsString == null) {
+            // Use default delayTime.
+            return ImmutableList.of(new Delay());
+        }
+
+        List<String> delayStrings = Splitter.on(',')
+            .omitEmptyStrings()
+            .trimResults()
+            .splitToList(delaysAsString);
+
+        ImmutableList.Builder<Delay> builder = ImmutableList.builder();
+        try {
+            for (String s : delayStrings) {
+                builder.add(Delay.from(s));
+            }
+            return builder.build();
+        } catch (Exception e) {
+            LOGGER.warn("Invalid delayTime setting: {}", delaysAsString);
+            return builder.build();
+        }
+    }
+
+    private static int computeTotalAttempts(List<Delay> delayList) {
+        return delayList.stream()
+            .mapToInt(Delay::getAttempts)
+            .sum();
+    }
+
+    private final int maxRetries;
+    private final List<Delay> delays;
+
+    @VisibleForTesting
+    DelaysAndMaxRetry(int maxRetries, List<Delay> delays) {
+        this.maxRetries = maxRetries;
+        this.delays = ImmutableList.copyOf(delays);
+    }
+
+    public int getMaxRetries() {
+        return maxRetries;
+    }
+
+    /**
+     * <p>
+     * This method expands an ArrayList containing Delay objects into an array
+     * holding the only delaytime in the order.
+     * </p>
+     * <p/>
+     * So if the list has 2 Delay objects the first having attempts=2 and
+     * delaytime 4000 the second having attempts=1 and delaytime=300000 will be
+     * expanded into this array:
+     * <p/>
+     * <pre>
+     * long[0] = 4000
+     * long[1] = 4000
+     * long[2] = 300000
+     * </pre>
+     *
+     * @param list the list to expand
+     * @return the expanded list
+     */
+    public List<Long> getExpandedDelays() {
+        ImmutableList.Builder<Long> builder = ImmutableList.builder();
+        for (Delay delay: delays) {
+            builder.addAll(delay.getExpendendDelays());
+        }
+        return builder.build();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof DelaysAndMaxRetry) {
+            DelaysAndMaxRetry that = (DelaysAndMaxRetry) o;
+            return Objects.equal(this.maxRetries, that.maxRetries)
+                && Objects.equal(this.delays, that.delays);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(maxRetries, delays);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/DeliveryRetriesHelper.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/DeliveryRetriesHelper.java
 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/DeliveryRetriesHelper.java
new file mode 100644
index 0000000..08071e5
--- /dev/null
+++ 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/DeliveryRetriesHelper.java
@@ -0,0 +1,50 @@
+/****************************************************************
+ * 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.transport.mailets.remote.delivery;
+
+import java.io.Serializable;
+
+import org.apache.mailet.Mail;
+
+public class DeliveryRetriesHelper {
+
+    public static final String DELIVERY_RETRY_COUNT = "delivery_retry_count";
+
+    public static int retrieveRetries(Mail mail) {
+        try {
+            Serializable value = mail.getAttribute(DELIVERY_RETRY_COUNT);
+            if (value != null) {
+                return (Integer) value;
+            }
+            return 0;
+        } catch (ClassCastException e) {
+            return 0;
+        }
+    }
+
+    public static void initRetries(Mail mail) {
+        mail.setAttribute(DELIVERY_RETRY_COUNT, 0);
+    }
+
+    public static void incrementRetries(Mail mail) {
+        mail.setAttribute(DELIVERY_RETRY_COUNT, retrieveRetries(mail) + 1);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/DeliveryRunnable.java
----------------------------------------------------------------------
diff --git 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/DeliveryRunnable.java
 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/DeliveryRunnable.java
new file mode 100644
index 0000000..53b0420
--- /dev/null
+++ 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/DeliveryRunnable.java
@@ -0,0 +1,181 @@
+/****************************************************************
+ * 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.transport.mailets.remote.delivery;
+
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Supplier;
+
+import org.apache.james.dnsservice.api.DNSService;
+import org.apache.james.lifecycle.api.LifecycleUtil;
+import org.apache.james.metrics.api.Metric;
+import org.apache.james.metrics.api.MetricFactory;
+import org.apache.james.metrics.api.TimeMetric;
+import org.apache.james.queue.api.MailPrioritySupport;
+import org.apache.james.queue.api.MailQueue;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailetContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+
+public class DeliveryRunnable implements Runnable {
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(DeliveryRunnable.class);
+
+    public static final Supplier<Date> CURRENT_DATE_SUPPLIER = Date::new;
+    public static final AtomicBoolean DEFAULT_NOT_STARTED = new 
AtomicBoolean(false);
+    private static final String OUTGOING_MAILS = "outgoingMails";
+    public static final String REMOTE_DELIVERY_TRIAL = "RemoteDeliveryTrial";
+
+    private final MailQueue queue;
+    private final RemoteDeliveryConfiguration configuration;
+    private final Metric outgoingMailsMetric;
+    private final MetricFactory metricFactory;
+    private final Bouncer bouncer;
+    private final MailDelivrer mailDelivrer;
+    private final AtomicBoolean isDestroyed;
+    private final Supplier<Date> dateSupplier;
+
+    public DeliveryRunnable(MailQueue queue, RemoteDeliveryConfiguration 
configuration, DNSService dnsServer, MetricFactory metricFactory,
+                            MailetContext mailetContext, Bouncer bouncer, 
AtomicBoolean isDestroyed) {
+        this(queue, configuration, metricFactory, bouncer,
+            new MailDelivrer(configuration, new 
MailDelivrerToHost(configuration, mailetContext), dnsServer, bouncer),
+            isDestroyed, CURRENT_DATE_SUPPLIER);
+    }
+
+    @VisibleForTesting
+    DeliveryRunnable(MailQueue queue, RemoteDeliveryConfiguration 
configuration, MetricFactory metricFactory, Bouncer bouncer,
+                     MailDelivrer mailDelivrer, AtomicBoolean isDestroyeds, 
Supplier<Date> dateSupplier) {
+        this.queue = queue;
+        this.configuration = configuration;
+        this.outgoingMailsMetric = metricFactory.generate(OUTGOING_MAILS);
+        this.bouncer = bouncer;
+        this.mailDelivrer = mailDelivrer;
+        this.isDestroyed = isDestroyeds;
+        this.dateSupplier = dateSupplier;
+        this.metricFactory = metricFactory;
+    }
+
+    @Override
+    public void run() {
+        try {
+            while (!Thread.interrupted() && !isDestroyed.get()) {
+                runStep();
+            }
+        } finally {
+            // Restore the thread state to non-interrupted.
+            Thread.interrupted();
+        }
+    }
+
+    private void runStep() {
+        TimeMetric timeMetric = null;
+        try {
+            // Get the 'mail' object that is ready for deliverying. If no 
message is
+            // ready, the 'accept' will block until message is ready.
+            // The amount of time to block is determined by the 'getWaitTime' 
method of the MultipleDelayFilter.
+            MailQueue.MailQueueItem queueItem = queue.deQueue();
+            timeMetric = metricFactory.timer(REMOTE_DELIVERY_TRIAL);
+            Mail mail = queueItem.getMail();
+
+            try {
+                if (configuration.isDebug()) {
+                    LOGGER.debug("{} will process mail {}", 
Thread.currentThread().getName(), mail.getName());
+                }
+                attemptDelivery(mail);
+                LifecycleUtil.dispose(mail);
+                mail = null;
+                queueItem.done(true);
+            } catch (Exception e) {
+                // Prevent unexpected exceptions from causing looping by 
removing message from outgoing.
+                // DO NOT CHANGE THIS to catch Error!
+                // For example, if there were an OutOfMemory condition caused 
because
+                // something else in the server was abusing memory, we would 
not want to start purging the retrying spool!
+                LOGGER.error("Exception caught in RemoteDelivery.run()", e);
+                LifecycleUtil.dispose(mail);
+                queueItem.done(false);
+            }
+
+        } catch (Throwable e) {
+            if (!isDestroyed.get()) {
+                LOGGER.error("Exception caught in RemoteDelivery.run()", e);
+            }
+        } finally {
+            if (timeMetric != null) {
+                timeMetric.stopAndPublish();
+            }
+        }
+    }
+
+    @VisibleForTesting
+    void attemptDelivery(Mail mail) throws MailQueue.MailQueueException {
+        ExecutionResult executionResult = mailDelivrer.deliver(mail);
+        switch (executionResult.getExecutionState()) {
+            case SUCCESS:
+                outgoingMailsMetric.increment();
+                break;
+            case TEMPORARY_FAILURE:
+                handleTemporaryFailure(mail, executionResult);
+                break;
+            case PERMANENT_FAILURE:
+                bouncer.bounce(mail, 
executionResult.getException().orElse(null));
+                break;
+        }
+    }
+
+    private void handleTemporaryFailure(Mail mail, ExecutionResult 
executionResult) throws MailQueue.MailQueueException {
+        if (!mail.getState().equals(Mail.ERROR)) {
+            mail.setState(Mail.ERROR);
+            DeliveryRetriesHelper.initRetries(mail);
+            mail.setLastUpdated(dateSupplier.get());
+        }
+        int retries = DeliveryRetriesHelper.retrieveRetries(mail);
+
+        if (retries < configuration.getMaxRetries()) {
+            reAttemptDelivery(mail, retries);
+        } else {
+            LOGGER.debug("Bouncing message {} after {} retries", 
mail.getName(), retries);
+            bouncer.bounce(mail, new Exception("Too many retries failure. 
Bouncing after " + retries + " retries.", 
executionResult.getException().orElse(null)));
+        }
+    }
+
+    private void reAttemptDelivery(Mail mail, int retries) throws 
MailQueue.MailQueueException {
+        LOGGER.debug("Storing message {} into outgoing after {} retries", 
mail.getName(), retries);
+        DeliveryRetriesHelper.incrementRetries(mail);
+        mail.setLastUpdated(dateSupplier.get());
+        // Something happened that will delay delivery. Store it back in the 
retry repository.
+        long delay = getNextDelay(DeliveryRetriesHelper.retrieveRetries(mail));
+
+        if (configuration.isUsePriority()) {
+            // Use lowest priority for retries. See JAMES-1311
+            mail.setAttribute(MailPrioritySupport.MAIL_PRIORITY, 
MailPrioritySupport.LOW_PRIORITY);
+        }
+        queue.enQueue(mail, delay, TimeUnit.MILLISECONDS);
+    }
+
+    private long getNextDelay(int retry_count) {
+        if (retry_count > configuration.getDelayTimes().size()) {
+            return Delay.DEFAULT_DELAY_TIME;
+        }
+        return configuration.getDelayTimes().get(retry_count - 1);
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to