Yi EungJun 2015-07-07
Sync 'next' with v0.8.2
@62aab165da5afebdc58879fd810d7c66d6fe6d6a
app/utils/Markdown.java
--- app/utils/Markdown.java
+++ app/utils/Markdown.java
@@ -21,6 +21,7 @@
 package utils;
 
 import models.Project;
+import org.apache.commons.lang.StringEscapeUtils;
 import org.jsoup.Jsoup;
 import org.jsoup.nodes.Document;
 import org.jsoup.nodes.Element;
@@ -33,10 +34,8 @@
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
-import java.lang.System;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.nio.charset.Charset;
 
 public class Markdown {
 
@@ -138,7 +137,7 @@
                     "highlight : function(sCode, sLang) { " +
                     "if(sLang) { try { return hljs.highlight(sLang.toLowerCase(), sCode).value;" +
                     " } catch(oException) { return sCode; } } }});");
-            String rendered = (String) ((Invocable) engine).invokeFunction("marked", source, options);
+            String rendered = renderByMarked(source, options);
             rendered = removeJavascriptInHref(rendered);
             rendered = checkReferrer(rendered);
             return sanitize(rendered);
@@ -147,13 +146,49 @@
         }
     }
 
+    /**
+     * Renders the source with Marked.
+     *
+     * @param source
+     * @param options
+     * @return the rendered result or the source if timeout occurs
+     */
+    private static String renderByMarked(@Nonnull String source, Object options) throws InterruptedException {
+        if (source.isEmpty()) {
+            return source;
+        }
+
+        // Try to render and wait at most 5 seconds.
+        final String[] rendered = new String[1];
+        @SuppressWarnings("deprecation")
+        Thread marked = new Thread() {
+            @Override
+            public void run() {
+                try {
+                    rendered[0] = (String) ((Invocable) engine).invokeFunction(
+                            "marked", source, options);
+                } catch (Exception e) {
+                    play.Logger.error("[Markdown] Failed to render: " + source, e);
+                }
+            }
+        };
+        marked.start();
+        marked.join(5000);
+
+        if (rendered[0] == null) {
+            // This is the only way to stop the script engine. Thread.interrupt does not work.
+            marked.stop();
+            return "<pre>" + StringEscapeUtils.escapeHtml(source) + "</pre>";
+        } else {
+            return rendered[0];
+        }
+    }
+
     public static String render(@Nonnull String source) {
         try {
             Object options = engine.eval("new Object({gfm: true, tables: true, breaks: true, " +
                     "pedantic: false, sanitize: false, smartLists: true});");
-            String rendered = (String) ((Invocable) engine).invokeFunction("marked", source,
-                    options);
-            return sanitize(rendered);
+            return sanitize(renderByMarked(source, options));
         } catch (Exception ex) {
             throw new RuntimeException(ex);
         }
 
docs/ko/relnotes/v0.8.2.txt (added)
+++ docs/ko/relnotes/v0.8.2.txt
@@ -0,0 +1,11 @@
+Yobi v0.8.2 릴리즈 노트
+=======================
+
+v0.8.1 이후 버그수정
+--------------------
+
+* Jsoup 버그로 인해 마크다운 렌더링 중 에러가 발생하는 문제 (b1d4bb9)
+* 메일을 통해 등록된 이슈에 불필요한 빈 줄이 너무 많은 문제 (a5c9992)
+* 마크다운 렌더링에 시간이 너무 많이 걸리는 경우, 시스템 전체에 장애를 일으킬
+  수 있는 문제 (ad9b0aa)
+* Java8에서 findbugs가 실행되지 않는 문제 (4ada856)
 
docs/relnotes/0.8.2.txt (added)
+++ docs/relnotes/0.8.2.txt
@@ -0,0 +1,11 @@
+Yobi v0.8.2 Release Notes
+=========================
+
+Fixes since v0.8.1
+------------------
+
+* Server error while rendering markdown text because of a Jsoup bug (b1d4bb9)
+* Ugly newlines of issues posted by email (a5c9992)
+* System might freeze because of markdown text which takes a long time to be
+  rendered. (ad9b0aa)
+* findbugs did not work on Java 8. (4ada856)
Add a comment
List