/****************************************************************
* 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;
import org.apache.avalon.cornerstone.services.store.Store;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.configuration.DefaultConfiguration;
import org.apache.avalon.framework.container.ContainerUtil;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.context.DefaultContext;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.logger.Logger;
import org.apache.avalon.framework.service.DefaultServiceManager;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.commons.collections.ReferenceMap;
import org.apache.james.context.AvalonContextUtilities;
import org.apache.james.core.MailHeaders;
import org.apache.james.core.MailImpl;
import org.apache.james.core.MailetConfigImpl;
import org.apache.james.services.DNSServer;
import org.apache.james.services.MailRepository;
import org.apache.james.services.MailServer;
import org.apache.james.services.SpoolRepository;
import org.apache.james.services.UsersRepository;
import org.apache.james.services.UsersStore;
import org.apache.james.transport.mailets.LocalDelivery;
import org.apache.james.userrepository.DefaultJamesUser;
import org.apache.mailet.Mail;
import org.apache.mailet.MailAddress;
import org.apache.mailet.Mailet;
import org.apache.mailet.MailetContext;
import org.apache.mailet.RFC2822Headers;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Vector;
/**
* Core class for JAMES. Provides three primary services:
* <br> 1) Instantiates resources, such as user repository, and protocol
* handlers
* <br> 2) Handles interactions between components
* <br> 3) Provides container services for Mailets
*
*
* @version This is $Revision: 494012 $
*/
public class James
extends AbstractLogEnabled
implements Contextualizable, Serviceable, Configurable, JamesMBean, Initializable, MailServer, MailetContext {
/**
* The software name and version
*/
private final static String SOFTWARE_NAME_VERSION = Constants.SOFTWARE_NAME + " " + Constants.SOFTWARE_VERSION;
/**
* The component manager used both internally by James and by Mailets.
*/
private DefaultServiceManager compMgr; //Components shared
/**
* TODO: Investigate what this is supposed to do. Looks like it
* was supposed to be the Mailet context.
*/
private DefaultContext context;
/**
* The top level configuration object for this server.
*/
private Configuration conf;
/**
* The logger used by the Mailet API.
*/
private Logger mailetLogger = null;
/**
* The mail store containing the inbox repository and the spool.
*/
private Store store;
/**
* The store containing the local user repository.
*/
private UsersStore usersStore;
/**
* The spool used for processing mail handled by this server.
*/
private SpoolRepository spool;
/**
* The root URL used to get mailboxes from the repository
*/
private String inboxRootURL;
/**
* The user repository for this mail server. Contains all the users with inboxes
* on this server.
*/
private UsersRepository localusers;
/**
* The collection of domain/server names for which this instance of James
* will receive and process mail.
*/
private Collection serverNames;
/**
* Whether to ignore case when looking up user names on this server
*/
private boolean ignoreCase;
/**
* The number of mails generated. Access needs to be synchronized for
* thread safety and to ensure that all threads see the latest value.
*/
private static long count;
/**
* The address of the postmaster for this server
*/
private MailAddress postmaster;
/**
* A map used to store mailboxes and reduce the cost of lookup of individual
* mailboxes.
*/
private Map mailboxes; //Not to be shared!
/**
* A hash table of server attributes
* These are the MailetContext attributes
*/
private Hashtable attributes = new Hashtable();
/**
* The Avalon context used by the instance
*/
protected Context myContext;
/**
* Currently used by storeMail to avoid code duplication (we moved store logic to that mailet).
* TODO We should remove this and its initialization when we remove storeMail method.
*/
protected Mailet localDeliveryMailet;
/**
* @see org.apache.avalon.framework.context.Contextualizable#contextualize(Context)
*/
public void contextualize(final Context context) {
this.myContext = context;
}
/**
* @see org.apache.avalon.framework.service.Serviceable#service(ServiceManager)
*/
public void service(ServiceManager comp) {
compMgr = new DefaultServiceManager(comp);
mailboxes = new ReferenceMap();
}
/**
* @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
*/
public void configure(Configuration conf) {
this.conf = conf;
}
/**
* @see org.apache.avalon.framework.activity.Initializable#initialize()
*/
public void initialize() throws Exception {
getLogger().info("JAMES init...");
// TODO: This should retrieve a more specific named thread pool from
// Context that is set up in server.xml
try {
store = (Store) compMgr.lookup( Store.ROLE );
} catch (Exception e) {
if (getLogger().isWarnEnabled()) {
getLogger().warn("Can't get Store: " + e);
}
}
if (getLogger().isDebugEnabled()) {
getLogger().debug("Using Store: " + store.toString());
}
try {
spool = (SpoolRepository) compMgr.lookup( SpoolRepository.ROLE );
} catch (Exception e) {
if (getLogger().isWarnEnabled()) {
getLogger().warn("Can't get spoolRepository: " + e);
}
}
if (getLogger().isDebugEnabled()) {
getLogger().debug("Using SpoolRepository: " + spool.toString());
}
try {
usersStore = (UsersStore) compMgr.lookup( UsersStore.ROLE );
} catch (Exception e) {
if (getLogger().isWarnEnabled()) {
getLogger().warn("Can't get Store: " + e);
}
}
if (getLogger().isDebugEnabled()) {
getLogger().debug("Using UsersStore: " + usersStore.toString());
}
String hostName = null;
try {
hostName = InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException ue) {
hostName = "localhost";
}
context = new DefaultContext();
context.put("HostName", hostName);
getLogger().info("Local host is: " + hostName);
// Get the domains and hosts served by this instance
serverNames = new HashSet();
Configuration serverConf = conf.getChild("servernames");
if (serverConf.getAttributeAsBoolean("autodetect") && (!hostName.equals("localhost"))) {
serverNames.add(hostName.toLowerCase(Locale.US));
}
final Configuration[] serverNameConfs =
conf.getChild( "servernames" ).getChildren( "servername" );
for ( int i = 0; i < serverNameConfs.length; i++ ) {
serverNames.add( serverNameConfs[i].getValue().toLowerCase(Locale.US));
if (serverConf.getAttributeAsBoolean("autodetectIP", true)) {
try {
/* This adds the IP address(es) for each host to support
* support <user@address-literal> - RFC 2821, sec 4.1.3.
* It might be proper to use the actual IP addresses
* available on this server, but we can't do that
* without NetworkInterface from JDK 1.4. Because of
* Virtual Hosting considerations, we may need to modify
* this to keep hostname and IP associated, rather than
* just both in the set.
*/
InetAddress[] addrs = InetAddress.getAllByName(serverNameConfs[i].getValue());
for (int j = 0; j < addrs.length ; j++) {
serverNames.add(addrs[j].getHostAddress());
}
}
catch(Exception genericException) {
getLogger().error("Cannot get IP address(es) for " + serverNameConfs[i].getValue());
}
}
}
if (serverNames.isEmpty()) {
throw new ConfigurationException( "Fatal configuration error: no servernames specified!");
}
if (getLogger().isInfoEnabled()) {
for (Iterator i = serverNames.iterator(); i.hasNext(); ) {
getLogger().info("Handling mail for: " + i.next());
}
}
String defaultDomain = (String) serverNames.iterator().next();
context.put(Constants.DEFAULT_DOMAIN, defaultDomain);
attributes.put(Constants.DEFAULT_DOMAIN, defaultDomain);
// Get postmaster
String postMasterAddress = conf.getChild("postmaster").getValue("postmaster").toLowerCase(Locale.US);
// if there is no @domain part, then add the first one from the
// list of supported domains that isn't localhost. If that
// doesn't work, use the hostname, even if it is localhost.
if (postMasterAddress.indexOf('@') < 0) {
String domainName = null; // the domain to use
// loop through candidate domains until we find one or exhaust the list
for ( int i = 0; domainName == null && i < serverNameConfs.length ; i++ ) {
String serverName = serverNameConfs[i].getValue().toLowerCase(Locale.US);
if (!("localhost".equals(serverName))) {
domainName = serverName; // ok, not localhost, so use it
}
}
// if we found a suitable domain, use it. Otherwise fallback to the host name.
postMasterAddress = postMasterAddress + "@" + (domainName != null ? domainName : hostName);
}
this.postmaster = new MailAddress( postMasterAddress );
context.put( Constants.POSTMASTER, postmaster );
if (!isLocalServer(postmaster.getHost())) {
StringBuffer warnBuffer
= new StringBuffer(320)
.append("The specified postmaster address ( ")
.append(postmaster)
.append(" ) is not a local address. This is not necessarily a problem, but it does mean that emails addressed to the postmaster will be routed to another server. For some configurations this may cause problems.");
getLogger().warn(warnBuffer.toString());
}
Configuration userNamesConf = conf.getChild("usernames");
ignoreCase = userNamesConf.getAttributeAsBoolean("ignoreCase", false);
boolean enableAliases = userNamesConf.getAttributeAsBoolean("enableAliases", false);
boolean enableForwarding = userNamesConf.getAttributeAsBoolean("enableForwarding", false);
attributes.put(Constants.DEFAULT_ENABLE_ALIASES,new Boolean(enableAliases));
attributes.put(Constants.DEFAULT_ENABLE_FORWARDING,new Boolean(enableForwarding));
attributes.put(Constants.DEFAULT_IGNORE_USERNAME_CASE,new Boolean(ignoreCase));
//Get localusers
try {
localusers = (UsersRepository) compMgr.lookup(UsersRepository.ROLE);
} catch (Exception e) {
getLogger().error("Cannot open private UserRepository");
throw e;
}
//}
compMgr.put( UsersRepository.ROLE, localusers);
getLogger().info("Local users repository opened");
Configuration inboxConf = conf.getChild("inboxRepository");
Configuration inboxRepConf = inboxConf.getChild("repository");
// we could delete this block. I didn't remove this because I'm not sure
// wether we need the "check" of the inbox repository here, or not.
try {
store.select(inboxRepConf);
} catch (Exception e) {
getLogger().error("Cannot open private MailRepository");
throw e;
}
inboxRootURL = inboxRepConf.getAttribute("destinationURL");
getLogger().info("Private Repository LocalInbox opened");
// Add this to comp
compMgr.put( MailServer.ROLE, this);
// For mailet engine provide MailetContext
//compMgr.put("org.apache.mailet.MailetContext", this);
// For AVALON aware mailets and matchers, we put the Component object as
// an attribute
attributes.put(Constants.AVALON_COMPONENT_MANAGER, compMgr);
//Temporary get out to allow complex mailet config files to stop blocking sergei sozonoff's work on bouce processing
java.io.File configDir = AvalonContextUtilities.getFile(myContext, "file://conf/");
attributes.put("confDir", configDir.getCanonicalPath());
// We can safely remove this and the localDeliveryField when we
// remove the storeMail method from James and from the MailetContext
DefaultConfiguration conf = new DefaultConfiguration("mailet", "generated:James.initialize()");
MailetConfigImpl configImpl = new MailetConfigImpl();
configImpl.setMailetName("LocalDelivery");
configImpl.setConfiguration(conf);
configImpl.setMailetContext(this);
localDeliveryMailet = new LocalDelivery();
localDeliveryMailet.init(configImpl);
System.out.println(SOFTWARE_NAME_VERSION);
getLogger().info("JAMES ...init end");
}
/**
* Place a mail on the spool for processing
*
* @param message the message to send
*
* @throws MessagingException if an exception is caught while placing the mail
* on the spool
*/
public void sendMail(MimeMessage message) throws MessagingException {
MailAddress sender = new MailAddress((InternetAddress)message.getFrom()[0]);
Collection recipients = new HashSet();
Address addresses[] = message.getAllRecipients();
if (addresses != null) {
for (int i = 0; i < addresses.length; i++) {
// Javamail treats the "newsgroups:" header field as a
// recipient, so we want to filter those out.
if ( addresses[i] instanceof InternetAddress ) {
recipients.add(new MailAddress((InternetAddress)addresses[i]));
}
}
}
sendMail(sender, recipients, message);
}
/**
* Place a mail on the spool for processing
*
* @param sender the sender of the mail
* @param recipients the recipients of the mail
* @param message the message to send
*
* @throws MessagingException if an exception is caught while placing the mail
* on the spool
*/
public void sendMail(MailAddress sender, Collection recipients, MimeMessage message)
throws MessagingException {
sendMail(sender, recipients, message, Mail.DEFAULT);
}
/**
* Place a mail on the spool for processing
*
* @param sender the sender of the mail
* @param recipients the recipients of the mail
* @param message the message to send
* @param state the state of the message
*
* @throws MessagingException if an exception is caught while placing the mail
* on the spool
*/
public void sendMail(MailAddress sender, Collection recipients, MimeMessage message, String state)
throws MessagingException {
MailImpl mail = new MailImpl(getId(), sender, recipients, message);
try {
mail.setState(state);
sendMail(mail);
} finally {
ContainerUtil.dispose(mail);
}
}
/**
* Place a mail on the spool for processing
*
* @param sender the sender of the mail
* @param recipients the recipients of the mail
* @param msg an <code>InputStream</code> containing the message
*
* @throws MessagingException if an exception is caught while placing the mail
* on the spool
*/
public void sendMail(MailAddress sender, Collection recipients, InputStream msg)
throws MessagingException {
// parse headers
MailHeaders headers = new MailHeaders(msg);
// if headers do not contains minimum REQUIRED headers fields throw Exception
if (!headers.isValid()) {
throw new MessagingException("Some REQURED header field is missing. Invalid Message");
}
ByteArrayInputStream headersIn = new ByteArrayInputStream(headers.toByteArray());
sendMail(new MailImpl(getId(), sender, recipients, new SequenceInputStream(headersIn, msg)));
}
/**
* Place a mail on the spool for processing
*
* @param mail the mail to place on the spool
*
* @throws MessagingException if an exception is caught while placing the mail
* on the spool
*/
public void sendMail(Mail mail) throws MessagingException {
try {
spool.store(mail);
} catch (Exception e) {
getLogger().error("Error storing message: " + e.getMessage(),e);
try {
spool.remove(mail);
} catch (Exception ignored) {
getLogger().error("Error removing message after an error storing it: " + e.getMessage(),e);
}
throw new MessagingException("Exception spooling message: " + e.getMessage(), e);
}
if (getLogger().isDebugEnabled()) {
StringBuffer logBuffer =
new StringBuffer(64)
.append("Mail ")
.append(mail.getName())
.append(" pushed in spool");
getLogger().debug(logBuffer.toString());
}
}
/**
* <p>Retrieve the mail repository for a user</p>
*
* <p>For POP3 server only - at the moment.</p>
*
* @param userName the name of the user whose inbox is to be retrieved
*
* @return the POP3 inbox for the user
*/
public synchronized MailRepository getUserInbox(String userName) {
MailRepository userInbox = null;
userInbox = (MailRepository) mailboxes.get(userName);
if (userInbox != null) {
return userInbox;
} else if (mailboxes.containsKey(userName)) {
// we have a problem
getLogger().error("Null mailbox for non-null key");
throw new RuntimeException("Error in getUserInbox.");
} else {
// need mailbox object
if (getLogger().isDebugEnabled()) {
getLogger().debug("Retrieving and caching inbox for " + userName );
}
StringBuffer destinationBuffer =
new StringBuffer(192)
.append(inboxRootURL)
.append(userName)
.append("/");
String destination = destinationBuffer.toString();
DefaultConfiguration mboxConf
= new DefaultConfiguration("repository", "generated:AvalonFileRepository.compose()");
mboxConf.setAttribute("destinationURL", destination);
mboxConf.setAttribute("type", "MAIL");
try {
userInbox = (MailRepository) store.select(mboxConf);
if (userInbox!=null) {
mailboxes.put(userName, userInbox);
}
} catch (Exception e) {
if (getLogger().isErrorEnabled())
{
getLogger().error("Cannot open user Mailbox" + e);
}
throw new RuntimeException("Error in getUserInbox." + e);
}
return userInbox;
}
}
/**
* Return a new mail id.
*
* @return a new mail id
*/
public String getId() {
long localCount = -1;
synchronized (James.class) {
localCount = count++;
}
StringBuffer idBuffer =
new StringBuffer(64)
.append("Mail")
.append(System.currentTimeMillis())
.append("-")
.append(localCount);
return idBuffer.toString();
}
/**
* The main method. Should never be invoked, as James must be called
* from within an Avalon framework container.
*
* @param args the command line arguments
*/
public static void main(String[] args) {
System.out.println("ERROR!");
System.out.println("Cannot execute James as a stand alone application.");
System.out.println("To run James, you need to have the Avalon framework installed.");
System.out.println("Please refer to the Readme file to know how to run James.");
}
//Methods for MailetContext
/**
* <p>Get the prioritized list of mail servers for a given host.</p>
*
* <p>TODO: This needs to be made a more specific ordered subtype of Collection.</p>
*
* @param host
*/
public Collection getMailServers(String host) {
DNSServer dnsServer = null;
try {
dnsServer = (DNSServer) compMgr.lookup( DNSServer.ROLE );
} catch ( final ServiceException cme ) {
getLogger().error("Fatal configuration error - DNS Servers lost!", cme );
throw new RuntimeException("Fatal configuration error - DNS Servers lost!");
}
return dnsServer.findMXRecords(host);
}
public Object getAttribute(String key) {
return attributes.get(key);
}
public void setAttribute(String key, Object object) {
attributes.put(key, object);
}
public void removeAttribute(String key) {
attributes.remove(key);
}
public Iterator getAttributeNames() {
Vector names = new Vector();
for (Enumeration e = attributes.keys(); e.hasMoreElements(); ) {
names.add(e.nextElement());
}
return names.iterator();
}
/**
* This generates a response to the Return-Path address, or the address of
* the message's sender if the Return-Path is not available. Note that
* this is different than a mail-client's reply, which would use the
* Reply-To or From header. This will send the bounce with the server's
* postmaster as the sender.
*/
public void bounce(Mail mail, String message) throws MessagingException {
bounce(mail, message, getPostmaster());
}
/**
* This generates a response to the Return-Path address, or the
* address of the message's sender if the Return-Path is not
* available. Note that this is different than a mail-client's
* reply, which would use the Reply-To or From header.
*
* Bounced messages are attached in their entirety (headers and
* content) and the resulting MIME part type is "message/rfc822".
*
* The attachment to the subject of the original message (or "No
* Subject" if there is no subject in the original message)
*
* There are outstanding issues with this implementation revolving
* around handling of the return-path header.
*
* MIME layout of the bounce message:
*
* multipart (mixed)/
* contentPartRoot (body) = mpContent (alternative)/
* part (body) = message
* part (body) = original
*
*/
public void bounce(Mail mail, String message, MailAddress bouncer) throws MessagingException {
if (mail.getSender() == null) {
if (getLogger().isInfoEnabled())
getLogger().info("Mail to be bounced contains a null (<>) reverse path. No bounce will be sent.");
return;
} else {
// Bounce message goes to the reverse path, not to the Reply-To address
if (getLogger().isInfoEnabled())
getLogger().info("Processing a bounce request for a message with a reverse path of " + mail.getSender().toString());
}
MailImpl reply = rawBounce(mail,message);
//Change the sender...
reply.getMessage().setFrom(bouncer.toInternetAddress());
reply.getMessage().saveChanges();
//Send it off ... with null reverse-path
reply.setSender(null);
sendMail(reply);
ContainerUtil.dispose(reply);
}
/**
* Generates a bounce mail that is a bounce of the original message.
*
* @param bounceText the text to be prepended to the message to describe the bounce condition
*
* @return the bounce mail
*
* @throws MessagingException if the bounce mail could not be created
*/
private MailImpl rawBounce(Mail mail, String bounceText) throws MessagingException {
//This sends a message to the james component that is a bounce of the sent message
MimeMessage original = mail.getMessage();
MimeMessage reply = (MimeMessage) original.reply(false);
reply.setSubject("Re: " + original.getSubject());
reply.setSentDate(new Date());
Collection recipients = new HashSet();
recipients.add(mail.getSender());
InternetAddress addr[] = { new InternetAddress(mail.getSender().toString())};
reply.setRecipients(Message.RecipientType.TO, addr);
reply.setFrom(new InternetAddress(mail.getRecipients().iterator().next().toString()));
reply.setText(bounceText);
reply.setHeader(RFC2822Headers.MESSAGE_ID, "replyTo-" + mail.getName());
return new MailImpl(
"replyTo-" + mail.getName(),
new MailAddress(mail.getRecipients().iterator().next().toString()),
recipients,
reply);
}
/**
* Returns whether that account has a local inbox on this server
*
* @param name the name to be checked
*
* @return whether the account has a local inbox
*/
public boolean isLocalUser(String name) {
if (ignoreCase) {
return localusers.containsCaseInsensitive(name);
} else {
return localusers.contains(name);
}
}
/**
* Returns the address of the postmaster for this server.
*
* @return the <code>MailAddress</code> for the postmaster
*/
public MailAddress getPostmaster() {
return postmaster;
}
/**
* Return the major version number for the server
*
* @return the major vesion number for the server
*/
public int getMajorVersion() {
return 2;
}
/**
* Return the minor version number for the server
*
* @return the minor vesion number for the server
*/
public int getMinorVersion() {
return 3;
}
/**
* Check whether the mail domain in question is to be
* handled by this server.
*
* @param serverName the name of the server to check
* @return whether the server is local
*/
public boolean isLocalServer( final String serverName ) {
return serverNames.contains(serverName.toLowerCase(Locale.US));
}
/**
* Return the type of the server
*
* @return the type of the server
*/
public String getServerInfo() {
return "Apache JAMES";
}
/**
* Return the logger for the Mailet API
*
* @return the logger for the Mailet API
*/
private Logger getMailetLogger() {
if (mailetLogger == null) {
mailetLogger = getLogger().getChildLogger("Mailet");
}
return mailetLogger;
}
/**
* Log a message to the Mailet logger
*
* @param message the message to pass to the Mailet logger
*/
public void log(String message) {
getMailetLogger().info(message);
}
/**
* Log a message and a Throwable to the Mailet logger
*
* @param message the message to pass to the Mailet logger
* @param t the <code>Throwable</code> to be logged
*/
public void log(String message, Throwable t) {
getMailetLogger().info(message,t);
}
/**
* Adds a user to this mail server. Currently just adds user to a
* UsersRepository.
*
* @param userName String representing user name, that is the portion of
* an email address before the '@<domain>'.
* @param password String plaintext password
* @return boolean true if user added succesfully, else false.
*
* @deprecated we deprecated this in the MailServer interface and this is an implementation
* this component depends already depends on a UsersRepository: clients could directly
* use the addUser of the usersRepository.
*/
public boolean addUser(String userName, String password) {
boolean success;
DefaultJamesUser user = new DefaultJamesUser(userName, "SHA");
user.setPassword(password);
user.initialize();
success = localusers.addUser(user);
return success;
}
/**
* Performs DNS lookups as needed to find servers which should or might
* support SMTP.
* Returns an Iterator over HostAddress, a specialized subclass of
* javax.mail.URLName, which provides location information for
* servers that are specified as mail handlers for the given
* hostname. This is done using MX records, and the HostAddress
* instances are returned sorted by MX priority. If no host is
* found for domainName, the Iterator returned will be empty and the
* first call to hasNext() will return false.
*
* @see org.apache.james.DNSServer#getSMTPHostAddresses(String)
* @since Mailet API v2.2.0a16-unstable
* @param domainName - the domain for which to find mail servers
* @return an Iterator over HostAddress instances, sorted by priority
*/
public Iterator getSMTPHostAddresses(String domainName) {
DNSServer dnsServer = null;
try {
dnsServer = (DNSServer) compMgr.lookup( DNSServer.ROLE );
} catch ( final ServiceException cme ) {
getLogger().error("Fatal configuration error - DNS Servers lost!", cme );
throw new RuntimeException("Fatal configuration error - DNS Servers lost!");
}
return dnsServer.getSMTPHostAddresses(domainName);
}
/**
* This method has been moved to LocalDelivery (the only client of the method).
* Now we can safely remove it from the Mailet API and from this implementation of MailetContext.
*
* The local field localDeliveryMailet will be removed when we remove the storeMail method.
*
* @deprecated since 2.2.0 look at the LocalDelivery code to find out how to do the local delivery.
* @see org.apache.mailet.MailetContext#storeMail(org.apache.mailet.MailAddress, org.apache.mailet.MailAddress, javax.mail.internet.MimeMessage)
*/
public void storeMail(MailAddress sender, MailAddress recipient, MimeMessage msg) throws MessagingException {
if (recipient == null) {
throw new IllegalArgumentException("Recipient for mail to be spooled cannot be null.");
}
if (msg == null) {
throw new IllegalArgumentException("Mail message to be spooled cannot be null.");
}
Collection recipients = new HashSet();
recipients.add(recipient);
MailImpl m = new MailImpl(getId(),sender,recipients,msg);
localDeliveryMailet.service(m);
ContainerUtil.dispose(m);
}
}
|