Author: jboynes Date: Mon Jan 24 22:35:47 2005 New Revision: 126350 URL: http://svn.apache.org/viewcvs?view=rev&rev=126350 Log: second part of JavaMail; use different protocols for store and transport in test Added: geronimo/trunk/specs/javamail/src/java/javax/mail/EventQueue.java Modified: geronimo/trunk/modules/mail/src/test-resources/META-INF/javamail.default.providers geronimo/trunk/modules/mail/src/test/org/apache/geronimo/mail/MailGBeanTest.java geronimo/trunk/specs/javamail/src/java/javax/mail/Authenticator.java geronimo/trunk/specs/javamail/src/java/javax/mail/Folder.java geronimo/trunk/specs/javamail/src/java/javax/mail/Message.java geronimo/trunk/specs/javamail/src/java/javax/mail/MessageContext.java geronimo/trunk/specs/javamail/src/java/javax/mail/Multipart.java geronimo/trunk/specs/javamail/src/java/javax/mail/Part.java geronimo/trunk/specs/javamail/src/java/javax/mail/PasswordAuthentication.java geronimo/trunk/specs/javamail/src/java/javax/mail/Provider.java geronimo/trunk/specs/javamail/src/java/javax/mail/Service.java geronimo/trunk/specs/javamail/src/java/javax/mail/Session.java geronimo/trunk/specs/javamail/src/java/javax/mail/Store.java geronimo/trunk/specs/javamail/src/java/javax/mail/Transport.java geronimo/trunk/specs/javamail/src/java/javax/mail/UIDFolder.java
Modified: geronimo/trunk/modules/mail/src/test-resources/META-INF/javamail.default.providers Url: http://svn.apache.org/viewcvs/geronimo/trunk/modules/mail/src/test-resources/META-INF/javamail.default.providers?view=diff&rev=126350&p1=geronimo/trunk/modules/mail/src/test-resources/META-INF/javamail.default.providers&r1=126349&p2=geronimo/trunk/modules/mail/src/test-resources/META-INF/javamail.default.providers&r2=126350 ============================================================================== --- geronimo/trunk/modules/mail/src/test-resources/META-INF/javamail.default.providers (original) +++ geronimo/trunk/modules/mail/src/test-resources/META-INF/javamail.default.providers Mon Jan 24 22:35:47 2005 @@ -1,2 +1,2 @@ -protocol=test;type=store;class=org.apache.geronimo.mail.TestStore;vendor=The Apache Software Foundation;version=test; -protocol=test;type=transport;class=org.apache.geronimo.mail.TestTransport;vendor=The Apache Software Foundation;version=test; +protocol=testStore;type=store;class=org.apache.geronimo.mail.TestStore;vendor=The Apache Software Foundation;version=test; +protocol=testTransport;type=transport;class=org.apache.geronimo.mail.TestTransport;vendor=The Apache Software Foundation;version=test; Modified: geronimo/trunk/modules/mail/src/test/org/apache/geronimo/mail/MailGBeanTest.java Url: http://svn.apache.org/viewcvs/geronimo/trunk/modules/mail/src/test/org/apache/geronimo/mail/MailGBeanTest.java?view=diff&rev=126350&p1=geronimo/trunk/modules/mail/src/test/org/apache/geronimo/mail/MailGBeanTest.java&r1=126349&p2=geronimo/trunk/modules/mail/src/test/org/apache/geronimo/mail/MailGBeanTest.java&r2=126350 ============================================================================== --- geronimo/trunk/modules/mail/src/test/org/apache/geronimo/mail/MailGBeanTest.java (original) +++ geronimo/trunk/modules/mail/src/test/org/apache/geronimo/mail/MailGBeanTest.java Mon Jan 24 22:35:47 2005 @@ -41,8 +41,8 @@ public void testProperties() throws Exception { Properties properties = new Properties(); - properties.put("mail.store.protocol", "test"); - properties.put("mail.transport.protocol", "test"); + properties.put("mail.store.protocol", "testStore"); + properties.put("mail.transport.protocol", "testTransport"); GBeanMBean cmf = new GBeanMBean(MailGBean.getGBeanInfo()); cmf.setAttribute("useDefault", new Boolean(true)); @@ -200,8 +200,8 @@ cmf.setReferencePattern("Protocols", new ObjectName("geronimo.server:J2EEServer=geronimo,J2EEApplication=null,type=JavaMailProtocol,*")); cmf.setAttribute("useDefault", new Boolean(true)); cmf.setAttribute("properties", properties); - cmf.setAttribute("storeProtocol", "test"); - cmf.setAttribute("transportProtocol", "test"); + cmf.setAttribute("storeProtocol", "testStore"); + cmf.setAttribute("transportProtocol", "testTransport"); mailName = ObjectName.getInstance("geronimo.server:J2EEServer=geronimo,J2EEApplication=null,J2EEType=JavaMailResource,name=default"); Modified: geronimo/trunk/specs/javamail/src/java/javax/mail/Authenticator.java Url: http://svn.apache.org/viewcvs/geronimo/trunk/specs/javamail/src/java/javax/mail/Authenticator.java?view=diff&rev=126350&p1=geronimo/trunk/specs/javamail/src/java/javax/mail/Authenticator.java&r1=126349&p2=geronimo/trunk/specs/javamail/src/java/javax/mail/Authenticator.java&r2=126350 ============================================================================== --- geronimo/trunk/specs/javamail/src/java/javax/mail/Authenticator.java (original) +++ geronimo/trunk/specs/javamail/src/java/javax/mail/Authenticator.java Mon Jan 24 22:35:47 2005 @@ -23,13 +23,23 @@ * @version $Rev$ $Date$ */ public abstract class Authenticator { - private InetAddress _host; - private int _port; - private String _prompt; - private String _protocol; + private InetAddress host; + private int port; + private String prompt; + private String protocol; + private String username; + + synchronized PasswordAuthentication authenticate(InetAddress host, int port, String protocol, String prompt, String username) { + this.host = host; + this.port = port; + this.protocol = protocol; + this.prompt = prompt; + this.username = username; + return getPasswordAuthentication(); + } protected final String getDefaultUserName() { - return System.getProperty("mail.user"); + return username; } protected PasswordAuthentication getPasswordAuthentication() { @@ -37,18 +47,18 @@ } protected final int getRequestingPort() { - return _port; + return port; } protected final String getRequestingPrompt() { - return _prompt; + return prompt; } protected final String getRequestingProtocol() { - return _protocol; + return protocol; } protected final InetAddress getRequestingSite() { - return _host; + return host; } } Added: geronimo/trunk/specs/javamail/src/java/javax/mail/EventQueue.java Url: http://svn.apache.org/viewcvs/geronimo/trunk/specs/javamail/src/java/javax/mail/EventQueue.java?view=auto&rev=126350 ============================================================================== --- (empty file) +++ geronimo/trunk/specs/javamail/src/java/javax/mail/EventQueue.java Mon Jan 24 22:35:47 2005 @@ -0,0 +1,41 @@ +/** + * + * Copyright 2003-2004 The Apache Software Foundation + * + * 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. + */ + +// +// This source code implements specifications defined by the Java +// Community Process. In order to remain compliant with the specification +// DO NOT add / change / or delete method signatures! +// +package javax.mail; + +import java.util.List; +import javax.mail.event.MailEvent; + +/** + * @version $Rev$ $Date$ + */ +class EventQueue { + // todo replace with version based on a work queue from Concurrent + void queueEvent(MailEvent event, List listeners) { + for (int i = 0; i < listeners.size(); i++) { + event.dispatch(listeners.get(i)); + } + } + + void stop() { + } +} Modified: geronimo/trunk/specs/javamail/src/java/javax/mail/Folder.java Url: http://svn.apache.org/viewcvs/geronimo/trunk/specs/javamail/src/java/javax/mail/Folder.java?view=diff&rev=126350&p1=geronimo/trunk/specs/javamail/src/java/javax/mail/Folder.java&r1=126349&p2=geronimo/trunk/specs/javamail/src/java/javax/mail/Folder.java&r2=126350 ============================================================================== --- geronimo/trunk/specs/javamail/src/java/javax/mail/Folder.java (original) +++ geronimo/trunk/specs/javamail/src/java/javax/mail/Folder.java Mon Jan 24 22:35:47 2005 @@ -18,15 +18,12 @@ package javax.mail; import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; import java.util.List; import javax.mail.Flags.Flag; import javax.mail.event.ConnectionEvent; import javax.mail.event.ConnectionListener; import javax.mail.event.FolderEvent; import javax.mail.event.FolderListener; -import javax.mail.event.MailEvent; import javax.mail.event.MessageChangedEvent; import javax.mail.event.MessageChangedListener; import javax.mail.event.MessageCountEvent; @@ -84,6 +81,7 @@ private final List folderListeners = new ArrayList(2); private final List messageChangedListeners = new ArrayList(2); private final List messageCountListeners = new ArrayList(2); + private final EventQueue queue = new EventQueue(); /** * Constructor that initializes the Store. @@ -644,7 +642,7 @@ } protected void notifyConnectionListeners(int type) { - notifyListeners(connectionListeners, new ConnectionEvent(this, type)); + queue.queueEvent(new ConnectionEvent(this, type), connectionListeners); } public void addFolderListener(FolderListener listener) { @@ -656,11 +654,11 @@ } protected void notifyFolderListeners(int type) { - notifyListeners(folderListeners, new FolderEvent(this, this, type)); + queue.queueEvent(new FolderEvent(this, this, type), folderListeners); } protected void notifyFolderRenamedListeners(Folder newFolder) { - notifyListeners(folderListeners, new FolderEvent(this, this, newFolder, FolderEvent.RENAMED)); + queue.queueEvent(new FolderEvent(this, this, newFolder, FolderEvent.RENAMED), folderListeners); } public void addMessageCountListener(MessageCountListener listener) { @@ -672,13 +670,11 @@ } protected void notifyMessageAddedListeners(Message[] messages) { - MailEvent event = new MessageCountEvent(this, MessageCountEvent.ADDED, false, messages); - notifyListeners(messageChangedListeners, event); + queue.queueEvent(new MessageCountEvent(this, MessageCountEvent.ADDED, false, messages), messageChangedListeners); } protected void notifyMessageRemovedListeners(boolean removed, Message[] messages) { - MessageCountEvent event = new MessageCountEvent(this, MessageCountEvent.REMOVED, removed, messages); - notifyListeners(messageChangedListeners, event); + queue.queueEvent(new MessageCountEvent(this, MessageCountEvent.REMOVED, removed, messages), messageChangedListeners); } public void addMessageChangedListener(MessageChangedListener listener) { @@ -690,21 +686,14 @@ } protected void notifyMessageChangedListeners(int type, Message message) { - MessageChangedEvent event = new MessageChangedEvent(this, type, message); - notifyListeners(messageChangedListeners, event); - } - - private void notifyListeners(Collection listeners, MailEvent event) { - // todo do we need to dispatch these events using a different thread? - for (Iterator iterator = listeners.iterator(); iterator.hasNext();) { - event.dispatch(iterator.next()); - } + queue.queueEvent(new MessageChangedEvent(this, type, message), messageChangedListeners); } /** * Unregisters all listeners. */ protected void finalize() throws Throwable { + queue.stop(); connectionListeners.clear(); folderListeners.clear(); messageChangedListeners.clear(); Modified: geronimo/trunk/specs/javamail/src/java/javax/mail/Message.java Url: http://svn.apache.org/viewcvs/geronimo/trunk/specs/javamail/src/java/javax/mail/Message.java?view=diff&rev=126350&p1=geronimo/trunk/specs/javamail/src/java/javax/mail/Message.java&r1=126349&p2=geronimo/trunk/specs/javamail/src/java/javax/mail/Message.java&r2=126350 ============================================================================== --- geronimo/trunk/specs/javamail/src/java/javax/mail/Message.java (original) +++ geronimo/trunk/specs/javamail/src/java/javax/mail/Message.java Mon Jan 24 22:35:47 2005 @@ -17,21 +17,32 @@ package javax.mail; +import java.io.InvalidObjectException; import java.io.ObjectStreamException; import java.io.Serializable; import java.util.Date; -import java.util.LinkedList; -import java.util.List; import javax.mail.search.SearchTerm; /** * @version $Rev$ $Date$ */ public abstract class Message implements Part { + /** + * Enumeration of types of recipients allowed by the Message class. + */ public static class RecipientType implements Serializable { - public static final RecipientType BCC = new RecipientType("Bcc"); - public static final RecipientType CC = new RecipientType("Cc"); + /** + * A "To" or primary recipient. + */ public static final RecipientType TO = new RecipientType("To"); + /** + * A "Cc" or carbon-copy recipient. + */ + public static final RecipientType CC = new RecipientType("Cc"); + /** + * A "Bcc" or blind carbon-copy recipient. + */ + public static final RecipientType BCC = new RecipientType("Bcc"); protected String type; protected RecipientType(String type) { @@ -46,7 +57,7 @@ } else if (type.equals("Bcc")) { return BCC; } else { - return this; + throw new InvalidObjectException("Invalid RecipientType: " + type); } } @@ -55,132 +66,365 @@ } } - private static final Address[] ADDRESS_ARRAY = new Address[0]; + /** + * The index of a message withing its folder, or zero if the message was not retrieved from a folder. + */ + protected int msgnum; + /** + * True if this message has been expunged from the Store. + */ protected boolean expunged; + /** + * The [EMAIL PROTECTED] Folder} that contains this message, or null if it was not obtained from a folder. + */ protected Folder folder; - protected int msgnum; + /** + * The [EMAIL PROTECTED] Session} associated with this message. + */ protected Session session; + /** + * Default constructor. + */ protected Message() { } - protected Message(Folder folder, int number) { + /** + * Constructor initializing folder and message msgnum; intended to be used by implementations of Folder. + * + * @param folder the folder that contains the message + * @param msgnum the message index within the folder + */ + protected Message(Folder folder, int msgnum) { this.folder = folder; - this.msgnum = number; + this.msgnum = msgnum; } + /** + * Constructor initializing the session; intended to by used by client created instances. + * + * @param session the session associated with this message + */ protected Message(Session session) { this.session = session; } - public abstract void addFrom(Address[] addresses) - throws MessagingException; + /** + * Return the "From" header indicating the identity of the person who the message is from; + * in some circumstances this may be different to the actual sender. + * + * @return a list of addresses this message is from; may be empty if the header is present but empty, or null + * if the header is not present + * @throws MessagingException if there was a problem accessing the store + */ + public abstract Address[] getFrom() throws MessagingException; - public void addRecipient(RecipientType type, Address address) - throws MessagingException { - addRecipients(type, new Address[]{address}); - } + /** + * Set the "From" header for this message to the value of the "mail.user" property, + * of if that property is not set, to the value of the system property "user.name" + * + * @throws MessagingException if there was a problem accessing the store + */ + public abstract void setFrom() throws MessagingException; - public abstract void addRecipients(RecipientType type, Address[] addresses) - throws MessagingException; + /** + * Set the "From" header to the supplied address. + * + * @param address the address of the person who the message is from + * @throws MessagingException if there was a problem accessing the store + */ + public abstract void setFrom(Address address) throws MessagingException; + /** + * Add multiple addresses to the "From" header. + * + * @param addresses the addresses to add + * @throws MessagingException if there was a problem accessing the store + */ + public abstract void addFrom(Address[] addresses) throws MessagingException; + + /** + * Get all recipients of the given type. + * + * @param type the type of recipient to get + * @return a list of addresses; may be empty if the header is present but empty, + * or null if the header is not present + * @throws MessagingException if there was a problem accessing the store + * @see RecipientType + */ + public abstract Address[] getRecipients(RecipientType type) throws MessagingException; + + /** + * Get all recipients of this message. + * The default implementation extracts the To, Cc, and Bcc recipients using [EMAIL PROTECTED] #getRecipients(javax.mail.Message.RecipientType)} + * and then concatentates the results into a single array; it returns null if no headers are defined. + * + * @return an array containing all recipients + * @throws MessagingException if there was a problem accessing the store + */ public Address[] getAllRecipients() throws MessagingException { Address[] to = getRecipients(RecipientType.TO); Address[] cc = getRecipients(RecipientType.CC); Address[] bcc = getRecipients(RecipientType.BCC); - List result = new LinkedList(); - for (int id = 0; to != null && id < to.length; id++) { - result.add(to[id]); + if (to == null && cc == null && bcc == null) { + return null; } - for (int id = 0; cc != null && id < cc.length; id++) { - result.add(cc[id]); + int length = (to != null ? to.length : 0) + (cc != null ? cc.length : 0) + (bcc != null ? bcc.length : 0); + Address[] result = new Address[length]; + int j = 0; + if (to != null) { + for (int i = 0; i < to.length; i++) { + result[j++] = to[i]; + } } - for (int id = 0; bcc != null && id < bcc.length; id++) { - result.add(bcc[id]); + if (cc != null) { + for (int i = 0; i < cc.length; i++) { + result[j++] = cc[i]; + } + } + if (bcc != null) { + for (int i = 0; i < bcc.length; i++) { + result[j++] = bcc[i]; + } } - return (Address[]) result.toArray(ADDRESS_ARRAY); + return result; } - public abstract Flags getFlags() throws MessagingException; - - public Folder getFolder() { - return folder; + /** + * Set the list of recipients for the specified type. + * + * @param type the type of recipient + * @param addresses the new addresses + * @throws MessagingException if there was a problem accessing the store + */ + public abstract void setRecipients(RecipientType type, Address[] addresses) throws MessagingException; + + /** + * Set the list of recipients for the specified type to a single address. + * + * @param type the type of recipient + * @param address the new address + * @throws MessagingException if there was a problem accessing the store + */ + public void setRecipient(RecipientType type, Address address) throws MessagingException { + setRecipients(type, new Address[]{address}); } - public abstract Address[] getFrom() throws MessagingException; - - public int getMessageNumber() { - return msgnum; + /** + * Add recipents of a specified type. + * + * @param type the type of recipient + * @param addresses the addresses to add + * @throws MessagingException if there was a problem accessing the store + */ + public abstract void addRecipients(RecipientType type, Address[] addresses) throws MessagingException; + + /** + * Add a recipent of a specified type. + * + * @param type the type of recipient + * @param address the address to add + * @throws MessagingException if there was a problem accessing the store + */ + public void addRecipient(RecipientType type, Address address) throws MessagingException { + addRecipients(type, new Address[]{address}); } - public abstract Date getReceivedDate() throws MessagingException; - - public abstract Address[] getRecipients(RecipientType type) - throws MessagingException; - + /** + * Get the addresses to which replies should be directed. + * <p/> + * As the most common behavior is to return to sender, the default implementation + * simply calls [EMAIL PROTECTED] #getFrom()}. + * + * @return a list of addresses to which replies should be directed + * @throws MessagingException if there was a problem accessing the store + */ public Address[] getReplyTo() throws MessagingException { return getFrom(); } - public abstract Date getSentDate() throws MessagingException; + /** + * Set the addresses to which replies should be directed. + * <p/> + * The default implementation throws a MethodNotSupportedException. + * + * @param addresses to which replies should be directed + * @throws MessagingException if there was a problem accessing the store + */ + public void setReplyTo(Address[] addresses) throws MessagingException { + throw new MethodNotSupportedException("setReplyTo not supported"); + } + /** + * Get the subject for this message. + * + * @return the subject + * @throws MessagingException if there was a problem accessing the store + */ public abstract String getSubject() throws MessagingException; - public boolean isExpunged() { - return expunged; - } + /** + * Set the subject of this message + * + * @param subject the subject + * @throws MessagingException if there was a problem accessing the store + */ + public abstract void setSubject(String subject) throws MessagingException; - public boolean isSet(Flags.Flag flag) throws MessagingException { - return getFlags().contains(flag); - } + /** + * Return the date that this message was sent. + * + * @return the date this message was sent + * @throws MessagingException if there was a problem accessing the store + */ + public abstract Date getSentDate() throws MessagingException; - public boolean match(SearchTerm term) throws MessagingException { - return term.match(this); - } + /** + * Set the date this message was sent. + * + * @param sent the date when this message was sent + * @throws MessagingException if there was a problem accessing the store + */ + public abstract void setSentDate(Date sent) throws MessagingException; - public abstract Message reply(boolean replyToAll) - throws MessagingException; + /** + * Return the date this message was received. + * + * @return the date this message was received + * @throws MessagingException if there was a problem accessing the store + */ + public abstract Date getReceivedDate() throws MessagingException; - public abstract void saveChanges() throws MessagingException; + /** + * Return a copy the flags associated with this message. + * + * @return a copy of the flags for this message + * @throws MessagingException if there was a problem accessing the store + */ + public abstract Flags getFlags() throws MessagingException; - protected void setExpunged(boolean expunged) { - this.expunged = expunged; + /** + * Check whether the supplied flag is set. + * The default implementation checks the flags returned by [EMAIL PROTECTED] #getFlags()}. + * + * @param flag the flags to check for + * @return true if the flags is set + * @throws MessagingException if there was a problem accessing the store + */ + public boolean isSet(Flags.Flag flag) throws MessagingException { + return getFlags().contains(flag); } - public void setFlag(Flags.Flag flag, boolean set) - throws MessagingException { - Flags flags = getFlags(); - if (set) { - flags.add(flag); - } else { - flags.remove(flag); - } + /** + * Set the flags specified to the supplied value; flags not included in the + * supplied [EMAIL PROTECTED] Flags} parameter are not affected. + * + * @param flags the flags to modify + * @param set the new value of those flags + * @throws MessagingException if there was a problem accessing the store + */ + public abstract void setFlags(Flags flags, boolean set) throws MessagingException; + + /** + * Set a flag to the supplied value. + * The default implmentation uses [EMAIL PROTECTED] #setFlags(Flags, boolean)}. + * + * @param flag the flag to set + * @param set the value for that flag + * @throws MessagingException if there was a problem accessing the store + */ + public void setFlag(Flags.Flag flag, boolean set) throws MessagingException { + setFlags(new Flags(flag), set); + } + + /** + * Return the message number for this Message. + * This number refers to the relative position of this message in a Folder; the message + * number for any given message can change during a seesion if the Folder is expunged. + * Message numbers for messages in a folder start at one; the value zero indicates that + * this message does not belong to a folder. + * + * @return the message number + */ + public int getMessageNumber() { + return msgnum; } - public abstract void setFlags(Flags flags, boolean set) - throws MessagingException; - - public abstract void setFrom() throws MessagingException; - - public abstract void setFrom(Address address) throws MessagingException; - + /** + * Set the message number for this Message. + * This must be invoked by implementation classes when the message number changes. + * + * @param number the new message number + */ protected void setMessageNumber(int number) { msgnum = number; } - public void setRecipient(RecipientType type, Address address) - throws MessagingException { - setRecipients(type, new Address[]{address}); + /** + * Return the folder containing this message. If this is a new or nested message + * then this method returns null. + * + * @return the folder containing this message + */ + public Folder getFolder() { + return folder; } - public abstract void setRecipients(RecipientType type, Address[] addresses) - throws MessagingException; + /** + * Checks to see if this message has been expunged. If true, all methods other than + * [EMAIL PROTECTED] #getMessageNumber()} are invalid. + * + * @return true if this method has been expunged + */ + public boolean isExpunged() { + return expunged; + } - public void setReplyTo(Address[] addresses) throws MessagingException { - throw new MethodNotSupportedException("setReplyTo not implemented"); + /** + * Set the expunged flag for this message. + * + * @param expunged true if this message has been expunged + */ + protected void setExpunged(boolean expunged) { + this.expunged = expunged; } - public abstract void setSentDate(Date sent) throws MessagingException; + /** + * Create a new message suitable as a reply to this message with all headers set + * up appropriately. The message body will be empty. + * <p/> + * if replyToAll is set then the new message will be addressed to all recipients + * of this message; otherwise the reply will be addressed only to the sender as + * returned by [EMAIL PROTECTED] #getReplyTo()}. + * <p/> + * The subject field will be initialized with the subject field from the orginal + * message; the text "Re:" will be prepended unless it is already present. + * + * @param replyToAll if true, indciates the message should be addressed to all recipients not just the sender + * @return a new message suitable as a reply to this message + * @throws MessagingException if there was a problem accessing the store + */ + public abstract Message reply(boolean replyToAll) throws MessagingException; + + /** + * To ensure changes are saved to the store, this message should be invoked + * before its containing folder is closed. Implementations may save modifications + * immediately but are free to defer such updates to they may be sent to the server + * in one batch; if saveChanges is not called then such changes may not be made + * permanent. + * + * @throws MessagingException if there was a problem accessing the store + */ + public abstract void saveChanges() throws MessagingException; - public abstract void setSubject(String subject) throws MessagingException; + /** + * Apply the specified search criteria to this message + * + * @param term the search criteria + * @return true if this message matches the search criteria. + * @throws MessagingException if there was a problem accessing the store + */ + public boolean match(SearchTerm term) throws MessagingException { + return term.match(this); + } } Modified: geronimo/trunk/specs/javamail/src/java/javax/mail/MessageContext.java Url: http://svn.apache.org/viewcvs/geronimo/trunk/specs/javamail/src/java/javax/mail/MessageContext.java?view=diff&rev=126350&p1=geronimo/trunk/specs/javamail/src/java/javax/mail/MessageContext.java&r1=126349&p2=geronimo/trunk/specs/javamail/src/java/javax/mail/MessageContext.java&r2=126350 ============================================================================== --- geronimo/trunk/specs/javamail/src/java/javax/mail/MessageContext.java (original) +++ geronimo/trunk/specs/javamail/src/java/javax/mail/MessageContext.java Mon Jan 24 22:35:47 2005 @@ -18,37 +18,46 @@ package javax.mail; /** + * The context in which a piece of message content is contained. + * * @version $Rev$ $Date$ */ public class MessageContext { - private Part _part; + private final Part part; + /** + * Create a MessageContext object describing the context of the supplied Part. + * + * @param part the containing part + */ public MessageContext(Part part) { - _part = part; + this.part = part; } - public Message getMessage() { - return getMessageFrom(getPart()); - } - - private Message getMessageFrom(Part part) { - if (part instanceof Message) { - return (Message) part; - } else if (part instanceof BodyPart) { - Part parent = ((Multipart) part).getParent(); - return getMessageFrom(parent); - } else if (part instanceof Multipart) { - Part parent = ((Multipart) part).getParent(); - return getMessageFrom(parent); - } else { - return null; - } + /** + * Return the [EMAIL PROTECTED] Part} that contains the content. + * + * @return the part + */ + public Part getPart() { + return part; } - public Part getPart() { - return _part; + /** + * Return the message that contains the content; if the Part is a [EMAIL PROTECTED] Multipart} + * then recurse up the chain until a [EMAIL PROTECTED] Message} is found. + * + * @return + */ + public Message getMessage() { + return getMessageFrom(part); } + /** + * Return the session associated with the Message containing this Part. + * + * @return the session associated with this context's root message + */ public Session getSession() { Message message = getMessage(); if (message == null) { @@ -56,5 +65,20 @@ } else { return message.session; } + } + + // recurse up the chain of MultiPart/BodyPart paris until we hit a message + private Message getMessageFrom(Part p) { + while (p != null) { + if (p instanceof Message) { + return (Message) p; + } + Multipart mp = ((BodyPart) p).getParent(); + if (mp == null) { + return null; + } + p = mp.getParent(); + } + return null; } } Modified: geronimo/trunk/specs/javamail/src/java/javax/mail/Multipart.java Url: http://svn.apache.org/viewcvs/geronimo/trunk/specs/javamail/src/java/javax/mail/Multipart.java?view=diff&rev=126350&p1=geronimo/trunk/specs/javamail/src/java/javax/mail/Multipart.java&r1=126349&p2=geronimo/trunk/specs/javamail/src/java/javax/mail/Multipart.java&r2=126350 ============================================================================== --- geronimo/trunk/specs/javamail/src/java/javax/mail/Multipart.java (original) +++ geronimo/trunk/specs/javamail/src/java/javax/mail/Multipart.java Mon Jan 24 22:35:47 2005 @@ -22,62 +22,143 @@ import java.util.Vector; /** + * A container for multiple [EMAIL PROTECTED] BodyPart BodyParts}. + * * @version $Rev$ $Date$ */ public abstract class Multipart { - protected String contentType; - protected Part parent; + /** + * Vector of sub-parts. + */ protected Vector parts = new Vector(); - protected Multipart() { - } - - public void addBodyPart(BodyPart part) throws MessagingException { - parts.add(part); - } + /** + * The content type of this multipart object; defaults to "multipart/mixed" + */ + protected String contentType = "multipart/mixed"; + + /** + * The Part that contains this multipart. + */ + protected Part parent; - public void addBodyPart(BodyPart part, int pos) throws MessagingException { - parts.add(pos, part); + protected Multipart() { } - public BodyPart getBodyPart(int index) throws MessagingException { - return (BodyPart) parts.get(index); + /** + * Initialize this multipart object from the supplied data source. + * This adds any [EMAIL PROTECTED] BodyPart BodyParts} into this object and initializes the content type. + * + * @param mds the data source + * @throws MessagingException + */ + protected void setMultipartDataSource(MultipartDataSource mds) throws MessagingException { + parts.clear(); + contentType = mds.getContentType(); + int size = mds.getCount(); + for (int i = 0; i < size; i++) { + parts.add(mds.getBodyPart(i)); + } } + /** + * Return the content type. + * + * @return the content type + */ public String getContentType() { return contentType; } + /** + * Return the number of enclosed parts + * + * @return the number of parts + * @throws MessagingException + */ public int getCount() throws MessagingException { return parts.size(); } - public Part getParent() { - return parent; + /** + * Get the specified part; numbering starts at zero. + * + * @param index the part to get + * @return the part + * @throws MessagingException + */ + public BodyPart getBodyPart(int index) throws MessagingException { + return (BodyPart) parts.get(index); } + /** + * Remove the supplied part from the list. + * + * @param part the part to remove + * @return true if the part was removed + * @throws MessagingException + */ public boolean removeBodyPart(BodyPart part) throws MessagingException { return parts.remove(part); } + /** + * Remove the specified part; all others move down one + * + * @param index the part to remove + * @throws MessagingException + */ public void removeBodyPart(int index) throws MessagingException { parts.remove(index); } - protected void setMultipartDataSource(MultipartDataSource mds) - throws MessagingException { - // TODO review implementation - contentType = mds.getContentType(); - int size = mds.getCount(); - for (int i = 0; i < size; i++) { - addBodyPart(mds.getBodyPart(i)); - } + /** + * Add a part to the end of the list. + * + * @param part the part to add + * @throws MessagingException + */ + public void addBodyPart(BodyPart part) throws MessagingException { + parts.add(part); + } + + /** + * Insert a part into the list at a designated point; all subsequent parts move down + * + * @param part the part to add + * @param pos the index of the new part + * @throws MessagingException + */ + public void addBodyPart(BodyPart part, int pos) throws MessagingException { + parts.add(pos, part); + } + + /** + * Encode and write this multipart to the supplied OutputStream; the encoding + * used is determined by the implementation. + * + * @param out the stream to write to + * @throws IOException + * @throws MessagingException + */ + public abstract void writeTo(OutputStream out) throws IOException, MessagingException; + + /** + * Return the Part containing this Multipart object or null if unknown. + * + * @return this Multipart's parent + */ + public Part getParent() { + return parent; } + /** + * Set the parent of this Multipart object + * + * @param part this object's parent + */ public void setParent(Part part) { parent = part; } - public abstract void writeTo(OutputStream out) - throws IOException, MessagingException; } Modified: geronimo/trunk/specs/javamail/src/java/javax/mail/Part.java Url: http://svn.apache.org/viewcvs/geronimo/trunk/specs/javamail/src/java/javax/mail/Part.java?view=diff&rev=126350&p1=geronimo/trunk/specs/javamail/src/java/javax/mail/Part.java&r1=126349&p2=geronimo/trunk/specs/javamail/src/java/javax/mail/Part.java&r2=126350 ============================================================================== --- geronimo/trunk/specs/javamail/src/java/javax/mail/Part.java (original) +++ geronimo/trunk/specs/javamail/src/java/javax/mail/Part.java Mon Jan 24 22:35:47 2005 @@ -24,6 +24,9 @@ import javax.activation.DataHandler; /** + * Note: Parts are used in Collections so implementing classes must provide + * a suitable implementation of equals and hashCode. + * * @version $Rev$ $Date$ */ public interface Part { Modified: geronimo/trunk/specs/javamail/src/java/javax/mail/PasswordAuthentication.java Url: http://svn.apache.org/viewcvs/geronimo/trunk/specs/javamail/src/java/javax/mail/PasswordAuthentication.java?view=diff&rev=126350&p1=geronimo/trunk/specs/javamail/src/java/javax/mail/PasswordAuthentication.java&r1=126349&p2=geronimo/trunk/specs/javamail/src/java/javax/mail/PasswordAuthentication.java&r2=126350 ============================================================================== --- geronimo/trunk/specs/javamail/src/java/javax/mail/PasswordAuthentication.java (original) +++ geronimo/trunk/specs/javamail/src/java/javax/mail/PasswordAuthentication.java Mon Jan 24 22:35:47 2005 @@ -18,22 +18,24 @@ package javax.mail; /** + * A data holder used by Authenticator to contain a username and password. + * * @version $Rev$ $Date$ */ public final class PasswordAuthentication { - private String _user; - private String _password; + private final String user; + private final String password; public PasswordAuthentication(String user, String password) { - _user = user; - _password = password; + this.user = user; + this.password = password; } public String getUserName() { - return _user; + return user; } public String getPassword() { - return _password; + return password; } } Modified: geronimo/trunk/specs/javamail/src/java/javax/mail/Provider.java Url: http://svn.apache.org/viewcvs/geronimo/trunk/specs/javamail/src/java/javax/mail/Provider.java?view=diff&rev=126350&p1=geronimo/trunk/specs/javamail/src/java/javax/mail/Provider.java&r1=126349&p2=geronimo/trunk/specs/javamail/src/java/javax/mail/Provider.java&r2=126350 ============================================================================== --- geronimo/trunk/specs/javamail/src/java/javax/mail/Provider.java (original) +++ geronimo/trunk/specs/javamail/src/java/javax/mail/Provider.java Mon Jan 24 22:35:47 2005 @@ -21,72 +21,66 @@ * @version $Rev$ $Date$ */ public class Provider { - Provider(String protocol, - String className, - Type type, - String vendor, - String version) { - _protocol = protocol; - _className = className; - _type = type; - _vendor = vendor; - _version = version; - } - + /** + * A enumeration inner class that defines Provider types. + */ public static class Type { - private String _name; - public static final Type STORE = new Type("store"); - public static final Type TRANSPORT = new Type("transport"); + /** + * A message store provider such as POP3 or IMAP4. + */ + public static final Type STORE = new Type(); + + /** + * A message transport provider such as SMTP. + */ + public static final Type TRANSPORT = new Type(); - private Type(String name) { - _name = name; - } - - static Type getType(String name) { - if (name.equals("store")) { - return STORE; - } else if (name.equals("transport")) { - return TRANSPORT; - } else { - return null; - } + private Type() { } } - private String _className; - private String _protocol; - private Type _type; - private String _vendor; - private String _version; + private final String className; + private final String protocol; + private final Type type; + private final String vendor; + private final String version; + + Provider(String protocol, String className, Type type, String vendor, String version) { + this.protocol = protocol; + this.className = className; + this.type = type; + this.vendor = vendor; + this.version = version; + } public String getClassName() { - return _className; + return className; } public String getProtocol() { - return _protocol; + return protocol; } public Type getType() { - return _type; + return type; } public String getVendor() { - return _vendor; + return vendor; } public String getVersion() { - return _version; + return version; } public String toString() { return "protocol=" - + _protocol + + protocol + "; type=" - + _type + + type + "; class=" - + _className - + (_vendor == null ? "" : "; vendor=" + _vendor) - + (_version == null ? "" : ";version=" + _version); + + className + + (vendor == null ? "" : "; vendor=" + vendor) + + (version == null ? "" : ";version=" + version); } } Modified: geronimo/trunk/specs/javamail/src/java/javax/mail/Service.java Url: http://svn.apache.org/viewcvs/geronimo/trunk/specs/javamail/src/java/javax/mail/Service.java?view=diff&rev=126350&p1=geronimo/trunk/specs/javamail/src/java/javax/mail/Service.java&r1=126349&p2=geronimo/trunk/specs/javamail/src/java/javax/mail/Service.java&r2=126350 ============================================================================== --- geronimo/trunk/specs/javamail/src/java/javax/mail/Service.java (original) +++ geronimo/trunk/specs/javamail/src/java/javax/mail/Service.java Mon Jan 24 22:35:47 2005 @@ -17,150 +17,199 @@ package javax.mail; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; import java.util.Vector; import javax.mail.event.ConnectionEvent; import javax.mail.event.ConnectionListener; import javax.mail.event.MailEvent; -import javax.mail.event.TransportListener; /** * @version $Rev$ $Date$ */ public abstract class Service { - private boolean _connected; - private List _connectionListeners = new LinkedList(); - protected boolean debug; + /** + * The session from which this service was created. + */ protected Session session; + /** + * The URLName of this service + */ protected URLName url; + /** + * Debug flag for this service, set from the Session's debug flag. + */ + protected boolean debug; + private boolean connected; + private final Vector connectionListeners = new Vector(2); + private final EventQueue queue = new EventQueue(); + + /** + * Construct a new Service. + * @param session the session from which this service was created + * @param url the URLName of this service + */ protected Service(Session session, URLName url) { this.session = session; this.url = url; } - public void addConnectionListener(ConnectionListener listener) { - _connectionListeners.add(listener); - } - - public void close() throws MessagingException { - // if we're not connected, ignore - setConnected(false); + /** + * A generic connect method that takes no parameters allowing subclasses + * to implement an appropriate authentication scheme. + * The default implementation calls <code>connect(null, null, null)</code> + * @throws AuthenticationFailedException if authentication fails + * @throws MessagingException for other failures + */ + public void connect() throws MessagingException { + connect(null, null, null); } - public void connect() throws MessagingException { - String host = session.getProperty("mail.host"); - String user = session.getProperty("mail.user"); - connect(host, -1, user, null); + /** + * Connect to the specified host using a simple username/password authenticaion scheme + * and the default port. + * The default implementation calls <code>connect(host, -1, user, password)</code> + * + * @param host the host to connect to + * @param user the user name + * @param password the user's password + * @throws AuthenticationFailedException if authentication fails + * @throws MessagingException for other failures + */ + public void connect(String host, String user, String password) throws MessagingException { + connect(host, -1, user, password); } - public void connect(String host, int port, String user, String password) - throws MessagingException { - if (_connected) { + /** + * Connect to the specified host at the specified port using a simple username/password authenticaion scheme. + * + * If this Service is already connected, an IllegalStateException is thrown. + * + * @param host the host to connect to + * @param port the port to connect to; pass -1 to use the default for the protocol + * @param user the user name + * @param password the user's password + * @throws AuthenticationFailedException if authentication fails + * @throws MessagingException for other failures + * @throws IllegalStateException if this service is already connected + */ + public void connect(String host, int port, String user, String password) throws MessagingException { + if (isConnected()) { throw new IllegalStateException("Already connected"); } - boolean retry = true; - while (retry) { - try { - retry = !protocolConnect(host, port, user, password); - } catch (AuthenticationFailedException e) { - // TODO I18N - try { - PasswordAuthentication pa = - session.requestPasswordAuthentication(InetAddress.getByName(host), - port, - null, - "Please enter your password", - user); - password = pa.getPassword(); - user = pa.getUserName(); - } catch (UnknownHostException uhe) { - throw new MessagingException(uhe.toString()); - } - } - } - setConnected(true); - // Either the provider will implement getURL, or it will have already set it using setURL. - // In either case, this is safe. - setURLName(getURLName()); - } - public void connect(String host, String user, String password) - throws MessagingException { - connect(host, -1, user, password); + // todo figure out what this is really meant to do + // todo get default + boolean connected = protocolConnect(host, port, user, password); + if (!connected) { + // todo get info from session + connected = protocolConnect(host, port, user, password); + // todo call setURLName + // todo save password if obtained from user + } + setConnected(connected); + notifyConnectionListeners(ConnectionEvent.OPENED); + } + + /** + * Attempt the protocol-specific connection; subclasses should override this to establish + * a connection in the appropriate manner. + * + * This method should return true if the connection was established. + * It may return false to cause the [EMAIL PROTECTED] #connect(String, int, String, String)} method to + * reattempt the connection after trying to obtain user and password information from the user. + * Alternatively it may throw a AuthenticatedFailedException to abandon the conection attempt. + * + * @param host + * @param port + * @param user + * @param password + * @return + * @throws AuthenticationFailedException if authentication fails + * @throws MessagingException for other failures + */ + protected boolean protocolConnect(String host, int port, String user, String password) throws MessagingException { + return false; } - protected void finalize() throws Throwable { - try { - super.finalize(); - } finally { - close(); - } + /** + * Check if this service is currently connected. + * The default implementation simply returns the value of a private boolean field; + * subclasses may wish to override this method to verify the physical connection. + * + * @return true if this service is connected + */ + public boolean isConnected() { + return connected; } - public URLName getURLName() { - return url; + /** + * Notification to subclasses that the connection state has changed. + * This method is called by the connect() and close() methods to indicate state change; + * subclasses should also call this method if the connection is automatically closed + * for some reason. + * + * @param connected the connection state + */ + protected void setConnected(boolean connected) { + this.connected = connected; } - public boolean isConnected() { - return _connected; + /** + * Close this service and terminate its physical connection. + * The default implementation simply calls setConnected(false) and then + * sends a CLOSED event to all registered ConnectionListeners. + * Subclasses overriding this method should still ensure it is closed; they should + * also ensure that it is called if the connection is closed automatically, for + * for example in a finalizer. + * + [EMAIL PROTECTED] MessagingException if there were errors closing; the connection is still closed + */ + public void close() throws MessagingException { + setConnected(false); + notifyConnectionListeners(ConnectionEvent.CLOSED); } - protected void notifyConnectionListeners(int type) { - ConnectionEvent event = new ConnectionEvent(this, type); - Iterator it = _connectionListeners.iterator(); - while (it.hasNext()) { - TransportListener listener = (TransportListener) it.next(); - event.dispatch(listener); - } + /** + * Return a copy of the URLName representing this service with the password and file information removed. + * + * @return the URLName for this service + */ + public URLName getURLName() { + + return url == null ? null : new URLName(url.getProtocol(), url.getHost(), url.getPort(), null, url.getUsername(), null); } - protected boolean protocolConnect(String host, - int port, - String user, - String password) - throws MessagingException { - return false; + /** + * Set the url field. + * @param url the new value + */ + protected void setURLName(URLName url) { + this.url = url; } - protected void queueEvent(MailEvent event, Vector listeners) { - Enumeration enumeration = listeners.elements(); - while (enumeration.hasMoreElements()) { - Object element = enumeration.nextElement(); - event.dispatch(listeners); - } + public void addConnectionListener(ConnectionListener listener) { + connectionListeners.add(listener); } public void removeConnectionListener(ConnectionListener listener) { - _connectionListeners.remove(listener); + connectionListeners.remove(listener); } - protected void setConnected(boolean connected) { - boolean old = _connected; - _connected = connected; - if (old != _connected) { - if (connected) { - notifyConnectionListeners(ConnectionEvent.OPENED); - } else { - notifyConnectionListeners(ConnectionEvent.CLOSED); - } - } + protected void notifyConnectionListeners(int type) { + queue.queueEvent(new ConnectionEvent(this, type), connectionListeners); } - protected void setURLName(URLName url) { - this.url = url; + public String toString() { + return url == null ? super.toString() : url.toString(); } - public String toString() { - if (url == null) { - return super.toString(); - } else { - return url.toString(); - } + protected void queueEvent(MailEvent event, Vector listeners) { + queue.queueEvent(event, listeners); + } + + protected void finalize() throws Throwable { + queue.stop(); + connectionListeners.clear(); + super.finalize(); } } Modified: geronimo/trunk/specs/javamail/src/java/javax/mail/Session.java Url: http://svn.apache.org/viewcvs/geronimo/trunk/specs/javamail/src/java/javax/mail/Session.java?view=diff&rev=126350&p1=geronimo/trunk/specs/javamail/src/java/javax/mail/Session.java&r1=126349&p2=geronimo/trunk/specs/javamail/src/java/javax/mail/Session.java&r2=126350 ============================================================================== --- geronimo/trunk/specs/javamail/src/java/javax/mail/Session.java (original) +++ geronimo/trunk/specs/javamail/src/java/javax/mail/Session.java Mon Jan 24 22:35:47 2005 @@ -17,346 +17,550 @@ package javax.mail; -import javax.mail.Provider.Type; -import javax.mail.internet.ParameterList; -import javax.mail.internet.ParseException; import java.io.BufferedReader; +import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintStream; import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.net.InetAddress; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.StringTokenizer; +import java.util.WeakHashMap; /** + * OK, so we have a final class in the API with a heck of a lot of implementation required... + * let's try and figure out what it is meant to do. + * <p/> + * It is supposed to collect together properties and defaults so that they can be + * shared by multiple applications on a desktop; with process isolation and no + * real concept of shared memory, this seems challenging. These properties and + * defaults rely on system properties, making management in a app server harder, + * and on resources loaded from "mail.jar" which may lead to skew between + * differnet independent implementations of this API. + * * @version $Rev$ $Date$ */ public final class Session { - - private static final Provider[] PROVIDER_ARRAY = new Provider[0]; private static final Class[] PARAM_TYPES = {Session.class, URLName.class}; private static final Map addressMap = new HashMap(); - private static final Map providers = new HashMap(); - private static Authenticator DEFAULT_AUTHENTICATOR; private static Session DEFAULT_SESSION; - private Authenticator authenticator; - private boolean debug; - private PrintStream debugOut; private Map passwordAuthentications = new HashMap(); - private Properties properties = new Properties(); + private final Properties properties; + private final Authenticator authenticator; + private boolean debug; + private PrintStream debugOut = System.out; + + private static final WeakHashMap providersByClassLoader = new WeakHashMap(); - private Session() { + /** + * No public constrcutor allowed. + */ + private Session(Properties properties, Authenticator authenticator) { + this.properties = properties; + this.authenticator = authenticator; + debug = Boolean.valueOf(properties.getProperty("mail.debug")).booleanValue(); + } + + /** + * Create a new session initialized with the supplied properties which uses the supplied authenticator. + * Clients should ensure the properties listed in Appendix A of the JavaMail specification are + * set as the defaults are unlikey to work in most scenarios; particular attention should be given + * to: + * <ul> + * <li>mail.store.protocol</li> + * <li>mail.transport.protocol</li> + * <li>mail.host</li> + * <li>mail.user</li> + * <li>mail.from</li> + * </ul> + * + * @param properties the session properties + * @param authenticator an authenticator for callbacks to the user + * @return a new session + */ + public static Session getInstance(Properties properties, Authenticator authenticator) { + return new Session(new Properties(properties), authenticator); + } + + /** + * Create a new session initialized with the supplied properties with no authenticator. + * + * @param properties the session properties + * @return a new session + * @see #getInstance(java.util.Properties, Authenticator) + */ + public static Session getInstance(Properties properties) { + return getInstance(properties, null); } + /** + * Get the "default" instance assuming no authenticator is required. + * + * @param properties the session properties + * @return if "default" session + * @throws SecurityException if the does not have permission to access the default session + */ public synchronized static Session getDefaultInstance(Properties properties) { - if (DEFAULT_SESSION == null) { - DEFAULT_SESSION = getInstance(properties); - } - return DEFAULT_SESSION; + return getDefaultInstance(properties, null); } + /** + * Get the "default" session. + * If there is not current "default", a new Session is created and installed as the default. + * + * @param properties + * @param authenticator + * @return if "default" session + * @throws SecurityException if the does not have permission to access the default session + */ public synchronized static Session getDefaultInstance(Properties properties, Authenticator authenticator) { - if (DEFAULT_AUTHENTICATOR == null - || DEFAULT_AUTHENTICATOR == authenticator - || DEFAULT_AUTHENTICATOR.getClass().getClassLoader() == authenticator.getClass().getClassLoader()) { - if (DEFAULT_SESSION == null) { - DEFAULT_SESSION = getInstance(properties, authenticator); - DEFAULT_AUTHENTICATOR = authenticator; - } - return DEFAULT_SESSION; + if (DEFAULT_SESSION == null) { + DEFAULT_SESSION = getInstance(properties, authenticator); } else { - throw new SecurityException("Cannot access default instance with given authenticator"); + if (authenticator != DEFAULT_SESSION.authenticator) { + if (authenticator == null || DEFAULT_SESSION.authenticator == null || authenticator.getClass().getClassLoader() != DEFAULT_SESSION.authenticator.getClass().getClassLoader()) { + throw new SecurityException(); + } + } + // todo we should check with the SecurityManager here as well } + return DEFAULT_SESSION; } - public static Session getInstance(Properties properties) { - return getInstance(properties, null); - } - - public static Session getInstance(Properties properties, Authenticator authenticator) { - Session session = new Session(); - session.authenticator = authenticator; - session.properties = new Properties(properties); - session.debug = Boolean.getBoolean(properties.getProperty("mail.debug", "false")); - - return session; + /** + * Enable debugging for this session. + * Debugging can also be enabled by setting the "mail.debug" property to true when + * the session is being created. + * + * @param debug the debug setting + */ + public void setDebug(boolean debug) { + this.debug = debug; } - + /** + * Get the debug setting for this session. + * + * @return the debug setting + */ public boolean getDebug() { return debug; } - public PrintStream getDebugOut() { - return System.err; - } - - public Folder getFolder(URLName name) throws MessagingException { - // TODO Implement - return null; - } - - public PasswordAuthentication getPasswordAuthentication(URLName name) { - return (PasswordAuthentication) passwordAuthentications.get(name); + /** + * Set the output stream where debug information should be sent. + * If set to null, System.out will be used. + * + * @param out the stream to write debug information to + */ + public void setDebugOut(PrintStream out) { + debugOut = out == null ? System.out : out; } - public Properties getProperties() { - return properties; + /** + * Return the debug output stream. + * + * @return the debug output stream + */ + public PrintStream getDebugOut() { + return debugOut; } - public String getProperty(String property) { - return getProperties().getProperty(property); + /** + * Return the list of providers available to this application. + * This method searches for providers that are defined in the javamail.providers + * and javamail.default.providers resources available through the current context + * classloader, or if that is not available, the classloader that loaded this class. + * <p/> + * As searching for providers is potentially expensive, this implementation maintains + * a WeakHashMap of providers indexed by ClassLoader. + * + * @return an array of providers + */ + public Provider[] getProviders() { + ProviderInfo info = getProviderInfo(); + return (Provider[]) info.all.toArray(new Provider[info.all.size()]); } + /** + * Return the provider for a specific protocol. + * This implementation initially looks in the Session properties for an property with the name + * "mail.<protocol>.class"; if found it attempts to create an instance of the class named in that + * property throwing a NoSuchProviderException if the class cannot be loaded. + * If this property is not found, it searches the providers returned by [EMAIL PROTECTED] #getProviders()} + * for a entry for the specified protocol. + * + * @param protocol the protocol to get a provider for + * @return a provider for that protocol + * @throws NoSuchProviderException + */ public Provider getProvider(String protocol) throws NoSuchProviderException { - // TODO Implement - // Lookup from - return (Provider) providers.get(new ProviderKey(Type.STORE, protocol)); + ProviderInfo info = getProviderInfo(); + Provider provider; + String providerName = properties.getProperty("nail." + protocol + ".class"); + if (providerName != null) { + provider = (Provider) info.byClassName.get(providerName); + } else { + provider = (Provider) info.byProtocol.get(protocol); + } + if (provider == null) { + throw new NoSuchProviderException("Unable to locate provider for protocol: " + protocol); + } + return provider; } - public Provider[] getProviders() { - return (Provider[]) providers.values().toArray(PROVIDER_ARRAY); + /** + * Make the supplied Provider the default for its protocol. + * + * @param provider the new default Provider + * @throws NoSuchProviderException + */ + public void setProvider(Provider provider) throws NoSuchProviderException { + ProviderInfo info = getProviderInfo(); + info.byProtocol.put(provider.getProtocol(), provider); } + /** + * Return a Store for the default protocol defined by the mail.store.protocol property. + * + * @return the store for the default protocol + * @throws NoSuchProviderException + */ public Store getStore() throws NoSuchProviderException { - return getStore(properties.getProperty("mail.store.protocol")); - } - - public Store getStore(Provider provider) throws NoSuchProviderException { - try { - Class clazz = Class.forName(provider.getClassName()); - Constructor constructor = clazz.getConstructor(PARAM_TYPES); - return (Store) constructor.newInstance(new Object[]{this, null}); - } catch (Exception e) { - throw new NoSuchProviderException(e.toString()); + String protocol = properties.getProperty("mail.store.protocol"); + if (protocol == null) { + throw new NoSuchProviderException("mail.store.protocol property is not set"); } + return getStore(protocol); } + /** + * Return a Store for the specified protocol. + * + * @param protocol the protocol to get a Store for + * @return a Store + * @throws NoSuchProviderException if no provider is defined for the specified protocol + */ public Store getStore(String protocol) throws NoSuchProviderException { - if (protocol == null) { - throw new NoSuchProviderException("No protocol specified in mail.store.protocol property or none given"); - } - Provider provider = (Provider) providers.get(new ProviderKey(Type.STORE, protocol)); - if (provider == null) { - throw new NoSuchProviderException("Unknown protocol for " + protocol); - } + Provider provider = getProvider(protocol); return getStore(provider); } + /** + * Return a Store for the protocol specified in the given URL + * + * @param url the URL of the Store + * @return a Store + * @throws NoSuchProviderException if no provider is defined for the specified protocol + */ public Store getStore(URLName url) throws NoSuchProviderException { - return getStore(url.getProtocol()); + return (Store) getService(getProvider(url.getProtocol()), url); } - public Transport getTransport() throws NoSuchProviderException { - return getTransport(properties.getProperty("mail.transport.protocol")); + /** + * Return the Store specified by the given provider. + * + * @param provider the provider to create from + * @return a Store + * @throws NoSuchProviderException if there was a problem creating the Store + */ + public Store getStore(Provider provider) throws NoSuchProviderException { + if (Provider.Type.STORE != provider.getType()) { + throw new NoSuchProviderException("Not a Store Provider: " + provider); + } + return (Store) getService(provider, null); } - public Transport getTransport(Address address) throws NoSuchProviderException { - String type = address.getType(); - // type is 'rfc822' -> 'smtp' - // type is 'news' -> 'nntp' - return getTransport((String) addressMap.get(type)); + /** + * Return a closed folder for the supplied URLName, or null if it cannot be obtained. + * <p/> + * The scheme portion of the URL is used to locate the Provider and create the Store; + * the returned Store is then used to obtain the folder. + * + * @param name the location of the folder + * @return the requested folder, or null if it is unavailable + * @throws NoSuchProviderException if there is no provider + * @throws MessagingException if there was a problem accessing the Store + */ + public Folder getFolder(URLName name) throws MessagingException { + Store store = getStore(name); + return store.getFolder(name); } - public Transport getTransport(Provider provider) throws NoSuchProviderException { - try { - Class clazz = Class.forName(provider.getClassName()); - Constructor constructor = clazz.getConstructor(PARAM_TYPES); - return (Transport) constructor.newInstance(new Object[]{this, null}); - } catch (Exception e) { - throw new NoSuchProviderException(e.toString()); + /** + * Return a Transport for the default protocol specified by the + * <code>mail.transport.protocol</code> property. + * + * @return a Transport + * @throws NoSuchProviderException + */ + public Transport getTransport() throws NoSuchProviderException { + String protocol = properties.getProperty("mail.transport.protocol"); + if (protocol == null) { + throw new NoSuchProviderException("mail.transport.protocol property is not set"); } + return getTransport(protocol); } + /** + * Return a Transport for the specified protocol. + * + * @param protocol the protocol to use + * @return a Transport + * @throws NoSuchProviderException + */ public Transport getTransport(String protocol) throws NoSuchProviderException { - if (protocol == null) { - throw new NoSuchProviderException("No protocol specified in mail.store.protocol property or none given"); - } - Provider provider = (Provider) providers.get(new ProviderKey(Type.TRANSPORT, protocol)); - if (provider == null) { - throw new NoSuchProviderException("Unknown protocol for " + protocol); - } + Provider provider = getProvider(protocol); return getTransport(provider); } + /** + * Return a transport for the protocol specified in the URL. + * + * @param name the URL whose scheme specifies the protocol + * @return a Transport + * @throws NoSuchProviderException + */ public Transport getTransport(URLName name) throws NoSuchProviderException { - return getTransport(name.getProtocol()); + return (Transport) getService(getProvider(name.getProtocol()), name); } - public PasswordAuthentication requestPasswordAuthentication(InetAddress host, int port, - String protocol, - String prompt, - String defaultUserName) { - // TODO Implement this, probably by showing a dialog box of some sorts? - throw new UnsupportedOperationException("Method not yet implemented"); - } - - public void setDebug(boolean debug) { - this.debug = debug; + /** + * Return a transport for the protocol associated with the type of this address. + * + * @param address the address we are trying to deliver to + * @return a Transport + * @throws NoSuchProviderException + */ + public Transport getTransport(Address address) throws NoSuchProviderException { + String type = address.getType(); + return getTransport((String) addressMap.get(type)); } - public void setDebugOut(PrintStream out) { - debugOut = out; + /** + * Return the Transport specified by a Provider + * + * @param provider the defining Provider + * @return a Transport + * @throws NoSuchProviderException + */ + public Transport getTransport(Provider provider) throws NoSuchProviderException { + return (Transport) getService(provider, null); } + /** + * Set the password authentication associated with a URL. + * + * @param name the url + * @param authenticator the authenticator + */ public void setPasswordAuthentication(URLName name, PasswordAuthentication authenticator) { - passwordAuthentications.put(name, authenticator); + if (authenticator == null) { + passwordAuthentications.remove(name); + } else { + passwordAuthentications.put(name, authenticator); + } } - public void setProvider(Provider provider) throws NoSuchProviderException { - providers.put(new ProviderKey(provider.getType(), provider.getProtocol()), provider); + /** + * Get the password authentication associated with a URL + * + * @param name the URL + * @return any authenticator for that url, or null if none + */ + public PasswordAuthentication getPasswordAuthentication(URLName name) { + return (PasswordAuthentication) passwordAuthentications.get(name); } - // Read in the files and configure the Providers - static { - loadProviders(); - loadAddressMap(); + /** + * Call back to the application supplied authenticator to get the needed username add password. + * + * @param host the host we are trying to connect to, may be null + * @param port the port on that host + * @param protocol the protocol trying to be used + * @param prompt a String to show as part of the prompt, may be null + * @param defaultUserName the default username, may be null + * @return the authentication information collected by the authenticator; may be null + */ + public PasswordAuthentication requestPasswordAuthentication(InetAddress host, int port, String protocol, String prompt, String defaultUserName) { + if (authenticator == null) { + return null; + } + return authenticator.authenticate(host, port, protocol, prompt, defaultUserName); + } + + /** + * Return the properties object for this Session; this is a live collection. + * + * @return the properties for the Session + */ + public Properties getProperties() { + return properties; } - private static void loadAddressMap() { - try { - String slash = System.getProperty("file.separator"); - String home = System.getProperty("java.home"); - loadAddressMap(new FileInputStream(home + slash + "lib" + slash + "javamail.address.map")); - } catch (RuntimeException e) { - // continue; we are trying to load a non-existent file; doesn't matter if we get security/IO exceptions - } catch (IOException e) { - // continue; we are trying to load a non-existent file; doesn't matter if we get security/IO exceptions - } - - try { - // Note: this is a class resouce, which always uses / - loadAddressMap("/META-INF/javamail.address.map"); - } catch (RuntimeException e) { - // continue; we are trying to load a non-existent file; doesn't matter if we get security/IO exceptions - } catch (IOException e) { - // continue; we are trying to load a non-existent file; doesn't matter if we get security/IO exceptions - } + /** + * Return the specified property. + * + * @param property the property to get + * @return its value, or null if not present + */ + public String getProperty(String property) { + return getProperties().getProperty(property); + } + private Service getService(Provider provider, URLName name) throws NoSuchProviderException { try { - // Note: this is a class resouce, which always uses / - loadAddressMap("/META-INF/javamail.default.address.map"); - } catch (RuntimeException e) { - // continue; we are trying to load a non-existent file; doesn't matter if we get security/IO exceptions - } catch (IOException e) { - // continue; we are trying to load a non-existent file; doesn't matter if we get security/IO exceptions + ClassLoader cl = getClassLoader(); + Class clazz = cl.loadClass(provider.getClassName()); + Constructor ctr = clazz.getConstructor(PARAM_TYPES); + return (Service) ctr.newInstance(new Object[]{this, name}); + } catch (ClassNotFoundException e) { + throw (NoSuchProviderException) new NoSuchProviderException("Unable to load class for provider: " + provider).initCause(e); + } catch (NoSuchMethodException e) { + throw (NoSuchProviderException) new NoSuchProviderException("Provider class does not have a constructor(Session, URLName): " + provider).initCause(e); + } catch (InstantiationException e) { + throw (NoSuchProviderException) new NoSuchProviderException("Unable to instantiate provider class: " + provider).initCause(e); + } catch (IllegalAccessException e) { + throw (NoSuchProviderException) new NoSuchProviderException("Unable to instantiate provider class: " + provider).initCause(e); + } catch (InvocationTargetException e) { + throw (NoSuchProviderException) new NoSuchProviderException("Exception from constructor of provider class: " + provider).initCause(e.getCause()); } } - private static void loadAddressMap(InputStream in) throws IOException { - if (in == null) { - return; - } - ; - - BufferedReader br = new BufferedReader(new InputStreamReader(in)); - String line; - while ((line = br.readLine()) != null) { - if (line.trim().equals("") || line.startsWith("#")) { - continue; // skip blank lines and comments - } - int eq = line.indexOf("="); - String type = line.substring(0, eq).trim(); - String transport = line.substring(eq + 1).trim(); - addressMap.put(type, transport); + private static ProviderInfo getProviderInfo() { + ClassLoader cl = getClassLoader(); + ProviderInfo info = (ProviderInfo) providersByClassLoader.get(cl); + if (info == null) { + info = loadProviders(cl); + } + return info; + } + + private static ClassLoader getClassLoader() { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if (cl == null) { + cl = Session.class.getClassLoader(); } - br.close(); + return cl; } - private static void loadAddressMap(String file) throws IOException { - loadAddressMap(Session.class.getResourceAsStream(file)); - } - - private static void loadProviders() { + private static ProviderInfo loadProviders(ClassLoader cl) { + ProviderInfo info = new ProviderInfo(); try { - String slash = System.getProperty("file.separator"); - String home = System.getProperty("java.home"); - loadProviders(new FileInputStream(home + slash + "lib" + slash + "javamail.providers")); - } catch (RuntimeException e) { - // continue; we are trying to load a non-existent file; doesn't matter if we get security/IO exceptions + File file = new File(System.getProperty("java.home"), "lib/javamail.providers"); + InputStream is = new FileInputStream(file); + try { + loadProviders(info, is); + } finally{ + is.close(); + } + } catch (SecurityException e) { + // ignore } catch (IOException e) { - // continue; we are trying to load a non-existent file; doesn't matter if we get security/IO exceptions - } catch (ParseException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + // ignore } + try { - // Note: this is a class resouce, which always uses / - loadProviders("/META-INF/javamail.providers"); - } catch (RuntimeException e) { - // continue; we are trying to load a non-existent file; doesn't matter if we get security/IO exceptions + Enumeration e = cl.getResources("/META-INF/javamail.providers"); + while (e.hasMoreElements()) { + URL url = (URL) e.nextElement(); + InputStream is = url.openStream(); + try { + loadProviders(info, is); + } finally{ + is.close(); + } + } + } catch (SecurityException e) { + // ignore } catch (IOException e) { - // continue; we are trying to load a non-existent file; doesn't matter if we get security/IO exceptions - } catch (ParseException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + // ignore } + try { - loadProviders("/META-INF/javamail.default.providers"); - } catch (RuntimeException e) { - // continue; we are trying to load a non-existent file; doesn't matter if we get security/IO exceptions + Enumeration e = cl.getResources("/META-INF/javamail.default.providers"); + while (e.hasMoreElements()) { + URL url = (URL) e.nextElement(); + InputStream is = url.openStream(); + try { + loadProviders(info, is); + } finally{ + is.close(); + } + } + } catch (SecurityException e) { + // ignore } catch (IOException e) { - // continue; we are trying to load a non-existent file; doesn't matter if we get security/IO exceptions - } catch (ParseException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + // ignore } + return info; } - private static void loadProviders(InputStream in) throws IOException, ParseException { - - BufferedReader br = new BufferedReader(new InputStreamReader(in)); + private static void loadProviders(ProviderInfo info, InputStream is) throws IOException { + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); String line; - while ((line = br.readLine()) != null) { - if (line.trim().equals("") || line.startsWith("#")) { - continue; // skip blank lines and comments + while ((line = reader.readLine()) != null) { + StringTokenizer tok = new StringTokenizer(line, ";"); + String protocol = null; + Provider.Type type = null; + String className = null; + String vendor = null; + String version = null; + while (tok.hasMoreTokens()) { + String property = tok.nextToken(); + int index = property.indexOf('='); + if (index == -1) { + continue; + } + String key = property.substring(0, index).trim(); + String value = property.substring(index+1).trim(); + if (protocol == null && "protocol".equals(key)) { + protocol = value; + } else if (type == null && "type".equals(key)) { + if ("store".equals(value)) { + type = Provider.Type.STORE; + } else if ("transport".equals(value)) { + type = Provider.Type.TRANSPORT; + } + } else if (className == null && "class".equals(key)) { + className = value; + } else if ("vendor".equals(key)) { + vendor = value; + } else if ("version".equals(key)) { + version = value; + } + } + if (protocol == null || type == null || className == null) { + //todo should we log a warning? + continue; } - ParameterList pl = new ParameterList(line); - pl.get(line); - // TODO Continue implementing - String protocol = pl.get("protocol"); - String className = pl.get("class"); - String typeString = pl.get("type"); - Type type = Provider.Type.getType(typeString); - String vendor = pl.get("vendor"); - String version = pl.get("version"); - Provider provider = new Provider(protocol, className, type, vendor, version); - - providers.put(new ProviderKey(type, protocol), provider); + if (!info.byClassName.containsKey(className)) { + info.byClassName.put(className, provider); + } + if (!info.byProtocol.containsKey(protocol)) { + info.byProtocol.put(protocol, provider); + } + info.all.add(provider); } - br.close(); - } - - private static void loadProviders(String file) throws ParseException, IOException { - loadProviders(Session.class.getResourceAsStream(file)); } - private static final class ProviderKey { - - private final Type type; - private final String protocol; - - ProviderKey(Type type, String protocol) { - this.type = type; - this.protocol = protocol; - } - - public int hashCode() { - return type.hashCode() ^ protocol.hashCode(); - } - - public boolean equals(Object obj) { - if (!(obj instanceof ProviderKey)) return false; - ProviderKey key = (ProviderKey) obj; - return type == key.type & protocol.equals(key.protocol); - } + private static class ProviderInfo { + private final Map byClassName = new HashMap(); + private final Map byProtocol = new HashMap(); + private final List all = new ArrayList(); } } Modified: geronimo/trunk/specs/javamail/src/java/javax/mail/Store.java Url: http://svn.apache.org/viewcvs/geronimo/trunk/specs/javamail/src/java/javax/mail/Store.java?view=diff&rev=126350&p1=geronimo/trunk/specs/javamail/src/java/javax/mail/Store.java&r1=126349&p2=geronimo/trunk/specs/javamail/src/java/javax/mail/Store.java&r2=126350 ============================================================================== --- geronimo/trunk/specs/javamail/src/java/javax/mail/Store.java (original) +++ geronimo/trunk/specs/javamail/src/java/javax/mail/Store.java Mon Jan 24 22:35:47 2005 @@ -17,86 +17,126 @@ package javax.mail; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; +import java.util.Vector; import javax.mail.event.FolderEvent; import javax.mail.event.FolderListener; import javax.mail.event.StoreEvent; import javax.mail.event.StoreListener; /** + * Abstract class that represents a message store. + * * @version $Rev$ $Date$ */ public abstract class Store extends Service { private static final Folder[] FOLDER_ARRAY = new Folder[0]; - private List _folderListeners = new LinkedList(); - private List _storeListeners = new LinkedList(); + private final Vector folderListeners = new Vector(2); + private final Vector storeListeners = new Vector(2); + /** + * Constructor specifying session and url of this store. + * Subclasses MUST provide a constructor with this signature. + * + * @param session the session associated with this store + * @param name the URL of the store + */ protected Store(Session session, URLName name) { super(session, name); } - public void addFolderListener(FolderListener listener) { - _folderListeners.add(listener); - } - - public void addStoreListener(StoreListener listener) { - _storeListeners.add(listener); - } - + /** + * Retutn a Folder object that represents the root of the namespace for the current user. + * + * Note that in some store configurations (such as IMAP4) then the root folder may + * not be the INBOX folder. + * + * @return the root Folder + * @throws MessagingException if there was a problem accessing the store + */ public abstract Folder getDefaultFolder() throws MessagingException; + /** + * Return the Folder corresponding to the given name. + * The folder may not physically exist; the [EMAIL PROTECTED] Folder#exists()} method can be used + * to determine if it is real. + * @param name the name of the Folder to return + * @return the corresponding folder + * @throws MessagingException if there was a problem accessing the store + */ public abstract Folder getFolder(String name) throws MessagingException; + /** + * Return the folder identified by the URLName; the URLName must refer to this Store. + * Implementations may use the [EMAIL PROTECTED] URLName#getFile()} method to determined the folder name. + * + * @param name the folder to return + * @return the corresponding folder + * @throws MessagingException if there was a problem accessing the store + */ public abstract Folder getFolder(URLName name) throws MessagingException; + /** + * Return the root folders of the personal namespace belonging to the current user. + * + * The default implementation simply returns an array containing the folder returned by [EMAIL PROTECTED] #getDefaultFolder()}. + * @return the root folders of the user's peronal namespaces + * @throws MessagingException if there was a problem accessing the store + */ public Folder[] getPersonalNamespaces() throws MessagingException { return new Folder[]{getDefaultFolder()}; } - public Folder[] getSharedNamespaces() throws MessagingException { + /** + * Return the root folders of the personal namespaces belonging to the supplied user. + * + * The default implementation simply returns an empty array. + * + * @param user the user whose namespaces should be returned + * @return the root folders of the given user's peronal namespaces + * @throws MessagingException if there was a problem accessing the store + */ + public Folder[] getUserNamespaces(String user) throws MessagingException { return FOLDER_ARRAY; } - public Folder[] getUserNamespaces(String name) throws MessagingException { + /** + * Return the root folders of namespaces that are intended to be shared between users. + * + * The default implementation simply returns an empty array. + * @return the root folders of all shared namespaces + * @throws MessagingException if there was a problem accessing the store + */ + public Folder[] getSharedNamespaces() throws MessagingException { return FOLDER_ARRAY; } - protected void notifyFolderListeners(int type, Folder folder) { - Iterator it = _folderListeners.iterator(); - FolderEvent event = new FolderEvent(this, folder, type); - while (it.hasNext()) { - FolderListener listener = (FolderListener) it.next(); - event.dispatch(listener); - } - } - - protected void notifyFolderRenamedListeners(Folder oldFolder, - Folder newFolder) { - Iterator it = _folderListeners.iterator(); - FolderEvent event = - new FolderEvent(this, oldFolder, newFolder, FolderEvent.RENAMED); - while (it.hasNext()) { - FolderListener listener = (FolderListener) it.next(); - event.dispatch(listener); - } + + public void addStoreListener(StoreListener listener) { + storeListeners.add(listener); + } + + public void removeStoreListener(StoreListener listener) { + storeListeners.remove(listener); } protected void notifyStoreListeners(int type, String message) { - Iterator it = _storeListeners.iterator(); - StoreEvent event = new StoreEvent(this, type, message); - while (it.hasNext()) { - StoreListener listener = (StoreListener) it.next(); - listener.notification(event); - } + queueEvent(new StoreEvent(this, type, message), storeListeners); + } + + + public void addFolderListener(FolderListener listener) { + folderListeners.add(listener); } public void removeFolderListener(FolderListener listener) { - _folderListeners.remove(listener); + folderListeners.remove(listener); } - public void removeStoreListener(StoreListener listener) { - _storeListeners.remove(listener); + protected void notifyFolderListeners(int type, Folder folder) { + queueEvent(new FolderEvent(this, folder, type), folderListeners); + } + + protected void notifyFolderRenamedListeners(Folder oldFolder, Folder newFolder) { + queueEvent(new FolderEvent(this, oldFolder, newFolder, FolderEvent.RENAMED), folderListeners); } } Modified: geronimo/trunk/specs/javamail/src/java/javax/mail/Transport.java Url: http://svn.apache.org/viewcvs/geronimo/trunk/specs/javamail/src/java/javax/mail/Transport.java?view=diff&rev=126350&p1=geronimo/trunk/specs/javamail/src/java/javax/mail/Transport.java&r1=126349&p2=geronimo/trunk/specs/javamail/src/java/javax/mail/Transport.java&r2=126350 ============================================================================== --- geronimo/trunk/specs/javamail/src/java/javax/mail/Transport.java (original) +++ geronimo/trunk/specs/javamail/src/java/javax/mail/Transport.java Mon Jan 24 22:35:47 2005 @@ -17,60 +17,107 @@ package javax.mail; +import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; +import java.util.Map; +import java.util.Vector; import javax.mail.event.TransportEvent; import javax.mail.event.TransportListener; /** + * Abstract class modeling a message transport. + * * @version $Rev$ $Date$ */ public abstract class Transport extends Service { + /** + * Send a message to all recipient addresses it contains (as returned by [EMAIL PROTECTED] Message#getAllRecipients()}) + * using message transports appropriate for each address. Message addresses are checked during submission, + * but there is no guarantee that the ultimate address is valid or that the message will ever be delivered. + * <p/> + * [EMAIL PROTECTED] Message#saveChanges()} will be called before the message is actually sent. + * + * @param message the message to send + * @throws MessagingException if there was a problem sending the message + */ public static void send(Message message) throws MessagingException { send(message, message.getAllRecipients()); } - public static void send(Message message, Address[] addresses) - throws MessagingException { - // TODO Implement - Transport transport = null; // Lookup based on protocol? - transport.sendMessage(message, addresses); - } + /** + * Send a message to all addresses provided irrespective of any recipients contained in the message itself + * using message transports appropriate for each address. Message addresses are checked during submission, + * but there is no guarantee that the ultimate address is valid or that the message will ever be delivered. + * <p/> + * [EMAIL PROTECTED] Message#saveChanges()} will be called before the message is actually sent. + * + * @param message the message to send + * @param addresses the addesses to send to + * @throws MessagingException if there was a problem sending the message + */ + public static void send(Message message, Address[] addresses) throws MessagingException { + Session session = message.session; + Map msgsByTransport = new HashMap(); + for (int i = 0; i < addresses.length; i++) { + Address address = addresses[i]; + Transport transport = session.getTransport(address); + List addrs = (List) msgsByTransport.get(transport); + if (addrs == null) { + addrs = new ArrayList(); + msgsByTransport.put(transport, addrs); + } + addrs.add(address); + } + + message.saveChanges(); - private List _listeners = new LinkedList(); + for (Iterator i = msgsByTransport.entrySet().iterator(); i.hasNext();) { + Map.Entry entry = (Map.Entry) i.next(); + Transport transport = (Transport) entry.getKey(); + List addrs = (List) entry.getValue(); + transport.sendMessage(message, (Address[]) addrs.toArray(new Address[addrs.size()])); + } + } + /** + * Constructor taking Session and URLName parameters required for [EMAIL PROTECTED] Service#Service(Session, URLName)}. + * + * @param session the Session this transport is for + * @param name the location this transport is for + */ public Transport(Session session, URLName name) { super(session, name); } - public void addTransportListener(TransportListener listener) { - _listeners.add(listener); - } + /** + * Send a message to the supplied addresses using this transport; if any of the addresses are + * invalid then a [EMAIL PROTECTED] SendFailedException} is thrown. Whether the message is actually sent + * to any of the addresses is undefined. + * <p/> + * Unlike the static [EMAIL PROTECTED] #send(Message, Address[])} method, [EMAIL PROTECTED] Message#saveChanges()} is + * not called. A [EMAIL PROTECTED] TransportEvent} will be sent to registered listeners once the delivery + * attempt has been made. + * + * @param message the message to send + * @param addresses list of addresses to send it to + * @throws SendFailedException if the send failed + * @throws MessagingException if there was a problem sending the message + */ + public abstract void sendMessage(Message message, Address[] addresses) throws MessagingException; - protected void notifyTransportListeners(int type, - Address[] validSent, - Address[] validUnsent, - Address[] invalid, - Message message) { - TransportEvent event = - new TransportEvent(this, - type, - validSent, - validUnsent, - invalid, - message); - Iterator it = _listeners.iterator(); - while (it.hasNext()) { - TransportListener listener = (TransportListener) it.next(); - event.dispatch(listener); - } + private Vector transportListeners = new Vector(); + + public void addTransportListener(TransportListener listener) { + transportListeners.add(listener); } public void removeTransportListener(TransportListener listener) { - _listeners.remove(listener); + transportListeners.remove(listener); } - public abstract void sendMessage(Message message, Address[] addresses) - throws MessagingException; + protected void notifyTransportListeners(int type, Address[] validSent, Address[] validUnsent, Address[] invalid, Message message) { + queueEvent(new TransportEvent(this, type, validSent, validUnsent, invalid, message), transportListeners); + } } Modified: geronimo/trunk/specs/javamail/src/java/javax/mail/UIDFolder.java Url: http://svn.apache.org/viewcvs/geronimo/trunk/specs/javamail/src/java/javax/mail/UIDFolder.java?view=diff&rev=126350&p1=geronimo/trunk/specs/javamail/src/java/javax/mail/UIDFolder.java&r1=126349&p2=geronimo/trunk/specs/javamail/src/java/javax/mail/UIDFolder.java&r2=126350 ============================================================================== --- geronimo/trunk/specs/javamail/src/java/javax/mail/UIDFolder.java (original) +++ geronimo/trunk/specs/javamail/src/java/javax/mail/UIDFolder.java Mon Jan 24 22:35:47 2005 @@ -21,6 +21,10 @@ * @version $Rev$ $Date$ */ public interface UIDFolder { + /** + * A special value than can be passed as the <code>end</code> parameter to + * [EMAIL PROTECTED] Folder#getMessages(int, int)} to indicate the last message in this folder. + */ public static final long LASTUID = -1; public abstract long getUIDValidity() throws MessagingException;