diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7e433e5..2638904 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,5 @@ name: Main + on: push: tags: @@ -11,6 +12,7 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 + - uses: actions/setup-java@v2 with: distribution: "adopt" diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..133d0c5 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,34 @@ +name: Pages + +on: + workflow_dispatch: + workflow_run: + workflows: + - Main + types: + - completed + +jobs: + pages: + runs-on: ubuntu-latest + if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - uses: actions/setup-java@v2 + with: + distribution: "adopt" + java-version: "8" + check-latest: true + cache: "maven" + + - name: Create GitHub Pages + run: mvn site + + - name: Deploy GitHub Pages + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./target/site diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..b117f95 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,141 @@ +name: Tests + +on: + - push + - pull_request + +jobs: + test-mvn-livecycle: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - uses: actions/setup-java@v2 + with: + distribution: "adopt" + java-version: "8" + check-latest: true + cache: "maven" + + - name: Lifecycle - Step 1/8 - mvn clean + run: mvn clean + + - name: Lifecycle - Step 2/8 - mvn validate + run: mvn validate + + - name: Lifecycle - Step 3/8 - mvn compile + run: mvn compile + + - name: Lifecycle - Step 4/8 - mvn test (with skipTests=true) + run: mvn test + + - name: Lifecycle - Step 5/8 - mvn package + run: mvn package + + - name: Lifecycle - Step 6/8 - mvn verify + run: mvn verify + + - name: Lifecycle - Step 7/8 - mvn install + run: mvn install + + - name: Lifecycle - Step 8/8 - mvn site + run: mvn site + + # test-embedded: + # runs-on: ubuntu-latest + # needs: test-mvn-livecycle + # steps: + # - uses: actions/checkout@v2 + # with: + # fetch-depth: 0 + + # - uses: actions/setup-java@v2 + # with: + # distribution: "adopt" + # java-version: "8" + # check-latest: true + # cache: "maven" + + # - name: Prepare embedded tests - Step 1/3 - mvn clean + # run: mvn clean + + # - name: Prepare embedded tests - Step 2/3 - mvn compile + # run: mvn compile + + # - name: Prepare embedded tests - Step 3/3 - mvn test (with skipTests=true) + # run: mvn test + + # - name: Run tests against jetty embedded server + # run: mvn test -DskipTests=false + + test-jetty: + runs-on: ubuntu-latest + needs: test-mvn-livecycle + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - uses: actions/setup-java@v2 + with: + distribution: "adopt" + java-version: "8" + check-latest: true + cache: "maven" + + - name: Prepare external tests - Step 1/3 - mvn clean + run: mvn clean + + - name: Prepare external tests - Step 2/3 - mvn compile + run: mvn compile + + - name: Prepare external tests - Step 3/3 - mvn test (with skipTests=true) + run: mvn test + + - name: Build the jetty docker stack + run: | + docker image build -f Dockerfile.jetty -t plantuml-server:local . + docker run -d -p 8080:8080 -e BASE_URL=plantuml plantuml-server:local + + - name: Check running containers + run: docker ps + + - name: run tests against jetty docker image + run: mvn test -DskipTests=false -DargLine="-Dsystem.test.server=http://localhost:8080/plantuml" + + test-tomcat: + runs-on: ubuntu-latest + needs: test-mvn-livecycle + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - uses: actions/setup-java@v2 + with: + distribution: "adopt" + java-version: "8" + check-latest: true + cache: "maven" + + - name: Prepare external tests - Step 1/3 - mvn clean + run: mvn clean + + - name: Prepare external tests - Step 2/3 - mvn compile + run: mvn compile + + - name: Prepare external tests - Step 3/3 - mvn test (with skipTests=true) + run: mvn test + + - name: Build the tomcat docker stack + run: | + docker image build -f Dockerfile.tomcat -t plantuml-server:local . + docker run -d -p 8080:8080 -e BASE_URL=plantuml plantuml-server:local + + - name: Check running containers + run: docker ps + + - name: run tests against tomcat docker image + run: mvn test -DskipTests=false -DargLine="-Dsystem.test.server=http://localhost:8080/plantuml" diff --git a/.vscode/settings.json b/.vscode/settings.json index 39e9ddb..c8c01dd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,7 +13,8 @@ "servlet", "servlets", "startditaa", - "startuml" + "startuml", + "utxt" ], "cSpell.allowCompoundWords": true } \ No newline at end of file diff --git a/README.md b/README.md index 7b3c236..32a8bb9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,13 @@ # PlantUML Server -![workflow status](https://github.com/plantuml/plantuml-server/actions/workflows/main.yml/badge.svg) -[![docker pulls](https://img.shields.io/docker/pulls/plantuml/plantuml-server.svg)](https://hub.docker.com/r/plantuml/plantuml-server) +[![GNU GENERAL PUBLIC LICENSE, Version 3, 29 June 2007](https://img.shields.io/github/license/plantuml/plantuml-server.svg?color=blue)](https://www.gnu.org/licenses/gpl-3.0) +[![latest tag](https://img.shields.io/github/v/tag/plantuml/plantuml-server)](https://github.com/plantuml/plantuml-server/tags) +![workflow status (Main)](https://github.com/plantuml/plantuml-server/actions/workflows/main.yml/badge.svg) +![workflow status (Tests)](https://github.com/plantuml/plantuml-server/actions/workflows/tests.yml/badge.svg) +![workflow status (Pages)](https://github.com/plantuml/plantuml-server/actions/workflows/pages.yml/badge.svg) +[![docker pulls](https://img.shields.io/docker/pulls/plantuml/plantuml-server.svg?color=blue)](https://hub.docker.com/r/plantuml/plantuml-server) +![Docker Image Size (Jetty)](https://img.shields.io/docker/image-size/plantuml/plantuml-server/jetty?label=jetty%20image%20size) +![Docker Image Size (Tomcat)](https://img.shields.io/docker/image-size/plantuml/plantuml-server/tomcat?label=tomcat%20image%20size) PlantUML Server is a web application to generate UML diagrams on-the-fly. @@ -84,21 +90,21 @@ docker run -d -p 8080:8080 -e THE_ENV_VARIABLE=THE_ENV_VALUE plantuml/plantuml-s You can set all the following variables: +* `BASE_URL` + * PlantUML Base URL path + * Default value: `ROOT` * `PLANTUML_LIMIT_SIZE` - * Limits image width and height - * Default value: `4096` -* `GRAPHVIZ_DOT` - * Link to 'dot' executable - * Default value: `/usr/local/bin/dot` or `/usr/bin/dot` + * Limits image width and height + * Default value: `4096` * `PLANTUML_STATS` - * Set it to `on` to enable [statistics report](https://plantuml.com/statistics-report) - * Default value: `off` + * Set it to `on` to enable [statistics report](https://plantuml.com/statistics-report) + * Default value: `off` * `HTTP_AUTHORIZATION` - * when calling the `proxy` endpoint, the value of `HTTP_AUTHORIZATION` will be used to set the HTTP Authorization header - * Default value: `null` + * when calling the `proxy` endpoint, the value of `HTTP_AUTHORIZATION` will be used to set the HTTP Authorization header + * Default value: `null` * `ALLOW_PLANTUML_INCLUDE` - * Enables `!include` processing which can read files from the server into diagrams. Files are read relative to the current working directory. - * Default value: `false` + * Enables `!include` processing which can read files from the server into diagrams. Files are read relative to the current working directory. + * Default value: `false` ## Alternate: How to build your docker image @@ -112,7 +118,7 @@ So, you can use following command to create a self-contained docker image that w docker image build -t plantuml-server:local . docker run -d -p 8080:8080 plantuml-server:local ``` -The server is now listening to [http://localhost:8080/plantuml](http://localhost:8080/plantuml). +The server is now listening to [http://localhost:8080](http://localhost:8080). You may specify the port in `-p` Docker command line argument. diff --git a/pom.xml b/pom.xml index 324e053..5b427ee 100644 --- a/pom.xml +++ b/pom.xml @@ -1,226 +1,138 @@ - + 4.0.0 + org.sourceforge.plantuml plantumlservlet 1-SNAPSHOT - PlantUML Servlet war - - plantuml - - - - org.apache.maven.plugins - maven-checkstyle-plugin - 3.1.2 - - - com.puppycrawl.tools - checkstyle - 9.0.1 - - - - ${basedir}/src/main/config/checkstyle.xml - false - true - true - - - - - - - maven-eclipse-plugin - 2.8 - - 1.5 - plantuml - - - - maven-compiler-plugin - 2.3.2 - - 1.7 - 1.7 - - - - org.mortbay.jetty - jetty-maven-plugin - ${jetty.version} - - 5 - - ${jetty.contextpath} - - - - jetty.port - ${jetty.port} - - - - - - org.apache.maven.plugins - maven-dependency-plugin - 2.3 - - - package - copy - - - - org.mortbay.jetty - jetty-runner - 8.1.9.v20130131 - jetty-runner.jar - - - - - - - - org.apache.maven.plugins - maven-war-plugin - 2.3 - - - - src/main/webapp - - *.jspf - - true - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.14.1 - - ${skipTests} - - - - org.apache.maven.plugins - maven-site-plugin - 3.0-beta-3 - - - org.apache.maven.plugins - maven-checkstyle-plugin - 3.1.2 - - - - validate - - check - - - - - - - - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 2.6 - - - - index - dependencies - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.3.1 - - private - true - - - - html - - javadoc - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - 3.1.2 - - - - checkstyle - checkstyle-aggregate - - - - - - + + PlantUML Servlet + https://plantuml.github.io/plantuml-server/index.html + + 8 + ${java.version} + ${java.version} UTF-8 UTF-8 - 8.0.4.v20111024 - 8080 - /plantuml - yyyyMMdd-HHmm - - ${maven.build.timestamp} + + true + + yyyyMMdd-HHmm + ${maven.build.timestamp} + + 1.5 + plantuml + + 8080 + /${wtp.contextName} + + + 1.2021.12 + 8.0.4.v20111024 + 5.63.0 + + + 1.2 + 2.5 + 1.14 + + 1.0.7 + ${jlatexmath.version} + ${jlatexmath.version} + + 4.13.2 + 2.53.0 + + + + 3.1.0 + 3.2.0 + 3.2.0 + 3.8.1 + 2.8.1 + 2.22.2 + 3.3.2 + 2.5.2 + 2.8.2 + 3.9.1 + 3.1.2 + 3.1.2 + 9.0.1 + + + 2.10 + + 8.1.9.v20130131 + ${jetty.version} + 3.3.1 + net.sourceforge.plantuml plantuml - 1.2021.12 + ${plantuml.version} javax.servlet jstl - 1.2 + ${jstl.version} - org.webjars + org.webjars.npm codemirror - 3.21 + ${codemirror.version} javax.servlet servlet-api - 2.5 + ${servlet-api.version} provided + + org.apache.xmlgraphics + batik-all + ${batik-all.version} + + + + org.scilab.forge + jlatexmath + ${jlatexmath.version} + + + org.scilab.forge + jlatexmath-font-greek + ${jlatexmath-font-greek.version} + + + org.scilab.forge + jlatexmath-font-cyrillic + ${jlatexmath-font-cyrillic.version} + + junit junit - 4.13.2 + ${junit.version} test net.sourceforge.htmlunit htmlunit - 2.53.0 + ${htmlunit.version} test @@ -235,25 +147,266 @@ 2.1.v20100127 test - - org.scilab.forge - jlatexmath - 1.0.7 - - - org.scilab.forge - jlatexmath-font-greek - 1.0.7 - - - org.scilab.forge - jlatexmath-font-cyrillic - 1.0.7 - - - org.apache.xmlgraphics - batik-all - 1.12 - + + + plantuml + + + + + maven-clean-plugin + ${maven-clean-plugin.version} + + + maven-dependency-plugin + ${maven-dependency-plugin.version} + + + maven-resources-plugin + ${maven-resources-plugin.version} + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + maven-war-plugin + ${maven-war-plugin.version} + + + maven-install-plugin + ${maven-install-plugin.version} + + + maven-deploy-plugin + ${maven-deploy-plugin.version} + + + maven-site-plugin + ${maven-site-plugin.version} + + + maven-project-info-reports-plugin + ${maven-project-info-reports-plugin.version} + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${maven-checkstyle-plugin.version} + + + com.puppycrawl.tools + checkstyle + ${checkstyle.version} + + + + ${basedir}/src/main/config/checkstyle.xml + false + true + true + + + + org.codehaus.mojo + versions-maven-plugin + ${versions-maven-plugin.version} + + ${project.build.directory}/outdated-dependencies.txt + file:///${basedir}/src/main/config/rules.xml + + + + + + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${maven.compiler.source} + ${maven.compiler.target} + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + ${skipTests} + + + + org.apache.maven.plugins + maven-site-plugin + ${maven-site-plugin.version} + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${maven-checkstyle-plugin.version} + + + + validate + + check + + + + + + org.codehaus.mojo + versions-maven-plugin + ${versions-maven-plugin.version} + + + validate + + display-property-updates + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven-javadoc-plugin.version} + + private + true + ${java.version} + true + + + + org.apache.maven.plugins + maven-eclipse-plugin + ${maven-eclipse-plugin.version} + + ${wtp.version} + ${wtp.contextName} + + + + org.mortbay.jetty + jetty-maven-plugin + ${jetty-maven-plugin.version} + + 5 + + ${jetty.contextpath} + + + + jetty.port + ${jetty.port} + + + + + + org.apache.maven.plugins + maven-dependency-plugin + ${maven-dependency-plugin.version} + + + package + + copy + + + + + org.mortbay.jetty + jetty-runner + ${jetty-runner.version} + jetty-runner.jar + + + + + + + + org.apache.maven.plugins + maven-war-plugin + ${maven-war-plugin.version} + + + + src/main/webapp + + *.jspf + + true + + + + + + + + + + + org.apache.maven.plugins + maven-project-info-reports-plugin + ${maven-project-info-reports-plugin.version} + + + + index + dependencies + + + + + + org.codehaus.mojo + versions-maven-plugin + ${versions-maven-plugin.version} + + + + + + property-updates-report + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven-javadoc-plugin.version} + + + html + + javadoc + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${maven-checkstyle-plugin.version} + + + + checkstyle + checkstyle-aggregate + + + + + + diff --git a/src/main/config/rules.xml b/src/main/config/rules.xml new file mode 100644 index 0000000..b69193d --- /dev/null +++ b/src/main/config/rules.xml @@ -0,0 +1,26 @@ + + + + + (?i).*Alpha(?:-?\d+)? + (?i).*a(?:-?\d+)? + (?i).*Beta(?:-?\d+)? + (?i).*-B(?:-?\d+)? + (?i).*RC(?:-?\d+)? + (?i).*CR(?:-?\d+)? + (?i).*M(?:-?\d+)? + + + + + + + + + + diff --git a/src/main/java/net/sourceforge/plantuml/servlet/DiagramResponse.java b/src/main/java/net/sourceforge/plantuml/servlet/DiagramResponse.java index b914d46..d969464 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/DiagramResponse.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/DiagramResponse.java @@ -48,41 +48,69 @@ import net.sourceforge.plantuml.version.Version; import net.sourceforge.plantuml.error.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. */ public class DiagramResponse { + /** + * {@link FileFormat} to http content type mapping. + */ + private static final Map CONTENT_TYPE; + /** + * X-Powered-By http header value included in every response by default. + */ 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(); - map.put(FileFormat.PNG, "image/png"); - map.put(FileFormat.SVG, "image/svg+xml"); - map.put(FileFormat.EPS, "application/postscript"); - map.put(FileFormat.UTXT, "text/plain;charset=UTF-8"); - map.put(FileFormat.BASE64, "text/plain; charset=x-user-defined"); - CONTENT_TYPE = Collections.unmodifiableMap(map); - } static { OptionFlags.ALLOW_INCLUDE = false; if ("true".equalsIgnoreCase(System.getenv("ALLOW_PLANTUML_INCLUDE"))) { OptionFlags.ALLOW_INCLUDE = true; } + CONTENT_TYPE = Collections.unmodifiableMap(new HashMap() {{ + put(FileFormat.PNG, "image/png"); + put(FileFormat.SVG, "image/svg+xml"); + put(FileFormat.EPS, "application/postscript"); + put(FileFormat.UTXT, "text/plain;charset=UTF-8"); + put(FileFormat.BASE64, "text/plain; charset=x-user-defined"); + }}); } - public DiagramResponse(HttpServletResponse r, FileFormat f, HttpServletRequest rq) { - response = r; - format = f; - request = rq; + /** + * Response format. + */ + private FileFormat format; + /** + * Http request. + */ + private HttpServletRequest request; + /** + * Http response. + */ + private HttpServletResponse response; + + /** + * Create new diagram response instance. + * + * @param res http response + * @param fmt target file format + * @param req http request + */ + public DiagramResponse(HttpServletResponse res, FileFormat fmt, HttpServletRequest req) { + response = res; + format = fmt; + request = req; } + /** + * Render and send a specific uml diagram. + * + * @param uml textual UML diagram(s) source + * @param idx diagram index of {@code uml} to send + * + * @throws IOException if an input or output exception occurred + */ public void sendDiagram(String uml, int idx) throws IOException { response.addHeader("Access-Control-Allow-Origin", "*"); response.setContentType(getContentType()); @@ -114,6 +142,13 @@ public class DiagramResponse { diagram.exportDiagram(response.getOutputStream(), idx, new FileFormatOption(format)); } + /** + * Is block uml unmodified? + * + * @param blockUml block uml + * + * @return true if unmodified; otherwise false + */ private boolean notModified(BlockUml blockUml) { final String ifNoneMatch = request.getHeader("If-None-Match"); final long ifModifiedSince = request.getDateHeader("If-Modified-Since"); @@ -127,7 +162,13 @@ public class DiagramResponse { return ifNoneMatch.contains(etag); } - + /** + * Produce and send the image map of the uml diagram in HTML format. + * + * @param uml textual UML diagram source + * + * @throws IOException if an input or output exception occurred + */ public void sendMap(String uml) throws IOException { response.setContentType(getContentType()); SourceStringReader reader = new SourceStringReader(uml); @@ -145,15 +186,29 @@ public class DiagramResponse { } } + /** + * Check the syntax of the diagram and send a report in TEXT format. + * + * @param uml textual UML diagram source + * + * @throws IOException if an input or output exception occurred + */ public void sendCheck(String uml) throws IOException { response.setContentType(getContentType()); SourceStringReader reader = new SourceStringReader(uml); DiagramDescription desc = reader.outputImage( - new NullOutputStream(), new FileFormatOption(FileFormat.PNG, false)); + new NullOutputStream(), + new FileFormatOption(FileFormat.PNG, false) + ); PrintWriter httpOut = response.getWriter(); httpOut.print(desc.getDescription()); } + /** + * Add default header including cache headers to response. + * + * @param blockUml response block uml + */ private void addHeaderForCache(BlockUml blockUml) { long today = System.currentTimeMillis(); // Add http headers to force the browser to cache the image @@ -177,12 +232,22 @@ public class DiagramResponse { addHeaders(response); } + /** + * Add default headers to response. + * + * @param response http response + */ public static void addHeaders(HttpServletResponse response) { response.addHeader("X-Powered-By", POWERED_BY); response.addHeader("X-Patreon", "Support us on https://plantuml.com/patreon"); response.addHeader("X-Donate", "https://plantuml.com/paypal"); } + /** + * Get response content type. + * + * @return response content type + */ private String getContentType() { return CONTENT_TYPE.get(format); } diff --git a/src/main/java/net/sourceforge/plantuml/servlet/MapServlet.java b/src/main/java/net/sourceforge/plantuml/servlet/MapServlet.java index 252b200..5097007 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/MapServlet.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/MapServlet.java @@ -32,7 +32,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.sourceforge.plantuml.FileFormat; - import net.sourceforge.plantuml.servlet.utility.UmlExtractor; /** @@ -58,6 +57,13 @@ public class MapServlet extends HttpServlet { dr = null; } + /** + * Extract UML source from URI. + * + * @param uri the complete URI as returned by `request.getRequestURI()` + * + * @return the encoded UML text + */ public String getSource(String uri) { String[] result = uri.split("/map/", 2); if (result.length != 2) { diff --git a/src/main/java/net/sourceforge/plantuml/servlet/OldProxyServlet.java b/src/main/java/net/sourceforge/plantuml/servlet/OldProxyServlet.java index 34e181d..be11ad2 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/OldProxyServlet.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/OldProxyServlet.java @@ -48,8 +48,10 @@ import net.sourceforge.plantuml.SourceStringReader; @SuppressWarnings("SERIAL") public class OldProxyServlet extends HttpServlet { + /** + * Proxy request URI regex pattern. + */ private static final Pattern PROXY_PATTERN = Pattern.compile("/\\w+/proxy/((\\d+)/)?((\\w+)/)?(https?://.*)"); - private String format; @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { @@ -64,20 +66,44 @@ public class OldProxyServlet extends HttpServlet { } String num = proxyMatcher.group(2); // Optional number of the diagram source - format = proxyMatcher.group(4); // Expected format of the generated diagram + String format = proxyMatcher.group(4); // Expected format of the generated diagram String sourceURL = proxyMatcher.group(5); - handleImageProxy(response, num, sourceURL); + handleImageProxy(response, num, format, sourceURL); } - private void handleImageProxy(HttpServletResponse response, String num, String source) throws IOException { + /** + * Handle image proxy request. + * + * @param response http response + * @param num image number/index of uml {@code source} + * @param format file format name + * @param source diagram source URL + * + * @throws IOException if an input or output exception occurred + */ + private void handleImageProxy( + HttpServletResponse response, + String num, + String format, + String source + ) throws IOException { SourceStringReader reader = new SourceStringReader(getSource(source)); int n = num == null ? 0 : Integer.parseInt(num); - FileFormat fileFormat = getOutputFormat(); + FileFormat fileFormat = getOutputFormat(format); response.addHeader("Content-Type", fileFormat.getMimeType()); reader.outputImage(response.getOutputStream(), n, new FileFormatOption(fileFormat, false)); } + /** + * Get textual diagram source from URL. + * + * @param uri diagram source URL + * + * @return textual diagram source + * + * @throws IOException if an input or output exception occurred + */ private String getSource(final String uri) throws IOException { final URL url = new URL(uri); try ( @@ -95,7 +121,15 @@ public class OldProxyServlet extends HttpServlet { } } - private FileFormat getOutputFormat() { + /** + * Get {@link FileFormat} instance from string. + * + * @param format file format name + * + * @return corresponding file format instance, + * if {@code format} is null or unknown the default {@link FileFormat#PNG} will be returned + */ + private FileFormat getOutputFormat(String format) { if (format == null) { return FileFormat.PNG; } diff --git a/src/main/java/net/sourceforge/plantuml/servlet/PlantUmlServlet.java b/src/main/java/net/sourceforge/plantuml/servlet/PlantUmlServlet.java index e40679e..5dde845 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/PlantUmlServlet.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/PlantUmlServlet.java @@ -44,7 +44,7 @@ import net.sourceforge.plantuml.code.TranscoderUtil; import net.sourceforge.plantuml.png.MetadataTag; /** - * Original idea from Achim Abeling for Confluence macro + * Original idea from Achim Abeling for Confluence macro. * * This class is the old all-in-one historic implementation of the PlantUml server. * See package.html for the new design. It's a work in progress. @@ -56,10 +56,19 @@ import net.sourceforge.plantuml.png.MetadataTag; @SuppressWarnings("SERIAL") public class PlantUmlServlet extends HttpServlet { + /** + * Default encoded uml text. + * Bob -> Alice : hello + */ private static final String DEFAULT_ENCODED_TEXT = "SyfFKj2rKt3CoKnELR1Io4ZDoSa70000"; - // Last part of the URL - public static final Pattern URL_PATTERN = Pattern.compile("^.*[^a-zA-Z0-9\\-\\_]([a-zA-Z0-9\\-\\_]+)"); + /** + * Regex pattern to fetch last part of the URL. + */ + private static final Pattern URL_PATTERN = Pattern.compile("^.*[^a-zA-Z0-9\\-\\_]([a-zA-Z0-9\\-\\_]+)"); + /** + * Regex pattern to fetch encoded uml text from an "uml" URL. + */ private static final Pattern RECOVER_UML_PATTERN = Pattern.compile("/uml/(.*)"); static { @@ -134,36 +143,75 @@ public class PlantUmlServlet extends HttpServlet { redirectNow(request, response, encoded); } + /** + * Get textual diagram source from URL. + * + * @param request http request which contains the source URL + * @param text fallback textual diagram source + * + * @return if successful textual diagram source from URL; otherwise fallback {@code text} + * + * @throws IOException if an input or output exception occurred + */ private String getTextFromUrl(HttpServletRequest request, String text) throws IOException { - String url = request.getParameter("url"); - final Matcher recoverUml = RECOVER_UML_PATTERN.matcher(request.getRequestURI().substring( - request.getContextPath().length())); + final Matcher recoverUml = RECOVER_UML_PATTERN.matcher( + request.getRequestURI().substring(request.getContextPath().length()) + ); // the URL form has been submitted if (recoverUml.matches()) { final String data = recoverUml.group(1); - text = getTranscoder().decode(data); - } else if (url != null && !url.trim().isEmpty()) { - // Catch the last part of the URL if necessary - final Matcher m1 = URL_PATTERN.matcher(url); - if (m1.find()) { - url = m1.group(1); - } - text = getTranscoder().decode(url); + return getTranscoder().decode(data); } + String url = request.getParameter("url"); + if (url != null && !url.trim().isEmpty()) { + // Catch the last part of the URL if necessary + final Matcher matcher = URL_PATTERN.matcher(url); + if (matcher.find()) { + url = matcher.group(1); + } + return getTranscoder().decode(url); + } + // fallback return text; } - private void redirectNow(HttpServletRequest request, HttpServletResponse response, String encoded) - throws IOException { + /** + * Send redirect response to encoded uml text. + * + * @param request http request + * @param response http response + * @param encoded encoded uml text + * + * @throws IOException if an input or output exception occurred + */ + private void redirectNow( + HttpServletRequest request, + HttpServletResponse response, + String encoded + ) throws IOException { final String result = request.getContextPath() + "/uml/" + encoded; response.sendRedirect(result); } + /** + * Get PlantUML transcoder. + * + * @return transcoder instance + */ private Transcoder getTranscoder() { return TranscoderUtil.getDefaultTranscoder(); } - static private HttpURLConnection getConnection(URL url) throws IOException { + /** + * Get open http connection from URL. + * + * @param url URL to open connection + * + * @return open http connection + * + * @throws IOException if an input or output exception occurred + */ + private static HttpURLConnection getConnection(URL url) throws IOException { if (url.getProtocol().startsWith("https")) { HttpsURLConnection con = (HttpsURLConnection) url.openConnection(); con.setRequestMethod("GET"); @@ -180,7 +228,16 @@ public class PlantUmlServlet extends HttpServlet { } } - static public InputStream getImage(URL url) throws IOException { + /** + * Get image input stream from URL. + * + * @param url URL to open connection + * + * @return response input stream from URL + * + * @throws IOException if an input or output exception occurred + */ + private static InputStream getImage(URL url) throws IOException { InputStream is = null; HttpURLConnection con = getConnection(url); is = con.getInputStream(); diff --git a/src/main/java/net/sourceforge/plantuml/servlet/ProxyServlet.java b/src/main/java/net/sourceforge/plantuml/servlet/ProxyServlet.java index 2d53589..7981dc0 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/ProxyServlet.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/ProxyServlet.java @@ -102,6 +102,15 @@ public class ProxyServlet extends HttpServlet { dr = null; } + /** + * Get textual uml diagram source from URL. + * + * @param url source URL + * + * @return textual uml diagram source + * + * @throws IOException if an input or output exception occurred + */ private String getSource(final URL url) throws IOException { String line; BufferedReader rd; @@ -124,6 +133,14 @@ public class ProxyServlet extends HttpServlet { return ""; } + /** + * Get {@link FileFormat} instance from string. + * + * @param format file format name + * + * @return corresponding file format instance, + * if {@code format} is null or unknown the default {@link FileFormat#PNG} will be returned + */ private FileFormat getOutputFormat(String format) { if (format == null) { return FileFormat.PNG; @@ -143,6 +160,15 @@ public class ProxyServlet extends HttpServlet { return FileFormat.PNG; } + /** + * Get open http connection from URL. + * + * @param url URL to open connection + * + * @return open http connection + * + * @throws IOException if an input or output exception occurred + */ private HttpURLConnection getConnection(final URL url) throws IOException { final HttpURLConnection con = (HttpURLConnection) url.openConnection(); //if (con instanceof HttpsURLConnection) { @@ -159,7 +185,8 @@ public class ProxyServlet extends HttpServlet { } /** - * Debug method used to dump the certificate info + * Debug method used to dump the certificate info. + * * @param con the https connection */ @SuppressWarnings("unused") diff --git a/src/main/java/net/sourceforge/plantuml/servlet/UmlDiagramService.java b/src/main/java/net/sourceforge/plantuml/servlet/UmlDiagramService.java index 650745b..dc46b10 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/UmlDiagramService.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/UmlDiagramService.java @@ -43,6 +43,11 @@ import java.util.regex.Pattern; @SuppressWarnings("SERIAL") public abstract class UmlDiagramService extends HttpServlet { + /** + * Regex pattern to fetch encoded uml text from an URL. + */ + private static final Pattern RECOVER_UML_PATTERN = Pattern.compile("/\\w+/(\\d+/)?(.*)"); + static { OptionFlags.ALLOW_INCLUDE = false; if ("true".equalsIgnoreCase(System.getenv("ALLOW_PLANTUML_INCLUDE"))) { @@ -52,7 +57,6 @@ public abstract class UmlDiagramService extends HttpServlet { @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { - // build the UML source from the compressed request parameter final String[] sourceAndIdx = getSourceAndIdx(request); final int idx = Integer.parseInt(sourceAndIdx[1]); @@ -70,7 +74,6 @@ public abstract class UmlDiagramService extends HttpServlet { @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { - // build the UML source from the compressed request parameter final String[] sourceAndIdx = getSourceAndIdx(request); final int idx = Integer.parseInt(sourceAndIdx[1]); @@ -88,13 +91,22 @@ public abstract class UmlDiagramService extends HttpServlet { doDiagramResponse(request, response, uml.toString(), idx); } + /** + * Send diagram response. + * + * @param request html request + * @param response html response + * @param uml textual UML diagram(s) source + * @param idx diagram index of {@code uml} to send + * + * @throws IOException if an input or output exception occurred + */ private void doDiagramResponse( HttpServletRequest request, HttpServletResponse response, String uml, - int idx) - throws IOException { - + int idx + ) throws IOException { // generate the response DiagramResponse dr = new DiagramResponse(response, getOutputFormat(), request); try { @@ -103,11 +115,8 @@ public abstract class UmlDiagramService extends HttpServlet { // Browser has closed the connection, so the HTTP OutputStream is closed // Silently catch the exception to avoid annoying log } - dr = null; } - private static final Pattern RECOVER_UML_PATTERN = Pattern.compile("/\\w+/(\\d+/)?(.*)"); - /** * Extracts the UML source text and its index from the HTTP request. * @@ -132,7 +141,7 @@ public abstract class UmlDiagramService extends HttpServlet { return new String[]{data, idx }; } } - return new String[]{"", "0" }; + return new String[]{"", "0"}; } /** diff --git a/src/main/java/net/sourceforge/plantuml/servlet/utility/Configuration.java b/src/main/java/net/sourceforge/plantuml/servlet/utility/Configuration.java index e35b67c..2489a06 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/utility/Configuration.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/utility/Configuration.java @@ -28,13 +28,22 @@ import java.io.InputStream; import java.util.Properties; +/** + * Shared PlantUML Server configuration. + */ public final class Configuration { + /** + * Singleton configuration instance. + */ private static Configuration instance; + /** + * Configuration properties. + */ private Properties config; /** - * Singleton constructor + * Singleton constructor. */ private Configuration() { config = new Properties(); @@ -57,7 +66,7 @@ public final class Configuration { } /** - * Get the configuration + * Get the configuration. * * @return the complete configuration */ @@ -69,7 +78,9 @@ public final class Configuration { } /** - * Get a boolean configuration value + * Get a boolean configuration value. + * + * @param key config property key * * @return true if the value is "on" */ diff --git a/src/main/java/net/sourceforge/plantuml/servlet/utility/NullOutputStream.java b/src/main/java/net/sourceforge/plantuml/servlet/utility/NullOutputStream.java index a217010..e5de2f0 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/utility/NullOutputStream.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/utility/NullOutputStream.java @@ -26,29 +26,19 @@ package net.sourceforge.plantuml.servlet.utility; import java.io.IOException; import java.io.OutputStream; +/** + * This output stream ignores everything and writes nothing. + */ public class NullOutputStream extends OutputStream { /** - * Writes to nowhere + * Writes to nowhere. + * + * @param b anything */ @Override public void write(int b) throws IOException { // Do nothing silently } - /** - * Overridden for performance reason - */ - @Override - public void write(byte[] b) throws IOException { - // Do nothing silently - } - - /** - * Overridden for performance reason - */ - @Override - public void write(byte[] b, int off, int len) throws IOException { - // Do nothing silently - } } diff --git a/src/main/java/net/sourceforge/plantuml/servlet/utility/UmlExtractor.java b/src/main/java/net/sourceforge/plantuml/servlet/utility/UmlExtractor.java index 08faaa2..49c30dd 100644 --- a/src/main/java/net/sourceforge/plantuml/servlet/utility/UmlExtractor.java +++ b/src/main/java/net/sourceforge/plantuml/servlet/utility/UmlExtractor.java @@ -35,7 +35,7 @@ import net.sourceforge.plantuml.code.TranscoderUtil; * Utility class to extract the UML source from the compressed UML source contained in the end part * of the requested URI. */ -public class UmlExtractor { +public abstract class UmlExtractor { static { OptionFlags.ALLOW_INCLUDE = false; @@ -49,6 +49,7 @@ public class UmlExtractor { * HTTP URI. * * @param source the last part of the URI containing the compressed UML + * * @return the textual UML source */ static public String getUmlSource(String source) { @@ -83,9 +84,4 @@ public class UmlExtractor { return uml; } - protected UmlExtractor() { - // prevents calls from subclass - throw new UnsupportedOperationException(); - } - } diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp index 896a4d2..8234913 100644 --- a/src/main/webapp/index.jsp +++ b/src/main/webapp/index.jsp @@ -28,9 +28,8 @@ - - - + +