1 /*
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3  *
4  * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved.
5  *
6  * The contents of this file are subject to the terms of either the GNU
7  * General Public License Version 2 only ("GPL") or the Common Development
8  * and Distribution License("CDDL") (collectively, the "License").  You
9  * may not use this file except in compliance with the License.  You can
10  * obtain a copy of the License at
11  * https://oss.oracle.com/licenses/CDDL+GPL-1.1
12  * or LICENSE.txt.  See the License for the specific
13  * language governing permissions and limitations under the License.
14  *
15  * When distributing the software, include this License Header Notice in each
16  * file and include the License file at LICENSE.txt.
17  *
18  * GPL Classpath Exception:
19  * Oracle designates this particular file as subject to the "Classpath"
20  * exception as provided by Oracle in the GPL Version 2 section of the License
21  * file that accompanied this code.
22  *
23  * Modifications:
24  * If applicable, add the following below the License Header, with the fields
25  * enclosed by brackets [] replaced by your own identifying information:
26  * "Portions Copyright [year] [name of copyright owner]"
27  *
28  * Contributor(s):
29  * If you wish your version of this file to be governed by only the CDDL or
30  * only the GPL Version 2, indicate your decision by adding "[Contributor]
31  * elects to include this software in this distribution under the [CDDL or GPL
32  * Version 2] license."  If you don't indicate a single choice of license, a
33  * recipient has the option to distribute your version of this file under
34  * either the CDDL, the GPL Version 2 or to extend the choice of license to
35  * its licensees as provided above.  However, if you add GPL Version 2 code
36  * and therefore, elected the GPL Version 2 license, then the option applies
37  * only if the new code is made subject to such option by the copyright
38  * holder.
39  */

40
41 package javax.mail;
42
43 import java.util.EventListener;
44 import java.util.Vector;
45 import java.util.Queue;
46 import java.util.WeakHashMap;
47 import java.util.concurrent.BlockingQueue;
48 import java.util.concurrent.LinkedBlockingQueue;
49 import java.util.concurrent.Executor;
50 import javax.mail.event.MailEvent;
51
52 /**
53  * Package private class used by Store & Folder to dispatch events.
54  * This class implements an event queue, and a dispatcher thread that
55  * dequeues and dispatches events from the queue.
56  *
57  * @author    Bill Shannon
58  */

59 class EventQueue implements Runnable {
60
61     private volatile BlockingQueue<QueueElement> q;
62     private Executor executor;
63
64     private static WeakHashMap<ClassLoader,EventQueue> appq;
65
66     /**
67      * A special event that causes the queue processing task to terminate.
68      */

69     static class TerminatorEvent extends MailEvent {
70     private static final long serialVersionUID = -2481895000841664111L;
71
72     TerminatorEvent() {
73         super(new Object());
74     }
75
76     @Override
77     public void dispatch(Object listener) {
78         // Kill the event dispatching thread.
79         Thread.currentThread().interrupt();
80     }
81     }
82
83     /**
84      * A "struct" to put on the queue.
85      */

86     static class QueueElement {
87     MailEvent event = null;
88     Vector<? extends EventListener> vector = null;
89
90     QueueElement(MailEvent event, Vector<? extends EventListener> vector) {
91         this.event = event;
92         this.vector = vector;
93     }
94     }
95
96     /**
97      * Construct an EventQueue using the specified Executor.
98      * If the Executor is null, threads will be created as needed.
99      */

100     EventQueue(Executor ex) {
101     this.executor = ex;
102     }
103
104     /**
105      * Enqueue an event.
106      */

107     synchronized void enqueue(MailEvent event,
108         Vector<? extends EventListener> vector) {
109     // if this is the first event, create the queue and start the event task
110     if (q == null) {
111         q = new LinkedBlockingQueue<>();
112         if (executor != null) {
113         executor.execute(this);
114         } else {
115         Thread qThread = new Thread(this"JavaMail-EventQueue");
116         qThread.setDaemon(true);  // not a user thread
117         qThread.start();
118         }
119     }
120     q.add(new QueueElement(event, vector));
121     }
122
123     /**
124      * Terminate the task running the queue, but only if there is a queue.
125      */

126     synchronized void terminateQueue() {
127     if (q != null) {
128         Vector<EventListener> dummyListeners = new Vector<>();
129         dummyListeners.setSize(1); // need atleast one listener
130         q.add(new QueueElement(new TerminatorEvent(), dummyListeners));
131         q = null;
132     }
133     }
134
135     /**
136      * Create (if necessary) an application-scoped event queue.
137      * Application scoping is based on the thread's context class loader.
138      */

139     static synchronized EventQueue getApplicationEventQueue(Executor ex) {
140     ClassLoader cl = Session.getContextClassLoader();
141     if (appq == null)
142         appq = new WeakHashMap<>();
143     EventQueue q = appq.get(cl);
144     if (q == null) {
145         q = new EventQueue(ex);
146         appq.put(cl, q);
147     }
148     return q;
149     }
150
151     /**
152      * Pull events off the queue and dispatch them.
153      */

154     @Override
155     public void run() {
156
157     BlockingQueue<QueueElement> bq = q;
158     if (bq == null)
159         return;
160     try {
161         loop:
162         for (;;) {
163         // block until an item is available
164         QueueElement qe = bq.take();
165         MailEvent e = qe.event;
166         Vector<? extends EventListener> v = qe.vector;
167
168         for (int i = 0; i < v.size(); i++)
169             try {
170             e.dispatch(v.elementAt(i));
171             } catch (Throwable t) {
172             if (t instanceof InterruptedException)
173                 break loop;
174             // ignore anything else thrown by the listener
175             }
176
177         qe = null; e = null; v = null;
178         }
179     } catch (InterruptedException e) {
180         // just die
181     }
182     }
183 }
184