You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by jb...@apache.org on 2005/01/25 07:35:51 UTC

svn commit: r126350 - in geronimo/trunk: modules/mail/src/test-resources/META-INF modules/mail/src/test/org/apache/geronimo/mail specs/javamail/src/java/javax/mail

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 {@link Folder} that contains this message, or null if it was not obtained from a folder.
+     */
     protected Folder folder;
-    protected int msgnum;
+    /**
+     * The {@link 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 {@link #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 {@link #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 {@link #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 {@link 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 {@link #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
+     * {@link #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 {@link #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 {@link 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 {@link Multipart}
+     * then recurse up the chain until a {@link 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 {@link 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 {@link 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 {@link #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.
+     *
+     *@throws 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 {@link #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 {@link 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 {@link 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 {@link #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 {@link 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/>
+     * {@link 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/>
+     * {@link 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 {@link 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 {@link SendFailedException} is thrown. Whether the message is actually sent
+     * to any of the addresses is undefined.
+     * <p/>
+     * Unlike the static {@link #send(Message, Address[])} method, {@link Message#saveChanges()} is
+     * not called. A {@link 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
+     * {@link Folder#getMessages(int, int)} to indicate the last message in this folder.
+     */
     public static final long LASTUID = -1;
 
     public abstract long getUIDValidity() throws MessagingException;

Re: svn commit: r126350

Posted by Jeremy Boynes <jb...@apache.org>.
jboynes@apache.org wrote:
> 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

I could not find in the spec what the behaviour should be if the same 
protocol is used as a store and a transport with different 
implementation classes. As in most common scenarios the transport will 
be SMTP and the store protocol POP3 or IMAP4, for now I have modified 
the test case in the mail module to use different protocols for each.

--
Jeremy