You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by ri...@apache.org on 2007/10/08 13:17:18 UTC
svn commit: r582780 - in
/geronimo/specs/trunk/geronimo-javamail_1.4_spec/src: main/java/javax/mail/
main/java/javax/mail/internet/ test/java/javax/mail/
Author: rickmcguire
Date: Mon Oct 8 04:17:15 2007
New Revision: 582780
URL: http://svn.apache.org/viewvc?rev=582780&view=rev
Log:
GERONIMO-3506 Javamail Notification events are supposed to be handled by a dispatcher thread.
Added:
geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/test/java/javax/mail/EventQueueTest.java (with props)
Modified:
geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/EventQueue.java
geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/Folder.java
geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/Service.java
geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/Session.java
geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/Transport.java
geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/internet/MimeMessage.java
Modified: geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/EventQueue.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/EventQueue.java?rev=582780&r1=582779&r2=582780&view=diff
==============================================================================
--- geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/EventQueue.java (original)
+++ geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/EventQueue.java Mon Oct 8 04:17:15 2007
@@ -24,20 +24,157 @@
//
package javax.mail;
+import java.util.ArrayList;
import java.util.List;
+
import javax.mail.event.MailEvent;
/**
+ * This is an event queue to dispatch javamail events on separate threads
+ * from the main thread. EventQueues are created by javamail Services
+ * (Transport and Store instances), as well as Folders created from Store
+ * instances. Each entity will have its own private EventQueue instance, but
+ * will delay creating it until it has an event to dispatch to a real listener.
+ *
+ * NOTE: It would be nice to use the concurrency support in Java 5 to
+ * manage the queue, but this code needs to run on Java 1.4 still. We also
+ * don't want to have dependencies on other packages with this, so no
+ * outside concurrency packages can be used either.
* @version $Rev$ $Date$
*/
-class EventQueue {
- // todo replace with version based on a work queue from Concurrent
- void queueEvent(MailEvent event, List listeners) {
+class EventQueue implements Runnable {
+ /**
+ * The dispatch thread that handles notification events.
+ */
+ protected Thread dispatchThread;
+
+ /**
+ * The dispatching queue for events.
+ */
+ protected List eventQueue = new ArrayList();
+
+ /**
+ * Create a new EventQueue, including starting the new thread.
+ */
+ public EventQueue() {
+ dispatchThread = new Thread(this, "JavaMail-EventQueue");
+ dispatchThread.setDaemon(true); // this is a background server thread.
+ // start the thread up
+ dispatchThread.start();
+ }
+
+ /**
+ * When an object implementing interface <code>Runnable</code> is used
+ * to create a thread, starting the thread causes the object's
+ * <code>run</code> method to be called in that separately executing
+ * thread.
+ * <p>
+ * The general contract of the method <code>run</code> is that it may
+ * take any action whatsoever.
+ *
+ * @see java.lang.Thread#run()
+ */
+ public void run() {
+ try {
+ while (true) {
+ // get the next event
+ PendingEvent p = dequeueEvent();
+ // an empty event on the queue means time to shut things down.
+ if (p.event == null) {
+ return;
+ }
+
+ // and tap the listeners on the shoulder.
+ dispatchEvent(p.event, p.listeners);
+ }
+ } catch (InterruptedException e) {
+ // been told to stop, so we stop
+ }
+ }
+
+
+ /**
+ * Stop the EventQueue. This will terminate the dispatcher thread as soon
+ * as it can, so there may be undispatched events in the queue that will
+ * not get dispatched.
+ */
+ public synchronized void stop() {
+ // if the thread has not been stopped yet, interrupt it
+ // and clear the reference.
+ if (dispatchThread != null) {
+ // push a dummy marker on to the event queue
+ // to force the dispatch thread to wake up.
+ queueEvent(null, null);
+ dispatchThread = null;
+ }
+ }
+
+ /**
+ * Add a new event to the queue.
+ *
+ * @param event The event to dispatch.
+ * @param listeners The List of listeners to dispatch this to. This is assumed to be a
+ * static snapshot of the listeners that will not change between the time
+ * the event is queued and the dispatcher thread makes the calls to the
+ * handlers.
+ */
+ public synchronized void queueEvent(MailEvent event, List listeners) {
+ // add an element to the list, then notify the processing thread.
+ // Note that we make a copy of the listeners list. This ensures
+ // we're going to dispatch this to the snapshot of the listeners
+ PendingEvent p = new PendingEvent(event, listeners);
+ eventQueue.add(p);
+ // wake up the dispatch thread
+ notify();
+ }
+
+ /**
+ * Remove the next event from the message queue.
+ *
+ * @return The PendingEvent item from the queue.
+ */
+ protected synchronized PendingEvent dequeueEvent() throws InterruptedException {
+ // a little spin loop to wait for an event
+ while (eventQueue.isEmpty()) {
+ wait();
+ }
+
+ // just remove the first element of this
+ return (PendingEvent)eventQueue.remove(0);
+ }
+
+
+ /**
+ * Dispatch an event to a list of listeners. Any exceptions thrown by
+ * the listeners will be swallowed.
+ *
+ * @param event The event to dispatch.
+ * @param listeners The list of listeners this gets dispatched to.
+ */
+ protected void dispatchEvent(MailEvent event, List listeners) {
+ // iterate through the listeners list calling the handlers.
for (int i = 0; i < listeners.size(); i++) {
- event.dispatch(listeners.get(i));
+ try {
+ event.dispatch(listeners.get(i));
+ } catch (Throwable e) {
+ // just eat these
+ }
}
}
-
- void stop() {
+
+
+ /**
+ * Small helper class to give a single reference handle for a pending event.
+ */
+ class PendingEvent {
+ // the event we're broadcasting
+ MailEvent event;
+ // the list of listeners we send this to.
+ List listeners;
+
+ PendingEvent(MailEvent event, List listeners) {
+ this.event = event;
+ this.listeners = listeners;
+ }
}
}
Modified: geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/Folder.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/Folder.java?rev=582780&r1=582779&r2=582780&view=diff
==============================================================================
--- geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/Folder.java (original)
+++ geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/Folder.java Mon Oct 8 04:17:15 2007
@@ -21,11 +21,13 @@
import java.util.ArrayList;
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;
@@ -79,11 +81,13 @@
*/
protected int mode = -1;
- private final List connectionListeners = new ArrayList(2);
- 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();
+ private final ArrayList connectionListeners = new ArrayList(2);
+ private final ArrayList folderListeners = new ArrayList(2);
+ private final ArrayList messageChangedListeners = new ArrayList(2);
+ private final ArrayList messageCountListeners = new ArrayList(2);
+ // the EventQueue spins off a new thread, so we only create this
+ // if we have actual listeners to dispatch an event to.
+ private EventQueue queue = null;
/**
* Constructor that initializes the Store.
@@ -645,7 +649,7 @@
}
protected void notifyConnectionListeners(int type) {
- queue.queueEvent(new ConnectionEvent(this, type), connectionListeners);
+ queueEvent(new ConnectionEvent(this, type), connectionListeners);
}
public void addFolderListener(FolderListener listener) {
@@ -657,11 +661,11 @@
}
protected void notifyFolderListeners(int type) {
- queue.queueEvent(new FolderEvent(this, this, type), folderListeners);
+ queueEvent(new FolderEvent(this, this, type), folderListeners);
}
protected void notifyFolderRenamedListeners(Folder newFolder) {
- queue.queueEvent(new FolderEvent(this, this, newFolder, FolderEvent.RENAMED), folderListeners);
+ queueEvent(new FolderEvent(this, this, newFolder, FolderEvent.RENAMED), folderListeners);
}
public void addMessageCountListener(MessageCountListener listener) {
@@ -673,11 +677,11 @@
}
protected void notifyMessageAddedListeners(Message[] messages) {
- queue.queueEvent(new MessageCountEvent(this, MessageCountEvent.ADDED, false, messages), messageChangedListeners);
+ queueEvent(new MessageCountEvent(this, MessageCountEvent.ADDED, false, messages), messageChangedListeners);
}
protected void notifyMessageRemovedListeners(boolean removed, Message[] messages) {
- queue.queueEvent(new MessageCountEvent(this, MessageCountEvent.REMOVED, removed, messages), messageChangedListeners);
+ queueEvent(new MessageCountEvent(this, MessageCountEvent.REMOVED, removed, messages), messageChangedListeners);
}
public void addMessageChangedListener(MessageChangedListener listener) {
@@ -689,14 +693,18 @@
}
protected void notifyMessageChangedListeners(int type, Message message) {
- queue.queueEvent(new MessageChangedEvent(this, type, message), messageChangedListeners);
+ queueEvent(new MessageChangedEvent(this, type, message), messageChangedListeners);
}
/**
* Unregisters all listeners.
*/
protected void finalize() throws Throwable {
- queue.stop();
+ // shut our queue down, if needed.
+ if (queue != null) {
+ queue.stop();
+ queue = null;
+ }
connectionListeners.clear();
folderListeners.clear();
messageChangedListeners.clear();
@@ -712,5 +720,28 @@
public String toString() {
String name = getFullName();
return name == null ? super.toString() : name;
+ }
+
+
+ /**
+ * Add an event on the event queue, creating the queue if this is the
+ * first event with actual listeners.
+ *
+ * @param event The event to dispatch.
+ * @param listeners The listener list.
+ */
+ private synchronized void queueEvent(MailEvent event, ArrayList listeners) {
+ // if there are no listeners to dispatch this to, don't put it on the queue.
+ // This allows us to delay creating the queue (and its new thread) until
+ // we
+ if (listeners.isEmpty()) {
+ return;
+ }
+ // first real event? Time to get the queue kicked off.
+ if (queue == null) {
+ queue = new EventQueue();
+ }
+ // tee it up and let it rip.
+ queue.queueEvent(event, (List)listeners.clone());
}
}
Modified: geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/Service.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/Service.java?rev=582780&r1=582779&r2=582780&view=diff
==============================================================================
--- geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/Service.java (original)
+++ geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/Service.java Mon Oct 8 04:17:15 2007
@@ -21,6 +21,7 @@
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.util.List;
import java.util.Vector;
import javax.mail.event.ConnectionEvent;
@@ -46,7 +47,9 @@
private boolean connected;
private final Vector connectionListeners = new Vector(2);
- private final EventQueue queue = new EventQueue();
+ // the EventQueue spins off a new thread, so we only create this
+ // if we have actual listeners to dispatch an event to.
+ private EventQueue queue = null;
/**
* Construct a new Service.
@@ -374,7 +377,7 @@
}
protected void notifyConnectionListeners(int type) {
- queue.queueEvent(new ConnectionEvent(this, type), connectionListeners);
+ queueEvent(new ConnectionEvent(this, type), connectionListeners);
}
public String toString() {
@@ -382,11 +385,25 @@
}
protected void queueEvent(MailEvent event, Vector listeners) {
- queue.queueEvent(event, listeners);
+ // if there are no listeners to dispatch this to, don't put it on the queue.
+ // This allows us to delay creating the queue (and its new thread) until
+ // we
+ if (listeners.isEmpty()) {
+ return;
+ }
+ // first real event? Time to get the queue kicked off.
+ if (queue == null) {
+ queue = new EventQueue();
+ }
+ // tee it up and let it rip.
+ queue.queueEvent(event, (List)listeners.clone());
}
protected void finalize() throws Throwable {
- queue.stop();
+ // stop our event queue if we had to create one
+ if (queue != null) {
+ queue.stop();
+ }
connectionListeners.clear();
super.finalize();
}
Modified: geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/Session.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/Session.java?rev=582780&r1=582779&r2=582780&view=diff
==============================================================================
--- geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/Session.java (original)
+++ geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/Session.java Mon Oct 8 04:17:15 2007
@@ -48,7 +48,7 @@
* 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
+ * and on resources loaded from "mail.jar" which may lead to skew between
* differnet independent implementations of this API.
*
* @version $Rev$ $Date$
Modified: geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/Transport.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/Transport.java?rev=582780&r1=582779&r2=582780&view=diff
==============================================================================
--- geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/Transport.java (original)
+++ geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/Transport.java Mon Oct 8 04:17:15 2007
@@ -203,4 +203,4 @@
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/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/internet/MimeMessage.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/internet/MimeMessage.java?rev=582780&r1=582779&r2=582780&view=diff
==============================================================================
--- geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/internet/MimeMessage.java (original)
+++ geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/main/java/javax/mail/internet/MimeMessage.java Mon Oct 8 04:17:15 2007
@@ -1285,10 +1285,26 @@
return headers.getNonMatchingHeaderLines(names);
}
+
+ /**
+ * 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 synchronized Flags getFlags() throws MessagingException {
return (Flags) flags.clone();
}
+
+ /**
+ * 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 synchronized boolean isSet(Flags.Flag flag) throws MessagingException {
return flags.contains(flag);
}
Added: geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/test/java/javax/mail/EventQueueTest.java
URL: http://svn.apache.org/viewvc/geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/test/java/javax/mail/EventQueueTest.java?rev=582780&view=auto
==============================================================================
--- geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/test/java/javax/mail/EventQueueTest.java (added)
+++ geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/test/java/javax/mail/EventQueueTest.java Mon Oct 8 04:17:15 2007
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package javax.mail;
+
+import java.util.Vector;
+
+import javax.mail.MessagingException;
+import javax.mail.event.FolderEvent;
+import javax.mail.event.FolderListener;
+
+import junit.framework.TestCase;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class EventQueueTest extends TestCase {
+ protected EventQueue queue;
+
+ public void setUp() throws Exception {
+ queue = new EventQueue();
+ }
+
+ public void tearDown() throws Exception {
+ queue.stop();
+ }
+
+ public void testEvent() {
+ doEventTests(FolderEvent.CREATED);
+ doEventTests(FolderEvent.RENAMED);
+ doEventTests(FolderEvent.DELETED);
+ }
+
+ private void doEventTests(int type) {
+
+ // These tests are essentially the same as the
+ // folder event tests, but done using the asynchronous
+ // event queue.
+ FolderEvent event = new FolderEvent(this, null, type);
+ assertEquals(this, event.getSource());
+ assertEquals(type, event.getType());
+ FolderListenerTest listener = new FolderListenerTest();
+ Vector listeners = new Vector();
+ listeners.add(listener);
+ queue.queueEvent(event, listeners);
+ // we need to make sure the queue thread has a chance to dispatch
+ // this before we check.
+ try {
+ Thread.currentThread().sleep(1000);
+ } catch (InterruptedException e ) {
+ }
+ assertEquals("Unexpcted method dispatched", type, listener.getState());
+ }
+
+ public static class FolderListenerTest implements FolderListener {
+ private int state = 0;
+ public void folderCreated(FolderEvent event) {
+ if (state != 0) {
+ fail("Recycled Listener");
+ }
+ state = FolderEvent.CREATED;
+ }
+ public void folderDeleted(FolderEvent event) {
+ if (state != 0) {
+ fail("Recycled Listener");
+ }
+ state = FolderEvent.DELETED;
+ }
+ public void folderRenamed(FolderEvent event) {
+ if (state != 0) {
+ fail("Recycled Listener");
+ }
+ state = FolderEvent.RENAMED;
+ }
+ public int getState() {
+ return state;
+ }
+ }
+}
+
Propchange: geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/test/java/javax/mail/EventQueueTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/test/java/javax/mail/EventQueueTest.java
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: geronimo/specs/trunk/geronimo-javamail_1.4_spec/src/test/java/javax/mail/EventQueueTest.java
------------------------------------------------------------------------------
svn:mime-type = text/plain