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