001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.classic.net; 015 016import ch.qos.logback.classic.ClassicTestConstants; 017import ch.qos.logback.classic.Logger; 018import ch.qos.logback.classic.LoggerContext; 019import ch.qos.logback.classic.PatternLayout; 020import ch.qos.logback.classic.html.HTMLLayout; 021import ch.qos.logback.classic.html.XHTMLEntityResolver; 022import ch.qos.logback.classic.joran.JoranConfigurator; 023import ch.qos.logback.classic.spi.ILoggingEvent; 024import ch.qos.logback.core.Layout; 025import ch.qos.logback.core.joran.spi.JoranException; 026import ch.qos.logback.core.status.OnConsoleStatusListener; 027import ch.qos.logback.core.testUtil.EnvUtilForTests; 028import ch.qos.logback.core.testUtil.RandomUtil; 029import ch.qos.logback.core.util.StatusListenerConfigHelper; 030 031import com.icegreen.greenmail.util.GreenMail; 032import com.icegreen.greenmail.util.GreenMailUtil; 033import com.icegreen.greenmail.util.ServerSetup; 034 035import org.dom4j.DocumentException; 036import org.dom4j.io.SAXReader; 037import org.junit.After; 038import org.junit.Before; 039import org.junit.Test; 040import org.slf4j.MDC; 041 042import javax.mail.MessagingException; 043import javax.mail.internet.MimeMessage; 044import javax.mail.internet.MimeMultipart; 045 046import java.io.ByteArrayInputStream; 047import java.io.ByteArrayOutputStream; 048import java.io.IOException; 049import java.io.InputStream; 050import java.util.concurrent.TimeUnit; 051 052import static org.junit.Assert.*; 053 054public class SMTPAppender_GreenTest { 055 056 static final String HEADER = "HEADER\n"; 057 static final String FOOTER = "FOOTER\n"; 058 static final String DEFAULT_PATTERN = "%-4relative %mdc [%thread] %-5level %class - %msg%n"; 059 060 static final boolean SYNCHRONOUS = false; 061 static final boolean ASYNCHRONOUS = true; 062 063 int port = RandomUtil.getRandomServerPort(); 064 // GreenMail cannot be static. As a shared server induces race conditions 065 GreenMail greenMailServer; 066 067 SMTPAppender smtpAppender; 068 LoggerContext loggerContext = new LoggerContext(); 069 Logger logger = loggerContext.getLogger(this.getClass()); 070 071 @Before 072 public void setUp() throws Exception { 073 074 StatusListenerConfigHelper.addOnConsoleListenerInstance(loggerContext, new OnConsoleStatusListener()); 075 MDC.clear(); 076 ServerSetup serverSetup = new ServerSetup(port, "localhost", ServerSetup.PROTOCOL_SMTP); 077 greenMailServer = new GreenMail(serverSetup); 078 greenMailServer.start(); 079 // give the server a head start 080 if (EnvUtilForTests.isRunningOnSlowJenkins()) { 081 Thread.sleep(2000); 082 } else { 083 Thread.sleep(50); 084 } 085 } 086 087 @After 088 public void tearDown() throws Exception { 089 greenMailServer.stop(); 090 } 091 092 void buildSMTPAppender(String subject, boolean synchronicity) throws Exception { 093 smtpAppender = new SMTPAppender(); 094 smtpAppender.setContext(loggerContext); 095 smtpAppender.setName("smtp"); 096 smtpAppender.setFrom("user@host.dom"); 097 smtpAppender.setSMTPHost("localhost"); 098 smtpAppender.setSMTPPort(port); 099 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}