Java tutorial
/** * Copyright 2010 Molindo GmbH * * Licensed 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 at.molindo.notify.channel.mail; import java.util.Collections; import java.util.Properties; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import javax.mail.Address; import javax.mail.MessagingException; import javax.mail.SendFailedException; import javax.mail.Session; import javax.naming.NamingException; import org.springframework.beans.factory.InitializingBean; import at.molindo.utils.collections.CollectionUtils; import at.molindo.utils.data.ExceptionUtils; import at.molindo.utils.net.DnsUtils; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.sun.mail.smtp.SMTPAddressFailedException; import com.sun.mail.smtp.SMTPSendFailedException; public class DirectMailClient extends AbstractMailClient implements InitializingBean { private static final int DEFAULT_CACHE_CONCURRENCY = 4; private static final long DEFAULT_CACHE_EXPIRATION_MIN = 10; private static final String CONNECTION_TIMEOUT_MS = "60000"; private static final String READ_TIMEOUT_MS = "60000"; // permanent errors private static final int MAILBOX_UNAVAILABLE = 550; private static final int MAILBOX_NOT_LOCAL = 551; private static final int MAILBOX_NAME_NOT_ALLOWED = 553; private static final int TRANSACTION_FAILED = 554; private static final Set<Integer> PERMANENT_ERROR_CODES = Collections.unmodifiableSet(CollectionUtils .set(MAILBOX_UNAVAILABLE, MAILBOX_NOT_LOCAL, MAILBOX_NAME_NOT_ALLOWED, TRANSACTION_FAILED)); private LoadingCache<String, Session> _sessionCache; private int _cacheConcurrency = DEFAULT_CACHE_CONCURRENCY; private long _cacheExpirationMin = DEFAULT_CACHE_EXPIRATION_MIN; private String _localAddress; private String _socksProxyHost; private String _socksProxyPort; private Boolean _proxySet; private String _localHost; private boolean _startTLSEnabled = false; @Override public DirectMailClient init() throws MailException { super.init(); _sessionCache = CacheBuilder.newBuilder().concurrencyLevel(_cacheConcurrency) .expireAfterAccess(_cacheExpirationMin, TimeUnit.MINUTES).build(new CacheLoader<String, Session>() { @Override public Session load(String domain) throws MailException { return createSmtpSession(domain); } }); return this; } @Override protected Session getSmtpSession(String recipient) throws MailException { try { return _sessionCache.get(MailUtils.domainFromAddress(recipient)); } catch (ExecutionException e) { if (e.getCause() instanceof MailException) { throw (MailException) e.getCause(); } else { throw new RuntimeException("unexpected exception while getting SMTP session for " + recipient, e); } } } protected Session createSmtpSession(String domain) throws MailException { try { final Properties props = new Properties(); props.setProperty("mail.smtp.host", DnsUtils.lookupMailHosts(domain)[0]); props.setProperty("mail.smtp.port", "25"); props.setProperty("mail.smtp.auth", "false"); props.setProperty("mail.smtp.starttls.enable", Boolean.toString(getStartTLSEnabled())); // set proxy if (Boolean.TRUE.equals(getProxySet())) { props.setProperty("proxySet", "true"); props.setProperty("socksProxyHost", getSocksProxyHost()); props.setProperty("socksProxyPort", getSocksProxyPort()); } if (getLocalHost() != null) { props.setProperty("mail.smtp.localhost", getLocalHost()); } if (getLocalAddress() != null) { props.setProperty("mail.smtp.localaddress", getLocalAddress()); } props.setProperty("mail.smtp.connectiontimeout", CONNECTION_TIMEOUT_MS); props.setProperty("mail.smtp.timeout", READ_TIMEOUT_MS); // props.put("mail.debug", "true"); return Session.getInstance(props); } catch (NamingException e) { throw new MailException("can't lookup mail host: " + domain, e, true); } } @Override protected String toErrorMessage(MessagingException e) { if (e instanceof SendFailedException) { if (e.getNextException() instanceof SMTPSendFailedException) { final SMTPSendFailedException se = (SMTPSendFailedException) e.getNextException(); return se.getCommand() + " failed " + " with " + se.getReturnCode() + " (" + e.getMessage() + ")"; } else if (e.getNextException() instanceof SMTPAddressFailedException) { // copied from above, as there is no common base class but same // methods final SMTPAddressFailedException se = (SMTPAddressFailedException) e.getNextException(); return se.getCommand() + " failed " + " with " + se.getReturnCode() + " (" + e.getMessage() + ")"; } else { final StringBuilder buf = new StringBuilder(); Address[] addresses = ((SendFailedException) e).getInvalidAddresses(); if (addresses != null) { for (final Address a : addresses) { buf.append(a).append(" "); } } return "invalied address(es): " + buf + "(" + ExceptionUtils.getAllMessages(e) + ")"; } } else { return super.toErrorMessage(e); } } @Override protected boolean isTemporary(MessagingException e) { if (e instanceof SendFailedException) { if (e.getNextException() instanceof SMTPSendFailedException) { final SMTPSendFailedException se = (SMTPSendFailedException) e.getNextException(); final int rc = se.getReturnCode(); return !PERMANENT_ERROR_CODES.contains(rc); } else if (e.getNextException() instanceof SMTPAddressFailedException) { // copied from above, as there is no common base class but same // methods final SMTPAddressFailedException se = (SMTPAddressFailedException) e.getNextException(); final int rc = se.getReturnCode(); return !PERMANENT_ERROR_CODES.contains(rc); } else { return true; } } else { return true; } } public int getCacheConcurrency() { return _cacheConcurrency; } public void setCacheConcurrency(int cacheConcurrency) { _cacheConcurrency = cacheConcurrency; } public long getCacheExpirationMin() { return _cacheExpirationMin; } public void setCacheExpirationMin(long cacheExpirationMin) { _cacheExpirationMin = cacheExpirationMin; } public void setLocalAddress(final String localAddress) { _localAddress = localAddress; } public void setLocalHost(final String localHost) { _localHost = localHost; } public String getLocalAddress() { return _localAddress; } public String getLocalHost() { return _localHost; } public void setStartTLSEnabled(boolean startTLSEnabled) { _startTLSEnabled = startTLSEnabled; } public boolean getStartTLSEnabled() { return _startTLSEnabled; } public void setSocksProxyHost(final String socksProxyHost) { _socksProxyHost = socksProxyHost; } public void setSocksProxyPort(final String socksProxyPort) { _socksProxyPort = socksProxyPort; } public void setProxySet(final Boolean proxySet) { _proxySet = proxySet; } private String getSocksProxyHost() { return _socksProxyHost; } private String getSocksProxyPort() { return _socksProxyPort; } private Boolean getProxySet() { return _proxySet; } }