View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.classic.net;
15  
16  import ch.qos.logback.classic.ClassicTestConstants;
17  import ch.qos.logback.classic.Logger;
18  import ch.qos.logback.classic.LoggerContext;
19  import ch.qos.logback.classic.PatternLayout;
20  import ch.qos.logback.classic.html.HTMLLayout;
21  import ch.qos.logback.classic.html.XHTMLEntityResolver;
22  import ch.qos.logback.classic.joran.JoranConfigurator;
23  import ch.qos.logback.classic.spi.ILoggingEvent;
24  import ch.qos.logback.core.Layout;
25  import ch.qos.logback.core.joran.spi.JoranException;
26  import ch.qos.logback.core.status.OnConsoleStatusListener;
27  import ch.qos.logback.core.testUtil.EnvUtilForTests;
28  import ch.qos.logback.core.testUtil.RandomUtil;
29  import ch.qos.logback.core.util.StatusListenerConfigHelper;
30  
31  import com.icegreen.greenmail.util.GreenMail;
32  import com.icegreen.greenmail.util.GreenMailUtil;
33  import com.icegreen.greenmail.util.ServerSetup;
34  
35  import org.dom4j.DocumentException;
36  import org.dom4j.io.SAXReader;
37  import org.junit.After;
38  import org.junit.Before;
39  import org.junit.Test;
40  import org.slf4j.MDC;
41  
42  import javax.mail.MessagingException;
43  import javax.mail.internet.MimeMessage;
44  import javax.mail.internet.MimeMultipart;
45  
46  import java.io.ByteArrayInputStream;
47  import java.io.ByteArrayOutputStream;
48  import java.io.IOException;
49  import java.io.InputStream;
50  import java.util.concurrent.TimeUnit;
51  
52  import static org.junit.Assert.*;
53  
54  public class SMTPAppender_GreenTest {
55  
56      static final String HEADER = "HEADER\n";
57      static final String FOOTER = "FOOTER\n";
58      static final String DEFAULT_PATTERN = "%-4relative %mdc [%thread] %-5level %class - %msg%n";
59  
60      static final boolean SYNCHRONOUS = false;
61      static final boolean ASYNCHRONOUS = true;
62  
63      int port = RandomUtil.getRandomServerPort();
64      // GreenMail cannot be static. As a shared server induces race conditions
65      GreenMail greenMailServer;
66  
67      SMTPAppender smtpAppender;
68      LoggerContext loggerContext = new LoggerContext();
69      Logger logger = loggerContext.getLogger(this.getClass());
70  
71      @Before
72      public void setUp() throws Exception {
73  
74          StatusListenerConfigHelper.addOnConsoleListenerInstance(loggerContext, new OnConsoleStatusListener());
75          MDC.clear();
76          ServerSetup serverSetup = new ServerSetup(port, "localhost", ServerSetup.PROTOCOL_SMTP);
77          greenMailServer = new GreenMail(serverSetup);
78          greenMailServer.start();
79          // give the server a head start
80          if (EnvUtilForTests.isRunningOnSlowJenkins()) {
81              Thread.sleep(2000);
82          } else {
83              Thread.sleep(50);
84          }
85      }
86  
87      @After
88      public void tearDown() throws Exception {
89          greenMailServer.stop();
90      }
91  
92      void buildSMTPAppender(String subject, boolean synchronicity) throws Exception {
93          smtpAppender = new SMTPAppender();
94          smtpAppender.setContext(loggerContext);
95          smtpAppender.setName("smtp");
96          smtpAppender.setFrom("user@host.dom");
97          smtpAppender.setSMTPHost("localhost");
98          smtpAppender.setSMTPPort(port);
99          smtpAppender.setSubject(subject);
100         smtpAppender.addTo("nospam@qos.ch");
101         smtpAppender.setAsynchronousSending(synchronicity);
102     }
103 
104     private Layout<ILoggingEvent> buildPatternLayout(String pattern) {
105         PatternLayout layout = new PatternLayout();
106         layout.setContext(loggerContext);
107         layout.setFileHeader(HEADER);
108         layout.setOutputPatternAsHeader(false);
109         layout.setPattern(pattern);
110         layout.setFileFooter(FOOTER);
111         layout.start();
112         return layout;
113     }
114 
115     private Layout<ILoggingEvent> buildHTMLLayout() {
116         HTMLLayout layout = new HTMLLayout();
117         layout.setContext(loggerContext);
118         layout.setPattern("%level%class%msg");
119         layout.start();
120         return layout;
121     }
122 
123     private void waitForServerToReceiveEmails(int emailCount) throws InterruptedException {
124         greenMailServer.waitForIncomingEmail(5000, emailCount);
125     }
126 
127     private MimeMultipart verifyAndExtractMimeMultipart(String subject) throws MessagingException, IOException, InterruptedException {
128         int oldCount = 0;
129         int expectedEmailCount = 1;
130         // wait for the server to receive the messages
131         waitForServerToReceiveEmails(expectedEmailCount);
132         MimeMessage[] mma = greenMailServer.getReceivedMessages();
133         assertNotNull(mma);
134         assertEquals(expectedEmailCount, mma.length);
135         MimeMessage mm = mma[oldCount];
136         // http://jira.qos.ch/browse/LBCLASSIC-67
137         assertEquals(subject, mm.getSubject());
138         return (MimeMultipart) mm.getContent();
139     }
140 
141     void waitUntilEmailIsSent() throws InterruptedException {
142         loggerContext.getExecutorService().shutdown();
143         loggerContext.getExecutorService().awaitTermination(1000, TimeUnit.MILLISECONDS);
144     }
145 
146     @Test
147     public void synchronousSmoke() throws Exception {
148         String subject = "synchronousSmoke";
149         buildSMTPAppender(subject, SYNCHRONOUS);
150 
151         smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN));
152         smtpAppender.start();
153         logger.addAppender(smtpAppender);
154         logger.debug("hello");
155         logger.error("en error", new Exception("an exception"));
156 
157         MimeMultipart mp = verifyAndExtractMimeMultipart(subject);
158         String body = GreenMailUtil.getBody(mp.getBodyPart(0));
159         assertTrue(body.startsWith(HEADER.trim()));
160         assertTrue(body.endsWith(FOOTER.trim()));
161     }
162 
163     @Test
164     public void asynchronousSmoke() throws Exception {
165         String subject = "asynchronousSmoke";
166         buildSMTPAppender(subject, ASYNCHRONOUS);
167         smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN));
168         smtpAppender.start();
169         logger.addAppender(smtpAppender);
170         logger.debug("hello");
171         logger.error("en error", new Exception("an exception"));
172 
173         waitUntilEmailIsSent();
174         MimeMultipart mp = verifyAndExtractMimeMultipart(subject);
175         String body = GreenMailUtil.getBody(mp.getBodyPart(0));
176         assertTrue(body.startsWith(HEADER.trim()));
177         assertTrue(body.endsWith(FOOTER.trim()));
178     }
179 
180     // See also http://jira.qos.ch/browse/LOGBACK-734
181     @Test
182     public void callerDataShouldBeCorrectlySetWithAsynchronousSending() throws Exception {
183         String subject = "LOGBACK-734";
184         buildSMTPAppender("LOGBACK-734", ASYNCHRONOUS);
185         smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN));
186         smtpAppender.setIncludeCallerData(true);
187         smtpAppender.start();
188         logger.addAppender(smtpAppender);
189         logger.debug("LOGBACK-734");
190         logger.error("callerData", new Exception("ShouldBeCorrectlySetWithAsynchronousSending"));
191 
192         waitUntilEmailIsSent();
193         MimeMultipart mp = verifyAndExtractMimeMultipart(subject);
194         String body = GreenMailUtil.getBody(mp.getBodyPart(0));
195         assertTrue("actual [" + body + "]", body.contains("DEBUG " + this.getClass().getName() + " - LOGBACK-734"));
196     }
197 
198     // lost MDC
199     @Test
200     public void LBCLASSIC_104() throws Exception {
201         String subject = "LBCLASSIC_104";
202         buildSMTPAppender(subject, SYNCHRONOUS);
203         smtpAppender.setAsynchronousSending(false);
204         smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN));
205         smtpAppender.start();
206         logger.addAppender(smtpAppender);
207         MDC.put("key", "val");
208         logger.debug("LBCLASSIC_104");
209         MDC.clear();
210         logger.error("en error", new Exception("test"));
211 
212         MimeMultipart mp = verifyAndExtractMimeMultipart(subject);
213         String body = GreenMailUtil.getBody(mp.getBodyPart(0));
214         assertTrue(body.startsWith(HEADER.trim()));
215         System.out.println(body);
216         assertTrue(body.contains("key=val"));
217         assertTrue(body.endsWith(FOOTER.trim()));
218     }
219 
220     @Test
221     public void html() throws Exception {
222         String subject = "html";
223         buildSMTPAppender(subject, SYNCHRONOUS);
224         smtpAppender.setAsynchronousSending(false);
225         smtpAppender.setLayout(buildHTMLLayout());
226         smtpAppender.start();
227         logger.addAppender(smtpAppender);
228         logger.debug("html");
229         logger.error("en error", new Exception("an exception"));
230 
231         MimeMultipart mp = verifyAndExtractMimeMultipart(subject);
232 
233         // verifyAndExtractMimeMultipart strict adherence to xhtml1-strict.dtd
234         SAXReader reader = new SAXReader();
235         reader.setValidation(true);
236         reader.setEntityResolver(new XHTMLEntityResolver());
237         byte[] messageBytes = getAsByteArray(mp.getBodyPart(0).getInputStream());
238         ByteArrayInputStream bais = new ByteArrayInputStream(messageBytes);
239         try {
240             reader.read(bais);
241         } catch (DocumentException de) {
242             System.out.println("incoming message:");
243             System.out.println(new String(messageBytes));
244             throw de;
245         }
246     }
247 
248     private byte[] getAsByteArray(InputStream inputStream) throws IOException {
249         ByteArrayOutputStream baos = new ByteArrayOutputStream();
250 
251         byte[] buffer = new byte[1024];
252         int n = -1;
253         while ((n = inputStream.read(buffer)) != -1) {
254             baos.write(buffer, 0, n);
255         }
256         return baos.toByteArray();
257     }
258 
259     private void configure(String file) throws JoranException {
260         JoranConfigurator jc = new JoranConfigurator();
261         jc.setContext(loggerContext);
262         loggerContext.putProperty("port", "" + port);
263         jc.doConfigure(file);
264     }
265 
266     @Test
267     public void testCustomEvaluator() throws Exception {
268         configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "smtp/customEvaluator.xml");
269 
270         logger.debug("test");
271         String msg2 = "CustomEvaluator";
272         logger.debug(msg2);
273         logger.debug("invisible");
274         waitUntilEmailIsSent();
275         MimeMultipart mp = verifyAndExtractMimeMultipart("testCustomEvaluator " + this.getClass().getName() + " - " + msg2);
276         String body = GreenMailUtil.getBody(mp.getBodyPart(0));
277         assertEquals("testCustomEvaluator", body);
278     }
279 
280     @Test
281     public void testCustomBufferSize() throws Exception {
282         configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "smtp/customBufferSize.xml");
283 
284         logger.debug("invisible1");
285         logger.debug("invisible2");
286         String msg = "hello";
287         logger.error(msg);
288         waitUntilEmailIsSent();
289         MimeMultipart mp = verifyAndExtractMimeMultipart("testCustomBufferSize " + this.getClass().getName() + " - " + msg);
290         String body = GreenMailUtil.getBody(mp.getBodyPart(0));
291         assertEquals(msg, body);
292     }
293 
294     // this test fails intermittently on Jenkins.
295     @Test
296     public void testMultipleTo() throws Exception {
297         buildSMTPAppender("testMultipleTo", SYNCHRONOUS);
298         smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN));
299         // buildSMTPAppender() already added one destination address
300         smtpAppender.addTo("Test <test@example.com>, other-test@example.com");
301         smtpAppender.start();
302         logger.addAppender(smtpAppender);
303         logger.debug("testMultipleTo hello");
304         logger.error("testMultipleTo en error", new Exception("an exception"));
305         Thread.yield();
306         int expectedEmailCount = 3;
307         waitForServerToReceiveEmails(expectedEmailCount);
308         MimeMessage[] mma = greenMailServer.getReceivedMessages();
309         assertNotNull(mma);
310         assertEquals(expectedEmailCount, mma.length);
311     }
312 
313     // http://jira.qos.ch/browse/LBCLASSIC-221
314     @Test
315     public void bufferShouldBeResetBetweenMessages() throws Exception {
316         buildSMTPAppender("bufferShouldBeResetBetweenMessages", SYNCHRONOUS);
317         smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN));
318         smtpAppender.start();
319         logger.addAppender(smtpAppender);
320         String msg0 = "hello zero";
321         logger.debug(msg0);
322         logger.error("error zero");
323 
324         String msg1 = "hello one";
325         logger.debug(msg1);
326         logger.error("error one");
327 
328         Thread.yield();
329         int oldCount = 0;
330         int expectedEmailCount = oldCount + 2;
331         waitForServerToReceiveEmails(expectedEmailCount);
332 
333         MimeMessage[] mma = greenMailServer.getReceivedMessages();
334         assertNotNull(mma);
335         assertEquals(expectedEmailCount, mma.length);
336 
337         MimeMessage mm0 = mma[oldCount];
338         MimeMultipart content0 = (MimeMultipart) mm0.getContent();
339         @SuppressWarnings("unused")
340         String body0 = GreenMailUtil.getBody(content0.getBodyPart(0));
341 
342         MimeMessage mm1 = mma[oldCount + 1];
343         MimeMultipart content1 = (MimeMultipart) mm1.getContent();
344         String body1 = GreenMailUtil.getBody(content1.getBodyPart(0));
345         // second body should not contain content from first message
346         assertFalse(body1.contains(msg0));
347     }
348 
349     @Test
350     public void multiLineSubjectTruncatedAtFirstNewLine() throws Exception {
351         String line1 = "line 1 of subject";
352         String subject = line1 + "\nline 2 of subject\n";
353         buildSMTPAppender(subject, ASYNCHRONOUS);
354 
355         smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN));
356         smtpAppender.start();
357         logger.addAppender(smtpAppender);
358         logger.debug("hello");
359         logger.error("en error", new Exception("an exception"));
360 
361         Thread.yield();
362         waitUntilEmailIsSent();
363         waitForServerToReceiveEmails(1);
364 
365         MimeMessage[] mma = greenMailServer.getReceivedMessages();
366         assertEquals(1, mma.length);
367         assertEquals(line1, mma[0].getSubject());
368     }
369 }