[logback-dev] svn commit: r1781 - in logback/trunk: logback-access/src/main/java/ch/qos/logback/access/html logback-classic/src/main/java/ch/qos/logback/classic/html logback-classic/src/main/java/ch/qos/logback/classic/net logback-classic/src/test/java/ch/qos/logback/classic/html logback-classic/src/test/java/ch/qos/logback/classic/net logback-core/src/main/java/ch/qos/logback/core logback-core/src/main/java/ch/qos/logback/core/html logback-core/src/main/java/ch/qos/logback/core/net

noreply.ceki at qos.ch noreply.ceki at qos.ch
Wed Aug 27 19:52:19 CEST 2008


Author: ceki
Date: Wed Aug 27 19:52:19 2008
New Revision: 1781

Added:
   logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/html/XHTMLEntityResolver.java
Modified:
   logback/trunk/logback-access/src/main/java/ch/qos/logback/access/html/DefaultCssBuilder.java
   logback/trunk/logback-access/src/main/java/ch/qos/logback/access/html/HTMLLayout.java
   logback/trunk/logback-access/src/main/java/ch/qos/logback/access/html/UrlCssBuilder.java
   logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/DefaultCssBuilder.java
   logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/DefaultThrowableRenderer.java
   logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/HTMLLayout.java
   logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/UrlCssBuilder.java
   logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/net/SMTPAppender.java
   logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/html/HTMLLayoutTest.java
   logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/net/SMTPAppender_GreenTest.java
   logback/trunk/logback-core/src/main/java/ch/qos/logback/core/CoreGlobal.java
   logback/trunk/logback-core/src/main/java/ch/qos/logback/core/html/CssBuilder.java
   logback/trunk/logback-core/src/main/java/ch/qos/logback/core/html/HTMLLayoutBase.java
   logback/trunk/logback-core/src/main/java/ch/qos/logback/core/html/IThrowableRenderer.java
   logback/trunk/logback-core/src/main/java/ch/qos/logback/core/html/NOPThrowableRenderer.java
   logback/trunk/logback-core/src/main/java/ch/qos/logback/core/net/SMTPAppenderBase.java

Log:
LBCORE-27 and LBCLASSIC-67

- Fixes both LBCORE-27 and LBCLASSIC-67. 

- added test cases (using GreenMail) that verify that the output generated
  by SMTPAppender and HTMLLayout conform to xhtml1-strict.dtd

- HTMLLayout* now use StringBuilder instead of StringBuffer. The former is significantly faster.


Modified: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/html/DefaultCssBuilder.java
==============================================================================
--- logback/trunk/logback-access/src/main/java/ch/qos/logback/access/html/DefaultCssBuilder.java	(original)
+++ logback/trunk/logback-access/src/main/java/ch/qos/logback/access/html/DefaultCssBuilder.java	Wed Aug 27 19:52:19 2008
@@ -24,8 +24,8 @@
   public DefaultCssBuilder() {
   }
 
-  public void addCss(StringBuffer sbuf) {
-    sbuf.append("<STYLE  type=\"text/css\">");
+  public void addCss(StringBuilder sbuf) {
+    sbuf.append("<style  type=\"text/css\">");
     sbuf.append("table{ ");
     sbuf.append("margin-left: 2em; ");
     sbuf.append("margin-right: 2em; ");
@@ -76,6 +76,6 @@
     sbuf.append(LINE_SEP);
     sbuf.append("  }");
     sbuf.append("}");
-    sbuf.append("</STYLE>");
+    sbuf.append("</style>");
   }
 }
\ No newline at end of file

