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