mirror of
https://github.com/octoleo/plantuml-server.git
synced 2024-11-17 09:45:11 +00:00
Add metadata
Servlet
- add new servlet to get meta data from PlantUML diagram - meta data get not only be requested as text but also as json if you set the `Accept`-header to json - add `metadata` servlet tests - GET: like the Proxy where you can pass a URL which the servlet will use to fetch the diagram image - POST: file upload
This commit is contained in:
parent
09517cca92
commit
ec7b9f9b1a
@ -202,6 +202,12 @@
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-params</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.platform</groupId>
|
||||
<artifactId>junit-platform-suite-api</artifactId>
|
||||
|
6
pom.xml
6
pom.xml
@ -180,6 +180,12 @@
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-params</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.platform</groupId>
|
||||
<artifactId>junit-platform-suite-api</artifactId>
|
||||
|
@ -15,6 +15,8 @@
|
||||
<module name="LineLength">
|
||||
<property name="max" value="120" />
|
||||
<property name="tabWidth" value="4" />
|
||||
<!-- ignore java doc including links, e.g.: `* @see <a href="https://...">PlantUML Code</a>` -->
|
||||
<property name="ignorePattern" value="^\s*(\*|//).*@see\s.*?\shref=.*$"/>
|
||||
</module>
|
||||
<module name="NewlineAtEndOfFile">
|
||||
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit" />
|
||||
@ -25,6 +27,7 @@
|
||||
<property name="message" value="Line has trailing spaces." />
|
||||
</module>
|
||||
<module name="Translation" />
|
||||
<module name="SuppressWarningsFilter" />
|
||||
<module name="TreeWalker">
|
||||
<module name="ArrayTypeStyle" />
|
||||
<module name="AvoidInlineConditionals">
|
||||
@ -113,5 +116,6 @@
|
||||
<module name="VisibilityModifier" />
|
||||
<module name="WhitespaceAfter" />
|
||||
<module name="WhitespaceAround" />
|
||||
<module name="SuppressWarningsHolder" />
|
||||
</module>
|
||||
</module>
|
||||
|
@ -0,0 +1,376 @@
|
||||
/* ========================================================================
|
||||
* PlantUML : a free UML diagram generator
|
||||
* ========================================================================
|
||||
*
|
||||
* Project Info: https://plantuml.com
|
||||
*
|
||||
* This file is part of PlantUML.
|
||||
*
|
||||
* PlantUML is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* PlantUML distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*/
|
||||
package net.sourceforge.plantuml.servlet;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.annotation.MultipartConfig;
|
||||
import jakarta.servlet.http.HttpServlet;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.Part;
|
||||
import net.sourceforge.plantuml.FileFormat;
|
||||
import net.sourceforge.plantuml.code.NoPlantumlCompressionException;
|
||||
import net.sourceforge.plantuml.code.TranscoderUtil;
|
||||
import net.sourceforge.plantuml.json.JsonObject;
|
||||
import net.sourceforge.plantuml.klimt.drawing.svg.SvgGraphics;
|
||||
import net.sourceforge.plantuml.png.MetadataTag;
|
||||
|
||||
/**
|
||||
* Meta data servlet for the webapp.
|
||||
* This servlet responses with the meta data of a specific file as text report or JSON object.
|
||||
*/
|
||||
@SuppressWarnings("SERIAL")
|
||||
@MultipartConfig
|
||||
public class MetadataServlet extends HttpServlet {
|
||||
|
||||
@Override
|
||||
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
||||
request.setCharacterEncoding("UTF-8");
|
||||
final String urlString = request.getParameter("src");
|
||||
// validate URL
|
||||
final URL url = ProxyServlet.validateURL(urlString, response);
|
||||
if (url == null) {
|
||||
return; // error is already set/handled inside `validateURL`
|
||||
}
|
||||
// fetch image via URL and extract meta data from it
|
||||
final HttpURLConnection conn = ProxyServlet.getConnection(url);
|
||||
try (InputStream is = conn.getInputStream()) {
|
||||
handleRequest(request, response, is, conn.getContentType(), null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response
|
||||
) throws IOException, ServletException {
|
||||
request.setCharacterEncoding("UTF-8");
|
||||
// get image via file upload
|
||||
final Part filePart = request.getPart("diagram");
|
||||
final String filename = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // MS IE fix
|
||||
try (InputStream is = filePart.getInputStream()) {
|
||||
handleRequest(request, response, is, null, filename);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle request no matter whether GET or POST and
|
||||
* response with the PlantUML diagram image in the in the desired format if possible.
|
||||
*
|
||||
* @param request an HttpServletRequest object that contains the request the client has made of the servlet
|
||||
* @param response an HttpServletResponse object that contains the response the servlet sends to the client
|
||||
* @param is PlantUML diagram image as input stream
|
||||
* @param contentType the PlantUML diagram image content type [optional]
|
||||
* @param filename the PlantUML diagram image filename [optional
|
||||
*
|
||||
* @throws IOException if an input or output error is detected when the servlet handles the request
|
||||
*/
|
||||
private void handleRequest(
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
InputStream is,
|
||||
String contentType,
|
||||
String filename
|
||||
) throws IOException {
|
||||
final String formString = request.getParameter("format");
|
||||
final String accept = request.getHeader("Accept");
|
||||
final boolean isJsonResponse = accept != null && accept.toLowerCase().contains("json");
|
||||
// extract meta data
|
||||
// @see <a href="https://github.com/plantuml/plantuml/blob/26874fe610617738f958b7e8d012128fe621cff6/src/net/sourceforge/plantuml/Run.java#L570-L592">PlantUML Code</a>
|
||||
final FileFormat format = getImageFileFormat(formString, contentType, filename, response);
|
||||
if (format == null) {
|
||||
return; // error is already set/handled inside `getImageFileFormat`
|
||||
}
|
||||
final Metadata metadata = getMetadata(is, format, response);
|
||||
if (metadata == null) {
|
||||
return; // error is already set/handled inside `getMetadata`
|
||||
}
|
||||
response.addHeader("Access-Control-Allow-Origin", "*");
|
||||
if (isJsonResponse) {
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.getWriter().write(metadata.toJson().toString());
|
||||
} else {
|
||||
response.setContentType(FileFormat.UTXT.getMimeType());
|
||||
response.getWriter().write(metadata.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file format from the PlantUML diagram image.
|
||||
*
|
||||
* @param format image format passed by the user via the request param `format`
|
||||
* @param contentType response content type where the PlantUML diagram image is from
|
||||
* @param filename diagram image file name
|
||||
* @param response response object to `sendError` including error message
|
||||
*
|
||||
* @return PlantUML diagram image format; if unknown format return `null`
|
||||
*
|
||||
* @throws IOException `response.sendError` can result in a `IOException`
|
||||
*/
|
||||
private FileFormat getImageFileFormat(
|
||||
String format, String contentType, String filename, HttpServletResponse response
|
||||
) throws IOException {
|
||||
if (format != null && !format.isEmpty()) {
|
||||
return getImageFileFormatFromFormatString(format, response);
|
||||
}
|
||||
if (filename != null && !filename.isEmpty()) {
|
||||
final FileFormat fileFormat = getImageFileFormatFromFilenameExtension(filename);
|
||||
if (fileFormat != null) {
|
||||
return fileFormat;
|
||||
}
|
||||
}
|
||||
if (contentType != null && !contentType.isEmpty()) {
|
||||
final FileFormat fileFormat = getImageFileFormatFromContentType(contentType);
|
||||
if (fileFormat != null) {
|
||||
return fileFormat;
|
||||
}
|
||||
}
|
||||
response.sendError(
|
||||
HttpServletResponse.SC_BAD_REQUEST,
|
||||
"PlantUML image format detection failed. Please set \"format\" (format) manually."
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file format from the PlantUML diagram image based on a format string.
|
||||
*
|
||||
* @param format image format passed by the user via the request param `format`
|
||||
* @param response response object to `sendError` including error message; if `null` no error will be send
|
||||
*
|
||||
* @return PlantUML diagram image format; if unknown format return `null`
|
||||
*
|
||||
* @throws IOException `response.sendError` can result in a `IOException`
|
||||
*/
|
||||
private FileFormat getImageFileFormatFromFormatString(
|
||||
String format, HttpServletResponse response
|
||||
) throws IOException {
|
||||
switch (format.toLowerCase()) {
|
||||
case "png": return FileFormat.PNG;
|
||||
case "svg": return FileFormat.SVG;
|
||||
default:
|
||||
if (response != null) {
|
||||
response.sendError(
|
||||
HttpServletResponse.SC_BAD_REQUEST,
|
||||
"The format \"" + format + "\" is not supported for meta data extraction."
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file format from the PlantUML diagram image based on the filenames extension.
|
||||
*
|
||||
* @param filename PlantUML image file name
|
||||
*
|
||||
* @return PlantUML diagram image format; if unknown format return `null`
|
||||
*
|
||||
* @throws IOException Can not happend! Will not occur.
|
||||
*/
|
||||
private FileFormat getImageFileFormatFromFilenameExtension(String filename) throws IOException {
|
||||
int extensionPosition = filename.lastIndexOf(".");
|
||||
if (extensionPosition != -1) {
|
||||
String extension = filename.substring(extensionPosition + 1);
|
||||
return getImageFileFormatFromFormatString(extension, null);
|
||||
}
|
||||
Logger logger = Logger.getLogger("com.plantuml");
|
||||
logger.log(Level.WARNING, "File name \"{0}\" is malformed. Should be: name.extension", filename);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file format from the PlantUML diagram image based on the response content type.
|
||||
*
|
||||
* @param contentType response content type where the PlantUML diagram image is from
|
||||
*
|
||||
* @return PlantUML diagram image format; if unknown content type return `null`
|
||||
*/
|
||||
private FileFormat getImageFileFormatFromContentType(String contentType) {
|
||||
final String ct = contentType.toLowerCase();
|
||||
if (ct.contains("png")) {
|
||||
return FileFormat.PNG;
|
||||
}
|
||||
if (ct.contains("svg") || ct.contains("xml")) {
|
||||
return FileFormat.SVG;
|
||||
}
|
||||
Logger logger = Logger.getLogger("com.plantuml");
|
||||
logger.log(Level.SEVERE, "Unknown content type \"{0}\" for meta data extraction", contentType);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get meta data from PlantUML diagram image.
|
||||
*
|
||||
* @param is PlantUML diagram image input stream
|
||||
* @param format PlantUML diagram image file format
|
||||
* @param response response object to `sendError` including error message
|
||||
*
|
||||
* @return parsed meta data; on error return `null`
|
||||
*
|
||||
* @throws IOException `response.sendError` can result in a `IOException`
|
||||
*/
|
||||
private Metadata getMetadata(
|
||||
InputStream is, FileFormat format, HttpServletResponse response
|
||||
) throws IOException {
|
||||
switch (format) {
|
||||
case PNG:
|
||||
return getMetadataFromPNG(is, response);
|
||||
case SVG:
|
||||
final String svg;
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
|
||||
svg = br.lines().collect(Collectors.joining("\n"));
|
||||
}
|
||||
return getMetadataFromSVG(svg, response);
|
||||
default:
|
||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Unsupported image format.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get meta data from PNG PlantUML diagram image.
|
||||
*
|
||||
* Challenge: PNG meta data is only a single String and contains more than the PlantUML diagram.
|
||||
* PNG meta data contains:
|
||||
* 1. decoded PlantUML code
|
||||
* 2. empty line
|
||||
* 3. version information
|
||||
* Notes:
|
||||
* - in theory the meta data could contain the PlantUML `RawString` as well as the `PlainString`
|
||||
* but since both are ALWAYS identical (methods to get them are identical), one will ALWAYS dropped.
|
||||
* @see <a href="https://github.com/plantuml/plantuml/blob/26874fe610617738f958b7e8d012128fe621cff6/src/net/sourceforge/plantuml/core/UmlSource.java#L173-L189">PlantUML Code</a>
|
||||
* - version information do not contain any empty lines
|
||||
* Solution: split meta data at the last occurring empty line the result in
|
||||
* a. decoded PlantUML diagram
|
||||
* b. version information
|
||||
*
|
||||
* @param is PNG image input stream
|
||||
* @param response response object to `sendError` including error message
|
||||
*
|
||||
* @return parsed meta data; on error return `null`
|
||||
*
|
||||
* @throws IOException `response.sendError` can result in a `IOException`
|
||||
*/
|
||||
private Metadata getMetadataFromPNG(InputStream is, HttpServletResponse response) throws IOException {
|
||||
final String rawMetadata = new MetadataTag(is, "plantuml").getData();
|
||||
if (rawMetadata == null) {
|
||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "No meta data found.");
|
||||
return null;
|
||||
}
|
||||
// parse meta data
|
||||
final Metadata metadata = new Metadata(rawMetadata.trim());
|
||||
metadata.decoded = metadata.rawContent.substring(0, metadata.rawContent.lastIndexOf("\n\n"));
|
||||
metadata.encoded = TranscoderUtil.getDefaultTranscoder().encode(metadata.decoded);
|
||||
metadata.version = metadata.rawContent.substring(rawMetadata.lastIndexOf("\n\n")).trim();
|
||||
// add additionally the encoded plantuml string to raw meta data since it's missing by default
|
||||
metadata.rawContent = metadata.encoded + "\n\n" + metadata.rawContent;
|
||||
return metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get meta data from SVG PlantUML diagram image.
|
||||
* @see <a href="https://github.com/plantuml/plantuml/blob/26874fe610617738f958b7e8d012128fe621cff6/src/net/sourceforge/plantuml/Run.java#L574-L587">PlantUML Code</a>
|
||||
*
|
||||
* @param svg PlantUML digram in SVG format
|
||||
* @param response response object to `sendError` including error message
|
||||
*
|
||||
* @return parsed meta data; on error return `null`
|
||||
*
|
||||
* @throws IOException `response.sendError` can result in a `IOException`
|
||||
*/
|
||||
private Metadata getMetadataFromSVG(String svg, HttpServletResponse response) throws IOException {
|
||||
final Metadata metadata = new Metadata();
|
||||
// search for meta data start token
|
||||
final int idx = svg.lastIndexOf(SvgGraphics.META_HEADER);
|
||||
if (idx == -1) {
|
||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "No meta data found.");
|
||||
return null;
|
||||
}
|
||||
// search for meta data end token
|
||||
final String part = svg.substring(idx + SvgGraphics.META_HEADER.length());
|
||||
final int idxEnd = part.indexOf("]");
|
||||
if (idxEnd == -1) {
|
||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid meta data: No end token found.");
|
||||
return null;
|
||||
}
|
||||
// parse meta data
|
||||
metadata.encoded = part.substring(0, idxEnd);
|
||||
try {
|
||||
metadata.decoded = TranscoderUtil.getDefaultTranscoderProtected().decode(metadata.encoded);
|
||||
} catch (NoPlantumlCompressionException ex) {
|
||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid meta data: PlantUML diagram is corrupted.");
|
||||
return null;
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to store meta data.
|
||||
*/
|
||||
@SuppressWarnings("checkstyle:VisibilityModifier")
|
||||
private class Metadata {
|
||||
public String rawContent;
|
||||
public String decoded;
|
||||
public String encoded;
|
||||
public String version;
|
||||
|
||||
Metadata() { }
|
||||
Metadata(String rawMetadataContent) {
|
||||
rawContent = rawMetadataContent;
|
||||
}
|
||||
|
||||
public JsonObject toJson() {
|
||||
JsonObject metadata = new JsonObject();
|
||||
metadata.add("encoded", encoded);
|
||||
metadata.add("decoded", decoded);
|
||||
if (version != null && !version.isEmpty()) {
|
||||
metadata.add("version", version);
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (rawContent != null && !rawContent.isEmpty()) {
|
||||
return rawContent;
|
||||
}
|
||||
if (version == null || version.isEmpty()) {
|
||||
return encoded + "\n\n" + decoded;
|
||||
}
|
||||
return encoded + "\n\n" + decoded + "\n\n" + version;
|
||||
}
|
||||
}
|
||||
}
|
@ -80,25 +80,45 @@ public class ProxyServlet extends HttpServlet {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate external URL.
|
||||
*
|
||||
* @param url URL to validate
|
||||
* @param response response object to `sendError` including error message; if `null` no error will be send
|
||||
*
|
||||
* @return valid URL; otherwise `null`
|
||||
*
|
||||
* @throws IOException `response.sendError` can result in a `IOException`
|
||||
*/
|
||||
public static URL validateURL(String url, HttpServletResponse response) throws IOException {
|
||||
final URL parsedUrl;
|
||||
try {
|
||||
parsedUrl = new URL(url);
|
||||
} catch (MalformedURLException mue) {
|
||||
if (response != null) {
|
||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "URL malformed.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// Check if URL is in a forbidden format (e.g. IP-Address)
|
||||
if (forbiddenURL(url)) {
|
||||
if (response != null) {
|
||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Forbidden URL format.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return parsedUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
||||
final String fmt = request.getParameter("fmt");
|
||||
final String source = request.getParameter("src");
|
||||
final String index = request.getParameter("idx");
|
||||
|
||||
// Check if the src URL is valid
|
||||
final URL srcUrl;
|
||||
try {
|
||||
srcUrl = new URL(source);
|
||||
} catch (MalformedURLException mue) {
|
||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "URL malformed.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if URL is in a forbidden format (e.g. IP-Address)
|
||||
if (forbiddenURL(source)) {
|
||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Forbidden URL format.");
|
||||
return;
|
||||
final URL srcUrl = validateURL(source, response);
|
||||
if (srcUrl == null) {
|
||||
return; // error is already set/handled inside `validateURL`
|
||||
}
|
||||
|
||||
// generate the response
|
||||
@ -175,7 +195,7 @@ public class ProxyServlet extends HttpServlet {
|
||||
*
|
||||
* @throws IOException if an input or output exception occurred
|
||||
*/
|
||||
private HttpURLConnection getConnection(final URL url) throws IOException {
|
||||
public static HttpURLConnection getConnection(final URL url) throws IOException {
|
||||
final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod("GET");
|
||||
String token = System.getenv("HTTP_AUTHORIZATION");
|
||||
|
@ -219,6 +219,14 @@
|
||||
<url-pattern>/ui-helper/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>metadataservlet</servlet-name>
|
||||
<servlet-class>net.sourceforge.plantuml.servlet.MetadataServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>metadataservlet</servlet-name>
|
||||
<url-pattern>/metadata/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- ========================================================== -->
|
||||
<!-- Error Handler -->
|
||||
|
BIN
src/main/webapp/resource/test/bob.png
Normal file
BIN
src/main/webapp/resource/test/bob.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
2
src/main/webapp/resource/test/bob.svg
Normal file
2
src/main/webapp/resource/test/bob.svg
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="windows-1252" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="120px" preserveAspectRatio="none" style="width:113px;height:120px;background:#FFFFFF;" version="1.1" viewBox="0 0 113 120" width="113px" zoomAndPan="magnify"><defs/><g><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="26" x2="26" y1="36.2969" y2="85.4297"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5.0,5.0;" x1="82" x2="82" y1="36.2969" y2="85.4297"/><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="43" x="5" y="5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="29" x="12" y="24.9951">Bob</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="43" x="5" y="84.4297"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="29" x="12" y="104.4248">Bob</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="49" x="58" y="5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="35" x="65" y="24.9951">Alice</text><rect fill="#E2E2F0" height="30.2969" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="49" x="58" y="84.4297"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="35" x="65" y="104.4248">Alice</text><polygon fill="#181818" points="70.5,63.4297,80.5,67.4297,70.5,71.4297,74.5,67.4297" style="stroke:#181818;stroke-width:1.0;"/><line style="stroke:#181818;stroke-width:1.0;" x1="26.5" x2="76.5" y1="67.4297" y2="67.4297"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="30" x="33.5" y="62.3638">hello</text><!--SRC=[SyfFKj2rKt3CoKnELR1Io4ZDoSa70000]--></g></svg>
|
After Width: | Height: | Size: 2.0 KiB |
146
src/test/java/net/sourceforge/plantuml/servlet/TestMetadata.java
Normal file
146
src/test/java/net/sourceforge/plantuml/servlet/TestMetadata.java
Normal file
@ -0,0 +1,146 @@
|
||||
package net.sourceforge.plantuml.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
import org.apache.hc.client5.http.entity.mime.ByteArrayBody;
|
||||
import org.apache.hc.client5.http.entity.mime.ContentBody;
|
||||
import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
|
||||
import org.apache.hc.core5.http.HttpEntity;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import net.sourceforge.plantuml.json.Json;
|
||||
import net.sourceforge.plantuml.json.JsonObject;
|
||||
import net.sourceforge.plantuml.servlet.utils.TestUtils;
|
||||
import net.sourceforge.plantuml.servlet.utils.WebappTestCase;
|
||||
|
||||
|
||||
public class TestMetadata extends WebappTestCase {
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"png, false, true",
|
||||
"png, true, true",
|
||||
"svg, false, false", // Note: PlantUML SVG diagram images do not include version information
|
||||
"svg, true, false",
|
||||
})
|
||||
public void testBobAliceSampleMetadataProxy(
|
||||
String format,
|
||||
boolean isJsonResponse,
|
||||
boolean hasVersion
|
||||
) throws IOException {
|
||||
final String resourceFilename = "bob." + format;
|
||||
final URL url = new URL(getServerUrl() + "/metadata?src=" + getTestResourceUrl(resourceFilename));
|
||||
checkMetadataResponse(url, isJsonResponse, hasVersion);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"png, false, true",
|
||||
"png, true, true",
|
||||
"svg, false, false", // Note: PlantUML SVG diagram images do not include version information
|
||||
"svg, true, false",
|
||||
})
|
||||
public void testBobAliceSampleMetadataFileUpload(
|
||||
String format,
|
||||
boolean isJsonResponse,
|
||||
boolean hasVersion
|
||||
) throws IOException {
|
||||
// build file upload request entity
|
||||
final String resourceFilename = "bob." + format;
|
||||
final URL resourceUrl = new URL(getTestResourceUrl(resourceFilename));
|
||||
final byte[] resource = getContentAsBytes(resourceUrl);
|
||||
final ContentBody contentPart = new ByteArrayBody(resource, resourceFilename);
|
||||
final MultipartEntityBuilder requestBuilder = MultipartEntityBuilder.create();
|
||||
requestBuilder.addPart("diagram", contentPart);
|
||||
HttpEntity requestEntity = requestBuilder.build();
|
||||
// send request including file
|
||||
final URL url = new URL(getServerUrl() + "/metadata");
|
||||
final HttpURLConnection conn = (HttpURLConnection)url.openConnection();
|
||||
conn.setUseCaches(false);
|
||||
conn.setDoInput(true);
|
||||
conn.setDoOutput(true);
|
||||
conn.setRequestMethod("POST");
|
||||
if (isJsonResponse) {
|
||||
conn.setRequestProperty("Accept", "application/json");
|
||||
}
|
||||
conn.setRequestProperty("Cache-Control", "no-cache");
|
||||
conn.setRequestProperty("Connection", "Keep-Alive");
|
||||
conn.addRequestProperty("Content-length", Long.toString(requestEntity.getContentLength()));
|
||||
conn.setRequestProperty("Content-Type", requestEntity.getContentType());
|
||||
try (OutputStream out = conn.getOutputStream()) {
|
||||
requestEntity.writeTo(out);
|
||||
}
|
||||
// check response
|
||||
checkMetadataResponse(conn, isJsonResponse, hasVersion);
|
||||
}
|
||||
|
||||
private void checkMetadataResponse(
|
||||
URL url,
|
||||
boolean isJsonResponse,
|
||||
boolean hasVersion
|
||||
) throws IOException {
|
||||
final HttpURLConnection conn = (HttpURLConnection)url.openConnection();
|
||||
if (isJsonResponse) {
|
||||
conn.setRequestProperty("Accept", "application/json");
|
||||
}
|
||||
checkMetadataResponse(conn, isJsonResponse, hasVersion);
|
||||
}
|
||||
|
||||
private void checkMetadataResponse(
|
||||
HttpURLConnection conn,
|
||||
boolean isJsonResponse,
|
||||
boolean hasVersion
|
||||
) throws IOException {
|
||||
// Verifies HTTP status code and the Content-Type
|
||||
Assertions.assertEquals(200, conn.getResponseCode(), "Bad HTTP status received");
|
||||
if (isJsonResponse) {
|
||||
Assertions.assertEquals(
|
||||
"application/json;charset=utf-8",
|
||||
conn.getContentType().toLowerCase(),
|
||||
"Response content type is not JSON or UTF-8"
|
||||
);
|
||||
} else {
|
||||
Assertions.assertEquals(
|
||||
"text/plain;charset=utf-8",
|
||||
conn.getContentType().toLowerCase(),
|
||||
"Response content type is not plain text or UTF-8"
|
||||
);
|
||||
}
|
||||
// Get and verify the content
|
||||
final String content = getContentText(conn);
|
||||
if (isJsonResponse) {
|
||||
checkMetadataContent(Json.parse(content).asObject(), hasVersion);
|
||||
} else {
|
||||
checkMetadataContent(content, hasVersion);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkMetadataContent(String metadata, boolean hasVersion) {
|
||||
Assertions.assertTrue(
|
||||
metadata.contains(TestUtils.SEQBOB),
|
||||
"Meta data does not contain encoded PlantUML diagram code"
|
||||
);
|
||||
Assertions.assertTrue(
|
||||
metadata.contains(TestUtils.SEQBOBCODE),
|
||||
"Meta data does not contain decoded PlantUML diagram code"
|
||||
);
|
||||
if (hasVersion) {
|
||||
Assertions.assertTrue(
|
||||
metadata.contains("PlantUML version"),
|
||||
"Meta data does not contain PlantUML version"
|
||||
);
|
||||
}
|
||||
}
|
||||
private void checkMetadataContent(JsonObject metadata, boolean hasVersion) {
|
||||
Assertions.assertEquals(TestUtils.SEQBOB, metadata.get("encoded").asString());
|
||||
Assertions.assertEquals(TestUtils.SEQBOBCODE, metadata.get("decoded").asString());
|
||||
if (hasVersion) {
|
||||
Assertions.assertNotNull(metadata.get("version").asString());
|
||||
}
|
||||
}
|
||||
}
|
@ -12,12 +12,14 @@ import net.sourceforge.plantuml.servlet.utils.WebappTestCase;
|
||||
|
||||
public class TestOldProxy extends WebappTestCase {
|
||||
|
||||
private static final String TEST_RESOURCE = "test2diagrams.txt";
|
||||
|
||||
/**
|
||||
* Verifies the proxified reception of the default Bob and Alice diagram
|
||||
*/
|
||||
@Test
|
||||
public void testDefaultProxy() throws IOException {
|
||||
final URL url = new URL(getServerUrl() + "/proxy/" + getTestDiagramUrl());
|
||||
final URL url = new URL(getServerUrl() + "/proxy/" + getTestResourceUrl(TEST_RESOURCE));
|
||||
final HttpURLConnection conn = (HttpURLConnection)url.openConnection();
|
||||
// Analyze response
|
||||
// Verifies HTTP status code and the Content-Type
|
||||
@ -39,7 +41,7 @@ public class TestOldProxy extends WebappTestCase {
|
||||
*/
|
||||
@Test
|
||||
public void testProxyWithFormat() throws IOException {
|
||||
final URL url = new URL(getServerUrl() + "/proxy/svg/" + getTestDiagramUrl());
|
||||
final URL url = new URL(getServerUrl() + "/proxy/svg/" + getTestResourceUrl(TEST_RESOURCE));
|
||||
final HttpURLConnection conn = (HttpURLConnection)url.openConnection();
|
||||
// Analyze response
|
||||
// Verifies HTTP status code and the Content-Type
|
||||
|
@ -12,12 +12,14 @@ import net.sourceforge.plantuml.servlet.utils.WebappTestCase;
|
||||
|
||||
public class TestProxy extends WebappTestCase {
|
||||
|
||||
private static final String TEST_RESOURCE = "test2diagrams.txt";
|
||||
|
||||
/**
|
||||
* Verifies the proxified reception of the default Bob and Alice diagram
|
||||
*/
|
||||
@Test
|
||||
public void testDefaultProxy() throws IOException {
|
||||
final URL url = new URL(getServerUrl() + "/proxy?src=" + getTestDiagramUrl());
|
||||
final URL url = new URL(getServerUrl() + "/proxy?src=" + getTestResourceUrl(TEST_RESOURCE));
|
||||
final HttpURLConnection conn = (HttpURLConnection)url.openConnection();
|
||||
// Analyze response
|
||||
// Verifies HTTP status code and the Content-Type
|
||||
@ -39,7 +41,7 @@ public class TestProxy extends WebappTestCase {
|
||||
*/
|
||||
@Test
|
||||
public void testProxyWithFormat() throws IOException {
|
||||
final URL url = new URL(getServerUrl() + "/proxy?fmt=svg&src=" + getTestDiagramUrl());
|
||||
final URL url = new URL(getServerUrl() + "/proxy?fmt=svg&src=" + getTestResourceUrl(TEST_RESOURCE));
|
||||
final HttpURLConnection conn = (HttpURLConnection)url.openConnection();
|
||||
// Analyze response
|
||||
// Verifies HTTP status code and the Content-Type
|
||||
@ -61,7 +63,7 @@ public class TestProxy extends WebappTestCase {
|
||||
*/
|
||||
@Test
|
||||
public void testProxyWithFormatIdx0() throws IOException {
|
||||
final URL url = new URL(getServerUrl() + "/proxy?fmt=svg&idx=0&src=" + getTestDiagramUrl());
|
||||
final URL url = new URL(getServerUrl() + "/proxy?fmt=svg&idx=0&src=" + getTestResourceUrl(TEST_RESOURCE));
|
||||
final HttpURLConnection conn = (HttpURLConnection)url.openConnection();
|
||||
// Analyze response
|
||||
// Verifies HTTP status code and the Content-Type
|
||||
@ -83,7 +85,7 @@ public class TestProxy extends WebappTestCase {
|
||||
*/
|
||||
@Test
|
||||
public void testProxyWithFormatIdx1() throws IOException {
|
||||
final URL url = new URL(getServerUrl() + "/proxy?fmt=svg&idx=1&src=" + getTestDiagramUrl());
|
||||
final URL url = new URL(getServerUrl() + "/proxy?fmt=svg&idx=1&src=" + getTestResourceUrl(TEST_RESOURCE));
|
||||
final HttpURLConnection conn = (HttpURLConnection)url.openConnection();
|
||||
// Analyze response
|
||||
// Verifies HTTP status code and the Content-Type
|
||||
|
@ -50,10 +50,10 @@ public abstract class WebappTestCase {
|
||||
return serverUtils.getServerUrl();
|
||||
}
|
||||
|
||||
public String getTestDiagramUrl() {
|
||||
public String getTestResourceUrl(String resource) {
|
||||
// NOTE: [Old]ProxyServlet.forbiddenURL do not allow URL with IP-Addresses or localhost.
|
||||
String serverUrl = getServerUrl().replace("/localhost", "/test.localhost");
|
||||
return serverUrl + "/resource/test2diagrams.txt";
|
||||
return serverUrl + "/resource/test/" + resource;
|
||||
}
|
||||
|
||||
public String getContentText(final URL url) throws IOException {
|
||||
|
Loading…
Reference in New Issue
Block a user