1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.mail.internal.thread

File PrepareMailRunnable.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart10.png
0% of files have more coverage

Code metrics

26
68
10
1
260
170
33
0.49
6.8
10
3.3

Classes

Class Line # Actions
PrepareMailRunnable 55 68 0% 33 9
0.9134615791.3%
 

Contributing tests

This file is covered by 8 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.mail.internal.thread;
21   
22    import java.util.Collections;
23    import java.util.Iterator;
24   
25    import javax.inject.Inject;
26    import javax.inject.Named;
27    import javax.inject.Singleton;
28    import javax.mail.Address;
29    import javax.mail.Message;
30    import javax.mail.MessagingException;
31    import javax.mail.internet.InternetAddress;
32    import javax.mail.internet.MimeMessage;
33   
34    import org.xwiki.component.annotation.Component;
35    import org.xwiki.context.ExecutionContext;
36    import org.xwiki.context.ExecutionContextException;
37    import org.xwiki.mail.ExtendedMimeMessage;
38    import org.xwiki.mail.MailContentStore;
39    import org.xwiki.mail.MailListener;
40    import org.xwiki.mail.MailStatusResult;
41    import org.xwiki.mail.internal.UpdateableMailStatusResult;
42   
43    import com.xpn.xwiki.XWikiContext;
44   
45    /**
46    * Runnable that regularly check for mail items on a Prepare Queue, and for each mail item there, generate the message
47    * to send and persist it and put that reference on the Send Queue for sending.
48    *
49    * @version $Id: 599bef0367a742006f1766f93a1300bb44556288 $
50    * @since 6.4
51    */
52    @Component
53    @Named("prepare")
54    @Singleton
 