Modified: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/html/HTMLLayout.java
==============================================================================
--- logback/trunk/logback-access/src/main/java/ch/qos/logback/access/html/HTMLLayout.java	(original)
+++ logback/trunk/logback-access/src/main/java/ch/qos/logback/access/html/HTMLLayout.java	Wed Aug 27 19:52:19 2008
@@ -56,8 +56,8 @@
   }
 
   public String doLayout(AccessEvent event) {
-    StringBuffer buf = new StringBuffer();
-    handleTableClosing(buf);
+    StringBuilder buf = new StringBuilder();
+    startNewTableIfLimitReached(buf);
 
     boolean odd = true;
     if (((counter++) & 1) == 0) {
@@ -84,7 +84,7 @@
     return buf.toString();
   }
 
-  private void appendEventToBuffer(StringBuffer buf, Converter<AccessEvent> c,
+  private void appendEventToBuffer(StringBuilder buf, Converter<AccessEvent> c,
       AccessEvent event) {
     buf.append("<td class=\"");
     buf.append(computeConverterName(c));

Modified: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/html/UrlCssBuilder.java
==============================================================================
--- logback/trunk/logback-access/src/main/java/ch/qos/logback/access/html/UrlCssBuilder.java	(original)
+++ logback/trunk/logback-access/src/main/java/ch/qos/logback/access/html/UrlCssBuilder.java	Wed Aug 27 19:52:19 2008
@@ -34,8 +34,8 @@
     this.url = url;
   }
   
-  public void addCss(StringBuffer sbuf) {
-    sbuf.append("<LINK REL=StyleSheet HREF=\"");
+  public void addCss(StringBuilder sbuf) {
+    sbuf.append("<link REL=StyleSheet HREF=\"");
     sbuf.append(url);
     sbuf.append("\" TITLE=\"Basic\" />");
   }

Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/DefaultCssBuilder.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/DefaultCssBuilder.java	(original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/DefaultCssBuilder.java	Wed Aug 27 19:52:19 2008
@@ -24,8 +24,8 @@
   public DefaultCssBuilder() {
   }
 
-  public void addCss(StringBuffer sbuf) {
-    sbuf.append("<STYLE  type=\"text/css\">");
+  public void addCss(StringBuilder sbuf) {
+    sbuf.append("<style  type=\"text/css\">");
     sbuf.append(LINE_SEP);
     sbuf
         .append("table { margin-left: 2em; margin-right: 2em; border-left: 2px solid #AAA; }");
@@ -66,7 +66,7 @@
         .append("TD.Exception { background: #A2AEE8; font-family: courier, monospace;}");
     sbuf.append(LINE_SEP);
 
-    sbuf.append("</STYLE>");
+    sbuf.append("</style>");
     sbuf.append(LINE_SEP);
   }
 }

Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/DefaultThrowableRenderer.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/DefaultThrowableRenderer.java	(original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/DefaultThrowableRenderer.java	Wed Aug 27 19:52:19 2008
@@ -20,7 +20,7 @@
     this.throwable = t;
   }
   
-  public void render(StringBuffer sbuf, String[] s) {
+  public void render(StringBuilder sbuf, String[] s) {
     if (s != null) {
       int len = s.length;
       if (len == 0) {
@@ -38,7 +38,7 @@
     }
   }
   
-  public void render(StringBuffer sbuf, Object eventObject) {
+  public void render(StringBuilder sbuf, Object eventObject) {
     LoggingEvent event = (LoggingEvent)eventObject;
     ThrowableInformation ti = event.getThrowableInformation();
     if (ti != null) {

Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/HTMLLayout.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/HTMLLayout.java	(original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/HTMLLayout.java	Wed Aug 27 19:52:19 2008
@@ -55,8 +55,8 @@
   }
 
   public String doLayout(LoggingEvent event) {
-    StringBuffer buf = new StringBuffer();
-    handleTableClosing(buf);
+    StringBuilder buf = new StringBuilder();
+    startNewTableIfLimitReached(buf);
 
     boolean odd = true;
     if (((counter++) & 1) == 0) {
@@ -89,7 +89,7 @@
     return buf.toString();
   }
 
-  private void appendEventToBuffer(StringBuffer buf, Converter<LoggingEvent> c,
+  private void appendEventToBuffer(StringBuilder buf, Converter<LoggingEvent> c,
       LoggingEvent event) {
     buf.append("<td class=\"");
     buf.append(computeConverterName(c));

Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/UrlCssBuilder.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/UrlCssBuilder.java	(original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/html/UrlCssBuilder.java	Wed Aug 27 19:52:19 2008
@@ -34,8 +34,8 @@
     this.url = url;
   }
   
-  public void addCss(StringBuffer sbuf) {
-    sbuf.append("<LINK REL=StyleSheet HREF=\"");
+  public void addCss(StringBuilder sbuf) {
+    sbuf.append("<link REL=StyleSheet HREF=\"");
     sbuf.append(url);
     sbuf.append("\" TITLE=\"Basic\" />");
   }

Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/net/SMTPAppender.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/net/SMTPAppender.java	(original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/net/SMTPAppender.java	Wed Aug 27 19:52:19 2008
@@ -113,6 +113,10 @@
     PatternLayout pl = new PatternLayout();
     pl.setContext(getContext());
     pl.setPattern(subjectStr);
+    // we don't want a ThrowableInformationConverter appended
+    // to the end of the converter chain
+    // This fixes issue LBCLASSIC-67
+    pl.setPostCompileProcessor(null);
     pl.start();
     return pl;
   }

Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/html/HTMLLayoutTest.java
==============================================================================
--- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/html/HTMLLayoutTest.java	(original)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/html/HTMLLayoutTest.java	Wed Aug 27 19:52:19 2008
@@ -1,35 +1,36 @@
 package ch.qos.logback.classic.html;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
 import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
-
-import junit.framework.TestCase;
 
 import org.dom4j.Document;
 import org.dom4j.Element;
 import org.dom4j.io.SAXReader;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
 import org.xml.sax.EntityResolver;
-import org.xml.sax.InputSource;
 
 import ch.qos.logback.classic.Level;
 import ch.qos.logback.classic.Logger;
 import ch.qos.logback.classic.LoggerContext;
 import ch.qos.logback.classic.spi.LoggingEvent;
 import ch.qos.logback.classic.spi.ThrowableInformation;
+import ch.qos.logback.core.CoreGlobal;
 import ch.qos.logback.core.read.ListAppender;
 
-public class HTMLLayoutTest extends TestCase {
+public class HTMLLayoutTest {
 
   LoggerContext lc;
   Logger logger;
   HTMLLayout layout;
 
-  @Override
+  @Before
   public void setUp() throws Exception {
-    super.setUp();
     lc = new LoggerContext();
     lc.setName("default");
 
@@ -46,17 +47,17 @@
     appender.start();
   }
 
-  @Override
+  @After
   public void tearDown() throws Exception {
-    super.tearDown();
     lc = null;
     layout = null;
   }
 
   @SuppressWarnings("unchecked")
-  public void testHeader() {
+  @Test
+  public void testHeader() throws Exception {
     String header = layout.getFileHeader();
-    //System.out.println(header);
+    // System.out.println(header);
 
     Document doc = parseOutput(header + "</body></html>");
     Element rootElement = doc.getRootElement();
@@ -64,11 +65,12 @@
   }
 
   @SuppressWarnings("unchecked")
-  public void testPresentationHeader() {
+  @Test
+  public void testPresentationHeader() throws Exception {
     String header = layout.getFileHeader();
     String presentationHeader = layout.getPresentationHeader();
     header = header + presentationHeader;
-    //System.out.println(header);
+    // System.out.println(header);
 
     Document doc = parseOutput(header + "</table></body></html>");
     Element rootElement = doc.getRootElement();
@@ -80,9 +82,10 @@
     assertEquals("Thread", elementList.get(1).getText());
     assertEquals("Message", elementList.get(2).getText());
   }
-  
+
+  @Test
   public void testAppendThrowable() throws Exception {
-    StringBuffer buf = new StringBuffer();
+    StringBuilder buf = new StringBuilder();
     String[] strArray = { "test1", "test2" };
     DefaultThrowableRenderer renderer = (DefaultThrowableRenderer) layout
         .getThrowableRenderer();
@@ -93,12 +96,32 @@
     assertEquals(DefaultThrowableRenderer.TRACE_PREFIX + "test2", result[1]);
   }
 
+  @Test
   public void testDoLayout() throws Exception {
     LoggingEvent le = createLoggingEvent();
-    String result = layout.doLayout(le);
+
+    String result = layout.getFileHeader();
+    result += layout.getPresentationHeader();
+    result += layout.doLayout(le);
+    result += layout.getPresentationFooter();
+    result += layout.getFileFooter();
+
     Document doc = parseOutput(result);
-    Element trElement = doc.getRootElement();
-    assertEquals(3, trElement.elements().size());
+    Element rootElement = doc.getRootElement();
+    rootElement.toString();
+
+    // the rest of this test is very dependent of the output generated
+    // by HTMLLayout. Given that the XML parser already verifies
+    // that the result conforms to xhtml-strict, we may want to
+    // skip the assertions below. However, the assertions below are another
+    // *independent* way to check the output format.
+
+    // head, body
+    assertEquals(2, rootElement.elements().size());
+    Element bodyElement = (Element) rootElement.elements().get(1);
+    Element tableElement = (Element) bodyElement.elements().get(3);
+    assertEquals("table", tableElement.getName());
+    Element trElement = (Element) tableElement.elements().get(1);
     {
       Element tdElement = (Element) trElement.elements().get(0);
       assertEquals("DEBUG", tdElement.getText());
@@ -111,11 +134,11 @@
       Element tdElement = (Element) trElement.elements().get(2);
       assertEquals("test message", tdElement.getText());
     }
-    // System.out.println(result);
   }
 
   @SuppressWarnings("unchecked")
-  public void testDoLayoutWithException() throws Exception {
+  @Test
+  public void layoutWithException() throws Exception {
     layout.setPattern("%level %thread %msg %ex");
     LoggingEvent le = createLoggingEvent();
     le.setThrowableInformation(new ThrowableInformation(new Exception(
@@ -141,16 +164,23 @@
     assertTrue(exceptionElement.getText().contains(
         "java.lang.Exception: test Exception"));
   }
-
-  // public void testLog() {
-  // for (int i = 1; i <= 10; i++) {
-  // if (i == 5 || i == 8) {
-  // logger.debug("test", new Exception("test exception"));
-  // } else {
-  // logger.debug("test message" + i);
-  // }
-  // }
-  // }
+  
+  @Test
+  public void rawLimit() throws Exception {
+    StringBuilder sb = new StringBuilder();
+    String header = layout.getFileHeader();
+    assertTrue(header.startsWith("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"));
+    sb.append(header);
+    sb.append(layout.getPresentationHeader());
+    for(int i = 0; i < CoreGlobal.TABLE_ROW_LIMIT*3; i++) {
+      sb.append(layout.doLayout( new LoggingEvent(this.getClass().getName(), logger,
+          Level.DEBUG, "test message"+i, null, null)));
+    }
+    sb.append(layout.getPresentationFooter());
+    sb.append(layout.getFileFooter());
+    // check that the output adheres to xhtml-strict.dtd
+    parseOutput(sb.toString());
+  }
 
   private LoggingEvent createLoggingEvent() {
     LoggingEvent le = new LoggingEvent(this.getClass().getName(), logger,
@@ -158,65 +188,11 @@
     return le;
   }
 
-  Document parseOutput(String output) {
-
+  Document parseOutput(String output) throws Exception {
     EntityResolver resolver = new XHTMLEntityResolver();
-    
     SAXReader reader = new SAXReader();
+    reader.setValidation(true);
     reader.setEntityResolver(resolver);
-    Document doc = null;
-    
-    try {  
-      doc = reader.read(new ByteArrayInputStream(output.getBytes()));
-    } catch (Exception e) {
-      e.printStackTrace();
-    }
-    return doc;
-
-    // try {
-    //      
-    // Document document = DocumentHelper.parseText(output);
-    // return document;
-    // } catch (Exception e) {
-    // System.err.println(e);
-    // fail();
-    // }
-    // return null;
-  }
-}
-
-class XHTMLEntityResolver implements EntityResolver {
-
-
-  // key: public id, value: relative path to DTD file
-  static Map<String, String> entityMap = new HashMap<String, String>();
-
-  static {
-    entityMap.put("-//W3C//DTD XHTML 1.0 Strict//EN",
-        "/dtd/xhtml1-strict.dtd");
-    entityMap.put("-//W3C//ENTITIES Latin 1 for XHTML//EN",
-        "/dtd/xhtml-lat1.ent");
-    entityMap.put("-//W3C//ENTITIES Symbols for XHTML//EN",
-        "/dtd/xhtml-symbol.ent");
-    entityMap.put("-//W3C//ENTITIES Special for XHTML//EN",
-        "/dtd/xhtml-special.ent");
-  }
-
-  public InputSource resolveEntity(String publicId, String systemId) {
-    //System.out.println(publicId);
-    final String relativePath = (String)entityMap.get(publicId);
-
-    if (relativePath != null) {
-      Class clazz = getClass();
-      InputStream in =
-        clazz.getResourceAsStream(relativePath);
-      if (in == null) {
-        return null;
-      } else {
-        return new InputSource(in);
-      }
-    } else {
-      return null;
-    }
+    return reader.read(new ByteArrayInputStream(output.getBytes()));
   }
 }

Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/html/XHTMLEntityResolver.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/html/XHTMLEntityResolver.java	Wed Aug 27 19:52:19 2008
@@ -0,0 +1,43 @@
+package ch.qos.logback.classic.html;
+
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+
+public class XHTMLEntityResolver implements EntityResolver {
+
+  // key: public id, value: relative path to DTD file
+  static Map<String, String> entityMap = new HashMap<String, String>();
+
+  static {
+    entityMap.put("-//W3C//DTD XHTML 1.0 Strict//EN",
+        "/dtd/xhtml1-strict.dtd");
+    entityMap.put("-//W3C//ENTITIES Latin 1 for XHTML//EN",
+        "/dtd/xhtml-lat1.ent");
+    entityMap.put("-//W3C//ENTITIES Symbols for XHTML//EN",
+        "/dtd/xhtml-symbol.ent");
+    entityMap.put("-//W3C//ENTITIES Special for XHTML//EN",
+        "/dtd/xhtml-special.ent");
+  }
+
+  public InputSource resolveEntity(String publicId, String systemId) {
+    //System.out.println(publicId);
+    final String relativePath = (String)entityMap.get(publicId);
+
+    if (relativePath != null) {
+      Class clazz = getClass();
+      InputStream in =
+        clazz.getResourceAsStream(relativePath);
+      if (in == null) {
+        return null;
+      } else {
+        return new InputSource(in);
+      }
+    } else {
+      return null;
+    }
+  }
+}

Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/net/SMTPAppender_GreenTest.java
==============================================================================
--- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/net/SMTPAppender_GreenTest.java	(original)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/net/SMTPAppender_GreenTest.java	Wed Aug 27 19:52:19 2008
@@ -1,22 +1,33 @@
 package ch.qos.logback.classic.net;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
+import java.io.FileOutputStream;
 import java.util.Random;
 
 import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
 
+import org.dom4j.io.SAXReader;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 
 import ch.qos.logback.classic.Logger;
 import ch.qos.logback.classic.LoggerContext;
 import ch.qos.logback.classic.PatternLayout;
+import ch.qos.logback.classic.html.HTMLLayout;
+import ch.qos.logback.classic.html.XHTMLEntityResolver;
 import ch.qos.logback.classic.spi.LoggingEvent;
+import ch.qos.logback.core.CoreGlobal;
 import ch.qos.logback.core.Layout;
+import ch.qos.logback.core.html.HTMLLayoutBase;
 
 import com.icegreen.greenmail.util.GreenMail;
+import com.icegreen.greenmail.util.GreenMailUtil;
 import com.icegreen.greenmail.util.ServerSetup;
 
 public class SMTPAppender_GreenTest {
@@ -27,8 +38,9 @@
   LoggerContext lc = new LoggerContext();
 
   static final String TEST_SUBJECT = "test subject";
-  
-  
+  static final String HEADER = "HEADER\n";
+  static final String FOOTER = "FOOTER\n";
+
   @Before
   public void setUp() throws Exception {
     ServerSetup serverSetup = new ServerSetup(diff, "localhost",
@@ -43,21 +55,29 @@
     smtpAppender.setContext(lc);
     smtpAppender.setName("smtp");
     smtpAppender.setFrom("user at host.dom");
-
-    smtpAppender.setLayout(buildLayout(lc));
     smtpAppender.setSMTPHost("localhost");
     smtpAppender.setSMTPPort(diff);
     smtpAppender.setSubject(TEST_SUBJECT);
     smtpAppender.addTo("nospam at qos.ch");
-//    smtpAppender.start();
+    // smtpAppender.start();
   }
 
-  private Layout<LoggingEvent> buildLayout(LoggerContext lc) {
+  private Layout<LoggingEvent> buildPatternLayout(LoggerContext lc) {
     PatternLayout layout = new PatternLayout();
     layout.setContext(lc);
-    layout.setFileHeader("Some header\n");
+    layout.setFileHeader(HEADER);
     layout.setPattern("%-4relative [%thread] %-5level %class - %msg%n");
-    layout.setFileFooter("Some footer");
+    layout.setFileFooter(FOOTER);
+    layout.start();
+    return layout;
+  }
+
+  private Layout<LoggingEvent> buildHTMLLayout(LoggerContext lc) {
+    HTMLLayout layout = new HTMLLayout();
+    layout.setContext(lc);
+    // layout.setFileHeader(HEADER);
+    layout.setPattern("%level%class%msg");
+    // layout.setFileFooter(FOOTER);
     layout.start();
     return layout;
   }
@@ -67,7 +87,8 @@
   }
 
   @Test
-  public void smoke() throws Exception  {
+  public void smoke() throws Exception {
+    smtpAppender.setLayout(buildPatternLayout(lc));
     smtpAppender.start();
     Logger logger = lc.getLogger("test");
     logger.addAppender(smtpAppender);
@@ -77,10 +98,69 @@
     assertNotNull(mma);
     assertEquals(1, mma.length);
     MimeMessage mm = mma[0];
-    
+    // http://jira.qos.ch/browse/LBCLASSIC-67
     assertEquals(TEST_SUBJECT, mm.getSubject());
-    //System.out.println(mm.getContent().toString());
-    
+
+    MimeMultipart mp = (MimeMultipart) mm.getContent();
+    String body = GreenMailUtil.getBody(mp.getBodyPart(0));
+    assertTrue(body.startsWith(HEADER.trim()));
+    assertTrue(body.endsWith(FOOTER.trim()));
+  }
+
+  @Test
+  public void html() throws Exception {
+    smtpAppender.setLayout(buildHTMLLayout(lc));
+    smtpAppender.start();
+    Logger logger = lc.getLogger("test");
+    logger.addAppender(smtpAppender);
+    logger.debug("hello");
+    logger.error("en error", new Exception("an exception"));
+    MimeMessage[] mma = greenMail.getReceivedMessages();
+    assertNotNull(mma);
+    assertEquals(1, mma.length);
+    MimeMessage mm = mma[0];
+    assertEquals(TEST_SUBJECT, mm.getSubject());
+
+    MimeMultipart mp = (MimeMultipart) mm.getContent();
+
+    // verify strict adherence to xhtml1-strict.dtd
+    SAXReader reader = new SAXReader();
+    reader.setValidation(true);
+    reader.setEntityResolver(new XHTMLEntityResolver());
+    reader.read(mp.getBodyPart(0).getInputStream());
+    // System.out.println(GreenMailUtil.getBody(mp.getBodyPart(0)));
+  }
+
+  @Test
+  /**
+   * Checks that even when many events are processed, the output is still
+   * conforms to xhtml-strict.dtd.
+   * 
+   * Note that SMTPAppender only keeps only 500 or so (=buffer size)
+   * events. So the generated output will be rather short.
+   */
+  public void htmlLong() throws Exception {
+    smtpAppender.setLayout(buildHTMLLayout(lc));
+    smtpAppender.start();
+    Logger logger = lc.getLogger("test");
+    logger.addAppender(smtpAppender);
+    for (int i = 0; i < CoreGlobal.TABLE_ROW_LIMIT * 3; i++) {
+      logger.debug("hello " + i);
+    }
+    logger.error("en error", new Exception("an exception"));
+    MimeMessage[] mma = greenMail.getReceivedMessages();
+    assertNotNull(mma);
+    assertEquals(1, mma.length);
+    MimeMessage mm = mma[0];
+    assertEquals(TEST_SUBJECT, mm.getSubject());
+
+    MimeMultipart mp = (MimeMultipart) mm.getContent();
+
+    // verify strict adherence to xhtml1-strict.dtd
+    SAXReader reader = new SAXReader();
+    reader.setValidation(true);
+    reader.setEntityResolver(new XHTMLEntityResolver());
+    reader.read(mp.getBodyPart(0).getInputStream());
   }
 
 }

Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/CoreGlobal.java
==============================================================================
--- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/CoreGlobal.java	(original)
+++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/CoreGlobal.java	Wed Aug 27 19:52:19 2008
@@ -57,4 +57,11 @@
   
   
   public final static char PERCENT_CHAR = '%';
+  
+  /** 
+   * Number of rows before in an HTML table before, 
+   * we close the table and create a new one
+   */
+  public static final int TABLE_ROW_LIMIT = 10000;
+
 }

Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/html/CssBuilder.java
==============================================================================
--- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/html/CssBuilder.java	(original)
+++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/html/CssBuilder.java	Wed Aug 27 19:52:19 2008
@@ -2,6 +2,6 @@
 
 public interface CssBuilder {
 
-  public void addCss(StringBuffer sbuf);
+  public void addCss(StringBuilder sbuf);
   
 }

Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/html/HTMLLayoutBase.java
==============================================================================
--- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/html/HTMLLayoutBase.java	(original)
+++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/html/HTMLLayoutBase.java	Wed Aug 27 19:52:19 2008
@@ -2,6 +2,7 @@
 
 import java.util.Map;
 
+import ch.qos.logback.core.CoreGlobal;
 import ch.qos.logback.core.LayoutBase;
 import ch.qos.logback.core.pattern.Converter;
 import ch.qos.logback.core.pattern.ConverterUtil;
@@ -22,7 +23,7 @@
 
   protected String title = "Logback Log Messages";
 
-  //It is the responsability of derived classes to set
+  //It is the responsibility of derived classes to set
   //this variable in their constructor to a default value.
   protected CssBuilder cssBuilder;
 
@@ -30,8 +31,6 @@
 
   // counter keeping track of the rows output
   protected long counter = 0;
-  // max number of rows before we close the table and create a new one
-  protected static final int ROW_LIMIT = 10000;
   
   /**
    * Set the <b>ConversionPattern </b> option. This is the string which controls
@@ -118,15 +117,15 @@
    */
   @Override
   public String getFileHeader() {
-    StringBuffer sbuf = new StringBuffer();
-    sbuf.append("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"");
+    StringBuilder sbuf = new StringBuilder();
+    sbuf.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"");
     sbuf.append(" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
     sbuf.append(LINE_SEP);
     sbuf.append("<html>");
     sbuf.append(LINE_SEP);
-    sbuf.append("<head>");
+    sbuf.append("  <head>");
     sbuf.append(LINE_SEP);
-    sbuf.append("<title>");
+    sbuf.append("    <title>");
     sbuf.append(title);
     sbuf.append("</title>");
     sbuf.append(LINE_SEP);
@@ -138,7 +137,7 @@
 //      cssBuilder.addExternalCSS(sbuf);
 //    }
     sbuf.append(LINE_SEP);
-    sbuf.append("</head>");
+    sbuf.append("  </head>");
     sbuf.append(LINE_SEP);
     sbuf.append("<body>");
     sbuf.append(LINE_SEP);
@@ -147,25 +146,24 @@
   }
   
   public String getPresentationHeader() {
-    StringBuffer sbuf = new StringBuffer();
-    sbuf.append("<hr size=\"1\" noshade=\"true\" width=\"50%\" align=\"left\" />");
+    StringBuilder sbuf = new StringBuilder();
+    sbuf.append("<hr/>");
     sbuf.append(LINE_SEP);
-    sbuf.append("Log session start time ");
+    sbuf.append("<p>Log session start time ");
     sbuf.append(new java.util.Date());
-    sbuf.append("<br />");
+    sbuf.append("</p><p></p>");
     sbuf.append(LINE_SEP);
-    sbuf.append("<br />");
     sbuf.append(LINE_SEP);
     sbuf.append("<table cellspacing=\"0\">");
     sbuf.append(LINE_SEP);
 
-    createTableHeader(sbuf);
+    buildHeaderRowForTable(sbuf);
     
     return sbuf.toString();
   }
 
 
-  private void createTableHeader(StringBuffer sbuf) {
+  private void buildHeaderRowForTable(StringBuilder sbuf) {
     Converter c = head;
     String name;
     sbuf.append("<tr class=\"header\">");
@@ -189,7 +187,7 @@
   }
   
   public String getPresentationFooter() {
-    StringBuffer sbuf = new StringBuffer();
+    StringBuilder sbuf = new StringBuilder();
     sbuf.append("</table>");
     return sbuf.toString();    
   }
@@ -199,21 +197,21 @@
    */
   @Override
   public String getFileFooter() {
-    StringBuffer sbuf = new StringBuffer();
+    StringBuilder sbuf = new StringBuilder();
     sbuf.append(LINE_SEP);
     sbuf.append("</body></html>");
     return sbuf.toString();
   }
   
-  protected void handleTableClosing(StringBuffer sbuf) {
-    if (this.counter >= ROW_LIMIT) {
+  protected void startNewTableIfLimitReached(StringBuilder sbuf) {
+    if (this.counter >= CoreGlobal.TABLE_ROW_LIMIT) {
       counter = 0;
       sbuf.append("</table>");
       sbuf.append(LINE_SEP);
-      sbuf.append("<br />");
+      sbuf.append("<p></p>");
       sbuf.append("<table cellspacing=\"0\">");
       sbuf.append(LINE_SEP);
-      createTableHeader(sbuf);
+      buildHeaderRowForTable(sbuf);
     }
   }
 

Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/html/IThrowableRenderer.java
==============================================================================
--- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/html/IThrowableRenderer.java	(original)
+++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/html/IThrowableRenderer.java	Wed Aug 27 19:52:19 2008
@@ -3,6 +3,6 @@
 
 public interface IThrowableRenderer {
   
-  public void render(StringBuffer sbuf, Object event);
+  public void render(StringBuilder sbuf, Object event);
   
 }

Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/html/NOPThrowableRenderer.java
==============================================================================
--- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/html/NOPThrowableRenderer.java	(original)
+++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/html/NOPThrowableRenderer.java	Wed Aug 27 19:52:19 2008
@@ -5,7 +5,7 @@
 
 public class NOPThrowableRenderer implements IThrowableRenderer {
 
-  public void render(StringBuffer sbuf, Object event) {
+  public void render(StringBuilder sbuf, Object event) {
     return;
   }
 

Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/net/SMTPAppenderBase.java
==============================================================================
--- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/net/SMTPAppenderBase.java	(original)
+++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/net/SMTPAppenderBase.java	Wed Aug 27 19:52:19 2008
@@ -213,14 +213,15 @@
         sbuf.append(presentationHeader);
       }
       fillBuffer(sbuf);
-      String footer = layout.getFileFooter();
-      if (footer != null) {
-        sbuf.append(footer);
-      }
       String presentationFooter = layout.getPresentationFooter();
       if (presentationFooter != null) {
         sbuf.append(presentationFooter);
       }
+      String footer = layout.getFileFooter();
+      if (footer != null) {
+        sbuf.append(footer);
+      }
+ 
 
       if (subjectLayout != null) {
         msg.setSubject(subjectLayout.doLayout(lastEventObject));


More information about the logback-dev mailing list