javax.mail.Folder Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package javax.mail;
import java.io.*;
import java.lang.*;
import java.util.Vector;
import java.util.StringTokenizer;
import javax.mail.search.SearchTerm;
import javax.mail.event.*;
/**
* Folder is an abstract class that represents a folder for mail
* messages. Subclasses implement protocol specific Folders.
*
* Folders can contain Messages, other Folders or both, thus providing
* a tree-like hierarchy rooted at the Store's default folder. (Note
* that some Folder implementations may not allow both Messages and
* other Folders in the same Folder).
*
* The interpretation of folder names is implementation dependent.
* The different levels of hierarchy in a folder's full name
* are separated from each other by the hierarchy delimiter
* character.
*
* The case-insensitive full folder name (that is, the full name
* relative to the default folder for a Store) INBOX
* is reserved to mean the "primary folder for this user on this
* server". Not all Stores will provide an INBOX folder, and not
* all users will have an INBOX folder at all times. The name
* INBOX is reserved to refer to this folder,
* when it exists, in Stores that provide it.
*
* A Folder object obtained from a Store need not actually exist
* in the backend store. The exists
method tests whether
* the folder exists or not. The create
method
* creates a Folder.
*
* A Folder is initially in the closed state. Certain methods are valid
* in this state; the documentation for those methods note this. A
* Folder is opened by calling its 'open' method. All Folder methods,
* except open
, delete
and
* renameTo
, are valid in this state.
*
* The only way to get a Folder is by invoking the
* getFolder
method on Store, Folder, or Session, or by invoking
* the list
or listSubscribed
methods
* on Folder. Folder objects returned by the above methods are not
* cached by the Store. Thus, invoking the getFolder
method
* with the same folder name multiple times will return distinct Folder
* objects. Likewise for the list
and listSubscribed
* methods.
*
* The Message objects within the Folder are cached by the Folder.
* Thus, invoking getMessage(msgno)
on the same message number
* multiple times will return the same Message object, until an
* expunge is done on this Folder.
*
* Message objects from a Folder are only valid while a Folder is open
* and should not be accessed after the Folder is closed, even if the
* Folder is subsequently reopened. Instead, new Message objects must
* be fetched from the Folder after the Folder is reopened.
*
* Note that a Message's message number can change within a
* session if the containing Folder is expunged using the expunge
* method. Clients that use message numbers as references to messages
* should be aware of this and should be prepared to deal with
* situation (probably by flushing out existing message number references
* and reloading them). Because of this complexity, it is better for
* clients to use Message objects as references to messages, rather than
* message numbers. Expunged Message objects still have to be
* pruned, but other Message objects in that folder are not affected by the
* expunge.
*
* @author John Mani
* @author Bill Shannon
*/
public abstract class Folder {
/**
* The parent store.
*/
protected Store store;
/**
* The open mode of this folder. The open mode is
* Folder.READ_ONLY
, Folder.READ_WRITE
,
* or -1 if not known.
* @since JavaMail 1.1
*/
protected int mode = -1;
/**
* Constructor that takes a Store object.
*
* @param store the Store that holds this folder
*/
protected Folder(Store store) {
this.store = store;
}
/**
* Returns the name of this Folder.
*
* This method can be invoked on a closed Folder.
*
* @return name of the Folder
*/
public abstract String getName();
/**
* Returns the full name of this Folder. If the folder resides under
* the root hierarchy of this Store, the returned name is relative
* to the root. Otherwise an absolute name, starting with the
* hierarchy delimiter, is returned.
*
* This method can be invoked on a closed Folder.
*
* @return full name of the Folder
*/
public abstract String getFullName();
/**
* Return a URLName representing this folder. The returned URLName
* does not include the password used to access the store.
*
* @return the URLName representing this folder
* @see URLName
* @since JavaMail 1.1
*/
public URLName getURLName() throws MessagingException {
URLName storeURL = getStore().getURLName();
String fullname = getFullName();
StringBuffer encodedName = new StringBuffer();
if (fullname != null) {
/*
// We need to encode each of the folder's names.
char separator = getSeparator();
StringTokenizer tok = new StringTokenizer(
fullname, new Character(separator).toString(), true);
while (tok.hasMoreTokens()) {
String s = tok.nextToken();
if (s.charAt(0) == separator)
encodedName.append(separator);
else
// XXX - should encode, but since there's no decoder...
//encodedName.append(java.net.URLEncoder.encode(s));
encodedName.append(s);
}
*/
// append the whole thing, until we can encode
encodedName.append(fullname);
}
/*
* Sure would be convenient if URLName had a
* constructor that took a base URLName.
*/
return new URLName(storeURL.getProtocol(), storeURL.getHost(),
storeURL.getPort(), encodedName.toString(),
storeURL.getUsername(),
null /* no password */);
}
/**
* Returns the Store that owns this Folder object.
* This method can be invoked on a closed Folder.
* @return the Store
*/
public Store getStore() {
return store;
}
/**
* Returns the parent folder of this folder.
* This method can be invoked on a closed Folder. If this folder
* is the top of a folder hierarchy, this method returns null.
*
* Note that since Folder objects are not cached, invoking this method
* returns a new distinct Folder object.
*
* @return Parent folder
*/
public abstract Folder getParent() throws MessagingException;
/**
* Tests if this folder physically exists on the Store.
* This method can be invoked on a closed Folder.
*
* @return true if the folder exists, otherwise false
* @see #create
* @exception MessagingException typically if the connection
* to the server is lost.
*/
public abstract boolean exists() throws MessagingException;
/**
* Returns a list of Folders belonging to this Folder's namespace
* that match the specified pattern. Patterns may contain the wildcard
* characters "%"
, which matches any character except hierarchy
* delimiters, and "*"
, which matches any character.
*
* As an example, given the folder hierarchy:
* Personal/
* Finance/
* Stocks
* Bonus
* StockOptions
* Jokes
*
* list("*")
on "Personal" will return the whole
* hierarchy.
* list("%")
on "Personal" will return "Finance" and
* "Jokes".
* list("Jokes")
on "Personal" will return "Jokes".
* list("Stock*")
on "Finance" will return "Stocks"
* and "StockOptions".
*
* Folder objects are not cached by the Store, so invoking this
* method on the same pattern multiple times will return that many
* distinct Folder objects.
*
* This method can be invoked on a closed Folder.
*
* @param pattern the match pattern
* @return array of matching Folder objects. An empty
* array is returned if no matching Folders exist.
* @see #listSubscribed
* @exception FolderNotFoundException if this folder does
* not exist.
* @exception MessagingException
*/
public abstract Folder[] list(String pattern) throws MessagingException;
/**
* Returns a list of subscribed Folders belonging to this Folder's
* namespace that match the specified pattern. If the folder does
* not support subscription, this method should resolve to
* list
.
* (The default implementation provided here, does just this).
* The pattern can contain wildcards as for list
.
*
* Note that, at a given level of the folder hierarchy, a particular
* folder may not be subscribed, but folders underneath that folder
* in the folder hierarchy may be subscribed. In order to allow
* walking the folder hierarchy, such unsubscribed folders may be
* returned, indicating that a folder lower in the hierarchy is
* subscribed. The isSubscribed
method on a folder will
* tell whether any particular folder is actually subscribed.
*
* Folder objects are not cached by the Store, so invoking this
* method on the same pattern multiple times will return that many
* distinct Folder objects.
*
* This method can be invoked on a closed Folder.
*
* @param pattern the match pattern
* @return array of matching subscribed Folder objects. An
* empty array is returned if no matching
* subscribed folders exist.
* @see #list
* @exception FolderNotFoundException if this folder does
* not exist.
* @exception MessagingException
*/
public Folder[] listSubscribed(String pattern) throws MessagingException {
return list(pattern);
}
/**
* Convenience method that returns the list of folders under this
* Folder. This method just calls the list(String pattern)
* method with "%"
as the match pattern. This method can
* be invoked on a closed Folder.
*
* @return array of Folder objects under this Folder. An
* empty array is returned if no subfolders exist.
* @see #list
* @exception FolderNotFoundException if this folder does
* not exist.
* @exception MessagingException
*/
public Folder[] list() throws MessagingException {
return list("%");
}
/**
* Convenience method that returns the list of subscribed folders
* under this Folder. This method just calls the
* listSubscribed(String pattern)
method with "%"
* as the match pattern. This method can be invoked on a closed Folder.
*
* @return array of subscribed Folder objects under this
* Folder. An empty array is returned if no subscribed
* subfolders exist.
* @see #listSubscribed
* @exception FolderNotFoundException if this folder does
* not exist.
* @exception MessagingException
*/
public Folder[] listSubscribed() throws MessagingException {
return listSubscribed("%");
}
/**
* Return the delimiter character that separates this Folder's pathname
* from the names of immediate subfolders. This method can be invoked
* on a closed Folder.
*
* @exception FolderNotFoundException if the implementation
* requires the folder to exist, but it does not
* @return Hierarchy separator character
*/
public abstract char getSeparator() throws MessagingException;
/**
* This folder can contain messages
*/
public final static int HOLDS_MESSAGES = 0x01;
/**
* This folder can contain other folders
*/
public final static int HOLDS_FOLDERS = 0x02;
/**
* Returns the type of this Folder, that is, whether this folder can hold
* messages or subfolders or both. The returned value is an integer
* bitfield with the appropriate bits set. This method can be invoked
* on a closed folder.
*
* @return integer with appropriate bits set
* @exception FolderNotFoundException if this folder does
* not exist.
* @see #HOLDS_FOLDERS
* @see #HOLDS_MESSAGES
*/
public abstract int getType() throws MessagingException;
/**
* Create this folder on the Store. When this folder is created, any
* folders in its path that do not exist are also created.
*
* If the creation is successful, a CREATED FolderEvent is delivered
* to any FolderListeners registered on this Folder and this Store.
*
* @param type The type of this folder.
*
* @return true if the creation succeeds, else false.
* @exception MessagingException
* @see #HOLDS_FOLDERS
* @see #HOLDS_MESSAGES
* @see javax.mail.event.FolderEvent
*/
public abstract boolean create(int type) throws MessagingException;
/**
* Returns true if this Folder is subscribed.
*
* This method can be invoked on a closed Folder.
*
* The default implementation provided here just returns true.
*
* @return true if this Folder is subscribed
*/
public boolean isSubscribed() {
return true;
}
/**
* Subscribe or unsubscribe this Folder. Not all Stores support
* subscription.
*
* This method can be invoked on a closed Folder.
*
* The implementation provided here just throws the
* MethodNotSupportedException.
*
* @param subscribe true to subscribe, false to unsubscribe
* @exception FolderNotFoundException if this folder does
* not exist.
* @exception MethodNotSupportedException if this store
* does not support subscription
* @exception MessagingException
*/
public void setSubscribed(boolean subscribe)
throws MessagingException {
throw new MethodNotSupportedException();
}
/**
* Returns true if this Folder has new messages since the last time
* this indication was reset. When this indication is set or reset
* depends on the Folder implementation (and in the case of IMAP,
* depends on the server). This method can be used to implement
* a lightweight "check for new mail" operation on a Folder without
* opening it. (For example, a thread that monitors a mailbox and
* flags when it has new mail.) This method should indicate whether
* any messages in the Folder have the RECENT
flag set.
*
* Note that this is not an incremental check for new mail, i.e.,
* it cannot be used to determine whether any new messages have
* arrived since the last time this method was invoked. To
* implement incremental checks, the Folder needs to be opened.
*
* This method can be invoked on a closed Folder that can contain
* Messages.
*
* @return true if the Store has new Messages
* @exception FolderNotFoundException if this folder does
* not exist.
* @exception MessagingException
*/
public abstract boolean hasNewMessages() throws MessagingException;
/**
* Return the Folder object corresponding to the given name. Note that
* this folder does not physically have to exist in the Store. The
* exists()
method on a Folder indicates whether it really
* exists on the Store.
*
* In some Stores, name can be an absolute path if it starts with the
* hierarchy delimiter. Otherwise, it is interpreted relative to
* this Folder.
*
* Folder objects are not cached by the Store, so invoking this
* method on the same name multiple times will return that many
* distinct Folder objects.
*
* This method can be invoked on a closed Folder.
*
* @param name name of the Folder
* @return Folder object
* @exception MessagingException
*/
public abstract Folder getFolder(String name)
throws MessagingException;
/**
* Delete this Folder. This method will succeed only on a closed
* Folder.
*
* The recurse
flag controls whether the deletion affects
* subfolders or not. If true, all subfolders are deleted, then this
* folder itself is deleted. If false, the behaviour is dependent on
* the folder type and is elaborated below:
*
*
* -
* The folder can contain only messages: (type == HOLDS_MESSAGES).
*
* All messages within the folder are removed. The folder
* itself is then removed. An appropriate FolderEvent is generated by
* the Store and this folder.
*
*
-
* The folder can contain only subfolders: (type == HOLDS_FOLDERS).
*
* If this folder is empty (does not contain any
* subfolders at all), it is removed. An appropriate FolderEvent is
* generated by the Store and this folder.
* If this folder contains any subfolders, the delete fails
* and returns false.
*
*
-
* The folder can contain subfolders as well as messages:
* If the folder is empty (no messages or subfolders), it
* is removed. If the folder contains no subfolders, but only messages,
* then all messages are removed. The folder itself is then removed.
* In both the above cases, an appropriate FolderEvent is
* generated by the Store and this folder.
*
* If the folder contains subfolders there are 3 possible
* choices an implementation is free to do:
*
*
* - The operation fails, irrespective of whether this folder
* contains messages or not. Some implementations might elect to go
* with this simple approach. The delete() method returns false.
*
*
- Any messages within the folder are removed. Subfolders
* are not removed. The folder itself is not removed or affected
* in any manner. The delete() method returns true. And the
* exists() method on this folder will return true indicating that
* this folder still exists.
* An appropriate FolderEvent is generated by the Store and this folder.
*
* - Any messages within the folder are removed. Subfolders are
* not removed. The folder itself changes its type from
* HOLDS_FOLDERS | HOLDS_MESSAGES to HOLDS_FOLDERS. Thus new
* messages cannot be added to this folder, but new subfolders can
* be created underneath. The delete() method returns true indicating
* success. The exists() method on this folder will return true
* indicating that this folder still exists.
* An appropriate FolderEvent is generated by the Store and this folder.
*
*
*
* @return true if the Folder is deleted successfully
* @exception FolderNotFoundException if this folder does
* not exist
* @exception IllegalStateException if this folder is not in
* the closed state.
* @exception MessagingException
* @see javax.mail.event.FolderEvent
*/
public abstract boolean delete(boolean recurse)
throws MessagingException;
/**
* Rename this Folder. This method will succeed only on a closed
* Folder.
*
* If the rename is successful, a RENAMED FolderEvent is delivered
* to FolderListeners registered on this folder and its containing
* Store.
*
* @param f a folder representing the new name for this Folder
* @return true if the Folder is renamed successfully
* @exception FolderNotFoundException if this folder does
* not exist
* @exception IllegalStateException if this folder is not in
* the closed state.
* @exception MessagingException
* @see javax.mail.event.FolderEvent
*/
public abstract boolean renameTo(Folder f) throws MessagingException;
/**
* The Folder is read only. The state and contents of this
* folder cannot be modified.
*/
public static final int READ_ONLY = 1;
/**
* The state and contents of this folder can be modified.
*/
public static final int READ_WRITE = 2;
/**
* Open this Folder. This method is valid only on Folders that
* can contain Messages and that are closed.
*
* If this folder is opened successfully, an OPENED ConnectionEvent
* is delivered to any ConnectionListeners registered on this
* Folder.
*
* The effect of opening multiple connections to the same folder
* on a specifc Store is implementation dependent. Some implementations
* allow multiple readers, but only one writer. Others allow
* multiple writers as well as readers.
*
* @param mode open the Folder READ_ONLY or READ_WRITE
* @exception FolderNotFoundException if this folder does
* not exist.
* @exception IllegalStateException if this folder is not in
* the closed state.
* @exception MessagingException
* @see #READ_ONLY
* @see #READ_WRITE
* @see #getType()
* @see javax.mail.event.ConnectionEvent
*/
public abstract void open(int mode) throws MessagingException;
/**
* Close this Folder. This method is valid only on open Folders.
*
* A CLOSED ConnectionEvent is delivered to any ConnectionListeners
* registered on this Folder. Note that the folder is closed even
* if this method terminates abnormally by throwing a
* MessagingException.
*
* @param expunge expunges all deleted messages if this flag is true
* @exception IllegalStateException if this folder is not opened
* @exception MessagingException
* @see javax.mail.event.ConnectionEvent
*/
public abstract void close(boolean expunge) throws MessagingException;
/**
* Indicates whether this Folder is in the 'open' state.
* @return true if this Folder is in the 'open' state.
*/
public abstract boolean isOpen();
/**
* Return the open mode of this folder. Returns
* Folder.READ_ONLY
, Folder.READ_WRITE
,
* or -1 if the open mode is not known (usually only because an older
* Folder
provider has not been updated to use this new
* method).
*
* @exception IllegalStateException if this folder is not opened
* @return the open mode of this folder
* @since JavaMail 1.1
*/
public synchronized int getMode() {
if (!isOpen())
throw new IllegalStateException("Folder not open");
return mode;
}
/**
* Get the permanent flags supported by this Folder. Returns a Flags
* object that contains all the flags supported.
*
* The special flag Flags.Flag.USER
indicates that this Folder
* supports arbitrary user-defined flags.
*
* The supported permanent flags for a folder may not be available
* until the folder is opened.
*
* @return permanent flags, or null if not known
*/
public abstract Flags getPermanentFlags();
/**
* Get total number of messages in this Folder.
*
* This method can be invoked on a closed folder. However, note
* that for some folder implementations, getting the total message
* count can be an expensive operation involving actually opening
* the folder. In such cases, a provider can choose not to support
* this functionality in the closed state, in which case this method
* must return -1.
*
* Clients invoking this method on a closed folder must be aware
* that this is a potentially expensive operation. Clients must
* also be prepared to handle a return value of -1 in this case.
*
* @return total number of messages. -1 may be returned
* by certain implementations if this method is
* invoked on a closed folder.
* @exception FolderNotFoundException if this folder does
* not exist.
* @exception MessagingException
*/
public abstract int getMessageCount() throws MessagingException;
/**
* Get the number of new messages in this Folder.
*
* This method can be invoked on a closed folder. However, note
* that for some folder implementations, getting the new message
* count can be an expensive operation involving actually opening
* the folder. In such cases, a provider can choose not to support
* this functionality in the closed state, in which case this method
* must return -1.
*
* Clients invoking this method on a closed folder must be aware
* that this is a potentially expensive operation. Clients must
* also be prepared to handle a return value of -1 in this case.
*
* This implementation returns -1 if this folder is closed. Else
* this implementation gets each Message in the folder using
* getMessage(int)
and checks whether its
* RECENT
flag is set. The total number of messages
* that have this flag set is returned.
*
* @return number of new messages. -1 may be returned
* by certain implementations if this method is
* invoked on a closed folder.
* @exception FolderNotFoundException if this folder does
* not exist.
* @exception MessagingException
*/
public synchronized int getNewMessageCount()
throws MessagingException {
if (!isOpen())
return -1;
int newmsgs = 0;
int total = getMessageCount();
for (int i = 1; i <= total; i++) {
try {
if (getMessage(i).isSet(Flags.Flag.RECENT))
newmsgs++;
} catch (MessageRemovedException me) {
// This is an expunged message, ignore it.
continue;
}
}
return newmsgs;
}
/**
* Get the total number of unread messages in this Folder.
*
* This method can be invoked on a closed folder. However, note
* that for some folder implementations, getting the unread message
* count can be an expensive operation involving actually opening
* the folder. In such cases, a provider can choose not to support
* this functionality in the closed state, in which case this method
* must return -1.
*
* Clients invoking this method on a closed folder must be aware
* that this is a potentially expensive operation. Clients must
* also be prepared to handle a return value of -1 in this case.
*
* This implementation returns -1 if this folder is closed. Else
* this implementation gets each Message in the folder using
* getMessage(int)
and checks whether its
* SEEN
flag is set. The total number of messages
* that do not have this flag set is returned.
*
* @return total number of unread messages. -1 may be returned
* by certain implementations if this method is
* invoked on a closed folder.
* @exception FolderNotFoundException if this folder does
* not exist.
* @exception MessagingException
*/
public synchronized int getUnreadMessageCount()
throws MessagingException {
if (!isOpen())
return -1;
int unread = 0;
int total = getMessageCount();
for (int i = 1; i <= total; i++) {
try {
if (!getMessage(i).isSet(Flags.Flag.SEEN))
unread++;
} catch (MessageRemovedException me) {
// This is an expunged message, ignore it.
continue;
}
}
return unread;
}
/**
* Get the number of deleted messages in this Folder.
*
* This method can be invoked on a closed folder. However, note
* that for some folder implementations, getting the deleted message
* count can be an expensive operation involving actually opening
* the folder. In such cases, a provider can choose not to support
* this functionality in the closed state, in which case this method
* must return -1.
*
* Clients invoking this method on a closed folder must be aware
* that this is a potentially expensive operation. Clients must
* also be prepared to handle a return value of -1 in this case.
*
* This implementation returns -1 if this folder is closed. Else
* this implementation gets each Message in the folder using
* getMessage(int)
and checks whether its
* DELETED
flag is set. The total number of messages
* that have this flag set is returned.
*
* @return number of deleted messages. -1 may be returned
* by certain implementations if this method is
* invoked on a closed folder.
* @exception FolderNotFoundException if this folder does
* not exist.
* @exception MessagingException
* @since JavaMail 1.3
*/
public synchronized int getDeletedMessageCount() throws MessagingException {
if (!isOpen())
return -1;
int deleted = 0;
int total = getMessageCount();
for (int i = 1; i <= total; i++) {
try {
if (getMessage(i).isSet(Flags.Flag.DELETED))
deleted++;
} catch (MessageRemovedException me) {
// This is an expunged message, ignore it.
continue;
}
}
return deleted;
}
/**
* Get the Message object corresponding to the given message
* number. A Message object's message number is the relative
* position of this Message in its Folder. Messages are numbered
* starting at 1 through the total number of message in the folder.
* Note that the message number for a particular Message can change
* during a session if other messages in the Folder are deleted and
* the Folder is expunged.
*
* Message objects are light-weight references to the actual message
* that get filled up on demand. Hence Folder implementations are
* expected to provide light-weight Message objects.
*
* Unlike Folder objects, repeated calls to getMessage with the
* same message number will return the same Message object, as
* long as no messages in this folder have been expunged.
*
* Since message numbers can change within a session if the folder
* is expunged , clients are advised not to use message numbers as
* references to messages. Use Message objects instead.
*
* @param msgnum the message number
* @return the Message object
* @see #getMessageCount
* @see #fetch
* @exception FolderNotFoundException if this folder does
* not exist.
* @exception IllegalStateException if this folder is not opened
* @exception IndexOutOfBoundsException if the message number
* is out of range.
* @exception MessagingException
*/
public abstract Message getMessage(int msgnum)
throws MessagingException;
/**
* Get the Message objects for message numbers ranging from start
* through end, both start and end inclusive. Note that message
* numbers start at 1, not 0.
*
* Message objects are light-weight references to the actual message
* that get filled up on demand. Hence Folder implementations are
* expected to provide light-weight Message objects.
*
* This implementation uses getMessage(index) to obtain the required
* Message objects. Note that the returned array must contain
* (end-start+1)
Message objects.
*
* @param start the number of the first message
* @param end the number of the last message
* @return the Message objects
* @see #fetch
* @exception FolderNotFoundException if this folder does
* not exist.
* @exception IllegalStateException if this folder is not opened.
* @exception IndexOutOfBoundsException if the start or end
* message numbers are out of range.
* @exception MessagingException
*/
public synchronized Message[] getMessages(int start, int end)
throws MessagingException {
Message[] msgs = new Message[end - start +1];
for (int i = start; i <= end; i++)
msgs[i - start] = getMessage(i);
return msgs;
}
/**
* Get the Message objects for message numbers specified in
* the array.
*
* Message objects are light-weight references to the actual message
* that get filled up on demand. Hence Folder implementations are
* expected to provide light-weight Message objects.
*
* This implementation uses getMessage(index) to obtain the required
* Message objects. Note that the returned array must contain
* msgnums.length
Message objects
*
* @param msgnums the array of message numbers
* @return the array of Message objects.
* @see #fetch
* @exception FolderNotFoundException if this folder does
* not exist.
* @exception IllegalStateException if this folder is not opened.
* @exception IndexOutOfBoundsException if any message number
* in the given array is out of range.
* @exception MessagingException
*/
public synchronized Message[] getMessages(int[] msgnums)
throws MessagingException {
int len = msgnums.length;
Message[] msgs = new Message[len];
for (int i = 0; i < len; i++)
msgs[i] = getMessage(msgnums[i]);
return msgs;
}
/**
* Get all Message objects from this Folder. Returns an empty array
* if the folder is empty.
*
* Clients can use Message objects (instead of sequence numbers)
* as references to the messages within a folder; this method supplies
* the Message objects to the client. Folder implementations are
* expected to provide light-weight Message objects, which get
* filled on demand.
*
* This implementation invokes getMessageCount()
to get
* the current message count and then uses getMessage()
* to get Message objects from 1 till the message count.
*
* @return array of Message objects, empty array if folder
* is empty.
* @see #fetch
* @exception FolderNotFoundException if this folder does
* not exist.
* @exception IllegalStateException if this folder is not opened.
* @exception MessagingException
*/
public synchronized Message[] getMessages() throws MessagingException {
if (!isOpen()) // otherwise getMessageCount might return -1
throw new IllegalStateException("Folder not open");
int total = getMessageCount();
Message[] msgs = new Message[total];
for (int i = 1; i <= total; i++)
msgs[i-1] = getMessage(i);
return msgs;
}
/**
* Append given Messages to this folder. This method can be
* invoked on a closed Folder. An appropriate MessageCountEvent
* is delivered to any MessageCountListener registered on this
* folder when the messages arrive in the folder.
*
* Folder implementations must not abort this operation if a
* Message in the given message array turns out to be an
* expunged Message.
*
* @param msgs array of Messages to be appended
* @exception FolderNotFoundException if this folder does
* not exist.
* @exception MessagingException if the append failed.
*/
public abstract void appendMessages(Message[] msgs)
throws MessagingException;
/**
* Prefetch the items specified in the FetchProfile for the
* given Messages.
*
* Clients use this method to indicate that the specified items are
* needed en-masse for the given message range. Implementations are
* expected to retrieve these items for the given message range in
* a efficient manner. Note that this method is just a hint to the
* implementation to prefetch the desired items.
*
* An example is a client filling its header-view window with
* the Subject, From and X-mailer headers for all messages in the
* folder.
*
*
* Message[] msgs = folder.getMessages();
*
* FetchProfile fp = new FetchProfile();
* fp.add(FetchProfile.Item.ENVELOPE);
* fp.add("X-mailer");
* folder.fetch(msgs, fp);
*
* for (int i = 0; i < folder.getMessageCount(); i++) {
* display(msg[i].getFrom());
* display(msg[i].getSubject());
* display(msg[i].getHeader("X-mailer"));
* }
*
*
*
* The implementation provided here just returns without
* doing anything useful. Providers wanting to provide a real
* implementation for this method should override this method.
*
* @param msgs fetch items for these messages
* @param fp the FetchProfile
* @exception IllegalStateException if this folder is not opened
* @exception MessagingException.
*/
public void fetch(Message[] msgs, FetchProfile fp)
throws MessagingException {
return;
}
/**
* Set the specified flags on the messages specified in the array.
* This will result in appropriate MessageChangedEvents being
* delivered to any MessageChangedListener registered on this
* Message's containing folder.
*
* Note that the specified Message objects must
* belong to this folder. Certain Folder implementations can
* optimize the operation of setting Flags for a group of messages,
* so clients might want to use this method, rather than invoking
* Message.setFlags
for each Message.
*
* This implementation degenerates to invoking setFlags()
* on each Message object. Specific Folder implementations that can
* optimize this case should do so.
* Also, an implementation must not abort the operation if a Message
* in the array turns out to be an expunged Message.
*
* @param msgs the array of message objects
* @param flag Flags object containing the flags to be set
* @param value set the flags to this boolean value
* @exception IllegalStateException if this folder is not opened
* or if it has been opened READ_ONLY.
* @exception MessagingException
* @see Message#setFlags
* @see javax.mail.event.MessageChangedEvent
*/
public synchronized void setFlags(Message[] msgs,
Flags flag, boolean value) throws MessagingException {
for (int i = 0; i < msgs.length; i++) {
try {
msgs[i].setFlags(flag, value);
} catch (MessageRemovedException me) {
// This message is expunged, skip
}
}
}
/**
* Set the specified flags on the messages numbered from start
* through end, both start and end inclusive. Note that message
* numbers start at 1, not 0.
* This will result in appropriate MessageChangedEvents being
* delivered to any MessageChangedListener registered on this
* Message's containing folder.
*
* Certain Folder implementations can
* optimize the operation of setting Flags for a group of messages,
* so clients might want to use this method, rather than invoking
* Message.setFlags
for each Message.
*
* The default implementation uses getMessage(int)
to
* get each Message
object and then invokes
* setFlags
on that object to set the flags.
* Specific Folder implementations that can optimize this case should do so.
* Also, an implementation must not abort the operation if a message
* number refers to an expunged message.
*
* @param start the number of the first message
* @param end the number of the last message
* @param flag Flags object containing the flags to be set
* @param value set the flags to this boolean value
* @exception IllegalStateException if this folder is not opened
* or if it has been opened READ_ONLY.
* @exception IndexOutOfBoundsException if the start or end
* message numbers are out of range.
* @exception MessagingException
* @see Message#setFlags
* @see javax.mail.event.MessageChangedEvent
*/
public synchronized void setFlags(int start, int end,
Flags flag, boolean value) throws MessagingException {
for (int i = start; i <= end; i++) {
try {
Message msg = getMessage(i);
msg.setFlags(flag, value);
} catch (MessageRemovedException me) {
// This message is expunged, skip
}
}
}
/**
* Set the specified flags on the messages whose message numbers
* are in the array.
* This will result in appropriate MessageChangedEvents being
* delivered to any MessageChangedListener registered on this
* Message's containing folder.
*
* Certain Folder implementations can
* optimize the operation of setting Flags for a group of messages,
* so clients might want to use this method, rather than invoking
* Message.setFlags
for each Message.
*
* The default implementation uses getMessage(int)
to
* get each Message
object and then invokes
* setFlags
on that object to set the flags.
* Specific Folder implementations that can optimize this case should do so.
* Also, an implementation must not abort the operation if a message
* number refers to an expunged message.
*
* @param msgnums the array of message numbers
* @param flag Flags object containing the flags to be set
* @param value set the flags to this boolean value
* @exception IllegalStateException if this folder is not opened
* or if it has been opened READ_ONLY.
* @exception IndexOutOfBoundsException if any message number
* in the given array is out of range.
* @exception MessagingException
* @see Message#setFlags
* @see javax.mail.event.MessageChangedEvent
*/
public synchronized void setFlags(int[] msgnums,
Flags flag, boolean value) throws MessagingException {
for (int i = 0; i < msgnums.length; i++) {
try {
Message msg = getMessage(msgnums[i]);
msg.setFlags(flag, value);
} catch (MessageRemovedException me) {
// This message is expunged, skip
}
}
}
/**
* Copy the specified Messages from this Folder into another
* Folder. This operation appends these Messages to the
* destination Folder. The destination Folder does not have to
* be opened. An appropriate MessageCountEvent
* is delivered to any MessageCountListener registered on the
* destination folder when the messages arrive in the folder.
*
* Note that the specified Message objects must
* belong to this folder. Folder implementations might be able
* to optimize this method by doing server-side copies.
*
* This implementation just invokes appendMessages()
* on the destination folder to append the given Messages. Specific
* folder implementations that support server-side copies should
* do so, if the destination folder's Store is the same as this
* folder's Store.
* Also, an implementation must not abort the operation if a
* Message in the array turns out to be an expunged Message.
*
* @param msgs the array of message objects
* @param folder the folder to copy the messages to
* @exception FolderNotFoundException if the destination
* folder does not exist.
* @exception IllegalStateException if this folder is not opened.
* @exception MessagingException
* @see #appendMessages
*/
public void copyMessages(Message[] msgs, Folder folder)
throws MessagingException {
if (!folder.exists())
throw new FolderNotFoundException(
folder.getFullName() + " does not exist",
folder);
folder.appendMessages(msgs);
}
/**
* Expunge (permanently remove) messages marked DELETED. Returns an
* array containing the expunged message objects. The
* getMessageNumber
method
* on each of these message objects returns that Message's original
* (that is, prior to the expunge) sequence number. A MessageCountEvent
* containing the expunged messages is delivered to any
* MessageCountListeners registered on the folder.
*
* Expunge causes the renumbering of Message objects subsequent to
* the expunged messages. Clients that use message numbers as
* references to messages should be aware of this and should be
* prepared to deal with the situation (probably by flushing out
* existing message number caches and reloading them). Because of
* this complexity, it is better for clients to use Message objects
* as references to messages, rather than message numbers. Any
* expunged Messages objects still have to be pruned, but other
* Messages in that folder are not affected by the expunge.
*
* After a message is expunged, only the isExpunged
and
* getMessageNumber
methods are still valid on the
* corresponding Message object; other methods may throw
* MessageRemovedException
*
* @return array of expunged Message objects
* @exception FolderNotFoundException if this folder does not
* exist
* @exception IllegalStateException if this folder is not opened.
* @exception MessagingException
* @see Message#isExpunged
* @see javax.mail.event.MessageCountEvent
*/
public abstract Message[] expunge() throws MessagingException;
/**
* Search this Folder for messages matching the specified
* search criterion. Returns an array containing the matching
* messages . Returns an empty array if no matches were found.
*
* This implementation invokes
* search(term, getMessages())
, to apply the search
* over all the messages in this folder. Providers that can implement
* server-side searching might want to override this method to provide
* a more efficient implementation.
*
* @param term the search criterion
* @return array of matching messages
* @exception javax.mail.search.SearchException if the search
* term is too complex for the implementation to handle.
* @exception FolderNotFoundException if this folder does
* not exist.
* @exception IllegalStateException if this folder is not opened.
* @exception MessagingException
* @see javax.mail.search.SearchTerm
*/
public Message[] search(SearchTerm term) throws MessagingException {
return search(term, getMessages());
}
/**
* Search the given array of messages for those that match the
* specified search criterion. Returns an array containing the
* matching messages. Returns an empty array if no matches were
* found.
*
* Note that the specified Message objects must
* belong to this folder.
*
* This implementation iterates through the given array of messages,
* and applies the search criterion on each message by calling
* its match()
method with the given term. The
* messages that succeed in the match are returned. Providers
* that can implement server-side searching might want to override
* this method to provide a more efficient implementation. If the
* search term is too complex or contains user-defined terms that
* cannot be executed on the server, providers may elect to either
* throw a SearchException or degenerate to client-side searching by
* calling super.search()
to invoke this implementation.
*
* @param term the search criterion
* @param msgs the messages to be searched
* @return array of matching messages
* @exception javax.mail.search.SearchException if the search
* term is too complex for the implementation to handle.
* @exception IllegalStateException if this folder is not opened
* @exception MessagingException
* @see javax.mail.search.SearchTerm
*/
public Message[] search(SearchTerm term, Message[] msgs)
throws MessagingException {
Vector matchedMsgs = new Vector();
// Run thru the given messages
for (int i = 0; i < msgs.length; i++) {
try {
if (msgs[i].match(term)) // matched
matchedMsgs.addElement(msgs[i]); // add it
} catch(MessageRemovedException mrex) { }
}
Message[] m = new Message[matchedMsgs.size()];
matchedMsgs.copyInto(m);
return m;
}
/*
* The set of listeners are stored in Vectors appropriate to their
* type. We mark all listener Vectors as "volatile" because, while
* we initialize them inside this folder's synchronization lock,
* they are accessed (checked for null) in the "notify" methods,
* which can't be synchronized due to lock ordering constraints.
* Since the listener fields (the handles on the Vector objects)
* are only ever set, and are never cleared, we believe this is
* safe. The code that dispatches the notifications will either
* see the null and assume there are no listeners or will see the
* Vector and will process the listeners. There's an inherent race
* between adding a listener and notifying the listeners; the lack
* of synchronization during notification does not make the race
* condition significantly worse. If one thread is setting a
* listener at the "same" time an event is being dispatched, the
* dispatch code might not see the listener right away. The
* dispatch code doesn't have to worry about the Vector handle
* being set to null, and thus using an out-of-date set of
* listeners, because we never set the field to null.
*/
// Vector of connection listeners.
private volatile Vector connectionListeners = null;
/**
* Add a listener for Connection events on this Folder.
*
* The implementation provided here adds this listener
* to an internal list of ConnectionListeners.
*
* @param l the Listener for Connection events
* @see javax.mail.event.ConnectionEvent
*/
public synchronized void
addConnectionListener(ConnectionListener l) {
if (connectionListeners == null)
connectionListeners = new Vector();
connectionListeners.addElement(l);
}
/**
* Remove a Connection event listener.
*
* The implementation provided here removes this listener
* from the internal list of ConnectionListeners.
*
* @param l the listener
* @see #addConnectionListener
*/
public synchronized void
removeConnectionListener(ConnectionListener l) {
if (connectionListeners != null)
connectionListeners.removeElement(l);
}
/**
* Notify all ConnectionListeners. Folder implementations are
* expected to use this method to broadcast connection events.
*
* The provided implementation queues the event into
* an internal event queue. An event dispatcher thread dequeues
* events from the queue and dispatches them to the registered
* ConnectionListeners. Note that the event dispatching occurs
* in a separate thread, thus avoiding potential deadlock problems.
*
* @param type the ConnectionEvent type
* @see javax.mail.event.ConnectionEvent
*/
protected void notifyConnectionListeners(int type) {
if (connectionListeners != null) {
ConnectionEvent e = new ConnectionEvent(this, type);
queueEvent(e, connectionListeners);
}
/* Fix for broken JDK1.1.x Garbage collector :
* The 'conservative' GC in JDK1.1.x occasionally fails to
* garbage-collect Threads which are in the wait state.
* This would result in thread (and consequently memory) leaks.
*
* We attempt to fix this by sending a 'terminator' event
* to the queue, after we've sent the CLOSED event. The
* terminator event causes the event-dispatching thread to
* self destruct.
*/
if (type == ConnectionEvent.CLOSED)
terminateQueue();
}
// Vector of folder listeners
private volatile Vector folderListeners = null;
/**
* Add a listener for Folder events on this Folder.
*
* The implementation provided here adds this listener
* to an internal list of FolderListeners.
*
* @param l the Listener for Folder events
* @see javax.mail.event.FolderEvent
*/
public synchronized void addFolderListener(FolderListener l) {
if (folderListeners == null)
folderListeners = new Vector();
folderListeners.addElement(l);
}
/**
* Remove a Folder event listener.
*
* The implementation provided here removes this listener
* from the internal list of FolderListeners.
*
* @param l the listener
* @see #addFolderListener
*/
public synchronized void removeFolderListener(FolderListener l) {
if (folderListeners != null)
folderListeners.removeElement(l);
}
/**
* Notify all FolderListeners registered on this Folder and
* this folder's Store. Folder implementations are expected
* to use this method to broadcast Folder events.
*
* The implementation provided here queues the event into
* an internal event queue. An event dispatcher thread dequeues
* events from the queue and dispatches them to the
* FolderListeners registered on this folder. The implementation
* also invokes notifyFolderListeners
on this folder's
* Store to notify any FolderListeners registered on the store.
*
* @param type type of FolderEvent
* @see #notifyFolderRenamedListeners
*/
protected void notifyFolderListeners(int type) {
if (folderListeners != null) {
FolderEvent e = new FolderEvent(this, this, type);
queueEvent(e, folderListeners);
}
store.notifyFolderListeners(type, this);
}
/**
* Notify all FolderListeners registered on this Folder and
* this folder's Store about the renaming of this folder.
* Folder implementations are expected to use this method to
* broadcast Folder events indicating the renaming of folders.
*
* The implementation provided here queues the event into
* an internal event queue. An event dispatcher thread dequeues
* events from the queue and dispatches them to the
* FolderListeners registered on this folder. The implementation
* also invokes notifyFolderRenamedListeners
on this
* folder's Store to notify any FolderListeners registered on the store.
*
* @param folder Folder representing the new name.
* @see #notifyFolderListeners
* @since JavaMail 1.1
*/
protected void notifyFolderRenamedListeners(Folder folder) {
if (folderListeners != null) {
FolderEvent e = new FolderEvent(this, this, folder,
FolderEvent.RENAMED);
queueEvent(e, folderListeners);
}
store.notifyFolderRenamedListeners(this, folder);
}
// Vector of MessageCount listeners
private volatile Vector messageCountListeners = null;
/**
* Add a listener for MessageCount events on this Folder.
*
* The implementation provided here adds this listener
* to an internal list of MessageCountListeners.
*
* @param l the Listener for MessageCount events
* @see javax.mail.event.MessageCountEvent
*/
public synchronized void addMessageCountListener(MessageCountListener l) {
if (messageCountListeners == null)
messageCountListeners = new Vector();
messageCountListeners.addElement(l);
}
/**
* Remove a MessageCount listener.
*
* The implementation provided here removes this listener
* from the internal list of MessageCountListeners.
*
* @param l the listener
* @see #addMessageCountListener
*/
public synchronized void
removeMessageCountListener(MessageCountListener l) {
if (messageCountListeners != null)
messageCountListeners.removeElement(l);
}
/**
* Notify all MessageCountListeners about the addition of messages
* into this folder. Folder implementations are expected to use this
* method to broadcast MessageCount events for indicating arrival of
* new messages.
*
* The provided implementation queues the event into
* an internal event queue. An event dispatcher thread dequeues
* events from the queue and dispatches them to the registered
* MessageCountListeners. Note that the event dispatching occurs
* in a separate thread, thus avoiding potential deadlock problems.
*/
protected void notifyMessageAddedListeners(Message[] msgs) {
if (messageCountListeners == null)
return;
MessageCountEvent e = new MessageCountEvent(
this,
MessageCountEvent.ADDED,
false,
msgs);
queueEvent(e, messageCountListeners);
}
/**
* Notify all MessageCountListeners about the removal of messages
* from this Folder. Folder implementations are expected to use this
* method to broadcast MessageCount events indicating removal of
* messages.
*
* The provided implementation queues the event into
* an internal event queue. An event dispatcher thread dequeues
* events from the queue and dispatches them to the registered
* MessageCountListeners. Note that the event dispatching occurs
* in a separate thread, thus avoiding potential deadlock problems.
*/
protected void notifyMessageRemovedListeners(boolean removed,
Message[] msgs) {
if (messageCountListeners == null)
return;
MessageCountEvent e = new MessageCountEvent(
this,
MessageCountEvent.REMOVED,
removed,
msgs);
queueEvent(e, messageCountListeners);
}
// Vector of MessageChanged listeners.
private volatile Vector messageChangedListeners = null;
/**
* Add a listener for MessageChanged events on this Folder.
*
* The implementation provided here adds this listener
* to an internal list of MessageChangedListeners.
*
* @param l the Listener for MessageChanged events
* @see javax.mail.event.MessageChangedEvent
*/
public synchronized void
addMessageChangedListener(MessageChangedListener l) {
if (messageChangedListeners == null)
messageChangedListeners = new Vector();
messageChangedListeners.addElement(l);
}
/**
* Remove a MessageChanged listener.
*
* The implementation provided here removes this listener
* from the internal list of MessageChangedListeners.
*
* @param l the listener
* @see #addMessageChangedListener
*/
public synchronized void
removeMessageChangedListener(MessageChangedListener l) {
if (messageChangedListeners != null)
messageChangedListeners.removeElement(l);
}
/**
* Notify all MessageChangedListeners. Folder implementations are
* expected to use this method to broadcast MessageChanged events.
*
* The provided implementation queues the event into
* an internal event queue. An event dispatcher thread dequeues
* events from the queue and dispatches them to registered
* MessageChangedListeners. Note that the event dispatching occurs
* in a separate thread, thus avoiding potential deadlock problems.
*/
protected void notifyMessageChangedListeners(int type, Message msg) {
if (messageChangedListeners == null)
return;
MessageChangedEvent e = new MessageChangedEvent(this, type, msg);
queueEvent(e, messageChangedListeners);
}
/*
* The queue of events to be delivered.
*/
private EventQueue q;
/*
* A lock for creating the EventQueue object. Only one thread should
* create an EventQueue for this folder. We can't synchronize on the
* folder's lock because that would violate the locking hierarchy in
* some cases. For details, see the IMAP provider.
*/
private Object qLock = new Object();
/*
* Add the event and vector of listeners to the queue to be delivered.
*/
private void queueEvent(MailEvent event, Vector vector) {
// synchronize creation of the event queue
synchronized (qLock) {
if (q == null)
q = new EventQueue();
}
/*
* Copy the vector in order to freeze the state of the set
* of EventListeners the event should be delivered to prior
* to delivery. This ensures that any changes made to the
* Vector from a target listener's method during the delivery
* of this event will not take effect until after the event is
* delivered.
*/
Vector v = (Vector)vector.clone();
q.enqueue(event, v);
}
static class TerminatorEvent extends MailEvent {
private static final long serialVersionUID = 3765761925441296565L;
TerminatorEvent() {
super(new Object());
}
public void dispatch(Object listener) {
// Kill the event dispatching thread.
Thread.currentThread().interrupt();
}
}
// Dispatch the terminator
private void terminateQueue() {
synchronized (qLock) {
if (q != null) {
Vector dummyListeners = new Vector();
dummyListeners.setSize(1); // need atleast one listener
q.enqueue(new TerminatorEvent(), dummyListeners);
q = null;
}
}
}
protected void finalize() throws Throwable {
super.finalize();
terminateQueue();
}
/**
* override the default toString(), it will return the String
* from Folder.getFullName() or if that is null, it will use
* the default toString() behavior.
*/
public String toString() {
String s = getFullName();
if (s != null)
return s;
else
return super.toString();
}
}