55    public class PrepareMailRunnable extends AbstractMailRunnable
56    {
57    @Inject
58    private MailQueueManager<PrepareMailQueueItem> prepareMailQueueManager;
59   
60    @Inject
61    private MailQueueManager<SendMailQueueItem> sendMailQueueManager;
62   
63    @Inject
64    @Named("filesystem")
65    private MailContentStore mailContentStore;
66   
 
67  12 toggle @Override
68    public void run()
69    {
70  12 do {
71  4221 try {
72    // Handle next message in the queue
73  4221 if (this.prepareMailQueueManager.hasMessage()) {
74    // Important: only remove the mail item after the message has been created and put on the sender
75    // queue.
76  44 PrepareMailQueueItem mailItem = this.prepareMailQueueManager.peekMessage();
77  44 try {
78  44 prepareMail(mailItem);
79    } finally {
80  44 this.prepareMailQueueManager.removeMessageFromQueue(mailItem);
81    }
82    }
83    // Note: a short pause to catch thread interruptions and to be kind on CPU.
84  4221 Thread.sleep(100L);
85    } catch (InterruptedException e) {
86    // Thread has been stopped, exit
87  12 this.logger.debug("Mail Prepare Thread was forcefully stopped", e);
88  12 break;
89    } catch (Exception e) {
90    // There was an unexpected problem, we just log the problem but keep the thread alive!
91  0 this.logger.error("Unexpected error in the Mail Prepare Thread", e);
92    }
93  4209 } while (!this.shouldStop);
94    }
95   
96    /**
97    * Prepare the messages to send, persist them and put them on the Mail Sender Queue.
98    *
99    * @param item the queue item containing all the data for sending the mail
100    * @throws org.xwiki.context.ExecutionContextException when the XWiki Context fails to be set up
101    */
 
102  44 toggle protected void prepareMail(PrepareMailQueueItem item) throws ExecutionContextException
103    {
104  44 Iterator<? extends MimeMessage> messageIterator = item.getMessages().iterator();
105  44 MailListener listener = item.getListener();
106   
107  44 if (listener != null) {
108  42 listener.onPrepareBegin(item.getBatchId(), Collections.<String, Object>emptyMap());
109    }
110   
111    // Count the total number of messages to process
112  44 long messageCounter = 0;
113   
114  44 try {
115  44 boolean shouldStop = false;
116  108 while (!shouldStop) {
117    // Note that we need to have the hasNext() call after the context is ready since the implementation can
118    // need a valid XWiki Context.
119  65 prepareContext(item.getContext());
120  65 try {
121  65 if (messageIterator.hasNext()) {
122  22 MimeMessage mimeMessage = messageIterator.next();
123  21 prepareSingleMail(mimeMessage, item);
124  21 messageCounter++;
125    } else {
126  43 shouldStop = true;
127    }
128    } finally {
129  65 removeContext();
130    }
131    }
132    } catch (Exception e) {
133  1 if (listener != null) {
134  1 listener.onPrepareFatalError(e, Collections.<String, Object>emptyMap());
135    }
136    } finally {
137  44 if (listener != null) {
138  42 MailStatusResult result = listener.getMailStatusResult();
139    // Update the listener with the total number of messages prepared so that the user can known when
140    // all the messages have been processed for the batch. We update here, even in case of failure
141    // so that waiting process have a chance to see an end.
142  42 if (result instanceof UpdateableMailStatusResult) {
143  42 ((UpdateableMailStatusResult) result).setTotalSize(messageCounter);
144    }
145  42 listener.onPrepareEnd(Collections.<String, Object>emptyMap());
146    }
147    }
148    }
149   
 
150  65 toggle protected void prepareContext(ExecutionContext executionContext) throws ExecutionContextException
151    {
152  65 try {
153  65 this.execution.setContext(executionContext);
154    } catch (Exception e) {
155    // If inheritance fails, we will get an unchecked exception here. So we'll wrap it in an
156    // ExecutionContextException.
157  0 throw new ExecutionContextException("Failed to set the execution context.", e);
158    }
159    }
160   
 
161  21 toggle private void prepareSingleMail(MimeMessage mimeMessage, PrepareMailQueueItem item)
162    {
163  21 MailListener listener = item.getListener();
164   
165    // Step 1: Try to complete message with From and Bcc from configuration if needed
166  21 completeMessage(mimeMessage);
167   
168    // Ensure mimeMessage to be extended
169  21 ExtendedMimeMessage message = ExtendedMimeMessage.wrap(mimeMessage);
170   
171    // Step 2: Persist the MimeMessage
172    // Note: Message identifier is stabilized at this step by the serialization process
173  21 try {
174  21 this.mailContentStore.save(item.getBatchId(), message);
175    } catch (Exception e) {
176    // An error occurred, notify the user if a listener has been provided
177  2 if (listener != null) {
178  2 listener.onPrepareMessageError(message, e, Collections.<String, Object>emptyMap());
179    }
180  2 return;
181    }
182   
183    // Step 3: Put the MimeMessage id on the Mail Send Queue for sending
184    // Extract the wiki id from the context
185  19 this.sendMailQueueManager.addToQueue(new SendMailQueueItem(message.getUniqueMessageId(),
186    item.getSession(), listener, item.getBatchId(), extractWikiId(item)));
187   
188    // Step 4: Notify the user that the MimeMessage is prepared
189  19 if (listener != null) {
190  17 listener.onPrepareMessageSuccess(message, Collections.<String, Object>emptyMap());
191    }
192   
193    }
194   
 
195  19 toggle private String extractWikiId(PrepareMailQueueItem item)
196    {
197  19 XWikiContext xcontext = (XWikiContext) item.getContext().getProperty(XWikiContext.EXECUTIONCONTEXT_KEY);
198  19 return xcontext.getWikiId();
199    }
200   
 
201  21 toggle private void completeMessage(MimeMessage mimeMessage)
202    {
203    // Note: We don't cache the default From and BCC addresses because they can be modified at runtime
204    // (from the Admin UI for example) and we need to always get the latest configured values.
205   
206    // If the user has not set the From header then try to use the default value from configuration
207  21 tryToEnsureFrom(mimeMessage);
208    // Else JavaMail won't be able to send the mail but we'll get the error in the status.
209   
210    // If the user has not set the BCC header then use the default value from configuration
211  21 tryToAddDefaultBccIfNeeded(mimeMessage);
212    }
213   
 
214  21 toggle private void tryToEnsureFrom(MimeMessage mimeMessage)
215    {
216  21 if (getFrom(mimeMessage) == null) {
217    // Try using the From address in the Session
218  10 String from = this.configuration.getFromAddress();
219  10 if (from != null) {
220  6 try {
221  6 mimeMessage.setFrom(new InternetAddress(from));
222    } catch (MessagingException e) {
223    // ignored
224    }
225    }
226    }
227    }
228   
 
229  21 toggle private void tryToAddDefaultBccIfNeeded(MimeMessage mimeMessage)
230    {
231  21 Address[] bccAddresses = getBccRecipients(mimeMessage);
232  21 if (bccAddresses == null || bccAddresses.length == 0) {
233  21 for (String address : this.configuration.getBCCAddresses()) {
234  6 try {
235  6 mimeMessage.addRecipient(Message.RecipientType.BCC, new InternetAddress(address));
236    } catch (MessagingException e) {
237    // ignored
238    }
239    }
240    }
241    }
242   
 
243  21 toggle private Address[] getFrom(MimeMessage mimeMessage)
244    {
245  21 try {
246  21 return mimeMessage.getFrom();
247    } catch (MessagingException e) {
248  0 return null;
249    }
250    }
251   
 
252  21 toggle private Address[] getBccRecipients(MimeMessage mimeMessage)
253    {
254  21 try {
255  21 return mimeMessage.getRecipients(Message.RecipientType.BCC);
256    } catch (MessagingException e) {
257  0 return null;
258    }
259    }
260    }