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