From 67e9aeb9926adc9befd42098ee3c39b73bdf2e25 Mon Sep 17 00:00:00 2001 From: Arnaud Roques Date: Wed, 5 Apr 2017 21:57:35 +0200 Subject: [PATCH] version 2017.09 --- pom.xml | 2 +- .../plantuml/servlet/AsciiServlet.java | 10 --- .../plantuml/servlet/CheckSyntaxServlet.java | 2 +- .../plantuml/servlet/DiagramResponse.java | 86 +++++++++++++++---- .../plantuml/servlet/ImgServlet.java | 10 --- .../plantuml/servlet/MapServlet.java | 2 +- .../plantuml/servlet/ProxyServlet.java | 4 +- .../plantuml/servlet/SvgServlet.java | 10 --- .../plantuml/servlet/UmlDiagramService.java | 37 +++++++- 9 files changed, 108 insertions(+), 55 deletions(-) diff --git a/pom.xml b/pom.xml index 21c1cfa..f617de3 100644 --- a/pom.xml +++ b/pom.xml @@ -157,7 +157,7 @@ net.sourceforge.plantuml plantuml - RELEASE + 2017.09 javax.servlet diff --git a/src/main/java/net/sourceforge/plantuml/servlet/AsciiServlet.java b/src/main/java/net/sourceforge/plantuml/servlet/AsciiServlet.java index 30e23be..f278b43 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/AsciiServlet.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/AsciiServlet.java @@ -32,16 +32,6 @@ import net.sourceforge.plantuml.FileFormat; @SuppressWarnings("serial") public class AsciiServlet extends UmlDiagramService { - @Override - public String getSource(String uri) { - String[] result = uri.split("/txt/", 2); - if (result.length != 2) { - return ""; - } else { - return result[1]; - } - } - @Override public FileFormat getOutputFormat() { return FileFormat.UTXT; diff --git a/src/main/java/net/sourceforge/plantuml/servlet/CheckSyntaxServlet.java b/src/main/java/net/sourceforge/plantuml/servlet/CheckSyntaxServlet.java index dd096c2..bec6280 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/CheckSyntaxServlet.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/CheckSyntaxServlet.java @@ -49,7 +49,7 @@ public class CheckSyntaxServlet extends HttpServlet { String uml = UmlExtractor.getUmlSource(getSource(request.getRequestURI())); // generate the response - DiagramResponse dr = new DiagramResponse(response, getOutputFormat()); + DiagramResponse dr = new DiagramResponse(response, getOutputFormat(), request); try { dr.sendCheck(uml); } catch (IIOException iioe) { diff --git a/src/main/java/net/sourceforge/plantuml/servlet/DiagramResponse.java b/src/main/java/net/sourceforge/plantuml/servlet/DiagramResponse.java index e2e0a5e..626c489 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/DiagramResponse.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/DiagramResponse.java @@ -30,21 +30,32 @@ import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletRequest; +import net.sourceforge.plantuml.BlockUml; import net.sourceforge.plantuml.FileFormat; import net.sourceforge.plantuml.FileFormatOption; import net.sourceforge.plantuml.SourceStringReader; import net.sourceforge.plantuml.StringUtils; import net.sourceforge.plantuml.core.DiagramDescription; +import net.sourceforge.plantuml.core.Diagram; import net.sourceforge.plantuml.servlet.utility.NullOutputStream; +import net.sourceforge.plantuml.version.Version; +import net.sourceforge.plantuml.PSystemError; +import net.sourceforge.plantuml.ErrorUml; + /** * Delegates the diagram generation from the UML source and the filling of the HTTP response with the diagram in the * right format. Its own responsibility is to produce the right HTTP headers. */ class DiagramResponse { + + private static final String POWERED_BY = "PlantUML Version " + Version.versionString(); + private HttpServletResponse response; private FileFormat format; + private HttpServletRequest request; private static final Map CONTENT_TYPE; static { Map map = new HashMap(); @@ -54,27 +65,50 @@ class DiagramResponse { CONTENT_TYPE = Collections.unmodifiableMap(map); } - DiagramResponse(HttpServletResponse r, FileFormat f) { + DiagramResponse(HttpServletResponse r, FileFormat f, HttpServletRequest rq) { response = r; format = f; + request = rq; } - void sendDiagram(String uml) throws IOException { - if (StringUtils.isDiagramCacheable(uml)) { - addHeaderForCache(); - } + void sendDiagram(String uml, int idx) throws IOException { response.setContentType(getContentType()); SourceStringReader reader = new SourceStringReader(uml); + final BlockUml blockUml = reader.getBlocks().get(0); + if (notModified(blockUml)) { + addHeaderForCache(blockUml); + response.sendError(HttpServletResponse.SC_NOT_MODIFIED); + return; + } + if (StringUtils.isDiagramCacheable(uml)) { + addHeaderForCache(blockUml); + } reader.generateImage(response.getOutputStream(), new FileFormatOption(format, false)); } - void sendMap(String uml) throws IOException { - if (StringUtils.isDiagramCacheable(uml)) { - addHeaderForCache(); + private boolean notModified(BlockUml blockUml) { + final String ifNoneMatch = request.getHeader("If-None-Match"); + final long ifModifiedSince = request.getDateHeader("If-Modified-Since"); + if (ifModifiedSince != -1 && ifModifiedSince != blockUml.lastModified()) { + return false; } + final String etag = blockUml.etag(); + if (ifNoneMatch == null) { + return false; + } + return ifNoneMatch.contains(etag); + } + + + void sendMap(String uml) throws IOException { response.setContentType(getContentType()); SourceStringReader reader = new SourceStringReader(uml); - String map = reader.generateImage(new NullOutputStream(), new FileFormatOption(FileFormat.PNG, false)); + final BlockUml blockUml = reader.getBlocks().get(0); + if (StringUtils.isDiagramCacheable(uml)) { + addHeaderForCache(blockUml); + } + String map = reader.generateImage(new NullOutputStream(), + new FileFormatOption(FileFormat.PNG, false)).getDescription(); String[] mapLines = map.split("[\\r\\n]"); PrintWriter httpOut = response.getWriter(); for (int i = 2; (i + 1) < mapLines.length; i++) { @@ -85,21 +119,41 @@ class DiagramResponse { void sendCheck(String uml) throws IOException { response.setContentType(getContentType()); SourceStringReader reader = new SourceStringReader(uml); - DiagramDescription desc = reader.generateDiagramDescription( + DiagramDescription desc = reader.generateImage( new NullOutputStream(), new FileFormatOption(FileFormat.PNG, false)); PrintWriter httpOut = response.getWriter(); httpOut.print(desc.getDescription()); } - private void addHeaderForCache() { + private void addHeaderForCache(BlockUml blockUml) { long today = System.currentTimeMillis(); // Add http headers to force the browser to cache the image - response.addDateHeader("Expires", today + 31536000000L); - // today + 1 year - response.addDateHeader("Last-Modified", 1261440000000L); - // 2009 dec 22 constant date in the past - response.addHeader("Cache-Control", "public"); + final int maxAge = 3600 * 24 * 5; + response.addDateHeader("Expires", today + 1000L * maxAge); + response.addDateHeader("Date", today); + + response.addDateHeader("Last-Modified", blockUml.lastModified()); + response.addHeader("Cache-Control", "public, max-age=" + maxAge); + // response.addHeader("Cache-Control", "max-age=864000"); + response.addHeader("Etag", "\"" + blockUml.etag() + "\""); + final Diagram diagram = blockUml.getDiagram(); + response.addHeader("X-PlantUML-Diagram-Description", diagram.getDescription().getDescription()); + if (diagram instanceof PSystemError) { + final PSystemError error = (PSystemError) diagram; + for (ErrorUml err : error.getErrorsUml()) { + response.addHeader("X-PlantUML-Diagram-Error", err.getError()); + response.addHeader("X-PlantUML-Diagram-Error-Line", "" + err.getPosition()); + } + } + addHeaders(response); } + public static void addHeaders(HttpServletResponse response) { + response.addHeader("X-Powered-By", POWERED_BY); + response.addHeader("X-Patreon", "Support us on http://plantuml.com/patreon"); + response.addHeader("X-Donate", "http://plantuml.com/paypal"); + } + + private String getContentType() { return CONTENT_TYPE.get(format); } diff --git a/src/main/java/net/sourceforge/plantuml/servlet/ImgServlet.java b/src/main/java/net/sourceforge/plantuml/servlet/ImgServlet.java index 6687315..8fae637 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/ImgServlet.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/ImgServlet.java @@ -32,16 +32,6 @@ import net.sourceforge.plantuml.FileFormat; @SuppressWarnings("serial") public class ImgServlet extends UmlDiagramService { - @Override - public String getSource(String uri) { - String[] result = uri.split("/img/|/png/", 2); - if (result.length != 2) { - return ""; - } else { - return result[1]; - } - } - @Override public FileFormat getOutputFormat() { return FileFormat.PNG; diff --git a/src/main/java/net/sourceforge/plantuml/servlet/MapServlet.java b/src/main/java/net/sourceforge/plantuml/servlet/MapServlet.java index 96975c4..d1b1081 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/MapServlet.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/MapServlet.java @@ -49,7 +49,7 @@ public class MapServlet extends HttpServlet { String uml = UmlExtractor.getUmlSource(getSource(request.getRequestURI())); // generate the response - DiagramResponse dr = new DiagramResponse(response, getOutputFormat()); + DiagramResponse dr = new DiagramResponse(response, getOutputFormat(), request); try { dr.sendMap(uml); } catch (IIOException iioe) { diff --git a/src/main/java/net/sourceforge/plantuml/servlet/ProxyServlet.java b/src/main/java/net/sourceforge/plantuml/servlet/ProxyServlet.java index 78e2ac4..1460d25 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/ProxyServlet.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/ProxyServlet.java @@ -83,9 +83,9 @@ public class ProxyServlet extends HttpServlet { //System.out.println("uml=" + uml); // generate the response - DiagramResponse dr = new DiagramResponse(response, getOutputFormat(fmt)); + DiagramResponse dr = new DiagramResponse(response, getOutputFormat(fmt), request); try { - dr.sendDiagram(uml); + dr.sendDiagram(uml, 0); } catch (IIOException iioe) { // Browser has closed the connection, so the HTTP OutputStream is closed // Silently catch the exception to avoid annoying log diff --git a/src/main/java/net/sourceforge/plantuml/servlet/SvgServlet.java b/src/main/java/net/sourceforge/plantuml/servlet/SvgServlet.java index 4d28793..e70709c 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/SvgServlet.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/SvgServlet.java @@ -32,16 +32,6 @@ import net.sourceforge.plantuml.FileFormat; @SuppressWarnings("serial") public class SvgServlet extends UmlDiagramService { - @Override - public String getSource(String uri) { - String[] result = uri.split("/svg/", 2); - if (result.length != 2) { - return ""; - } else { - return result[1]; - } - } - @Override public FileFormat getOutputFormat() { return FileFormat.SVG; diff --git a/src/main/java/net/sourceforge/plantuml/servlet/UmlDiagramService.java b/src/main/java/net/sourceforge/plantuml/servlet/UmlDiagramService.java index fb2b284..c663b66 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/UmlDiagramService.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/UmlDiagramService.java @@ -24,6 +24,8 @@ package net.sourceforge.plantuml.servlet; import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.imageio.IIOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -43,12 +45,21 @@ public abstract class UmlDiagramService extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // build the UML source from the compressed request parameter - String uml = UmlExtractor.getUmlSource(getSource(request.getRequestURI())); + final String[] sourceAndIdx = getSourceAndIdx(request.getRequestURI()); + final String uml; + try { + uml = UmlExtractor.getUmlSource(sourceAndIdx[0]); + } catch (Exception e) { + e.printStackTrace(); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Bad Request"); + return; + } // generate the response - DiagramResponse dr = new DiagramResponse(response, getOutputFormat()); + DiagramResponse dr = new DiagramResponse(response, getOutputFormat(), request); + final int idx = Integer.parseInt(sourceAndIdx[1]); try { - dr.sendDiagram(uml); + dr.sendDiagram(uml, idx); } catch (IIOException iioe) { // Browser has closed the connection, so the HTTP OutputStream is closed // Silently catch the exception to avoid annoying log @@ -56,6 +67,8 @@ public abstract class UmlDiagramService extends HttpServlet { dr = null; } + private static final Pattern RECOVER_UML_PATTERN = Pattern.compile("/\\w+/\\w+/(\\d+/)?(.*)"); + /** * Extracts the compressed UML source from the HTTP URI. * @@ -63,7 +76,23 @@ public abstract class UmlDiagramService extends HttpServlet { * the complete URI as returned by request.getRequestURI() * @return the compressed UML source */ - abstract public String getSource(String uri); + public final String[] getSourceAndIdx(String uri) { + final Matcher recoverUml = RECOVER_UML_PATTERN.matcher(uri); + // the URL form has been submitted + if (recoverUml.matches()) { + final String data = recoverUml.group(2); + if (data.length() >= 4) { + String idx = recoverUml.group(1); + if (idx == null) { + idx = "0"; + } else { + idx = idx.substring(0, idx.length() - 1); + } + return new String[]{data, idx }; + } + } + return new String[]{"", "0" }; + } /** * Gives the wished output format of the diagram. This value is used by the DiagramResponse class.