1
0
mirror of https://github.com/octoleo/plantuml-server.git synced 2024-06-01 08:00:48 +00:00

Compare commits

...

174 Commits

Author SHA1 Message Date
Arnaud Roques
da7e041070 chore: version 1.2024.5 2024-05-26 13:08:32 +02:00
Arnaud Roques
2680086893 fix URL 2024-05-14 18:30:09 +02:00
Arnaud Roques
eafc9c442c chore: version 1.2024.4 2024-04-06 17:33:37 +02:00
Arnaud Roques
8c532d4caa chore: version 1.2024.3 2024-02-15 23:07:44 +01:00
Arnaud Roques
bba12d9c3c chore: version 1.2024.2 2024-02-14 19:50:38 +01:00
Arnaud Roques
449e4d8ae4 chore: version 1.2024.1 2024-02-08 23:32:44 +01:00
Arnaud Roques
861b034ebc chore: version 1.2024.0 2024-02-08 21:59:52 +01:00
Tibor Casteleijn
334f039e90 Change image tag from jetty-local to jetty 2024-01-12 10:42:36 +01:00
Arnaud Roques
c127b4c466 chore: version 1.2023.13 2023-12-12 19:44:59 +01:00
Michele Adduci
74eb159a36 Updated Jetty and JRE in Docker, fixed Alpine build 2023-11-20 08:30:50 +01:00
Arnaud Roques
0514103cc1 chore: version 1.2023.12 2023-10-22 00:30:22 +02:00
HeinrichAD
46077bf5b7
Set java.awt.headless to true due to Windows 11 issues (#312)
* set java.awt.headless to true due to Windows 11 issues

* do not force headles mode but us it as default
2023-09-06 21:23:55 +02:00
HeinrichAD
608b58b3e0 add the same support to maps
sendMap should work similar to sendDiagram w.r.t. diagram source selection
2023-07-19 23:08:13 +02:00
HeinrichAD
5859c3b427 support diagrams without startuml, enduml
Add support for diagram without @startuml and @enduml.
2023-07-19 23:08:13 +02:00
Arnaud Roques
af8f7d0c33 v1.2023.10 2023-07-19 18:29:41 +02:00
HeinrichAD
d495c13e6e
Merge pull request #305 from HeinrichAD/empty-commit
Close solved issues after re-evaluation
2023-07-15 22:28:58 +02:00
HeinrichAD
e484f532d4 empty commit 2023-07-15 22:14:08 +02:00
HeinrichAD
f03bb68407 add jetty "host" property
Add the posibility to set the network interface the Jetty server connector binds to as an IP address or a hostname. If null or 0.0.0.0, then its automatically binds to all interfaces.

Currently, the sercer connector uses 0.0.0.0 as fallback and always binds to all interfaces.

References: <https://eclipse.dev/jetty/javadoc/jetty-11/org/eclipse/jetty/server/AbstractNetworkConnector.html#getHost()>
2023-07-11 22:44:51 +02:00
Kálmán Vince
294b2a2418 Add diagram indexing support for all formats 2023-06-20 21:12:14 +02:00
HeinrichAD
45994f81bf remove unused import OptionFlags 2023-06-13 22:57:07 +02:00
HeinrichAD
4202730c11 remove ALLOW_PLANTUML_INCLUDE + update to 1.2023.9
- update Plantuml to v1.2023.9
- PlantUML core removed `OptionFlags.ALLOW_INCLUDE` and uses now the `PLANTUML_SECURITY_PROFILE`
2023-06-13 22:57:07 +02:00
HeinrichAD
09a7ce4973 add security features + java property support
- set `ALLOW_PLANTUML_INCLUDE` only once and decentralized inside the `DiagramResponse` class and call this init method after initializing the server
- set `PLANTUML_SECURITY_PROFILE` to `INTERNET` by default (BREAKING CHANGES)
- add possibility to set PlantUML system properties over a file with `PLANTUML_PROPERTY_FILE`
- adjust documentation
- add "Breaking changes" hint to README
2023-06-13 22:57:07 +02:00
HeinrichAD
5fa6cbc82f catch 400 response for broken diagrams 2023-06-12 22:57:06 +02:00
HeinrichAD
178384370f switch from fop to fop-core package 2023-06-11 20:55:03 +02:00
HeinrichAD
8d7c681ae4 copy pom.parent.xml to docker container 2023-06-09 17:31:19 +02:00
HeinrichAD
98dfd1763a create parent pom to improve the maintanace 2023-06-09 17:31:19 +02:00
Florian
f6fa448ed8 add doc: how to add fonts
Add an example about how to add additional fonts indside the PlantUML docker container.
2023-05-26 10:19:40 +02:00
The-Lum
303976279f doc: fix some minor issues (links, order, shortcut)
- [x] Add shortcut (`Ctrl+,`) for Setting dialog on the doc
- [x] Add `Editor Watcher Timeout` and `Editor Options`
- [x] Rearange level of titles and order of links
2023-05-24 16:32:16 +02:00
The-Lum
be95ece466 doc: fix some minor issues
- [x] fix README link to docs
- [x] rename `Alice and Bob` to `First example: "Alice and Bob"`
- [x] add `Import/Export`link
- [x] add `<kbd>` tag on `Ctrl+S`
2023-05-23 23:46:58 +02:00
Arnaud Roques
4cbefc3dcc Import version 1.2023.8 2023-05-22 18:03:58 +02:00
Florian
155250286f Add dependency tests
- add `testGraphviz`: request `version` as AsciiArt and search for
  "Installation seems OK. File generation OK"
- irgnore `testGraphviz` inside github workflow tests except for the
  docker container tests, since only in the docker container case
  graphviz is available
- add `testMonacoEditorWebJar`: request `loader.js` and check if the
  server answers with status code 200
- add a note inside the pom where the webjar versions are hard coded
  inside the project
2023-05-19 23:12:22 +02:00
Florian
478ef3bce7 front-end code refactoring
Since the front-end received much more code (features) than first expected, the files became much too large. For this reason, the JS and CSS code has now been split by component and thus into several small files. However, since there are now many small files, a JS and CSS bundle tool had to come :D.
2023-05-19 13:55:18 +02:00
Arnaud Roques
080cbaada9 chore: fix typo
https://github.com/plantuml/plantuml-server/issues/290
2023-05-17 19:13:46 +02:00
Arnaud Roques
6c2bae2f8a chore: update to version 1.2023.7 2023-05-15 18:38:27 +02:00
Florian
6ca582fcb7 add import and export diagram
- export diagram
  * add a diagram export dialog where you can choose the file name
    and download type (code, png, pdf, ...)
  * set default download type to code
  * open file save dialog via menu or Ctrl+S (or Meta+S for Mac)
- import diagram
  * similar to [draw.io](https://app.diagrams.net)
  * open a PlantUML diagram image, use metat data to get diagram code
    and load this diagram (Note: meta data is currently only supported
    by PNG and SVG diagram files)
  * support drag&drop
  * add diagram import dialog
- since three are now multiple options/action -> create a little
  editor menu
- add documentation (including gif examples)
2023-05-14 11:22:44 +02:00
Florian
ec7b9f9b1a 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
2023-05-11 21:34:32 +02:00
Florian
09517cca92 add emoji auto completion (suggestion)
- typing `<:` will start the emoji auto complition inside the plantuml editor
- for the sake of simplicity the emoji preview of the completion documentation will fetch the image from the original github repository (not plantuml). The reason is that the images (SVGs) inside plantuml have sometimes removed their svg tag, hence it's difficult to set the correct rendering size.
- expand auto completion (suggestion) documentation by default
- add emoji example GIF and documentation
- set charset to utf-8 for each website
- refactor JSON creation inside UI Helper
2023-05-10 22:10:58 +02:00
Florian
6538be2047 fix: PDF servlet
Since the update of `bastik` the PDF servlet didn't work anymore.

Problems:
- No PDF UnitTest existed
- Pom dependency `org.apache.xmlgraphics.batik-all` change nearly every dependency to `optional` starting with version `1.15`, hence some important dependencies like the SVG converter were missing
- `DiagramResponse.CONTENT_TYPE` had no mime type value for PDF

Changes:
- add PDF UnitTest
- remove `batik-all` dependency and only include the dependencies PlantUML requires for the PDF generation:
  * batik-dom
  * batik-svgrasterizer (includes batik-dom)
  * batik-svggen
  * fop
- remove own `DiagramResponse.CONTENT_TYPE` mapping and use `FileFormat.getMimeType()`
2023-05-09 19:45:21 +02:00
Florian
a6e69b33eb docker: use always latest Graphviz release
This will change the Graphviz behaviour inside the docker container.
Until now it was always a fixed Graphviz version inside the Dockerfile.
With these changes the docker container will always use the latest Graphviz release if the version was not manually set via the docker build argument `GRAPHVIZ_VERSION`.

If not set as build arg fetch latest version of Graphviz based on their release name by default.
An alternative would be to use `tag_name`.

Also, to avoid the overhead of getting all kinds of relase data, GitLabs GraphQL interface could be used.
```bash
curl -s \
  -H "Content-Type:application/json" \
  -d '{"query": "{project(fullPath:\"graphviz/graphviz\"){releases(first:5,sort:RELEASED_AT_DESC){nodes{name}}}}"}' \
  "https://gitlab.com/api/graphql" \
| jq -r '.data.project.releases.nodes[].name' | sort -V -r | head -n 1
```
In GraphQL the alternative to `name` would be `tagName`.
2023-05-09 09:34:27 +02:00
Florian
f0d78a146b fix mobile view
- add viewport to header (otherwise smartphones will render the desktop version)
- set focus to the code editor after loading the page
- fix some android dark mode theme issues by settings clear default for the light theme as well
2023-05-09 09:33:57 +02:00
Florian
5d7c4e1a03 update docker base images and reduce size
There were same base image and naming changes for the jetty and tomcat images plantuml-server uses => plantuml-server uses a pretty old and vulnerable images.

- update base images for jetty and tomcat
- add a alpine version for jetty (not tomcat has no official alpine version)
- add autoremove and clean commands after graphviz installation. This reduces the images by about ~280 MB
2023-05-05 17:33:39 -04:00
Florian
323aad1525 JDK8 problem checkstyle
no JDK8 support starting version 10.0.0
https://checkstyle.org/releasenotes.html#Release_10.0
2023-05-05 17:33:39 -04:00
Florian
e5d11fb89a update all dependencies (maven artifacts)
- PDF dependency was missing in the pom file the JDK8
  * We should think about creating a parent pom - in that case all plantuml dependencies could be in the parent pom and we would only the mantain one pom file.
    (It is also possible to drop the Java 8 support.)
  * Why do we not have any PDF tests?
- add rule to ignore version update hint with `-dev` followed by a dot and date (e.g. `0.37.0-dev.20230308`)
- migration from JUnit4 to JUnit5
2023-05-05 17:33:39 -04:00
Florian
e6566b58bd revert missing tests + small fixes
- revert the 4 missing tests, e.g. proxy test from commit 20468f5
- add virtual host name `test.localhost` to embedded jetty server
  (JUnit Tests) since localhost and IP-Addresses are no longer
  supported by the proxy and use this address inside proxy `src`
- add `test-localhost` support for the docker tests. To support
  this the docker hostname need to be set to test.localhost by:
  `--hostname=test.localhost` (only for the docker tests)
- proxy: add file format support for PDF
- proxy: add error messages on "bad request" response
- proxy: remove dead code
- old proxy: add error messages on "bad request" response
- fix incorrect README link to docs
- add `HTTP_PROXY_READ_TIMEOUT` option -- close #240
2023-05-04 18:30:58 -04:00
Florian
ed49010303 add documentation 2023-05-03 13:23:47 -04:00
Florian
f727c6dd13 frontend 2.0: initial version
- auto refresh function
- light and dark theme
- monaco editor (vscode) with "apex" as syntax highlighting language
  * apex seems to work quite fine (better than no highlighting)
  * future possibility: own plantuml language syntax support
  * future possibility: autocomplete (to much work but maybe partial)
    - implemented example for `!theme ...`
    - implemented example for `<&icon>`
  * future possibility: code validation
    - implemented example for `@start...` and `@end...`:
      * should be the first or last command
      * should be of the some type (e.g. `@startyaml` and @endyaml)
      * should be used exactly once per document/diagram
- editor and preview is splitable into two windows like the
  "Extract window" functionality on
  (plantuml.com)[https://www.plantuml.com/plantuml]
- multi index / multi paging diagram support
- diagram can be displayed/rended as PNG, SVG, ASCII Art or PDF
- Ctrl+s download the PlantUML Code as code file (diagram.puml)
- Ctrl+, opens the settings and Esc closes the settings
2023-05-03 13:23:47 -04:00
Florian
763976abdd add servlet to encode and decode diagrams
.
2023-04-18 23:07:59 +02:00
Arnaud Roques
3ef176edae chore: update to 1.2023.6 2023-04-18 22:46:32 +02:00
Marco Beelen
385fa1274a feat: Keeps curl to support healthchecks
Closes #220
Closes #273
2023-04-12 12:14:35 +02:00
Florian Greinacher
972d136665 fix: add more missing libs 2023-04-11 19:26:09 +02:00
Florian Greinacher
329aae7fc6 fix: add missing build dependency for graphviz 2023-04-11 13:46:55 +02:00
Arnaud Roques
ebece93726 Update to version 1.2023.5 2023-04-10 16:26:56 +02:00
Florian Greinacher
8a9825395a fix: use up-to-date graphviz version in tomcat image 2023-03-29 19:36:33 +02:00
Florian Greinacher
df081c20a4 refactor: isolate build 2023-03-29 15:15:04 +02:00
Florian Greinacher
4137d8460e fix: use up-to-date graphviz version 2023-03-29 15:15:04 +02:00
Eric Vantillard
a627da7a1a chore: copy issue templates from plantuml repository 2023-03-28 14:05:19 +02:00
Florian Heinrich
a9bd29a91d Fix workflows deprecated warnings.
- The `set-output` command is deprecated and will be disabled soon. Please upgrade to using Environment Files. For more information see: https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
- Node.js 12 actions are deprecated. Please update the following actions to use Node.js 16: actions/checkout@v2, actions/setup-java@v2, actions/upload-artifact@v2, actions/download-artifact@v2. For more information see: https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/
2023-03-21 14:25:56 +01:00
Florian Heinrich
638724925e Refactoring relative paths PR#209.
- use html `base` tag containing the context path once instead inside
every single URL/link.
- update and enhance `nginx-contextpath` example
- export javascript code into separated file
- Add TODO note to javascript clipboard check (from PR#250) since
Firefox and Safari do not support the current implementation
2023-03-21 14:14:09 +01:00
Artur Propp
1245b15e01 Add explicit VOLUME instruction for jetty image
Set a mount point for read-only root file system configurations.
Without the explicit mount point definition in the image, we were facing
issues on Amazon ECS, because the directory got mounted with different
ownership (root) and mode (0755).

For reference please see also:
https://docs.aws.amazon.com/AmazonECS/latest/developerguide/bind-mounts.html#bind-mount-considerations
2023-03-08 17:30:00 +01:00
Florian
99f85c0c9b Use relative paths as far as possible.
Switch from absolute paths `hostpath` to relative paths `contextpath`.
Unfortunately, for the url input javascript is necessary to resolve the
relative url.
Also see Issue #205.
2023-02-24 09:26:48 +01:00
Joel Pearson
4a5e204e16 Adding PDF support #130 2023-02-17 19:43:59 +01:00
Arnaud Roques
8cb5ca0daf Version 1.2023.1 2023-01-29 23:48:19 +01:00
Florian Greinacher
afd8bbcceb feat: support deep base URLs 2023-01-26 10:29:03 +01:00
Arnaud Roques
6d90304fd7 Restore https://github.com/plantuml/plantuml-server/pull/251 2023-01-15 18:19:07 +01:00
Arnaud Roques
f62ba44e7d Restore https://github.com/plantuml/plantuml-server/pull/250 2023-01-15 18:08:07 +01:00
Arnaud Roques
823b506900 Import version 1.2023.0 2023-01-12 20:01:23 +01:00
Arnaud Roques
b458bfad19 Version 1.2022.14 2022-12-07 22:51:38 +01:00
Arnaud Roques
a7a5b91933 Temporary remove tests 2022-12-06 19:20:32 +01:00
Arnaud Roques
20468f5bd9 Temporary remove tests 2022-12-06 19:18:43 +01:00
Arnaud Roques
10dd88714a test 2022-12-06 19:12:57 +01:00
Arnaud Roques
df9c10604a improve tests 2022-12-06 19:06:00 +01:00
Arnaud Roques
0154160c7d Improve tests 2022-12-06 18:52:30 +01:00
Arnaud Roques
4d65def8bb Improve proxy management 2022-12-06 18:42:54 +01:00
Arnaud Roques
efd53664f2 Import version 1.2022.13 2022-11-20 10:37:51 +01:00
Hans
345e996673 fix typo 2022-11-07 13:00:28 +01:00
Hans
dcd4436fcf add openshift permission 2022-11-07 13:00:28 +01:00
Hans
7285ce1cc8 Update Dockerfile.jetty
change user
2022-11-07 13:00:28 +01:00
Hans
a0ed47b51c add openshift permission 2022-11-07 13:00:28 +01:00
Arnaud Roques
052a7ea96f Import version 1.2022.12 2022-11-05 13:05:40 +01:00
Arnaud Roques
b8cb1e2ff0 Version 1.2022.7 2022-08-23 19:20:37 +02:00
Thomas Mons
cf717eff0c Introduced parameter PLANTUML_CONFIG_FILE, that allows to specify a PlantUML config file. 2022-07-29 12:49:22 +02:00
Markus Opolka
820fcca9ac Add simple Kubernetes example 2022-07-21 15:22:50 +02:00
aadrian
2d011e233e The ProxyServlet should handle maps too. 2022-06-29 23:35:41 +02:00
aadrian
dcc06f9afc CORS should work for imagemaps too. 2022-06-29 23:35:41 +02:00
aadrian
4c76f0389d ignore IntelliJ too. 2022-06-29 23:35:41 +02:00
Arnaud Roques
dd028e9579 Import version 1.2022.6 2022-06-22 09:42:33 +02:00
Arnaud Roques
94678b720e Import version 1.2022.5 2022-05-26 10:15:44 +02:00
Wolfgang Werner
b5c21c76b6 Remove note about building .war before the docker image
Both Dockerfiles also build the war as part of a multi stage build, so I guess that comment is obsolete.
2022-04-27 15:47:20 +02:00
oholimoli
ca3e9312b3 fixed nginx-contextpath example 2022-04-15 17:16:28 +02:00
Arnaud Roques
ba6af87b2d Import version 1.2022.4 2022-04-10 23:09:48 +02:00
Arnaud Roques
249163e149 Import version 1.2022.3 2022-03-30 17:52:44 +02:00
Arnaud Roques
0330b7c4a2 Import version 1.2022.2 2022-03-07 22:36:31 +01:00
Arnaud Roques
cebca916fe Import version 1.2022.2 2022-03-07 21:57:53 +01:00
Nikolai Emil Damm
d5ef062af4 Fix 2022-03-07 12:45:59 +01:00
Nikolai Emil Damm
74f35846a7 Update main.yml 2022-03-07 12:45:59 +01:00
Nikolai Emil Damm
e83dfa2b3a Update main.yml 2022-03-07 12:45:59 +01:00
Nikolai Emil Damm
8c45a7b106 Update main.yml 2022-03-07 12:45:59 +01:00
PlantUML
f14337933d
Update README.md 2022-01-25 10:34:34 +01:00
Arnaud Roques
c94711c2d5 code formatting 2022-01-22 13:08:43 +01:00
Arnaud Roques
b995dcdb61 Code formatting 2022-01-22 13:07:01 +01:00
Arnaud Roques
494dfba063 textarea improvement 2022-01-22 13:03:15 +01:00
PlantUML
c8954cbe4a
Migration to https://plantuml.io 2022-01-15 12:20:55 +01:00
Arnaud Roques
5e0ccaf328 Import version 1.2022.0 2022-01-12 19:43:56 +01:00
PlantUML
86bb70d079
Add sponsor badge 2022-01-06 11:09:29 +01:00
Florian
da290a15fe add nginx reverse proxy examples 2021-12-26 23:48:51 +01:00
PlantUML
12224aa16e
Add reverse-proxy documentation 2021-12-25 18:05:29 +01:00
PlantUML
c5f088e50a
About vulnerability 2021-12-14 11:54:53 +01:00
Arnaud Roques
54016d325d Fully remove log4j use 2021-12-12 15:01:14 +01:00
PlantUML
7b0022de44
Adding peak rate 2021-12-09 21:40:54 +01:00
Florian
32e05b62bd add jetty and tomcat recommendations 2021-12-09 13:50:34 +01:00
Arnaud Roques
e5cb82ef2c Import version 1.2021.16 2021-12-08 21:49:32 +01:00
Arnaud Roques
7c578c482f Import version 1.2021.15 2021-11-30 23:33:33 +01:00
PlantUML
422ebe1792
Add counter 2021-11-17 23:51:13 +01:00
Arnaud Roques
67fbe35fc3 Add FUNDING.yml 2021-11-17 19:09:35 +01:00
PlantUML
807ca66fe1
Remove non-working badge 2021-11-16 23:27:05 +01:00
Arnaud Roques
d99366a33d Import version 1.2021.14 2021-11-12 18:36:59 +01:00
Steve Hipwell
1580aa2b6f chore: merge main actions back to single workflow
Signed-off-by: Steve Hipwell <steve.hipwell@gmail.com>
2021-11-12 16:52:29 +01:00
Arnaud Roques
aea068abed Update version 2021-11-11 12:19:51 +01:00
Florian
3763ee737e Improve multipage (index) handling 2021-10-18 15:40:40 +02:00
Florian
db31315485 give "mvn jetty:run" more start up time 2021-10-17 18:46:03 +02:00
Florian
9cb9cec6ca update jetty and tomcat to latest version 2021-10-17 18:46:03 +02:00
Mingmin Xu
b1b7dfb84b enable vertical resize for input text area 2021-10-13 23:54:06 +02:00
Florian
deda3c2256 update + restructure pom and add missing javadoc 2021-10-13 12:25:18 +02:00
Florian
098e630a28 update and fix checkstyle and javadoc plugins 2021-10-11 19:22:18 +02:00
Florian
8d5be87f03 update junit test classes and there dependencies 2021-10-11 18:43:18 +02:00
Florian
221af78afc improve docker handling 2021-10-11 18:39:11 +02:00
Arnaud Roques
5190a72cc8 Import version 1.2021.12 2021-10-05 19:12:48 +02:00
Steve Hipwell
a7789025eb Refactor GitHub action 2021-10-04 14:13:16 +02:00
Mubarak Imam
61cbf1d3e5 change CI trigger to tag creation 2021-09-11 00:31:27 +02:00
Mubarak Imam
eee8ca6084 use github actions CI
- remove travis-ci.yml
- remove comments from main workflow
2021-09-11 00:31:27 +02:00
Mubarak Imam
f4c55bb171 added docker build 2021-09-11 00:31:27 +02:00
Mubarak Imam
eb38f39419 testing binary releases 2021-09-10 19:22:38 +02:00
Arnaud Roques
285d5ebfdf Import version 1.2021.10 2021-08-30 23:08:39 +02:00
Arnaud Roques
6487acdde0 Import version 1.2021.9 2021-07-26 17:21:38 +02:00
Arnaud Roques
32b8d60bf1 Import version 1.2021.8 2021-06-27 18:53:08 +02:00
Florian Heinrich
6cb587e156 Use hostpath consistently. 2021-06-01 11:43:47 +02:00
Arnaud Roques
755b62c885 Import version 1.2021.7 2021-05-24 12:09:03 +02:00
Arnaud Roques
c92c4de36a Import version 1.2021.6 2021-05-14 21:25:38 +02:00
Jackie Weng
3c217b2de1 Update README with read-only container instruction 2021-05-12 23:32:24 +02:00
Jackie Weng
f08db467c8 Made jetty container support read-only file system 2021-05-12 23:32:24 +02:00
Arnaud Roques
d4d00ca79e Import version 1.2021.5 2021-04-26 08:47:29 +02:00
Peter Dave Hello
04af2a25d2 Remove apt-get clean in Dockerfile.{jetty,tomcat}
Also manually cleaned up `/var/lib/apt/lists` in original commits but
got conflict with #97, then removed.

Ref:
https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

> In addition, when you clean up the apt cache by removing
> /var/lib/apt/lists it reduces the image size, since the apt cache is not
> stored in a layer.

> Official Debian and Ubuntu images automatically run apt-get clean,
> so explicit invocation is not required.
2021-04-23 22:25:45 +02:00
Arnaud Roques
eae269348f Import version 1.2021.4 2021-04-04 23:18:44 +02:00
Arnaud Roques
c4cec58783 Import version 1.2021.3 2021-03-22 22:53:54 +01:00
Arnaud Roques
ac05a3c8c5 Import version 1.2021.2 2021-03-07 18:29:34 +01:00
Arnaud Roques
aba539f62d Import version 1.2021.1 2021-02-02 11:13:55 +01:00
Arnaud Roques
2f8bd087b8 Import version 1.2021.0 2021-01-10 22:07:55 +01:00
Arnaud Roques
a94ab06493 Import version 1.2020.26 2020-12-21 22:35:55 +01:00
Arnaud Roques
6839b93ac2 Import version 1.2020.24 2020-12-19 22:23:00 +01:00
Arnaud Roques
69f55398cb Import version 1.2020.23 2020-12-14 19:32:19 +01:00
Arnaud Roques
51b4286467 Import version 1.2020.22 2020-12-06 22:44:31 +01:00
Arnaud Roques
a87a198725 Import version 1.2020.21 2020-12-01 22:50:38 +01:00
Arnaud Roques
934a0edc52 Import version 1.2020.20 2020-11-21 18:35:15 +01:00
terje
8805b90913 Build and use Graphviz 2.44.1 straight from source 2020-11-12 08:47:43 +01:00
dependabot[bot]
1a02835f8f Bump junit from 3.8.2 to 4.13.1
Bumps [junit](https://github.com/junit-team/junit4) from 3.8.2 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r3.8.2...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-12 23:24:14 +02:00
Arnaud Roques
f4f6ca5773 Import version 1.2020.19 2020-10-12 23:21:41 +02:00
Arnaud Roques
3c6b8da0ff Import version 1.2020.18 2020-10-01 19:24:16 +02:00
Arnaud Roques
e2e1c915a6 Import version 1.2020.17 2020-09-20 11:53:19 +02:00
Arnaud Roques
bde7ee5683 Import version 1.2020.16 2020-08-28 19:26:32 +02:00
NateScarlet
5aa3b56840 Add JLatexMath support
https://github.com/plantuml/plantuml-server/issues/116#issuecomment-565530486
2020-07-27 19:32:24 +02:00
Abtin Gramian
d53fb43ac4 Bump docker parent image versions to target Java 11 2020-07-21 22:42:05 +02:00
Erik van het Hof
d6dcdab421 Update tomcat tag from 9.0-jre8-slim to 9.0-jdk8-openjdk-slim 2020-07-03 16:05:03 +02:00
Arnaud Roques
655f53fc28 Import version 1.2020.15 2020-06-28 22:21:33 +02:00
Arnaud Roques
4f7b56e80b Import version 1.2020.14 2020-06-21 22:38:00 +02:00
Arnaud Roques
af01537429 version 1.2020.13 2020-06-14 23:16:31 +02:00
Arnaud Roques
92e88f3fa4 version 1.2020.12 2020-06-07 12:05:13 +02:00
Arnaud Roques
4433e6cc1b version 1.2020.11 2020-05-30 17:21:09 +02:00
Arnaud Roques
35af5917e7 version 1.2020.10 2020-05-17 23:18:07 +02:00
John Jarvis
2f18333605 Skips cleanup for release assets 2020-05-11 09:02:22 +02:00
Arnaud Roques
f355dc6666 version 1.2020.9 2020-05-10 23:37:56 +02:00
John Jarvis
36ccc2f25c Adds tag for deploy artifacts 2020-05-01 23:04:31 +02:00
Arnaud Roques
ba6081902b version 1.2020.8 2020-04-26 20:37:03 +02:00
Arnaud Roques
5da0a51e9d version 1.2020.7 2020-04-21 13:48:03 +02:00
John Jarvis
51e0922eea Fixes docker-tag and docker-push stages in travis-ci
* Sets dist/os to the defaults
* Does not check for branch is master and a tag since these will never be true
* Removes on: tags=true
* Removes deprecated: sudo: required
* Replaces deprecated `api_token` with `token`
* Uses unique names so travis doesn't warn
2020-04-12 17:33:33 +02:00
196 changed files with 9133 additions and 1995 deletions

25
.editorconfig Normal file
View File

@ -0,0 +1,25 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
[*.java]
indent_size = 4
[*.md]
max_line_length = off
trim_trailing_whitespace = false
[*.puml]
insert_final_newline = false
[{Dockerfile,Dockerfile.*}]
indent_size = 4
[.vscode/*.json]
indent_size = 4

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
* text=auto eol=lf

5
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,5 @@
# These are supported funding model platforms
github: plantuml
patreon: plantuml
liberapay: plantuml

38
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,27 @@
---
name: Documentation request
about: Identify an area for improvement in documentation
---
**What is the URL of the documentation?**
- **Example:** https://github.com/evantill/docker-cheerpj/blob/main/README.md#example-of-using-cheerpj-on-your-computer
- *Note:* This URL includes the web page and the section of the documentation.
**What can be improved?**
A clear and concise description of what can be improved.
Examples:
- "I don't understand where the ${XYZ} variable is set."
- "There seems to be a step missing between 'X' and 'Z'. I don't know how to get to 'Z'."
- "When I run `command sub-command ...` I get the following error:"
- "I don't know what is meant by 'gerble barb gazoink` in the instructions".
**Additional context**
Add any other context or screenshots to help describe the documentation improvement.
If you think the documentation improvement is operating system specific,
please indicate which operating system is being used.

View File

@ -0,0 +1,17 @@
---
name: Feature request
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

207
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,207 @@
name: Main
on:
push:
tags:
- "v*"
jobs:
build-jdk11:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: 11
check-latest: true
cache: "maven"
- name: get tag name
id: version
run: echo "VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
- name: build with maven
run: mvn --batch-mode --define java.net.useSystemProxies=true package
- name: create renamed build
run: cp target/plantuml.war target/plantuml-${{ steps.version.outputs.VERSION }}.war
- name: build with maven (including the apache-jsp artifact)
run: mvn --batch-mode --define java.net.useSystemProxies=true -Dapache-jsp.scope=compile package
- name: create renamed build (including the apache-jsp artifact)
run: cp target/plantuml.war target/plantuml-jsp-${{ steps.version.outputs.VERSION }}.war
- name: temporarily save generated war files
uses: actions/upload-artifact@v3
with:
name: war-jre11
path: target/plantuml*-${{ steps.version.outputs.VERSION }}.war
build-jdk8:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: 8
check-latest: true
cache: "maven"
- name: get tag name
id: version
run: echo "VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
- name: Remove test code (not java 8 compatible)
run: rm -rf src/test
- name: build with maven
run: mvn --batch-mode -f pom.jdk8.xml --define java.net.useSystemProxies=true package
- name: create renamed build
run: cp target/plantuml.war target/plantuml-jre8-${{ steps.version.outputs.VERSION }}.war
- name: build with maven (including the apache-jsp artifact)
run: mvn --batch-mode -f pom.jdk8.xml --define java.net.useSystemProxies=true -Dapache-jsp.scope=compile package
- name: create renamed build (including the apache-jsp artifact)
run: cp target/plantuml.war target/plantuml-jre8-jsp-${{ steps.version.outputs.VERSION }}.war
- name: temporarily save generated war files
uses: actions/upload-artifact@v3
with:
name: war-jre8
path: target/plantuml*-${{ steps.version.outputs.VERSION }}.war
publish-releases:
runs-on: ubuntu-latest
needs:
- build-jdk11
- build-jdk8
steps:
- name: retrieve generated war files (jre8)
uses: actions/download-artifact@v3
with:
name: war-jre8
path: artifacts
- name: retrieve generated war files (jre11)
uses: actions/download-artifact@v3
with:
name: war-jre11
path: artifacts
- name: display structure of downloaded files
run: ls -lah artifacts
- name: upload binaries to release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/plantuml*.war
tag: ${{ github.ref }}
overwrite: true
file_glob: true
publish-docker:
runs-on: ubuntu-latest
needs:
- publish-releases
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Docker tomcat meta
id: docker_meta_tomcat
uses: crazy-max/ghaction-docker-meta@v3
with:
flavor: |
latest=false
prefix=
suffix=
images: plantuml/plantuml-server
tags: |
type=semver,pattern=tomcat-{{raw}}
type=raw,value=tomcat
- name: Docker jetty meta
id: docker_meta_jetty
uses: crazy-max/ghaction-docker-meta@v3
with:
flavor: |
latest=true
prefix=
suffix=
images: plantuml/plantuml-server
tags: |
type=semver,pattern={{raw}}
type=semver,pattern=jetty-{{raw}}
type=raw,value=jetty
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build & push tomcat
id: docker_build_tomcat
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile.tomcat
platforms: linux/amd64, linux/arm64
push: true
tags: ${{ steps.docker_meta_tomcat.outputs.tags }}
labels: ${{ steps.docker_meta_tomcat.outputs.labels }}
- name: Build & push jetty
id: docker_build_jetty
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile.jetty
platforms: linux/amd64, linux/arm64
push: true
tags: ${{ steps.docker_meta_jetty.outputs.tags }}
labels: ${{ steps.docker_meta_jetty.outputs.labels }}
publish-pages:
runs-on: ubuntu-latest
needs:
- publish-releases
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: 11
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

258
.github/workflows/tests.yml vendored Normal file
View File

@ -0,0 +1,258 @@
name: Tests
on:
- push
- pull_request
jobs:
test-java-8-war-generation:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: 8
check-latest: true
cache: "maven"
- name: Remove test code (not java 8 compatible)
run: rm -rf src/test
- name: Generate war file (including apache-jsp artifact)
run: mvn --batch-mode -f pom.jdk8.xml -D java.net.useSystemProxies=true -Dapache-jsp.scope=compile clean package
- name: Generate war file
run: mvn --batch-mode -f pom.jdk8.xml -D java.net.useSystemProxies=true clean package
- name: temporarily save generated files
uses: actions/upload-artifact@v3
with:
name: war-jre8
path: target/plantuml.war
retention-days: 1
test-java-8-war:
runs-on: ubuntu-latest
needs: test-java-8-war-generation
strategy:
matrix:
java-version: [ 11, 17 ]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: retrieve generated files (jre8)
uses: actions/download-artifact@v3
with:
name: war-jre8
path: artifacts
- uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: ${{ matrix.java-version }}
check-latest: true
cache: "maven"
- name: Prepare jetty-runner tests
run: mvn --batch-mode clean package
- name: Start jetty server over jetty-runner
run: java -jar target/dependency/jetty-runner.jar --config src/main/config/jetty.xml --path /plantuml artifacts/plantuml.war &
- name: Wait 5 seconds (to let jetty-runner start the jetty server)
run: sleep 5s
- name: Run tests against "mvn jetty:run" server
run: mvn --batch-mode test -DskipTests=false -Dgroups=\!graphviz-test -DargLine="-Dsystem.test.server=http://localhost:8080/plantuml"
test-mvn-livecycle:
runs-on: ubuntu-latest
strategy:
matrix:
java-version: [ 11, 17 ]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: ${{ matrix.java-version }}
check-latest: true
cache: "maven"
- name: Lifecycle - Step 1/8 - mvn clean
run: mvn --batch-mode clean
- name: Lifecycle - Step 2/8 - mvn validate
run: mvn --batch-mode validate
- name: Lifecycle - Step 3/8 - mvn compile
run: mvn --batch-mode compile
- name: Lifecycle - Step 4/8 - mvn test (with skipTests=true)
run: mvn --batch-mode test
- name: Lifecycle - Step 5/8 - mvn package
run: mvn --batch-mode package
- name: Lifecycle - Step 6/8 - mvn verify
run: mvn --batch-mode verify
- name: Lifecycle - Step 7/8 - mvn install
run: mvn --batch-mode install
- name: Lifecycle - Step 8/8 - mvn site
run: mvn --batch-mode site
test-embedded:
runs-on: ubuntu-latest
needs: test-mvn-livecycle
strategy:
matrix:
java-version: [ 11, 17 ]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: ${{ matrix.java-version }}
check-latest: true
cache: "maven"
- name: Run tests against jetty embedded server
run: mvn --batch-mode clean test -DskipTests=false -Dgroups=\!graphviz-test
test-mvn-jetty-run:
runs-on: ubuntu-latest
needs: test-mvn-livecycle
strategy:
matrix:
java-version: [ 11, 17 ]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: ${{ matrix.java-version }}
check-latest: true
cache: "maven"
- name: Prepare "mvn jetty:run" tests
run: mvn --batch-mode clean test
- name: Start jetty server over maven
run: mvn --batch-mode jetty:run &
- name: Wait 20 seconds (to let maven start the jetty server)
run: sleep 20s
- name: Run tests against "mvn jetty:run" server
run: mvn --batch-mode test -DskipTests=false -Dgroups=\!graphviz-test -DargLine="-Dsystem.test.server=http://localhost:8080/plantuml"
test-jetty-runner:
runs-on: ubuntu-latest
needs: test-mvn-livecycle
strategy:
matrix:
java-version: [ 11, 17 ]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: ${{ matrix.java-version }}
check-latest: true
cache: "maven"
- name: Prepare jetty-runner tests
run: mvn --batch-mode clean package
- name: Start jetty server over jetty-runner
run: java -jar target/dependency/jetty-runner.jar --config src/main/config/jetty.xml --path /plantuml target/plantuml.war &
- name: Wait 5 seconds (to let jetty-runner start the jetty server)
run: sleep 5s
- name: Run tests against "mvn jetty:run" server
run: mvn --batch-mode test -DskipTests=false -Dgroups=\!graphviz-test -DargLine="-Dsystem.test.server=http://localhost:8080/plantuml"
test-jetty:
runs-on: ubuntu-latest
needs: test-mvn-livecycle
strategy:
matrix:
java-version: [ 11, 17 ]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: ${{ matrix.java-version }}
check-latest: true
cache: "maven"
- name: Prepare external tests
run: mvn --batch-mode clean test
- name: Build the jetty docker stack
run: |
docker image build -f Dockerfile.jetty -t plantuml-server:local .
docker run -d --hostname=test.localhost -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 --batch-mode test -DskipTests=false -DargLine="-Dsystem.test.server=http://localhost:8080/plantuml"
test-tomcat:
runs-on: ubuntu-latest
needs: test-mvn-livecycle
strategy:
matrix:
java-version: [ 11, 17 ]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: ${{ matrix.java-version }}
check-latest: true
cache: "maven"
- name: Prepare external tests
run: mvn --batch-mode clean test
- name: Build the tomcat docker stack
run: |
docker image build -f Dockerfile.tomcat -t plantuml-server:local .
docker run -d --hostname=test.localhost -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 --batch-mode test -DskipTests=false -DargLine="-Dsystem.test.server=http://localhost:8080/plantuml"

8
.gitignore vendored
View File

@ -1,5 +1,13 @@
# Eclipse Ignores
target
.settings
.classpath
.project
.checkstyle
# IntelliJ ignores
.idea/
out/
*.iml
*.ipr
*.iws

View File

@ -1,65 +0,0 @@
language: java
sudo: required
jdk:
- openjdk8
services:
- docker
stages:
- build
- name: docker-tag
if: (tag IS present) AND (branch = master) AND (type != pull_request)
- name: docker-push
if: (branch = master) AND (type != pull_request)
jobs:
include:
- stage: build
name: war
script: mvn --batch-mode --define java.net.useSystemProxies=true package
before_deploy: cp target/plantuml.war target/plantuml-${TRAVIS_BRANCH}.war
deploy:
provider: releases
api_key: "$GITHUB_TOKEN"
file: "target/plantuml-${TRAVIS_BRANCH}.war"
skip_cleanup: true
on:
tags: true
- stage: docker-tag
name: jetty
script:
- if [ -z "$TRAVIS_TAG" ]; then exit 0; fi
- docker login -u $DOCKER_LOGIN -p $DOCKER_PASSWORD
- docker build --pull -t plantuml/plantuml-server:jetty-$TRAVIS_TAG -f Dockerfile.jetty .
- docker push plantuml/plantuml-server:jetty-$TRAVIS_TAG
on:
tags: true
- stage: docker-tag
name: tomcat
script:
- if [ -z "$TRAVIS_TAG" ]; then exit 0; fi
- docker login -u $DOCKER_LOGIN -p $DOCKER_PASSWORD
- docker build --pull -t plantuml/plantuml-server:tomcat-$TRAVIS_TAG -f Dockerfile.tomcat .
- docker push plantuml/plantuml-server:tomcat-$TRAVIS_TAG
on:
tags: true
- stage: docker-push
name: jetty
script:
- docker login -u $DOCKER_LOGIN -p $DOCKER_PASSWORD
- docker build --pull -t plantuml/plantuml-server:jetty -f Dockerfile.jetty .
- docker tag plantuml/plantuml-server:jetty plantuml/plantuml-server:latest
- docker push plantuml/plantuml-server:jetty
- docker push plantuml/plantuml-server:latest
- stage: docker-push
name: tomcat
script:
- docker login -u $DOCKER_LOGIN -p $DOCKER_PASSWORD
- docker build --pull -t plantuml/plantuml-server:tomcat -f Dockerfile.tomcat .
- docker push plantuml/plantuml-server:tomcat

30
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,30 @@
{
"java.configuration.updateBuildConfiguration": "automatic",
"cSpell.words": [
"Arnaud",
"buildx",
"ditaa",
"endditaa",
"enduml",
"epstext",
"etag",
"ghaction",
"inmemory",
"Lalloni",
"monaco",
"plantuml",
"puml",
"Roques",
"servlet",
"servlets",
"startditaa",
"startuml",
"undock",
"utxt"
],
"cSpell.allowCompoundWords": true,
"svg.preview.background": "dark-transparent",
"files.associations": {
"*.jspf": "html"
}
}

View File

@ -1,7 +1,7 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found.
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
<https://www.gnu.org/licenses/why-not-lgpl.html>.

View File

@ -1,20 +0,0 @@
FROM maven:3-jdk-8
RUN apt-get update && apt-get install -y --no-install-recommends graphviz fonts-wqy-zenhei && rm -rf /var/lib/apt/lists/*
COPY pom.xml /app/
COPY src /app/src/
ENV MAVEN_CONFIG=/app/.m2
WORKDIR /app
RUN mvn package
# chmod required to ensure any user can run the app
RUN mkdir /app/.m2 && chmod -R a+w /app
EXPOSE 8080
ENV HOME /app
CMD java -Djetty.contextpath=/ -jar target/dependency/jetty-runner.jar target/plantuml.war
# To run with debugging enabled instead
#CMD java -Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog -Dorg.eclipse.jetty.LEVEL=DEBUG -Djetty.contextpath=/ -jar target/dependency/jetty-runner.jar target/plantuml.war

View File

@ -1,25 +1,79 @@
FROM maven:3.6-jdk-8 AS builderjetty
FROM maven:3-eclipse-temurin-17 AS builder
COPY pom.xml /app/
COPY src /app/src/
COPY pom.xml pom.parent.xml /app/
COPY src/main /app/src/main/
WORKDIR /app
RUN mvn --batch-mode --define java.net.useSystemProxies=true package
########################################################################################
FROM jetty:9.4-jre8
MAINTAINER D.Ducatel
FROM jetty:11.0.18-jre17-eclipse-temurin
# Proxy and OldProxy need empty path segments support in URIs
# Hence: allow AMBIGUOUS_EMPTY_SEGMENT
# Changes are only active if `/generate-jetty-start.sh` is called!
RUN sed -i 's/# jetty\.httpConfig\.uriCompliance=DEFAULT/jetty.httpConfig.uriCompliance=DEFAULT,AMBIGUOUS_EMPTY_SEGMENT/g' /var/lib/jetty/start.d/server.ini
USER root
RUN apt-get update && \
apt-get install -y --no-install-recommends graphviz fonts-noto-cjk && \
apt-get clean && rm -rf /var/lib/apt/lists/*
apt-get install -y --no-install-recommends \
curl \
fonts-noto-cjk \
libgd3 \
&& \
rm -rf /var/lib/apt/lists/* && \
/generate-jetty-start.sh
# Build Graphviz from source because there are no binary distributions for recent versions
ARG GRAPHVIZ_VERSION
ARG GRAPHVIZ_BUILD_DIR=/tmp/graphiz-build
RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
jq \
libexpat1-dev \
libgd-dev \
zlib1g-dev \
&& \
mkdir -p $GRAPHVIZ_BUILD_DIR && \
cd $GRAPHVIZ_BUILD_DIR && \
GRAPHVIZ_VERSION=${GRAPHVIZ_VERSION:-$(curl -s https://gitlab.com/api/v4/projects/4207231/releases/ | jq -r '.[] | .name' | sort -V -r | head -1)} && \
curl -o graphviz.tar.gz https://gitlab.com/api/v4/projects/4207231/packages/generic/graphviz-releases/${GRAPHVIZ_VERSION}/graphviz-${GRAPHVIZ_VERSION}.tar.gz && \
tar -xzf graphviz.tar.gz && \
cd graphviz-$GRAPHVIZ_VERSION && \
./configure && \
make && \
make install && \
apt-get remove -y \
build-essential \
jq \
libexpat1-dev \
libgd-dev \
zlib1g-dev \
&& \
apt-get autoremove -y && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* && \
rm -rf $GRAPHVIZ_BUILD_DIR
COPY docker-entrypoint.jetty.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
USER jetty
ENV GRAPHVIZ_DOT=/usr/bin/dot
ENV WEBAPP_PATH=$JETTY_BASE/webapps
RUN rm -rf $WEBAPP_PATH && \
mkdir -p $WEBAPP_PATH
COPY --from=builder /app/target/plantuml.war /plantuml.war
COPY ROOT.jetty.xml $WEBAPP_PATH/ROOT.xml
ARG BASE_URL=ROOT
COPY --from=builderjetty /app/target/plantuml.war /var/lib/jetty/webapps/$BASE_URL.war
# Openshift https://docs.openshift.com/container-platform/4.9/openshift_images/create-images.html#images-create-guide-openshift_create-images
USER root
RUN chgrp -R 0 $JETTY_BASE && chmod -R g=u $JETTY_BASE
RUN chgrp -R 0 /tmp && chmod -R g=u /tmp
USER jetty
ENTRYPOINT ["/entrypoint.sh"]
VOLUME ["/tmp/jetty"]

76
Dockerfile.jetty-alpine Normal file
View File

@ -0,0 +1,76 @@
FROM maven:3-eclipse-temurin-17-alpine AS builder
COPY pom.xml pom.parent.xml /app/
COPY src/main /app/src/main/
WORKDIR /app
RUN mvn --batch-mode --define java.net.useSystemProxies=true package
########################################################################################
FROM jetty:11.0.18-jre17-alpine-eclipse-temurin
# Proxy and OldProxy need empty path segments support in URIs
# Hence: allow AMBIGUOUS_EMPTY_SEGMENT
# Changes are only active if `/generate-jetty-start.sh` is called!
RUN sed -i 's/# jetty\.httpConfig\.uriCompliance=DEFAULT/jetty.httpConfig.uriCompliance=DEFAULT,AMBIGUOUS_EMPTY_SEGMENT/g' /var/lib/jetty/start.d/server.ini
USER root
RUN apk add --no-cache \
curl \
font-noto-cjk \
libgd \
&& \
/generate-jetty-start.sh
#RUN apk add --no-cache graphviz
ARG GRAPHVIZ_VERSION
ARG GRAPHVIZ_BUILD_DIR=/tmp/graphiz-build
RUN apk add --no-cache \
g++ \
jq \
expat-dev \
make \
zlib \
pkgconf \
&& \
mkdir -p $GRAPHVIZ_BUILD_DIR && \
cd $GRAPHVIZ_BUILD_DIR && \
GRAPHVIZ_VERSION=${GRAPHVIZ_VERSION:-$(curl -s https://gitlab.com/api/v4/projects/4207231/releases/ | jq -r '.[] | .name' | sort -V -r | head -1)} && \
curl -o graphviz.tar.gz https://gitlab.com/api/v4/projects/4207231/packages/generic/graphviz-releases/${GRAPHVIZ_VERSION}/graphviz-${GRAPHVIZ_VERSION}.tar.gz && \
tar -xzf graphviz.tar.gz && \
cd graphviz-$GRAPHVIZ_VERSION && \
./configure && \
make && \
make install && \
apk del --no-cache \
g++ \
jq \
expat-dev \
make \
zlib \
&& \
rm -rf $GRAPHVIZ_BUILD_DIR
COPY docker-entrypoint.jetty.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
USER jetty
ENV WEBAPP_PATH=$JETTY_BASE/webapps
RUN rm -rf $WEBAPP_PATH && \
mkdir -p $WEBAPP_PATH
COPY --from=builder /app/target/plantuml.war /plantuml.war
COPY ROOT.jetty.xml $WEBAPP_PATH/ROOT.xml
# Openshift https://docs.openshift.com/container-platform/4.9/openshift_images/create-images.html#images-create-guide-openshift_create-images
USER root
RUN chgrp -R 0 $JETTY_BASE && \
chmod -R g=u $JETTY_BASE
RUN chgrp -R 0 /tmp && \
chmod -R g=u /tmp
USER jetty
ENTRYPOINT ["/entrypoint.sh"]
VOLUME ["/tmp/jetty"]

View File

@ -1,22 +1,65 @@
FROM maven:3.6-jdk-8 AS buildertomcat
FROM maven:3-eclipse-temurin-11 AS builder
COPY pom.xml /app/
COPY src /app/src/
COPY pom.xml pom.parent.xml /app/
COPY src/main /app/src/main/
WORKDIR /app
RUN mvn --batch-mode --define java.net.useSystemProxies=true package
RUN mvn --batch-mode --define java.net.useSystemProxies=true -Dapache-jsp.scope=compile package
########################################################################################
FROM tomcat:9.0-jre8-slim
MAINTAINER D.Ducatel
FROM tomcat:10-jdk11
RUN apt-get update && \
apt-get install -y --no-install-recommends graphviz fonts-noto-cjk && \
apt-get clean && rm -rf /var/lib/apt/lists/*
apt-get install -y --no-install-recommends \
curl \
fonts-noto-cjk \
libgd3 \
&& \
rm -rf /var/lib/apt/lists/*
ENV GRAPHVIZ_DOT=/usr/bin/dot
# Build Graphviz from source because there are no binary distributions for recent versions
ARG GRAPHVIZ_VERSION
ARG GRAPHVIZ_BUILD_DIR=/tmp/graphiz-build
RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
jq \
libexpat1-dev \
libgd-dev \
zlib1g-dev \
&& \
mkdir -p $GRAPHVIZ_BUILD_DIR && \
cd $GRAPHVIZ_BUILD_DIR && \
GRAPHVIZ_VERSION=${GRAPHVIZ_VERSION:-$(curl -s https://gitlab.com/api/v4/projects/4207231/releases/ | jq -r '.[] | .name' | sort -V -r | head -1)} && \
curl -o graphviz.tar.gz https://gitlab.com/api/v4/projects/4207231/packages/generic/graphviz-releases/${GRAPHVIZ_VERSION}/graphviz-${GRAPHVIZ_VERSION}.tar.gz && \
tar -xzf graphviz.tar.gz && \
cd graphviz-$GRAPHVIZ_VERSION && \
./configure && \
make && \
make install && \
apt-get remove -y \
build-essential \
jq \
libexpat1-dev \
libgd-dev \
zlib1g-dev \
&& \
apt-get autoremove -y && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* && \
rm -rf $GRAPHVIZ_BUILD_DIR
ARG BASE_URL=ROOT
RUN rm -rf /usr/local/tomcat/webapps/$BASE_URL
COPY --from=buildertomcat /app/target/plantuml.war /usr/local/tomcat/webapps/$BASE_URL.war
COPY docker-entrypoint.tomcat.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENV WEBAPP_PATH=$CATALINA_HOME/webapps
RUN rm -rf $WEBAPP_PATH && \
mkdir -p $WEBAPP_PATH
COPY --from=builder /app/target/plantuml.war /plantuml.war
# Openshift https://docs.openshift.com/container-platform/4.9/openshift_images/create-images.html#images-create-guide-openshift_create-images
RUN chgrp -R 0 $CATALINA_HOME && chmod -R g=u $CATALINA_HOME
ENTRYPOINT ["/entrypoint.sh"]
CMD ["catalina.sh", "run"]

210
README.md
View File

@ -1,120 +1,196 @@
PlantUML Server
===============
[![Build Status](https://travis-ci.org/plantuml/plantuml-server.png?branch=master)](https://travis-ci.org/plantuml/plantuml-server)
[![](https://images.microbadger.com/badges/image/plantuml/plantuml-server.svg)](https://microbadger.com/images/plantuml/plantuml-server "Get your own image badge on microbadger.com")
[![Docker Pull](https://img.shields.io/docker/pulls/plantuml/plantuml-server.svg)](https://hub.docker.com/r/plantuml/plantuml-server/)
# 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)
[![online](https://img.shields.io/endpoint?url=https://www.plantuml.com/plantuml/badge)](https://www.plantuml.com/plantuml)
[![rate](https://img.shields.io/endpoint?url=https://www.plantuml.com/plantuml/rate)](https://www.plantuml.com/plantuml)
[![peak](https://img.shields.io/endpoint?url=https://www.plantuml.com/plantuml/rate?peak)](https://www.plantuml.com/plantuml)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/plantuml?logo=github)](https://github.com/sponsors/plantuml/)
[![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.
![](https://raw.githubusercontent.com/ftomassetti/plantuml-server/readme/screenshots/screenshot.png)
> [PlantUML is **not** affected by the log4j vulnerability.](https://github.com/plantuml/plantuml/issues/826)
To know more about PlantUML, please visit http://plantuml.com/.
> **Breaking changes**:
> The PlantUML core removed the deprecated `ALLOW_PLANTUML_INCLUDE` environment property feature and switch to the
> `PLANTUML_SECURITY_PROFILE` concept with version `v1.2023.9`.
> All details about PlantUML's security can be found on <https://plantuml.com/security>.
>
> By default PlantUML server sets the `PLANTUML_SECURITY_PROFILE` to `INTERNET`.
> If you need more access to e.g. other ports than 80 (http) and 443 (https) or even access to local files, please
> consider using one of the allowlist features.
> It is strongly advised **not** to set the `PLANTUML_SECURITY_PROFILE` below `INTERNET`!
![PlantUML Server](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/screenshot.png)
More examples and features about the Web UI can be found in [docs/WebUI](https://github.com/plantuml/plantuml-server/tree/master/docs/WebUI).
To know more about PlantUML, please visit https://plantuml.com.
Requirements
============
## Requirements
* jre/jdk 1.6.0 or above
* apache maven 3.0.2 or above
- jre/jdk 11 or above
- apache maven 3.0.2 or above
How to run the server
=====================
## Recommendations
- Jetty 11 or above
- Tomcat 10 or above
## How to run the server
Just run:
```
```sh
mvn jetty:run
```
The server is now listing to [http://localhost:8080/plantuml](http://localhost:8080/plantuml).
The server is now listening to [http://localhost:8080/plantuml](http://localhost:8080/plantuml).
In this way the server is run on an embedded jetty server.
You can specify the port at which it runs:
```
mvn jetty:run -Djetty.port=9999
```sh
mvn jetty:run -Djetty.http.port=9999
```
How to run the server with Docker
=================================
## How to run the server with Docker
You can run Plantuml with jetty or tomcat container
```
```sh
docker run -d -p 8080:8080 plantuml/plantuml-server:jetty
docker run -d -p 8080:8080 plantuml/plantuml-server:tomcat
```
The server is now listing to [http://localhost:8080](http://localhost:8080).
The server is now listening to [http://localhost:8080](http://localhost:8080).
### Read-only container
The jetty container supports read-only files system, you can run the read-only mode with:
```sh
docker run -d -p 8080:8080 --read-only -v /tmp/jetty plantuml/plantuml-server:jetty
```
This makes the container compatible with more restricted environment such as OpenShift, just make sure you mount a volume (can be ephemeral) on `/tmp/jetty`.
### Change base URL
To run plantuml using different base url, change the `docker-compose.yml` file:
~~~
args:
BASE_URL: plantuml
~~~
And run `docker-compose up --build`. This will build a modified version of the image using
the base url `/plantuml`, e.g. http://localhost/plantuml
How to set PlantUML options
=================================
You can apply some option to your PlantUML server with environement variable.
If you run the directly the jar, you can pass the option with `-D` flag
```yaml
environment:
- BASE_URL=plantuml
```
java -D THE_ENV_VARIABLE=THE_ENV_VALUE -Djetty.contextpath=/ -jar target/dependency/jetty-runner.jar target/plantuml.war
And run `docker-compose up`. This will start a modified version of the image using the base url `/plantuml`, e.g. http://localhost:8080/plantuml
## How to set PlantUML options
You can apply some option to your PlantUML server with environment variable.
If you run the directly the jar:
```sh
# NOTE: jetty-runner is deprecated.
# build war file and jetty-runner
mvn package
# start directly
# java $JVM_ARGS -jar jetty-runner.jar $JETTY_ARGS
java -jar target/dependency/jetty-runner.jar --config src/main/config/jetty.xml --port 9999 --path /plantuml target/plantuml.war
# see help for more possible options
java -jar target/dependency/jetty-runner.jar --help
```
or
```
mvn jetty:run -D THE_ENV_VARIABLE=THE_ENV_VALUE -Djetty.port=9999
Note: `--config src/main/config/jetty.xml` is only necessary if you need support for empty path segments in URLs (e.g. for the old proxy)
Alternatively, start over maven and pass the option with `-D` flag
```sh
mvn jetty:run -D THE_ENV_VARIABLE=THE_ENV_VALUE -Djetty.http.port=9999
```
If you use docker, you can use the `-e` flag:
```
docker run -d -p 8080:8080 -e THE_ENV_VARIABLE=THE_ENV_VALUE plantuml/plantuml-server:jetty
```sh
docker run -d -p 9999:8080 -e THE_ENV_VARIABLE=THE_ENV_VALUE plantuml/plantuml-server:jetty
```
You can set all the following variables:
* `BASE_URL`
* PlantUML Base URL path
* Default value: `ROOT`
* `PLANTUML_SECURITY_PROFILE`
* Set PlantUML security profile. See [PlantUML security](https://plantuml.com/security).
* If you need more access to e.g. other ports than 80 (http) and 443 (https) or even access to local files, please consider using one of the allowlist features:
* `plantuml.allowlist.path`
* `plantuml.include.path`
* `plantuml.allowlist.url`
* It is strongly advised **not** to set the `PLANTUML_SECURITY_PROFILE` below `INTERNET`!
* Default value: `INTERNET`
* `PLANTUML_PROPERTY_FILE`
* Set PlantUML system properties (like over the Java command line using the `-Dpropertyname=value` syntax).
* To see what kind of file content is supported, see the documentation of [`java.util.Properties.load`](https://docs.oracle.com/javase/8/docs/api/java/util/Properties.html#load-java.io.Reader-).
* Default value: `null`
* `PLANTUML_CONFIG_FILE`
* Local path to a PlantUML configuration file (identical to the `-config` flag on the CLI)
* File content will be added before each PlantUML diagram code.
* Default value: `null`
* `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](http://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`
* `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`
* when calling the `proxy` endpoint, the value of `HTTP_AUTHORIZATION` will be used to set the HTTP Authorization header
* Default value: `null`
* `HTTP_PROXY_READ_TIMEOUT`
* when calling the `proxy` endpoint, the value of `HTTP_PROXY_READ_TIMEOUT` will be the connection read timeout in milliseconds
* Default value: `10000` (10 seconds)
Alternate: How to build your docker image
======================================================
## Alternate: How to build your docker image
This method uses maven to run the application. That requires internet connectivity.
So, you can use following command to create a self-contained docker image that will "just-work".
*Note: Generate the WAR (instructions further below) prior to running "docker build"*
So, you can use following command to create a self-contained docker image that will "just work".
```sh
docker image build -f Dockerfile.jetty -t plantuml-server:local .
docker run -d -p 8080:8080 plantuml-server:local
```
docker image build -t plantuml-server .
docker run -d -p 8080:8080 plantuml-server
```
The server is now listing 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.
How to generate the war
=======================
## How to generate the war
To build the war, just run:
```
```sh
mvn package
```
at the root directory of the project to produce plantuml.war in the target/ directory.
NOTE: If you want that the generated war includes the `apache-jsp` artifact run:
```sh
mvn package -Dapache-jsp.scope=compile
```
If you want to generate the war with java 8 as target just remove the src/test directory and use `pom.jdk8.xml`.
```sh
rm -rf src/test
mvn package -f pom.jdk8.xml [-Dapache-jsp.scope=compile]
```
## Use with reverse-proxy
It is possible to use PlantUML with a reverse proxy.
You can find this and other examples [here](https://github.com/plantuml/plantuml-server/tree/master/examples).

9
ROOT.jetty.xml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
<Set name="contextPath">
<Env name="CONTEXT_PATH" />
</Set>
<Set name="war">/plantuml.war</Set>
</Configure>

12
SECURITY.md Normal file
View File

@ -0,0 +1,12 @@
# Security Policy
## Reporting a Vulnerability
If you find any security concern, please send a mail to plantuml@gmail.com
with title **Security concern**.
We will then study the concern and will answer back by email.
Thanks!

View File

@ -5,9 +5,9 @@ services:
build:
context: .
dockerfile: Dockerfile.jetty
args:
BASE_URL: plantuml
image: plantuml/plantuml-server:local
image: plantuml/plantuml-server:jetty
container_name: plantuml-server
ports:
- 8080:8080
- 8080:8080
environment:
- BASE_URL=plantuml

14
docker-entrypoint.jetty.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/sh
# cspell:words mkdir
# cspell:enableCompoundWords
###########################################################
# ensure context path starts with a slash
export CONTEXT_PATH="/${BASE_URL#'/'}"
# base image entrypoint
if [ -x /docker-entrypoint.sh ]; then
/docker-entrypoint.sh "$@"
else
exec "$@"
fi

18
docker-entrypoint.tomcat.sh Executable file
View File

@ -0,0 +1,18 @@
#!/bin/sh
# cspell:words mkdir
# cspell:enableCompoundWords
###########################################################
# choose war file name so that context path is correctly set based on BASE_URL,
# following the rules from https://tomcat.apache.org/tomcat-9.0-doc/config/context.html#Naming,
# specifically remove leading and trailing slashes and replace the remaining ones by hashes.
export FILE_NAME="$(echo "$BASE_URL" | sed -e 's:^/::' -e 's:/$::' -e 's:/:#:g')"
export FILE_PATH="$WEBAPP_PATH/$FILE_NAME.war"
mv /plantuml.war "$FILE_PATH"
# base image entrypoint
if [ -x /docker-entrypoint.sh ]; then
/docker-entrypoint.sh "$@"
else
exec "$@"
fi

25
docs/WebUI/README.md Normal file
View File

@ -0,0 +1,25 @@
# PlantUML Server Web UI
## First example: "Alice and Bob"
![alice-bob](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/alice-bob.gif)
## Multipaging
Just see what you want.
And if its the second diagram page, so be it.
![multipaging](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/multipaging.gif)
## Split Screen
You have multiple monitors? You want to share your Window but only show the diagram and not the code? Than do it!
![multipaging](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/split-screen.gif)
## More
- [Settings](https://github.com/plantuml/plantuml-server/blob/master/docs/WebUI/settings.md)
- [PlantUML Language Features](https://github.com/plantuml/plantuml-server/blob/master/docs/WebUI/language-features.md)
- [Import/Export](https://github.com/plantuml/plantuml-server/blob/master/docs/WebUI/import-export.md)
- [Mobile Version](https://github.com/plantuml/plantuml-server/blob/master/docs/WebUI/mobile.md)

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 KiB

View File

@ -0,0 +1,21 @@
# Import and Export editable PlantUML Diagrams
Similar to [draw.io](https://app.diagrams.net) it is possible to load and continue editing PlantUML diagram images.
## Export a diagram
Via the editor menu or <kbd>Ctrl</kbd> + <kbd>S</kbd> (or <kbd>Meta</kbd> + <kbd>S</kbd> in the case of a Mac) you can open the file save dialog.
Here you can edit the file name, choose a file/diagram type and download the diagram.
The default is to download the PlantUML code.
![export](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/diagram-export.gif)
## Import a diagram
This feature is based on the PlantUML meta data which currently **support only PNG and SVG** diagrams.
Besides a diagram image, you can of course also load a diagram code file.
Moreover, because it is so nice and convenient, we also added a drag-and-drop feature.
![import](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/diagram-import.gif)

View File

@ -0,0 +1,41 @@
# PlantUML Language Features
## Auto Completion
### Icons
- type `<&` to get a list of PlantUML available icons
- see a preview of the suggested icon in its description
- [PlantUML documentation](https://plantuml.com/openiconic)
![icons](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/auto-completion-icons.gif)
### Emojis
- type `<:` to get a list of PlantUML available icons
- see a preview of the suggested icon in its description
- [PlantUML documentation](https://plantuml.com/creole#68305e25f5788db0)
![emojis](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/auto-completion-emojis.gif)
### Themes
- type `!t` to get the suggestion `theme`
- type `!theme ` to get a list of (local) available PlantUML themes.
- [PlantUML documentation](https://plantuml.com/theme)
![themes](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/auto-completion-themes.gif)
## Validation
### `@start...` and `@end...`
- `@start...` should always be the first command
- `@end...` should alway be the last command
- `@start...` should only exists once
- `@end...` should only exists once
- `@end...` should have the same type as `@start...`
e.g.: `@startjson ... @endjson`
![start-end](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/validation-start-end.gif)

12
docs/WebUI/mobile.md Normal file
View File

@ -0,0 +1,12 @@
# Mobile Version
PlantUML Server is mobile ready.
## First example: "Alice and Bob"
![alice-bob](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/mobile-alice-bob.gif)
## Settings
![settings](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/mobile-settings.gif)

38
docs/WebUI/settings.md Normal file
View File

@ -0,0 +1,38 @@
# Settings
Via the menu or <kbd>Ctrl</kbd> + <kbd>,</kbd> (or <kbd>Meta</kbd> + <kbd>,</kbd> in the case of a Mac) you can open the Setting dialog.
## Theme
_The sun is too bright? You live on the dark side or only in the basement?_
Choose between the `dark` and `light` theme.
![theme](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/settings-theme.gif)
## Rendering Type
You want always to work and see only the SVG version? Not Problem.
Choose the rendering type you want to see.
![rendering-type](https://raw.githubusercontent.com/plantuml/plantuml-server/master/docs/WebUI/gifs/settings-rendering-type.gif)
## Editor Watcher Timeout
You can change the Editor Watcher Timeout, by default it is `500 ms`.
## Editor Options
You can change the options of the editor:
```yaml
{
automaticLayout: true,
fixedOverflowWidgets: true,
minimap: { enabled: false },
scrollbar: { alwaysConsumeMouseWheel: false },
scrollBeyondLastLine: false,
tabSize: 2,
theme: "vs", // "vs-dark"
}
```

View File

@ -0,0 +1,83 @@
# Front-end Contribution
## Web UI
The Web UI uses vanilla javascript.
As online editor Microsoft's [Monaco Editor](https://github.com/microsoft/monaco-editor).
The documentation can be found [here](https://microsoft.github.io/monaco-editor/docs.html).
You may recognize the editor since it's the code editor from [VS Code](https://github.com/microsoft/vscode).
The main entry file are `index.jsp`, `previewer.jsp` and `error.jsp`.
The code structure is mainly divided into `components` and `js`:
- `components` are for example a modal or dialog.
Anything that include things directly seen and rendered on the page.
- `js` contains more the things that do not have a direct influence on the UI. For example the PlantUML language features or the methods for cross-browser/cross-tab communication.
## PlantUML Language Features
At the moment there is no defined PlantUML language.
Feel free to create one!
But until then the syntax highlighting form `apex` is used.
IMHO it works quite well.
All PlantUML language features are bundled into a seperate file `plantuml-language.min.js`.
Therefore anything under `js/language` should be independent!
### Code Completion
What do you need to do to create a new code completion feature:
1. create a new JS file under `js/language/completion` - let's say `xxx.js`
2. create a new `registerXxxCompletion` method
_It may help you if you look into the [documentation](https://microsoft.github.io/monaco-editor/docs.html#functions/languages.registerCompletionItemProvider.html) or at the provided [sample code](https://microsoft.github.io/monaco-editor/playground.html?source=v0.38.0#example-extending-language-services-completion-provider-example) to understand more about `monaco.languages.registerCompletionItemProvider`._
```js
PlantUmlLanguageFeatures.prototype.registerEmojiCompletion = function() {
monaco.languages.registerCompletionItemProvider(PlantUmlLanguageFeatures.languageSelector, {
provideCompletionItems: async (model, position) => {
// ...
return { suggestions };
}
});
};
```
4. add your new method inside the language initialization inside `js/language/language.js`
```diff
const PlantUmlLanguageFeatures = function(initialize = true) {
if (initialize) {
// initialize all validation and code completion methods
this.addStartEndValidationListeners();
this.registerThemeCompletion();
this.registerIconCompletion();
this.registerEmojiCompletion();
+ this.registerXxxCompletion();
}
};
```
### Code Validation
What do you need to do to create a new code validation feature:
1. create a new JS file under `js/language/validation/listeners` - let's say `zzz-validation.js`
2. register your validation methods to the designated event listener
The validation event order is: `before` &#8594; `code` &#8594; `line` &#8594; `after`
You may look at `js/language/validation/listeners/start-end-validation.js` to get an idea how to register a new listener.
3. add your new method inside the language initialization inside `js/language/language.js`
```diff
const PlantUmlLanguageFeatures = function(initialize = true) {
if (initialize) {
// initialize all validation and code completion methods
this.addStartEndValidationListeners();
+ this.addZzzValidationListeners();
this.registerThemeCompletion();
this.registerIconCompletion();
this.registerEmojiCompletion();
}
};
```
### Tipps
- `pom.xml`: set `withoutCSSJSCompress` to `true` to deactivate the minification
- use `mvn fizzed-watcher:run` to watch changes and automatically update the bundled `plantuml.min.{css,js}` and `plantuml-language.min.js` files
- if the browser get the error `ReferenceError: require is not defined` or something similar related to the webjars, try `mvn clean install` to get things straight

BIN
docs/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

6
examples/README.md Normal file
View File

@ -0,0 +1,6 @@
# Examples
- [Additional fonts inside the PlantUML docker container](./additional-fonts)
- [Nginx simple reverse proxy example](./nginx-simple)
- [Nginx reverse proxy example with defined location directive (different context path)](./nginx-contextpath)
- [Kubernetes simple deployment](./kubernetes-simple)

View File

@ -0,0 +1,73 @@
# Additional fonts inside the PlantUML docker container
It is possible to make additional fonts available to PlantUML by mapping them via a volume within the docker container.
Since the base image from the docker container is using Ubuntu, fonts can easily be provided by just adding them somewhere inside the `~/.local/share/fonts` directory.
**Tipp**: to not overwrite the container fonts add the additional files inside an own sub-folder, e.g., `custom` or `host`.
In the following you can find an example how to provide all fonts of your host machine in the PlantUML docker container for Jetty and Tomcat.
## Jetty
In the case of the Jetty docker container the home directory is `/var/lib/jetty`.
```yaml
services:
plantuml-server:
image: plantuml/plantuml-server:jetty
container_name: plantuml-server
ports:
- "80:8080"
environment:
- TZ=Europe/Berlin
- BASE_URL=plantuml
volumes:
- /usr/share/fonts:/var/lib/jetty/.local/share/fonts/host:ro
```
## Tomcat
In the case of the Tomcat docker container the home directory is `/root`.
_Yes, the tomcat container is running as `root` which is basically a really bad idea w.r.t. security. Create a pull request and maintain it if you want to change that._
```yaml
services:
plantuml-server:
image: plantuml/plantuml-server:tomcat
container_name: plantuml-server
ports:
- "80:8080"
environment:
- TZ=Europe/Berlin
- BASE_URL=plantuml
volumes:
- /usr/share/fonts:/root/.local/share/fonts/host:ro
```
## Verification
The following command will print a list of all available fonts inside the docker container:
```bash
docker exec -it plantuml-server fc-list
```
To find a special font add a grep filter to the command:
```bash
docker exec -it plantuml-server fc-list | grep "<name-of-font>"
```
Naturally, it is also possible to check this via PlantUML itself by rendering the following diagram:
```plantuml
@startuml
listfonts
@enduml
```
**Note**: If you have added a lot of fonts: (a) this diagram may take a few seconds to generate, and (b) eventually the PNG image may be clipped. To avoid the latter, render the diagram as an SVG image.

View File

@ -0,0 +1,13 @@
version: "3"
services:
plantuml-server:
image: plantuml/plantuml-server:jetty
container_name: plantuml-server
ports:
- "80:8080"
environment:
- TZ=Europe/Berlin
- BASE_URL=plantuml
volumes:
- /usr/share/fonts:/var/lib/jetty/.local/share/fonts/host:ro

View File

@ -0,0 +1,51 @@
# PlantUML Kubernetes Deployment
In this example, PlantUML is deployed on an Kubernetes cluster using a `Deployment`, a `Service` and an `Ingress`.
## Quick start
Install:
```bash
# Hint: Adjust the Ingress host to your URL
kubectl create ns plantuml
kubectl -n plantuml apply -f deployment.yaml
```
Uninstall:
```bash
kubectl -n plantuml delete -f deployment.yaml
kubectl delete ns plantuml
```
## TLS configuration
Create a TLS `Secret` and extend the `Ingress` spec with a TLS configuration:
```bash
[...]
tls:
- hosts:
- plantuml-example.localhost
secretName: plantuml-tls
```
Since the `Ingress Controller` terminates the TLS and routes `http` to the application, we might need to tell the application explicitly that it got a forwarded request.
This configuration changes depending on the `Ingress Controller`. Here an nginx example:
```
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "X-Forwarded-Proto: https";
```
## Useful commands
```bash
# see whats going on inside your Deployment
kubectl -n plantuml logs -l "app.kubernetes.io/name=plantuml"
```

View File

@ -0,0 +1,84 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: plantuml
labels:
app.kubernetes.io/name: plantuml
app.kubernetes.io/instance: plantuml
spec:
replicas: 3 # Can be adjusted
selector:
matchLabels:
app.kubernetes.io/name: plantuml
app.kubernetes.io/instance: plantuml
template:
metadata:
labels:
app.kubernetes.io/name: plantuml
app.kubernetes.io/instance: plantuml
spec:
containers:
- name: plantuml
securityContext:
allowPrivilegeEscalation: false
image: plantuml/plantuml-server:jetty-v1.2022.6
imagePullPolicy: Always
# env: # In case of different base URL
# - name: BASE_URL
# value: plantuml
ports:
- name: http
containerPort: 8080
protocol: TCP
livenessProbe:
tcpSocket:
port: http
readinessProbe:
tcpSocket:
port: http
resources:
limits:
cpu: 500m
memory: 2048Mi
requests:
cpu: 250m
memory: 1024Mi
---
apiVersion: v1
kind: Service
metadata:
name: plantuml
labels:
app.kubernetes.io/name: plantuml
app.kubernetes.io/instance: plantuml
spec:
type: ClusterIP
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
selector:
app.kubernetes.io/name: plantuml
app.kubernetes.io/instance: plantuml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: plantuml
labels:
app.kubernetes.io/name: plantuml
app.kubernetes.io/instance: plantuml
spec:
rules:
- host: plantuml-example.localhost
http:
paths:
- backend:
service:
name: plantuml
port:
number: 80
path: /
pathType: Prefix

View File

@ -0,0 +1,119 @@
# Nginx reverse proxy example with defined location directive
In this example, the reverse proxy is defined only under the `/plantuml` context path.
All other context paths (locations) are not affected and are freely available.
This allows the server to be used for more than "just" PlantUML.
References:
- [Nginx documentation](https://nginx.org/en/docs/)
- [Nginx beginner's guide](https://nginx.org/en/docs/beginners_guide.html)
## Quick start
Be sure to have [`docker-compose.yml`](./docker-compose.yml) and [`nginx.conf`](./nginx.conf) inside your current working directory.
```bash
# start nginx and plantuml server
docker-compose up -d
# stop nginx and plantuml server
docker-compose down
```
Check with `docker ps` if both container are up and running:
```
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
217e753a0dcf plantuml/plantuml-server:jetty "/entrypoint.sh" 4 seconds ago Up 3 seconds 8080/tcp plantuml-server
9b1290c100f5 nginx:alpine "/docker-entrypoint.…" 4 seconds ago Up 3 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp nginx
```
Open [http://localhost/plantuml](http://localhost/plantuml) inside your browser.
YEAH! You are now using PlantUML behind a simple Nginx reverse proxy.
## Nginx configuration
```nginx
...
# PlantUML
location /plantuml/ {
proxy_pass http://plantuml-server:8080/plantuml/;
}
...
```
- `location /plantuml/` to reverse only the context path `/plantuml`
- `proxy_pass http://plantuml-server:8080/plantuml/` to set reverse proxy path to plantuml server.
Use the docker container name `plantuml-server` instead of ip addresses.
Also, use the same context path (`BASE_URL`) as PlantUML, which is configurable as an environment variable in the docker-compose file.
NOTE: `BASE_URL`, `location` and therefore the `proxy_pass` should have the some context path!
If that is not possible it may be possible to solve the problem by using NGINX `sub_filter`:
```nginx
# PlantUML
location /plantuml/ {
sub_filter '<base href="/" />' '<base href="/plantuml/" />';
sub_filter_types text/html;
proxy_pass http://plantuml-server:8080/;
}
```
NOTE: Since [PR#256](https://github.com/plantuml/plantuml-server/pull/256) it is possible to use deep base URLs.
So with e.g. `BASE_URL=foo/bar` the following is possible:
```nginx
# PlantUML
location /foo/bar/ {
proxy_pass http://plantuml-server:8080/foo/bar/;
}
```
## Nginx and PlantUML server
```yaml
version: "3"
services:
plantuml-server:
image: plantuml/plantuml-server:jetty
container_name: plantuml-server
environment:
- TZ=Europe/Berlin
- BASE_URL=plantuml
nginx:
image: nginx:alpine
container_name: nginx
ports:
- "80:80"
environment:
- TZ=Europe/Berlin
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
```
- Set `container_name` to use them instead of e.g. ip addresses
- Set the environment `TZ` the ensure the same timezone.
For example to server timezone (`cat /etc/timezone`)?
- plantuml-server
* plantuml-server already exposes port `8080` to it's own local network (but not outside).
Since plantuml-server and nginx are sharing a network, nginx can reach plantuml-server without further settings.
* Set the environment `BASE_URL` to the preferred context path
- nginx
* open/link port `80` to the outside
* `./nginx.conf:/etc/nginx/nginx.conf:ro` to use your own Nginx configuration (readonly)
## Useful commands
```bash
# see whats going on inside your docker containers
docker logs --tail 50 --follow --timestamps nginx
docker logs --tail 50 --follow --timestamps plantuml-server
```

View File

@ -0,0 +1,19 @@
version: "3"
services:
plantuml-server:
image: plantuml/plantuml-server:jetty
container_name: plantuml-server
environment:
- TZ=Europe/Berlin
- BASE_URL=plantuml
nginx:
image: nginx:alpine
container_name: nginx
ports:
- "80:80"
environment:
- TZ=Europe/Berlin
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro

View File

@ -0,0 +1,44 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name localhost;
# PlantUML
location /plantuml/ {
proxy_pass http://plantuml-server:8080/plantuml/;
}
}
client_max_body_size 0;
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}

View File

@ -0,0 +1,97 @@
# Nginx simple reverse proxy example
References:
- [Nginx documentation](https://nginx.org/en/docs/)
- [Nginx beginner's guide](https://nginx.org/en/docs/beginners_guide.html)
## Quick start
Be sure to have [`docker-compose.yml`](./docker-compose.yml) and [`nginx.conf`](./nginx.conf) inside your current working directory.
```bash
# start nginx and plantuml server
docker-compose up -d
# stop nginx and plantuml server
docker-compose down
```
Check with `docker ps` if both container are up and running:
```
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
217e753a0dcf plantuml/plantuml-server:jetty "/entrypoint.sh" 4 seconds ago Up 3 seconds 8080/tcp plantuml-server
9b1290c100f5 nginx:alpine "/docker-entrypoint.…" 4 seconds ago Up 3 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp nginx
```
Open [http://localhost](http://localhost) inside your browser.
YEAH! You are now using PlantUML behind a simple Nginx reverse proxy.
## Nginx configuration
```nginx
...
# PlantUML
location / {
proxy_set_header HOST $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://plantuml-server:8080/;
}
...
```
- `location /` to reverse complete server
- `proxy_set_header HOST $host` and `proxy_set_header X-Forwarded-Host $host` to replaces local plantuml server ip with FQDN
- `proxy_set_header X-Forwarded-Proto $scheme` to use reverse proxy protocol schema instead of communication schema between reverse proxy and plantuml server
- `proxy_pass http://plantuml-server:8080/` to set reverse proxy path to plantuml server.
Use the docker container name `plantuml-server` instead of ip addresses.
## Nginx and PlantUML server
```yaml
version: "3"
services:
plantuml-server:
image: plantuml/plantuml-server:jetty
container_name: plantuml-server
environment:
- TZ=Europe/Berlin
nginx:
image: nginx:alpine
container_name: nginx
ports:
- "80:80"
environment:
- TZ=Europe/Berlin
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
```
- Set `container_name` to use them instead of e.g. ip addresses
- Set the environment `TZ` the ensure the same timezone.
For example to server timezone (`cat /etc/timezone`)?
- plantuml-server
* plantuml-server already exposes port `8080` to it's own local network (but not outside).
Since plantuml-server and nginx are sharing a network, nginx can reach plantuml-server without further settings.
- nginx
* open/link port `80` to the outside
* `./nginx.conf:/etc/nginx/nginx.conf:ro` to use your own Nginx configuration (readonly)
## Useful commands
```bash
# see whats going on inside your docker containers
docker logs --tail 50 --follow --timestamps nginx
docker logs --tail 50 --follow --timestamps plantuml-server
```

View File

@ -0,0 +1,18 @@
version: "3"
services:
plantuml-server:
image: plantuml/plantuml-server:jetty
container_name: plantuml-server
environment:
- TZ=Europe/Berlin
nginx:
image: nginx:alpine
container_name: nginx
ports:
- "80:80"
environment:
- TZ=Europe/Berlin
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro

View File

@ -0,0 +1,48 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name localhost;
# PlantUML
location / {
proxy_set_header HOST $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://plantuml-server:8080/;
}
}
client_max_body_size 0;
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}

82
pom.jdk8.xml Normal file
View File

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.sourceforge.plantuml</groupId>
<artifactId>plantumlservlet-parent</artifactId>
<version>1-SNAPSHOT</version>
<relativePath>pom.parent.xml</relativePath>
</parent>
<artifactId>plantumlservlet</artifactId>
<packaging>war</packaging>
<properties>
<java.version>8</java.version>
<!-- build plugin management -->
<!-- no JDK8 support starting version 10.0.0 -->
<checkstyle.version>9.3</checkstyle.version>
<!-- plugins -->
<!-- no JDK8 support starting version 2.5.0 -->
<resources-optimizer-maven-plugin.version>2.4.4</resources-optimizer-maven-plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>apache-jsp</artifactId>
<version>${apache-jsp.version}</version>
<scope>${apache-jsp.scope}</scope>
<exclusions>
<exclusion>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-jakarta-servlet-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-schemas</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-annotations</artifactId>
<version>${jetty-annotations.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-jakarta-servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty-server.version}</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-jakarta-servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

673
pom.parent.xml Normal file
View File

@ -0,0 +1,673 @@
<?xml version="1.0" encoding="UTF-8"?>
<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<modelVersion>4.0.0</modelVersion>
<groupId>org.sourceforge.plantuml</groupId>
<artifactId>plantumlservlet-parent</artifactId>
<version>1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>PlantUML Servlet</name>
<url>https://plantuml.github.io/plantuml-server/index.html</url>
<properties>
<!-- NOTE: property `java.version` has to be set inside the child pom. -->
<!-- <java.version>XX</java.version> -->
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!--
Skip tests by default.
Run tests manually:
- mvn test -DskipTests=false
- mvn test -DskipTests=false -DargLine="-Dsystem.test.server=http://localhost:8080/plantuml"
-->
<skipTests>true</skipTests>
<!--
JS and CSS compression / minify
If false minify is enabled.
Dev Tipp: set to `true` and run `mvn fizzed-watcher:run` while developing the frontend
-->
<withoutCSSJSCompress>false</withoutCSSJSCompress>
<!--
This artifact is required for:
1. EmbeddedJettyServer -> scope: test
2. Tomcat docker image -> scope: compile
BUT: Jetty docker image as well as jetty-runner will crash on runtime if
this artifact is included because it's already provided so that the
artifact would apear multiple times on the classpath.
You can test it via: `mvn jetty:run [-Dapache-jsp.scope=compile]`
Error: java.util.ServiceConfigurationError: org.apache.juli.logging.Log: org.eclipse.jetty.apache.jsp.JuliLog not a subtype
HENCE: Default is the "test" scope and for Tomcat docker image building add:
-Dapache-jsp.scope=compile
-->
<apache-jsp.scope>test</apache-jsp.scope>
<maven.build.timestamp.format>yyyyMMdd-HHmm</maven.build.timestamp.format>
<timestamp>${maven.build.timestamp}</timestamp>
<wtp.version>1.5</wtp.version>
<wtp.contextName>plantuml</wtp.contextName>
<jetty.http.port>8080</jetty.http.port>
<jetty.contextpath>/${wtp.contextName}</jetty.contextpath>
<!-- main versions -->
<plantuml.version>1.2024.5</plantuml.version>
<!-- Please keep the jetty version identical with the docker image -->
<jetty.version>11.0.18</jetty.version>
<!--
While changing the version, please update the versions in the following files as well:
- src/main/webapp/components/app-head.jsp (script import)
- src/main/webapp/components/editor/editor.js : loadMonacoCodeEditorAsync (require.config)
- src/test/java/net/sourceforge/plantuml/servlet/TestDependencies.java : testMonacoEditorWebJar (JUnit Test)
-->
<monaco-editor.version>0.36.1</monaco-editor.version>
<!-- dependencies -->
<jstl.version>1.2</jstl.version>
<apache-jsp.version>${jetty.version}</apache-jsp.version>
<jetty-annotations.version>${jetty.version}</jetty-annotations.version>
<glassfish-jstl.version>${jetty.version}</glassfish-jstl.version>
<!-- jlatexmath -->
<jlatexmath.version>1.0.7</jlatexmath.version>
<jlatexmath-font-greek.version>${jlatexmath.version}</jlatexmath-font-greek.version>
<jlatexmath-font-cyrillic.version>${jlatexmath.version}</jlatexmath-font-cyrillic.version>
<!-- PDF -->
<batik.version>1.16</batik.version>
<fop.version>2.8</fop.version>
<!-- Testing -->
<junit.version>5.9.3</junit.version>
<junit-suite.version>1.9.3</junit-suite.version>
<selenium.version>4.10.0</selenium.version>
<selenium-webdrivermanager.version>5.3.3</selenium-webdrivermanager.version>
<commons-io.version>2.11.0</commons-io.version>
<jetty-server.version>${jetty.version}</jetty-server.version>
<!-- build plugin management -->
<!-- lock down plugins versions to avoid using Maven defaults -->
<maven-clean-plugin.version>3.2.0</maven-clean-plugin.version>
<maven-dependency-plugin.version>3.5.0</maven-dependency-plugin.version>
<maven-resources-plugin.version>3.3.1</maven-resources-plugin.version>
<maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version>
<versions-maven-plugin.version>2.15.0</versions-maven-plugin.version>
<maven-surefire-plugin.version>3.1.0</maven-surefire-plugin.version>
<maven-war-plugin.version>3.3.2</maven-war-plugin.version>
<maven-install-plugin.version>3.1.1</maven-install-plugin.version>
<maven-deploy-plugin.version>3.1.1</maven-deploy-plugin.version>
<maven-site-plugin.version>3.12.1</maven-site-plugin.version>
<maven-project-info-reports-plugin.version>3.4.3</maven-project-info-reports-plugin.version>
<maven-checkstyle-plugin.version>3.2.2</maven-checkstyle-plugin.version>
<checkstyle.version>10.12.0</checkstyle.version>
<!-- plugins -->
<maven-eclipse-plugin.version>2.10</maven-eclipse-plugin.version>
<jetty-runner.version>${jetty.version}</jetty-runner.version>
<jetty-maven-plugin.version>${jetty.version}</jetty-maven-plugin.version>
<duplicate-finder-maven-plugin.version>1.5.1</duplicate-finder-maven-plugin.version>
<maven-javadoc-plugin.version>3.5.0</maven-javadoc-plugin.version>
<resources-optimizer-maven-plugin.version>2.5.6</resources-optimizer-maven-plugin.version>
<fizzed-watcher-maven-plugin.verson>1.0.6</fizzed-watcher-maven-plugin.verson>
</properties>
<dependencies>
<dependency>
<groupId>net.sourceforge.plantuml</groupId>
<artifactId>plantuml</artifactId>
<version>${plantuml.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.webjars.npm</groupId>
<artifactId>monaco-editor</artifactId>
<version>${monaco-editor.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>apache-jsp</artifactId>
<version>${apache-jsp.version}</version>
<scope>${apache-jsp.scope}</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-annotations</artifactId>
<version>${jetty-annotations.version}</version>
<scope>provided</scope>
</dependency>
<!-- jlatexmath -->
<dependency>
<groupId>org.scilab.forge</groupId>
<artifactId>jlatexmath</artifactId>
<version>${jlatexmath.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.scilab.forge</groupId>
<artifactId>jlatexmath-font-greek</artifactId>
<version>${jlatexmath-font-greek.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.scilab.forge</groupId>
<artifactId>jlatexmath-font-cyrillic</artifactId>
<version>${jlatexmath-font-cyrillic.version}</version>
<scope>runtime</scope>
</dependency>
<!-- PDF
PlantUMLs PDF generation requires:
- batik-dom
- batik-svgrasterizer (includes batik-dom)
- batik-svggen
- fop-core
-->
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-svgrasterizer</artifactId>
<version>${batik.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-svggen</artifactId>
<version>${batik.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>fop-core</artifactId>
<version>${fop.version}</version>
<scope>runtime</scope>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<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>
<version>${junit-suite.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>${selenium-webdrivermanager.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty-server.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>plantuml</finalName>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>${maven-clean-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>${maven-dependency-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>${maven-resources-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>${maven-war-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>${maven-install-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>${maven-deploy-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>${maven-site-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>${maven-project-info-reports-plugin.version}</version>
</plugin>
<!-- set up java style rules -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>${maven-checkstyle-plugin.version}</version>
<dependencies>
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>${checkstyle.version}</version>
</dependency>
</dependencies>
<configuration>
<configLocation>${basedir}/src/main/config/checkstyle.xml</configLocation>
<linkXRef>false</linkXRef>
<consoleOutput>true</consoleOutput>
<failsOnError>true</failsOnError>
</configuration>
</plugin>
<!-- set up version validation rules -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>${versions-maven-plugin.version}</version>
<configuration>
<outputFile>${project.build.directory}/outdated-dependencies.txt</outputFile>
<rulesUri>file:///${basedir}/src/main/config/rules.xml</rulesUri>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<!-- set java compile version -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<!-- configure surefire to skip unit tests if skipTests is set -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<skipTests>${skipTests}</skipTests>
</configuration>
</plugin>
<!-- configure plugin for project's reports -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>${maven-site-plugin.version}</version>
</plugin>
<!-- setup java style checks -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>${maven-checkstyle-plugin.version}</version>
<executions>
<execution>
<?m2e execute onConfiguration,onIncremental?>
<phase>validate</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- check and display possible version updates during validation
manual execution:
- mvn versions:display-property-updates
- mvn versions:display-dependency-updates
-->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>${versions-maven-plugin.version}</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>display-property-updates</goal>
<goal>display-dependency-updates</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- check for duplicate classes/resources
also see:
- mvn dependency:analyze
- mvn dependency:tree
-->
<plugin>
<groupId>org.basepom.maven</groupId>
<artifactId>duplicate-finder-maven-plugin</artifactId>
<version>${duplicate-finder-maven-plugin.version}</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<configuration>
<ignoredResourcePatterns>
<ignoredResourcePattern>^about\.html$</ignoredResourcePattern>
<ignoredResourcePattern>^license/LICENSE\.dom-software\.txt$</ignoredResourcePattern>
<!-- <ignoredResourcePattern>^org/apache/batik/apps/rasterizer/resources/rasterizer\.policy$</ignoredResourcePattern> -->
</ignoredResourcePatterns>
</configuration>
</plugin>
<!-- check for missing java documentation -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>${maven-javadoc-plugin.version}</version>
<configuration>
<show>private</show>
<nohelp>true</nohelp>
<source>${java.version}</source>
<failOnWarnings>true</failOnWarnings>
</configuration>
</plugin>
<!-- provide dependencies -->
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>${maven-dependency-plugin.version}</version>
<executions>
<!-- provide webjars for the embedded jetty server for junit tests -->
<execution>
<?m2e execute onConfiguration,onIncremental?>
<id>unpack-resources</id>
<phase>generate-test-sources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.webjars.npm</groupId>
<artifactId>monaco-editor</artifactId>
<version>${monaco-editor.version}</version>
<includes>**/min/vs/loader.js,**/min/vs/**/*,**/min-maps/vs/**/*</includes>
<outputDirectory>${project.build.outputDirectory}</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
<!-- provide jetty-runner -->
<execution>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-runner</artifactId>
<version>${jetty-runner.version}</version>
<destFileName>jetty-runner.jar</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<!-- configure eclipse web tools platform (WTP) -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>${maven-eclipse-plugin.version}</version>
<configuration>
<wtpversion>${wtp.version}</wtpversion>
<wtpContextName>${wtp.contextName}</wtpContextName>
</configuration>
</plugin>
<!-- configure jetty -->
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>${jetty-maven-plugin.version}</version>
<configuration>
<!-- jetty.xml
Only necessary to support old proxy.
The old proxy needs empty path segments support in URIs.
Hence: allow AMBIGUOUS_EMPTY_SEGMENT
-->
<jettyXmls>${basedir}/src/main/config/jetty.xml</jettyXmls>
<scanIntervalSeconds>5</scanIntervalSeconds>
<webApp>
<contextPath>${jetty.contextpath}</contextPath>
</webApp>
</configuration>
</plugin>
<!-- configure java server pages (JSP) web resources -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>${maven-war-plugin.version}</version>
<configuration>
<webResources>
<resource>
<directory>${basedir}/src/main/webapp</directory>
<includes>
<include>*.jspf</include>
</includes>
<filtering>true</filtering>
</resource>
</webResources>
</configuration>
</plugin>
<!-- remove minified web resources (css, js) before regeneration -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<executions>
<execution>
<id>clean-minified-resources</id>
<phase>initialize</phase>
<goals>
<goal>clean</goal>
</goals>
<configuration>
<excludeDefaultDirectories>true</excludeDefaultDirectories>
<filesets>
<fileset>
<directory>${basedir}/src/main/webapp/min</directory>
</fileset>
</filesets>
</configuration>
</execution>
</executions>
</plugin>
<!-- optimize/minimize web resources (css, js) -->
<plugin>
<groupId>org.primefaces.extensions</groupId>
<artifactId>resources-optimizer-maven-plugin</artifactId>
<version>${resources-optimizer-maven-plugin.version}</version>
<executions>
<execution>
<id>optimize</id>
<phase>generate-resources</phase>
<goals>
<goal>optimize</goal>
</goals>
</execution>
</executions>
<configuration>
<warningLevel>DEFAULT</warningLevel>
<failOnWarning>true</failOnWarning>
<suffix>.min</suffix>
<languageIn>ECMASCRIPT_2020</languageIn>
<languageOut>ECMASCRIPT5_STRICT</languageOut>
<emitUseStrict>true</emitUseStrict>
<resourcesSets>
<!-- combine and optimize all JS files for the web server except the PlantUML JS language features -->
<resourcesSet>
<inputDir>${basedir}/src/main/webapp</inputDir>
<includes>
<include>components/**/*.js</include>
<include>js/**/*.js</include>
</includes>
<excludes>
<exclude>js/language/**</exclude>
</excludes>
<aggregations>
<aggregation>
<withoutCompress>${withoutCSSJSCompress}</withoutCompress>
<removeIncluded>false</removeIncluded>
<outputFile>${basedir}/src/main/webapp/min/plantuml.min.js</outputFile>
</aggregation>
</aggregations>
</resourcesSet>
<!-- combine and optimize all PlantUML JS language features -->
<resourcesSet>
<inputDir>${basedir}/src/main/webapp/js/language</inputDir>
<includes>
<include>language.js</include>
<include>validation/validation.js</include>
<include>**/*.js</include>
</includes>
<aggregations>
<aggregation>
<withoutCompress>${withoutCSSJSCompress}</withoutCompress>
<removeIncluded>false</removeIncluded>
<outputFile>${basedir}/src/main/webapp/min/plantuml-language.min.js</outputFile>
</aggregation>
</aggregations>
</resourcesSet>
<!-- combine and optimize all web server style files -->
<resourcesSet>
<inputDir>${basedir}/src/main/webapp/components</inputDir>
<includes>
<include>**/*.css</include>
</includes>
<aggregations>
<aggregation>
<withoutCompress>${withoutCSSJSCompress}</withoutCompress>
<removeIncluded>false</removeIncluded>
<outputFile>${basedir}/src/main/webapp/min/plantuml.min.css</outputFile>
</aggregation>
</aggregations>
</resourcesSet>
</resourcesSets>
</configuration>
</plugin>
<!-- watch for changes in web resources (css, js) and regenerate minified resources (only for development) -->
<plugin>
<groupId>com.fizzed</groupId>
<artifactId>fizzed-watcher-maven-plugin</artifactId>
<version>${fizzed-watcher-maven-plugin.verson}</version>
<configuration>
<watches>
<watch>
<directory>${basedir}/src/main/webapp/components</directory>
<recursive>true</recursive>
<includes>
<include>*.js</include>
<include>*.css</include>
</includes>
<excludes>
<exclude>*.min.js</exclude>
<exclude>*.min.css</exclude>
</excludes>
</watch>
</watches>
<goals>
<goal>clean:clean@clean-minified-resources</goal>
<goal>org.primefaces.extensions:resources-optimizer-maven-plugin:optimize</goal>
</goals>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>${maven-project-info-reports-plugin.version}</version>
<reportSets>
<reportSet>
<reports>
<report>index</report>
<report>dependencies</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>${versions-maven-plugin.version}</version>
<reportSets>
<reportSet>
<reports>
<!-- <report>dependency-updates-report</report> -->
<!-- <report>plugin-updates-report</report> -->
<report>property-updates-report</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>${maven-javadoc-plugin.version}</version>
<reportSets>
<reportSet>
<id>html</id>
<reports>
<report>javadoc</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>${maven-checkstyle-plugin.version}</version>
<reportSets>
<reportSet>
<reports>
<report>checkstyle</report>
<report>checkstyle-aggregate</report>
</reports>
</reportSet>
</reportSets>
</plugin>
</plugins>
</reporting>
</project>

246
pom.xml
View File

@ -1,236 +1,22 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<?xml version="1.0" encoding="UTF-8"?>
<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<modelVersion>4.0.0</modelVersion>
<groupId>org.sourceforge.plantuml</groupId>
<parent>
<groupId>org.sourceforge.plantuml</groupId>
<artifactId>plantumlservlet-parent</artifactId>
<version>1-SNAPSHOT</version>
<relativePath>pom.parent.xml</relativePath>
</parent>
<artifactId>plantumlservlet</artifactId>
<version>1-SNAPSHOT</version>
<name>PlantUML Servlet</name>
<packaging>war</packaging>
<build>
<finalName>plantuml</finalName>
<plugins>
<plugin>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.8</version>
<configuration>
<wtpversion>1.5</wtpversion>
<wtpContextName>plantuml</wtpContextName>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>${jetty.version}</version>
<configuration>
<scanIntervalSeconds>5</scanIntervalSeconds>
<webApp>
<contextPath>${jetty.contextpath}</contextPath>
</webApp>
<systemProperties>
<systemProperty>
<name>jetty.port</name>
<value>${jetty.port}</value>
</systemProperty>
</systemProperties>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>copy</goal></goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-runner</artifactId>
<version>8.1.9.v20130131</version>
<destFileName>jetty-runner.jar</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.3</version>
<configuration>
<webResources>
<resource>
<directory>src/main/webapp</directory>
<includes>
<include>*.jspf</include>
</includes>
<filtering>true</filtering>
</resource>
</webResources>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.14.1</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.0-beta-3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>2.11</version>
<executions>
<execution>
<id>validate</id>
<phase>validate</phase>
<configuration>
<configLocation>${basedir}/src/main/config/checkstyle.xml</configLocation>
<encoding>UTF-8</encoding>
<consoleOutput>true</consoleOutput>
<failsOnError>true</failsOnError>
<linkXRef>false</linkXRef>
</configuration>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>2.6</version>
<reportSets>
<reportSet>
<reports><!-- select reports -->
<report>index</report>
<report>dependencies</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<configuration>
<show>private</show>
<nohelp>true</nohelp>
</configuration>
<reportSets>
<reportSet>
<id>html</id>
<reports>
<report>javadoc</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>2.11</version>
<configuration>
<configLocation>${basedir}/src/main/config/checkstyle.xml</configLocation>
<encoding>UTF-8</encoding>
<linkXRef>false</linkXRef>
</configuration>
<reportSets>
<reportSet>
<reports>
<report>checkstyle</report>
</reports>
</reportSet>
</reportSets>
</plugin>
</plugins>
</reporting>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jetty.version>8.0.4.v20111024</jetty.version>
<jetty.port>8080</jetty.port>
<jetty.contextpath>/plantuml</jetty.contextpath>
<maven.build.timestamp.format>yyyyMMdd-HHmm
</maven.build.timestamp.format>
<timestamp>${maven.build.timestamp}</timestamp>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>net.sourceforge.plantuml</groupId>
<artifactId>plantuml</artifactId>
<version>1.2020.6</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>codemirror</artifactId>
<version>3.21</version>
</dependency>
<dependency>
<groupId>HTTPClient</groupId>
<artifactId>HTTPClient</artifactId>
<version>0.3-3</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>httpunit</groupId>
<artifactId>httpunit</artifactId>
<version>1.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>rhino</groupId>
<artifactId>js</artifactId>
<version>1.7R2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-all</artifactId>
<version>${jetty.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jsp-2.1-glassfish</artifactId>
<version>2.1.v20100127</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

View File

@ -1,126 +1,121 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN" "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<!--
This configuration file was written by the eclipse-cs plugin configuration editor
-->
<!--
Checkstyle-Configuration: checkstyle
Description: none
-->
<module name="Checker">
<property name="severity" value="error"/>
<property name="charset" value="UTF-8"/>
<module name="TreeWalker">
<module name="JavadocMethod">
<property name="severity" value="ignore"/>
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
</module>
<module name="JavadocType">
<property name="severity" value="ignore"/>
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
</module>
<module name="JavadocVariable">
<property name="severity" value="ignore"/>
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
</module>
<module name="JavadocStyle">
<property name="severity" value="ignore"/>
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
</module>
<module name="ConstantName"/>
<module name="LocalFinalVariableName"/>
<module name="LocalVariableName"/>
<module name="MemberName"/>
<module name="MethodName"/>
<module name="PackageName"/>
<module name="ParameterName"/>
<module name="StaticVariableName"/>
<module name="TypeName"/>
<module name="AvoidStarImport"/>
<module name="IllegalImport"/>
<module name="RedundantImport"/>
<module name="UnusedImports"/>
<module name="LineLength">
<property name="max" value="120"/>
<property name="tabWidth" value="4"/>
</module>
<module name="MethodLength"/>
<module name="ParameterNumber"/>
<module name="EmptyForIteratorPad"/>
<module name="MethodParamPad"/>
<module name="NoWhitespaceAfter"/>
<module name="NoWhitespaceBefore"/>
<module name="OperatorWrap"/>
<module name="ParenPad"/>
<module name="TypecastParenPad"/>
<module name="WhitespaceAfter"/>
<module name="WhitespaceAround"/>
<module name="ModifierOrder">
<property name="severity" value="ignore"/>
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
</module>
<module name="RedundantModifier"/>
<module name="AvoidNestedBlocks"/>
<module name="EmptyBlock">
<property name="severity" value="ignore"/>
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
</module>
<module name="LeftCurly"/>
<module name="NeedBraces"/>
<module name="RightCurly"/>
<module name="AvoidInlineConditionals">
<property name="severity" value="ignore"/>
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
</module>
<module name="EmptyStatement"/>
<module name="EqualsHashCode"/>
<module name="HiddenField"/>
<module name="IllegalInstantiation"/>
<module name="InnerAssignment"/>
<module name="MagicNumber">
<property name="severity" value="ignore"/>
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
</module>
<module name="MissingSwitchDefault"/>
<module name="RedundantThrows"/>
<module name="SimplifyBooleanExpression">
<property name="severity" value="ignore"/>
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
</module>
<module name="SimplifyBooleanReturn"/>
<module name="DesignForExtension">
<property name="severity" value="ignore"/>
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
</module>
<module name="FinalClass"/>
<module name="HideUtilityClassConstructor"/>
<module name="InterfaceIsType"/>
<module name="VisibilityModifier"/>
<module name="ArrayTypeStyle"/>
<module name="FinalParameters">
<property name="severity" value="ignore"/>
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
</module>
<module name="TodoComment"/>
<module name="UpperEll"/>
<property name="charset" value="UTF-8" />
<property name="severity" value="error" />
<module name="FileLength" />
<module name="FileTabCharacter">
<property name="eachLine" value="true" />
</module>
<module name="JavadocPackage">
<property name="severity" value="ignore"/>
<property name="allowLegacy" value="true"/>
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit" />
<property name="allowLegacy" value="true" />
<property name="severity" value="ignore" />
</module>
<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">
<property name="severity" value="ignore"/>
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit"/>
</module>
<module name="Translation"/>
<module name="FileLength"/>
<module name="FileTabCharacter">
<property name="eachLine" value="true"/>
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit" />
<property name="severity" value="ignore" />
</module>
<module name="RegexpSingleline">
<property name="format" value="\s+$"/>
<property name="message" value="Line has trailing spaces."/>
<property name="format" value="\s+$" />
<property name="message" value="Line has trailing spaces." />
</module>
<module name="Translation" />
<module name="SuppressWarningsFilter" />
<module name="TreeWalker">
<module name="ArrayTypeStyle" />
<module name="AvoidInlineConditionals">
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit" />
<property name="severity" value="ignore" />
</module>
<module name="AvoidNestedBlocks" />
<module name="AvoidStarImport" />
<module name="ConstantName" />
<module name="DesignForExtension">
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit" />
<property name="severity" value="ignore" />
</module>
<module name="EmptyBlock">
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit" />
<property name="severity" value="ignore" />
</module>
<module name="EmptyForIteratorPad" />
<module name="EmptyStatement" />
<module name="EqualsHashCode" />
<module name="FinalClass" />
<module name="FinalParameters">
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit" />
<property name="severity" value="ignore" />
</module>
<module name="HiddenField" />
<module name="HideUtilityClassConstructor" />
<module name="IllegalImport" />
<module name="IllegalInstantiation" />
<module name="InnerAssignment" />
<module name="InterfaceIsType" />
<module name="JavadocMethod">
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit" />
<property name="severity" value="ignore" />
</module>
<module name="JavadocStyle">
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit" />
<property name="severity" value="ignore" />
</module>
<module name="JavadocType">
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit" />
<property name="severity" value="ignore" />
</module>
<module name="JavadocVariable">
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit" />
<property name="severity" value="ignore" />
</module>
<module name="LeftCurly" />
<module name="LocalFinalVariableName" />
<module name="LocalVariableName" />
<module name="MagicNumber">
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit" />
<property name="severity" value="ignore" />
</module>
<module name="MemberName" />
<module name="MethodLength" />
<module name="MethodName" />
<module name="MethodParamPad" />
<module name="MissingSwitchDefault" />
<module name="ModifierOrder">
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit" />
<property name="severity" value="ignore" />
</module>
<module name="NeedBraces" />
<module name="NoWhitespaceAfter" />
<module name="NoWhitespaceBefore" />
<module name="OperatorWrap" />
<module name="PackageName" />
<module name="ParameterName" />
<module name="ParameterNumber" />
<module name="ParenPad" />
<module name="RedundantImport" />
<module name="RedundantModifier" />
<module name="RightCurly" />
<module name="SimplifyBooleanExpression">
<metadata name="net.sf.eclipsecs.core.lastEnabledSeverity" value="inherit" />
<property name="severity" value="ignore" />
</module>
<module name="SimplifyBooleanReturn" />
<module name="StaticVariableName" />
<module name="TodoComment" />
<module name="TypecastParenPad" />
<module name="TypeName" />
<module name="UnusedImports" />
<module name="UpperEll" />
<module name="VisibilityModifier" />
<module name="WhitespaceAfter" />
<module name="WhitespaceAround" />
<module name="SuppressWarningsHolder" />
</module>
</module>

78
src/main/config/jetty.xml Normal file
View File

@ -0,0 +1,78 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<!-- =============================================================== -->
<!-- Configure a Jetty Server instance with an ID "Server" -->
<!-- Other configuration files may also configure the "Server" -->
<!-- ID, in which case they are adding configuration to the same -->
<!-- instance. If other configuration have a different ID, they -->
<!-- will create and configure another instance of Jetty. -->
<!-- Consult the javadoc of o.e.j.server.Server for all -->
<!-- configuration that may be set here. -->
<!-- =============================================================== -->
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<!-- =========================================================== -->
<!-- Http Configuration. -->
<!-- This is a common configuration instance used by all -->
<!-- connectors that can carry HTTP semantics (HTTP, HTTPS, etc.)-->
<!-- It configures the non wire protocol aspects of the HTTP -->
<!-- semantic. -->
<!-- -->
<!-- This configuration is only defined here and is used by -->
<!-- reference from other XML files such as jetty-http.xml, -->
<!-- jetty-https.xml and other configuration files which -->
<!-- instantiate the connectors. -->
<!-- -->
<!-- Consult the javadoc of o.e.j.server.HttpConfiguration -->
<!-- for all configuration that may be set here. -->
<!-- =========================================================== -->
<New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
<Set name="secureScheme" property="jetty.httpConfig.secureScheme"/>
<Set name="securePort" property="jetty.httpConfig.securePort"/>
<Set name="outputBufferSize" property="jetty.httpConfig.outputBufferSize"/>
<Set name="outputAggregationSize" property="jetty.httpConfig.outputAggregationSize"/>
<Set name="requestHeaderSize" property="jetty.httpConfig.requestHeaderSize"/>
<Set name="responseHeaderSize" property="jetty.httpConfig.responseHeaderSize"/>
<Set name="sendServerVersion" property="jetty.httpConfig.sendServerVersion"/>
<Set name="sendDateHeader"><Property name="jetty.httpConfig.sendDateHeader" default="false"/></Set>
<Set name="headerCacheSize" property="jetty.httpConfig.headerCacheSize"/>
<Set name="delayDispatchUntilContent" property="jetty.httpConfig.delayDispatchUntilContent"/>
<Set name="maxErrorDispatches" property="jetty.httpConfig.maxErrorDispatches"/>
<Set name="persistentConnectionsEnabled" property="jetty.httpConfig.persistentConnectionsEnabled"/>
<Set name="httpCompliance"><Call class="org.eclipse.jetty.http.HttpCompliance" name="from"><Arg><Property name="jetty.httpConfig.compliance" deprecated="jetty.http.compliance" default="RFC7230"/></Arg></Call></Set>
<!-- Changed from "SAFE" to "DEFAULT,AMBIGUOUS_EMPTY_SEGMENT" -->
<Set name="uriCompliance"><Call class="org.eclipse.jetty.http.UriCompliance" name="from"><Arg><Property name="jetty.httpConfig.uriCompliance" default="DEFAULT,AMBIGUOUS_EMPTY_SEGMENT"/></Arg></Call></Set>
<Set name="requestCookieCompliance"><Call class="org.eclipse.jetty.http.CookieCompliance" name="valueOf"><Arg><Property name="jetty.httpConfig.requestCookieCompliance" default="RFC6265"/></Arg></Call></Set>
<Set name="responseCookieCompliance"><Call class="org.eclipse.jetty.http.CookieCompliance" name="valueOf"><Arg><Property name="jetty.httpConfig.responseCookieCompliance" default="RFC6265"/></Arg></Call></Set>
<Set name="relativeRedirectAllowed"><Property name="jetty.httpConfig.relativeRedirectAllowed" default="false"/></Set>
<Set name="useInputDirectByteBuffers" property="jetty.httpConfig.useInputDirectByteBuffers"/>
<Set name="useOutputDirectByteBuffers" property="jetty.httpConfig.useOutputDirectByteBuffers"/>
</New>
<New id="httpConnectionFactory" class="org.eclipse.jetty.server.HttpConnectionFactory">
<Arg name="config"><Ref refid="httpConfig" /></Arg>
</New>
<New id="httpConnector" class="org.eclipse.jetty.server.ServerConnector">
<Arg name="server"><Ref refid="Server" /></Arg>
<Arg name="factories">
<Array type="org.eclipse.jetty.server.ConnectionFactory">
<Item>
<Ref refid="httpConnectionFactory" />
</Item>
</Array>
</Arg>
<!-- Change host and port according to properties. Default is 0.0.0.0 and 8080. -->
<Set name="host"><Property name="jetty.http.host" deprecated="jetty.host" default="0.0.0.0" /></Set>
<Set name="port"><Property name="jetty.http.port" deprecated="jetty.port" default="8080" /></Set>
</New>
<!-- =========================================================== -->
<!-- Add http Connector -->
<!-- =========================================================== -->
<Call name="addConnector">
<Arg><Ref refid="httpConnector" /></Arg>
</Call>
</Configure>

27
src/main/config/rules.xml Normal file
View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<ruleset
comparisonMethod="maven"
xmlns="http://mojo.codehaus.org/versions-maven-plugin/rule/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://mojo.codehaus.org/versions-maven-plugin/rule/2.0.0 https://www.mojohaus.org/versions-maven-plugin/xsd/rule-2.0.0.xsd"
>
<ignoreVersions>
<!-- Ignore Alpha's, Beta's, release candidates and milestones -->
<ignoreVersion type="regex">(?i).*Alpha(?:-?\d+)?</ignoreVersion>
<ignoreVersion type="regex">(?i).*a(?:-?\d+)?</ignoreVersion>
<ignoreVersion type="regex">(?i).*Beta(?:-?\d+)?</ignoreVersion>
<ignoreVersion type="regex">(?i).*-B(?:-?\d+)?</ignoreVersion>
<ignoreVersion type="regex">(?i).*RC(?:-?\d+)?</ignoreVersion>
<ignoreVersion type="regex">(?i).*CR(?:-?\d+)?</ignoreVersion>
<ignoreVersion type="regex">(?i).*M(?:-?\d+)?</ignoreVersion>
<ignoreVersion type="regex">(?i).*-dev((?:-?\d+)|(?:\.20\d{6}))?</ignoreVersion>
</ignoreVersions>
<rules>
<rule groupId="net.sourceforge.plantuml" artifactId="plantuml" comparisonMethod="maven">
<ignoreVersions>
<!-- allow only version like this: 1.20XX.X* -->
<ignoreVersion type="regex"><![CDATA[^(.(?<!1\.20.{2}\.\d))*?$]]></ignoreVersion>
</ignoreVersions>
</rule>
</rules>
</ruleset>

View File

@ -0,0 +1,151 @@
/* ========================================================================
* 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.util.regex.Matcher;
import java.util.regex.Pattern;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.sourceforge.plantuml.code.Transcoder;
import net.sourceforge.plantuml.code.TranscoderUtil;
import net.sourceforge.plantuml.servlet.utility.UrlDataExtractor;
/**
* ASCII encoder and decoder servlet for the webapp.
* This servlet encodes the diagram in text format or decodes the compressed diagram string.
*/
@SuppressWarnings("SERIAL")
public class AsciiCoderServlet extends HttpServlet {
/**
* 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\\-\\_]+)");
/**
* Context path from the servlet mapping URL pattern.
*
* @return servlet context path without leading or tailing slash
*/
protected String getServletContextPath() {
return "coder";
}
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
final String encodedText = getEncodedTextFromUrl(request);
String text = "";
try {
text = getTranscoder().decode(encodedText);
} catch (Exception e) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
e.printStackTrace();
}
response.addHeader("Access-Control-Allow-Origin", "*");
response.setContentType("text/plain;charset=UTF-8");
response.getWriter().write(text);
}
@Override
protected void doPost(
HttpServletRequest request,
HttpServletResponse response
) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
// read textual diagram source from request body
final StringBuilder uml = new StringBuilder();
try (BufferedReader in = request.getReader()) {
String line;
while ((line = in.readLine()) != null) {
uml.append(line).append('\n');
}
}
// encode textual diagram source
String encoded = "";
try {
encoded = getTranscoder().encode(uml.toString());
} catch (Exception e) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
e.printStackTrace();
}
response.addHeader("Access-Control-Allow-Origin", "*");
response.setContentType("text/plain;charset=UTF-8");
response.getWriter().write(encoded);
}
/**
* Get PlantUML transcoder.
*
* @return transcoder instance
*/
protected Transcoder getTranscoder() {
return TranscoderUtil.getDefaultTranscoder();
}
/**
* Get encoded textual diagram source from URL.
*
* @param request http request which contains the source URL
*
* @return if successful encoded textual diagram source from URL; otherwise empty string
*
* @throws IOException if an input or output exception occurred
*/
protected String getEncodedTextFromUrl(HttpServletRequest request) throws IOException {
// textual diagram source from request URI
String url = request.getRequestURI();
final String contextpath = "/" + getServletContextPath() + "/";
if (url.contains(contextpath) && !url.endsWith(contextpath)) {
final String encoded = UrlDataExtractor.getEncodedDiagram(request.getRequestURI(), "");
if (!encoded.isEmpty()) {
return encoded;
}
}
// textual diagram source from "url" parameter
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 url;
}
// nothing found
return "";
}
}

View File

@ -2,7 +2,7 @@
* PlantUML : a free UML diagram generator
* ========================================================================
*
* Project Info: http://plantuml.sourceforge.net
* Project Info: https://plantuml.com
*
* This file is part of PlantUML.
*
@ -25,13 +25,19 @@ package net.sourceforge.plantuml.servlet;
import net.sourceforge.plantuml.FileFormat;
/*
/**
* ASCII servlet of the webapp.
* This servlet produces the UML sequence diagram in text format.
*/
@SuppressWarnings("serial")
@SuppressWarnings("SERIAL")
public class AsciiServlet extends UmlDiagramService {
/**
* Gives the wished output format of the diagram.
* This value is used by the DiagramResponse class.
*
* @return the format for ASCII responses
*/
@Override
public FileFormat getOutputFormat() {
return FileFormat.UTXT;

View File

@ -2,7 +2,7 @@
* PlantUML : a free UML diagram generator
* ========================================================================
*
* Project Info: http://plantuml.sourceforge.net
* Project Info: https://plantuml.com
*
* This file is part of PlantUML.
*
@ -25,13 +25,19 @@ package net.sourceforge.plantuml.servlet;
import net.sourceforge.plantuml.FileFormat;
/*
/**
* Base64 servlet of the webapp.
* This servlet produces the UML diagram in Base64 format.
*/
@SuppressWarnings("serial")
@SuppressWarnings("SERIAL")
public class Base64Servlet extends UmlDiagramService {
/**
* Gives the wished output format of the diagram.
* This value is used by the DiagramResponse class.
*
* @return the format for Base64 responses
*/
@Override
public FileFormat getOutputFormat() {
return FileFormat.BASE64;

View File

@ -2,7 +2,7 @@
* PlantUML : a free UML diagram generator
* ========================================================================
*
* Project Info: http://plantuml.sourceforge.net
* Project Info: https://plantuml.com
*
* This file is part of PlantUML.
*
@ -26,47 +26,45 @@ package net.sourceforge.plantuml.servlet;
import java.io.IOException;
import javax.imageio.IIOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.servlet.utility.UmlExtractor;
import net.sourceforge.plantuml.servlet.utility.UrlDataExtractor;
/*
/**
* Check servlet of the webapp.
* This servlet checks the syntax of the diagram and send a report in TEXT format.
*/
@SuppressWarnings("serial")
@SuppressWarnings("SERIAL")
public class CheckSyntaxServlet extends HttpServlet {
@Override
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 url = request.getRequestURI();
final String uml = UmlExtractor.getUmlSource(UrlDataExtractor.getEncodedDiagram(url, ""));
// generate the response
DiagramResponse dr = new DiagramResponse(response, getOutputFormat(), request);
try {
dr.sendCheck(uml);
} catch (IIOException iioe) {
} catch (IIOException e) {
// Browser has closed the connection, do nothing
}
dr = null;
}
public String getSource(String uri) {
String[] result = uri.split("/check/", 2);
if (result.length != 2) {
return "";
} else {
return result[1];
}
}
/**
* Gives the wished output format of the diagram.
* This value is used by the DiagramResponse class.
*
* @return the format for check responses
*/
public FileFormat getOutputFormat() {
return FileFormat.UTXT;
}

View File

@ -2,7 +2,7 @@
* PlantUML : a free UML diagram generator
* ========================================================================
*
* Project Info: http://plantuml.sourceforge.net
* Project Info: https://plantuml.com
*
* This file is part of PlantUML.
*
@ -23,95 +23,254 @@
*/
package net.sourceforge.plantuml.servlet;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.ByteArrayOutputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.sourceforge.plantuml.BlockUml;
import net.sourceforge.plantuml.ErrorUml;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.OptionFlags;
import net.sourceforge.plantuml.NullOutputStream;
import net.sourceforge.plantuml.SourceStringReader;
import net.sourceforge.plantuml.StringUtils;
import net.sourceforge.plantuml.code.Base64Coder;
import net.sourceforge.plantuml.core.DiagramDescription;
import net.sourceforge.plantuml.utils.Base64Coder;
import net.sourceforge.plantuml.core.Diagram;
import net.sourceforge.plantuml.core.DiagramDescription;
import net.sourceforge.plantuml.core.ImageData;
import net.sourceforge.plantuml.version.Version;
import net.sourceforge.plantuml.error.PSystemError;
import net.sourceforge.plantuml.ErrorUml;
import net.sourceforge.plantuml.preproc.Defines;
import net.sourceforge.plantuml.security.SecurityProfile;
import net.sourceforge.plantuml.security.SecurityUtils;
import net.sourceforge.plantuml.version.Version;
/**
* 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 {
public class DiagramResponse {
private static final String POWERED_BY = "PlantUML Version " + Version.versionString();
private static class BlockSelection {
private final BlockUml block;
private final int systemIdx;
private HttpServletResponse response;
private FileFormat format;
private HttpServletRequest request;
private static final Map<FileFormat, String> CONTENT_TYPE;
static {
Map<FileFormat, String> map = new HashMap<FileFormat, String>();
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;
BlockSelection(BlockUml blk, int idx) {
block = blk;
systemIdx = idx;
}
}
DiagramResponse(HttpServletResponse r, FileFormat f, HttpServletRequest rq) {
response = r;
format = f;
request = rq;
/**
* X-Powered-By http header value included in every response by default.
*/
private static final String POWERED_BY = "PlantUML Version " + Version.versionString();
/**
* PLANTUML_CONFIG_FILE content.
*/
private static final List<String> CONFIG = new ArrayList<>();
/**
* Cache/flag to ensure that the `init()` method is called only once.
*/
private static boolean initialized = false;
static {
init();
}
void sendDiagram(String uml, int idx) throws IOException {
/**
* 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;
}
/**
* Initialize PlantUML configurations and properties as well as loading the PlantUML config file.
*/
public static void init() {
if (initialized) {
return;
}
initialized = true;
// set headless mode manually since otherwise Windows 11 seems to have some issues with it
// see Issue#311 :: https://github.com/plantuml/plantuml-server/issues/311
// NOTE: This can only be set before any awt/X11/... related stuff is loaded
System.setProperty("java.awt.headless", System.getProperty("java.awt.headless", "true"));
// set security profile to INTERNET by default
// NOTE: this property is cached inside PlantUML and cannot be changed after the first call of PlantUML
System.setProperty("PLANTUML_SECURITY_PROFILE", SecurityProfile.INTERNET.toString());
if (System.getenv("PLANTUML_SECURITY_PROFILE") != null) {
System.setProperty("PLANTUML_SECURITY_PROFILE", System.getenv("PLANTUML_SECURITY_PROFILE"));
}
// load properties from file
if (System.getenv("PLANTUML_PROPERTY_FILE") != null) {
try (FileReader propertyFileReader = new FileReader(System.getenv("PLANTUML_PROPERTY_FILE"))) {
System.getProperties().load(propertyFileReader);
} catch (IOException e) {
e.printStackTrace();
}
}
// load PlantUML config file
if (System.getenv("PLANTUML_CONFIG_FILE") != null) {
try (BufferedReader br = new BufferedReader(new FileReader(System.getenv("PLANTUML_CONFIG_FILE")))) {
br.lines().forEach(CONFIG::add);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 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());
SourceStringReader reader = new SourceStringReader(uml);
if (idx < 0) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, String.format("Invalid diagram index: {0}", idx));
return;
}
final SourceStringReader reader = getSourceStringReader(uml);
if (reader == null) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "No UML diagram found");
return;
}
if (format == FileFormat.BASE64) {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final DiagramDescription result = reader.outputImage(baos, idx, new FileFormatOption(FileFormat.PNG));
baos.close();
final String encodedBytes = "data:image/png;base64,"
+ Base64Coder.encodeLines(baos.toByteArray()).replaceAll("\\s", "");
byte[] imageBytes;
try (ByteArrayOutputStream outstream = new ByteArrayOutputStream()) {
reader.outputImage(outstream, idx, new FileFormatOption(FileFormat.PNG));
imageBytes = outstream.toByteArray();
}
final String base64 = Base64Coder.encodeLines(imageBytes).replaceAll("\\s", "");
final String encodedBytes = "data:image/png;base64," + base64;
response.getOutputStream().write(encodedBytes.getBytes());
return;
}
final BlockUml blockUml = reader.getBlocks().get(0);
if (notModified(blockUml)) {
addHeaderForCache(blockUml);
final BlockSelection blockSelection = getOutputBlockSelection(reader, idx);
if (blockSelection == null) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
if (notModified(blockSelection.block)) {
addHeaderForCache(blockSelection.block);
response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
if (StringUtils.isDiagramCacheable(uml)) {
addHeaderForCache(blockUml);
addHeaderForCache(blockSelection.block);
}
final Diagram diagram = blockUml.getDiagram();
final Diagram diagram = blockSelection.block.getDiagram();
if (diagram instanceof PSystemError) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
final ImageData result = diagram.exportDiagram(response.getOutputStream(), idx, new FileFormatOption(format));
diagram.exportDiagram(response.getOutputStream(), blockSelection.systemIdx, new FileFormatOption(format));
}
private BlockSelection getOutputBlockSelection(SourceStringReader reader, int numImage) {
if (numImage < 0) {
return null;
}
Collection<BlockUml> blocks = reader.getBlocks();
if (blocks.isEmpty()) {
return null;
}
for (BlockUml b : blocks) {
final Diagram system = b.getDiagram();
final int nbInSystem = system.getNbImages();
if (numImage < nbInSystem) {
return new BlockSelection(b, numImage);
}
numImage -= nbInSystem;
}
return null;
}
private SourceStringReader getSourceStringReader(String uml) {
SourceStringReader reader = getSourceStringReaderWithConfig(uml);
if (reader.getBlocks().isEmpty()) {
uml = "@startuml\n" + uml + "\n@enduml";
reader = getSourceStringReaderWithConfig(uml);
if (reader.getBlocks().isEmpty()) {
return null;
}
}
return reader;
}
private SourceStringReader getSourceStringReaderWithConfig(String uml) {
final Defines defines = getPreProcDefines();
SourceStringReader reader = new SourceStringReader(defines, uml, CONFIG);
if (!CONFIG.isEmpty() && reader.getBlocks().get(0).getDiagram().getWarningOrError() != null) {
reader = new SourceStringReader(defines, uml);
}
return reader;
}
/**
* Get PlantUML preprocessor defines.
*
* @return preprocessor defines
*/
private Defines getPreProcDefines() {
final Defines defines;
if (SecurityUtils.getSecurityProfile() == SecurityProfile.UNSECURE) {
// set dirpath to current dir but keep filename and filenameNoExtension undefined
defines = Defines.createWithFileName(new java.io.File("dummy.puml"));
defines.overrideFilename("");
} else {
defines = Defines.createEmpty();
}
return defines;
}
/**
* 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");
@ -125,32 +284,75 @@ class DiagramResponse {
return ifNoneMatch.contains(etag);
}
void sendMap(String uml) throws IOException {
/**
* Produce and send the image map of the uml diagram in HTML format.
*
* @param uml textual UML diagram source
* @param idx diagram index of {@code uml} to send
*
* @throws IOException if an input or output exception occurred
*/
public void sendMap(String uml, int idx) throws IOException {
response.addHeader("Access-Control-Allow-Origin", "*");
response.setContentType(getContentType());
SourceStringReader reader = new SourceStringReader(uml);
final BlockUml blockUml = reader.getBlocks().get(0);
if (StringUtils.isDiagramCacheable(uml)) {
addHeaderForCache(blockUml);
if (idx < 0) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, String.format("Invalid diagram index: {0}", idx));
return;
}
final Diagram diagram = blockUml.getDiagram();
ImageData map = diagram.exportDiagram(new NullOutputStream(), 0,
new FileFormatOption(FileFormat.PNG, false));
final SourceStringReader reader = getSourceStringReader(uml);
if (reader == null) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "No UML diagram found");
return;
}
final BlockSelection blockSelection = getOutputBlockSelection(reader, idx);
if (blockSelection == null) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
if (StringUtils.isDiagramCacheable(uml)) {
addHeaderForCache(blockSelection.block);
}
final Diagram diagram = blockSelection.block.getDiagram();
if (diagram instanceof PSystemError) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
ImageData map = diagram.exportDiagram(
new NullOutputStream(),
blockSelection.systemIdx,
new FileFormatOption(FileFormat.PNG, false)
);
if (map.containsCMapData()) {
PrintWriter httpOut = response.getWriter();
final String cmap = map.getCMapData("plantuml");
httpOut.print(cmap);
}
}
}
void sendCheck(String uml) throws IOException {
/**
* 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
@ -174,15 +376,24 @@ class DiagramResponse {
addHeaders(response);
}
public static void addHeaders(HttpServletResponse response) {
/**
* Add default headers to response.
*
* @param response http response
*/
private 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");
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);
return format.getMimeType();
}
}

View File

@ -2,7 +2,7 @@
* PlantUML : a free UML diagram generator
* ========================================================================
*
* Project Info: http://plantuml.sourceforge.net
* Project Info: https://plantuml.com
*
* This file is part of PlantUML.
*
@ -25,13 +25,19 @@ package net.sourceforge.plantuml.servlet;
import net.sourceforge.plantuml.FileFormat;
/*
/**
* EPS servlet of the webapp.
* This servlet produces the UML diagram in EPS format.
*/
@SuppressWarnings("serial")
@SuppressWarnings("SERIAL")
public class EpsServlet extends UmlDiagramService {
/**
* Gives the wished output format of the diagram.
* This value is used by the DiagramResponse class.
*
* @return the format for EPS responses
*/
@Override
public FileFormat getOutputFormat() {
return FileFormat.EPS;

View File

@ -2,7 +2,7 @@
* PlantUML : a free UML diagram generator
* ========================================================================
*
* Project Info: http://plantuml.sourceforge.net
* Project Info: https://plantuml.com
*
* This file is part of PlantUML.
*
@ -25,13 +25,19 @@ package net.sourceforge.plantuml.servlet;
import net.sourceforge.plantuml.FileFormat;
/*
* EPS servlet of the webapp.
* This servlet produces the UML diagram in EPS format.
/**
* EPS Text servlet of the webapp.
* This servlet produces the UML diagram in EPS Text format.
*/
@SuppressWarnings("serial")
@SuppressWarnings("SERIAL")
public class EpsTextServlet extends UmlDiagramService {
/**
* Gives the wished output format of the diagram.
* This value is used by the DiagramResponse class.
*
* @return the format for EPS Text responses
*/
@Override
public FileFormat getOutputFormat() {
return FileFormat.EPS_TEXT;

View File

@ -2,7 +2,7 @@
* PlantUML : a free UML diagram generator
* ========================================================================
*
* Project Info: http://plantuml.sourceforge.net
* Project Info: https://plantuml.com
*
* This file is part of PlantUML.
*
@ -25,13 +25,19 @@ package net.sourceforge.plantuml.servlet;
import net.sourceforge.plantuml.FileFormat;
/*
/**
* Image servlet of the webapp.
* This servlet produces the UML diagram in PNG format.
*/
@SuppressWarnings("serial")
@SuppressWarnings("SERIAL")
public class ImgServlet extends UmlDiagramService {
/**
* Gives the wished output format of the diagram.
* This value is used by the DiagramResponse class.
*
* @return the format for image responses
*/
@Override
public FileFormat getOutputFormat() {
return FileFormat.PNG;

View File

@ -2,7 +2,7 @@
* PlantUML : a free UML diagram generator
* ========================================================================
*
* Project Info: http://plantuml.sourceforge.net
* Project Info: https://plantuml.com
*
* This file is part of PlantUML.
*
@ -23,15 +23,16 @@
*/
package net.sourceforge.plantuml.servlet;
import net.sourceforge.plantuml.syntax.LanguageDescriptor;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintStream;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.sourceforge.plantuml.syntax.LanguageDescriptor;
/**
* Servlet used to inspect the language keywords of the running PlantUML server.
* Same as {@code java -jar plantuml.jar -language}

View File

@ -2,7 +2,7 @@
* PlantUML : a free UML diagram generator
* ========================================================================
*
* Project Info: http://plantuml.sourceforge.net
* Project Info: https://plantuml.com
*
* This file is part of PlantUML.
*
@ -26,47 +26,46 @@ package net.sourceforge.plantuml.servlet;
import java.io.IOException;
import javax.imageio.IIOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.servlet.utility.UmlExtractor;
import net.sourceforge.plantuml.servlet.utility.UrlDataExtractor;
/*
/**
* MAP servlet of the webapp.
* This servlet produces the image map of the diagram in HTML format.
*/
@SuppressWarnings("serial")
@SuppressWarnings("SERIAL")
public class MapServlet extends HttpServlet {
@Override
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 url = request.getRequestURI();
final String uml = UmlExtractor.getUmlSource(UrlDataExtractor.getEncodedDiagram(url, ""));
final int idx = UrlDataExtractor.getIndex(url, 0);
// generate the response
DiagramResponse dr = new DiagramResponse(response, getOutputFormat(), request);
try {
dr.sendMap(uml);
} catch (IIOException iioe) {
dr.sendMap(uml, idx);
} catch (IIOException e) {
// Browser has closed the connection, do nothing
}
dr = null;
}
public String getSource(String uri) {
String[] result = uri.split("/map/", 2);
if (result.length != 2) {
return "";
} else {
return result[1];
}
}
/**
* Gives the wished output format of the diagram.
* This value is used by the DiagramResponse class.
*
* @return the format for map responses
*/
public FileFormat getOutputFormat() {
return FileFormat.UTXT;
}

View File

@ -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;
}
}
}

View File

@ -2,7 +2,7 @@
* PlantUML : a free UML diagram generator
* ========================================================================
*
* Project Info: http://plantuml.sourceforge.net
* Project Info: https://plantuml.com
*
* This file is part of PlantUML.
*
@ -23,87 +23,118 @@
*/
package net.sourceforge.plantuml.servlet;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import HTTPClient.CookieModule;
import HTTPClient.HTTPConnection;
import HTTPClient.HTTPResponse;
import HTTPClient.ModuleException;
import HTTPClient.ParseException;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.SourceStringReader;
/*
/**
* Proxy servlet of the webapp.
* This servlet retrieves the diagram source of a web resource (web html page)
* and renders it.
*/
@SuppressWarnings("serial")
@SuppressWarnings("SERIAL")
public class OldProxyServlet extends HttpServlet {
private static final Pattern PROXY_PATTERN = Pattern.compile("/\\w+/proxy/((\\d+)/)?((\\w+)/)?(http://.*)");
private String format;
/**
* Proxy request URI regex pattern.
*/
private static final Pattern PROXY_PATTERN = Pattern.compile("/\\w+/proxy/((\\d+)/)?((\\w+)/)?(https?://[^@]*)");
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
final String uri = request.getRequestURI();
// Check if the src URL is valid
Matcher proxyMatcher = PROXY_PATTERN.matcher(uri);
if (proxyMatcher.matches()) {
String num = proxyMatcher.group(2); // Optional number of the diagram source
format = proxyMatcher.group(4); // Expected format of the generated diagram
String sourceURL = proxyMatcher.group(5);
handleImageProxy(response, num, sourceURL);
} else {
request.setAttribute("net.sourceforge.plantuml.servlet.decoded", "ERROR Invalid proxy syntax : " + uri);
request.removeAttribute("net.sourceforge.plantuml.servlet.encoded");
// forward to index.jsp
RequestDispatcher dispatcher = request.getRequestDispatcher("/index.jsp");
dispatcher.forward(request, response);
if (!proxyMatcher.matches()) {
// Bad URI format.
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "URL malformed.");
return;
}
String num = proxyMatcher.group(2); // Optional number of the diagram source
String format = proxyMatcher.group(4); // Expected format of the generated diagram
String sourceURL = proxyMatcher.group(5);
if (ProxyServlet.forbiddenURL(sourceURL)) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Forbidden URL format.");
return;
}
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);
reader.generateImage(response.getOutputStream(), n, new FileFormatOption(getOutputFormat(), false));
FileFormat fileFormat = getOutputFormat(format);
response.addHeader("Content-Type", fileFormat.getMimeType());
reader.outputImage(response.getOutputStream(), n, new FileFormatOption(fileFormat, false));
}
private String getSource(String uri) throws IOException {
CookieModule.setCookiePolicyHandler(null);
final Pattern p = Pattern.compile("http://[^/]+(/?.*)");
final Matcher m = p.matcher(uri);
if (m.find() == false) {
throw new IOException(uri);
}
/**
* 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);
final HTTPConnection httpConnection = new HTTPConnection(url);
try {
final HTTPResponse resp = httpConnection.Get(m.group(1));
return resp.getText();
} catch (ModuleException e) {
throw new IOException(e.toString());
} catch (ParseException e) {
throw new IOException(e.toString());
try (
InputStream responseStream = url.openStream();
InputStreamReader isr = new InputStreamReader(responseStream);
BufferedReader br = new BufferedReader(isr);
) {
String line;
StringBuffer sb = new StringBuffer();
while ((line = br.readLine()) != null) {
sb.append(line);
sb.append("\n");
}
return sb.toString().trim();
}
}
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;
}

View File

@ -2,7 +2,7 @@
* PlantUML : a free UML diagram generator
* ========================================================================
*
* Project Info: http://plantuml.sourceforge.net
* Project Info: https://plantuml.com
*
* This file is part of PlantUML.
*
@ -23,31 +23,24 @@
*/
package net.sourceforge.plantuml.servlet;
import java.io.IOException;
import net.sourceforge.plantuml.FileFormat;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/*
* Welcome servlet of the webapp.
* Displays the sample Bob and Alice sequence diagram.
/**
* PDF servlet of the webapp.
* This servlet produces the UML diagram in PDF format.
*/
@SuppressWarnings("serial")
public class Welcome extends HttpServlet {
@SuppressWarnings("SERIAL")
public class PdfServlet extends UmlDiagramService {
/**
* Gives the wished output format of the diagram.
* This value is used by the DiagramResponse class.
*
* @return the format for pdf responses
*/
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
// set the sample
request.setAttribute("decoded", "Bob -> Alice : hello");
request.setAttribute("encoded", "SyfFKj2rKt3CoKnELR1Io4ZDoSa70000");
// forward to index.jsp
RequestDispatcher dispatcher = request.getRequestDispatcher("/index.jsp");
dispatcher.forward(request, response);
public FileFormat getOutputFormat() {
return FileFormat.PDF;
}
}

View File

@ -2,7 +2,7 @@
* PlantUML : a free UML diagram generator
* ========================================================================
*
* Project Info: http://plantuml.sourceforge.net
* Project Info: https://plantuml.com
*
* This file is part of PlantUML.
*
@ -27,25 +27,22 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.net.ssl.HttpsURLConnection;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sourceforge.plantuml.OptionFlags;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.sourceforge.plantuml.api.PlantumlUtils;
import net.sourceforge.plantuml.code.Transcoder;
import net.sourceforge.plantuml.code.TranscoderUtil;
import net.sourceforge.plantuml.code.NoPlantumlCompressionException;
import net.sourceforge.plantuml.png.MetadataTag;
import net.sourceforge.plantuml.servlet.utility.Configuration;
import net.sourceforge.plantuml.servlet.utility.UmlExtractor;
import net.sourceforge.plantuml.servlet.utility.UrlDataExtractor;
/*
* Original idea from Achim Abeling for Confluence macro
* See http://www.banapple.de/display/BANAPPLE/plantuml+user+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.
@ -53,124 +50,270 @@ import net.sourceforge.plantuml.png.MetadataTag;
* Modified by Arnaud Roques
* Modified by Pablo Lalloni
* Modified by Maxime Sinclair
*
*/
@SuppressWarnings("serial")
public class PlantUmlServlet extends HttpServlet {
@SuppressWarnings("SERIAL")
public class PlantUmlServlet extends AsciiCoderServlet {
static {
// Initialize the PlantUML server.
// You could say that this is like the `static void main(String[] args)` of the PlantUML server.
DiagramResponse.init();
}
/**
* 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\\-\\_]+)");
@Override
protected String getServletContextPath() {
return "uml";
}
private static final Pattern RECOVER_UML_PATTERN = Pattern.compile("/uml/(.*)");
static {
OptionFlags.ALLOW_INCLUDE = false;
if ("true".equalsIgnoreCase(System.getenv("ALLOW_PLANTUML_INCLUDE"))) {
OptionFlags.ALLOW_INCLUDE = true;
/**
* Encode arbitrary string to HTML string.
*
* @param string arbitrary string
*
* @return html encoded string
*/
public static String stringToHTMLString(String string) {
final StringBuilder sb = new StringBuilder(string.length());
// true if last char was blank
final int length = string.length();
int offset = 0;
while (offset < length) {
final int c = string.codePointAt(offset);
if (c == ' ') {
sb.append(' ');
} else if (c == '"') {
sb.append("&quot;");
} else if (c == '&') {
sb.append("&amp;");
} else if (c == '<') {
sb.append("&lt;");
} else if (c == '>') {
sb.append("&gt;");
} else if (c == '\r') {
sb.append("\r");
} else if (c == '\n') {
sb.append("\n");
} else {
int ci = 0xffffff & c;
if (ci < 160) {
// nothing special only 7 Bit
sb.append((char) c);
} else {
// Not 7 Bit use the unicode system
sb.append("&#");
sb.append(ci);
sb.append(';');
}
}
offset += Character.charCount(c);
}
return sb.toString();
}
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
String text = request.getParameter("text");
String metadata = request.getParameter("metadata");
if (metadata != null) {
InputStream img = null;
try {
img = getImage(new URL(metadata));
MetadataTag metadataTag = new MetadataTag(img, "plantuml");
String data = metadataTag.getData();
if (data != null) {
text = data;
}
} finally {
if (img != null) {
img.close();
}
}
}
try {
text = getTextFromUrl(request, text);
} catch (Exception e) {
e.printStackTrace();
}
// textual diagram source
final String text = getText(request).trim();
// no Text form has been submitted
if (text == null || text.trim().isEmpty()) {
if (text.isEmpty()) {
redirectNow(request, response, DEFAULT_ENCODED_TEXT);
return;
}
final String encoded = getTranscoder().encode(text);
request.setAttribute("decoded", text);
request.setAttribute("encoded", encoded);
// diagram index to render
final int idx = UrlDataExtractor.getIndex(request.getRequestURI());
// check if an image map is necessary
if (text != null && PlantumlUtils.hasCMapData(text)) {
request.setAttribute("mapneeded", Boolean.TRUE);
}
// forward to index.jsp
final RequestDispatcher dispatcher = request.getRequestDispatcher("/index.jsp");
final String path;
final String view = request.getParameter("view");
if (view != null && view.equalsIgnoreCase("previewer")) {
path = "/previewer.jsp";
} else {
path = "/index.jsp";
}
prepareRequestForDispatch(request, text, idx);
final RequestDispatcher dispatcher = request.getRequestDispatcher(path);
dispatcher.forward(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
protected void doPost(
HttpServletRequest request,
HttpServletResponse response
) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String text = request.getParameter("text");
String encoded = DEFAULT_ENCODED_TEXT;
// diagram index to render
final int idx = UrlDataExtractor.getIndex(request.getRequestURI());
// encoded diagram source
String encoded;
try {
text = getTextFromUrl(request, text);
String text = getText(request).trim();
encoded = getTranscoder().encode(text);
} catch (Exception e) {
encoded = DEFAULT_ENCODED_TEXT;
e.printStackTrace();
}
redirectNow(request, response, encoded);
redirectNow(request, response, encoded, idx);
}
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()));
// 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);
/**
* Get textual diagram.
* Search for textual diagram in following order:
* 1. URL {@link PlantUmlServlet.getTextFromUrl}
* 2. metadata
* 3. request parameter "text"
*
* @param request http request
*
* @return if successful textual diagram source; otherwise empty string
*
* @throws IOException if an input or output exception occurred
*/
private String getText(final HttpServletRequest request) throws IOException {
String text;
// 1. URL
try {
text = getTextFromUrl(request);
if (text != null && !text.isEmpty()) {
return text;
}
text = getTranscoder().decode(url);
} catch (NoPlantumlCompressionException e) {
// no textual diagram source available from Url
// ignore and try 2. method (metadata) below
// do not spam output console
} catch (Exception e) {
e.printStackTrace();
}
return text;
// 2. metadata
String metadata = request.getParameter("metadata");
if (metadata != null) {
try (InputStream img = getImage(new URL(metadata))) {
MetadataTag metadataTag = new MetadataTag(img, "plantuml");
String data = metadataTag.getData();
if (data != null) {
return data;
}
}
}
// 3. request parameter text
text = request.getParameter("text");
if (text != null && !text.isEmpty()) {
return text;
}
// nothing found
return "";
}
private void redirectNow(HttpServletRequest request, HttpServletResponse response, String encoded)
throws IOException {
final String result = request.getContextPath() + "/uml/" + encoded;
response.sendRedirect(result);
/**
* Get textual diagram source from URL.
*
* @param request http request which contains the source URL
*
* @return if successful textual diagram source from URL; otherwise empty string
*
* @throws IOException if an input or output exception occurred
*/
private String getTextFromUrl(HttpServletRequest request) throws IOException {
return getTranscoder().decode(getEncodedTextFromUrl(request));
}
private Transcoder getTranscoder() {
return TranscoderUtil.getDefaultTranscoder();
/**
* Prepare request for dispatch and get request dispatcher.
*
* @param request http request which will be further prepared for dispatch
* @param text textual diagram source
*
* @throws IOException if an input or output exception occurred
*/
private void prepareRequestForDispatch(HttpServletRequest request, String text, int idx) throws IOException {
final String encoded = getTranscoder().encode(text);
// diagram sources
request.setAttribute("encoded", encoded);
request.setAttribute("decoded", text);
request.setAttribute("index", (idx < 0) ? "" : idx);
// properties
request.setAttribute("showSocialButtons", Configuration.get("SHOW_SOCIAL_BUTTONS"));
request.setAttribute("showGithubRibbon", Configuration.get("SHOW_GITHUB_RIBBON"));
// map for diagram source if necessary
String map = "";
if (PlantumlUtils.hasCMapData(text)) {
try {
map = UmlExtractor.extractMap(text);
} catch (Exception e) {
e.printStackTrace();
}
}
request.setAttribute("map", map);
}
static private HttpURLConnection getConnection(URL url) 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 {
redirectNow(request, response, encoded, null);
}
/**
* Send redirect response to encoded uml text.
*
* @param request http request
* @param response http response
* @param encoded encoded uml text
* @param index diagram index
*
* @throws IOException if an input or output exception occurred
*/
private void redirectNow(
HttpServletRequest request,
HttpServletResponse response,
String encoded,
Integer index
) throws IOException {
final String path;
if (index == null || index < 0) {
path = request.getContextPath() + "/uml/" + encoded;
} else {
path = request.getContextPath() + "/uml/" + index + "/" + encoded;
}
response.sendRedirect(path);
}
/**
* 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");
con.setReadTimeout(10000); // 10 seconds
// printHttpsCert(con);
con.connect();
return con;
} else {
@ -182,13 +325,20 @@ 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();
return is;
}
}

View File

@ -0,0 +1,202 @@
/* ========================================================================
* 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.io.StringWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.emoji.data.Dummy;
import net.sourceforge.plantuml.json.Json;
import net.sourceforge.plantuml.json.JsonArray;
import net.sourceforge.plantuml.theme.ThemeUtils;
import net.sourceforge.plantuml.openiconic.data.DummyIcon;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* Small PlantUML frontend or UI helper.
*/
@SuppressWarnings("SERIAL")
public class PlantUmlUIHelperServlet extends HttpServlet {
private interface HelperConsumer {
void accept(HttpServletRequest request, HttpServletResponse response) throws IOException;
}
private final Map<String, HelperConsumer> helpers = new HashMap<>();
private String svgIconsSpriteCache = null;
public PlantUmlUIHelperServlet() {
// add all supported request items/helper methods
helpers.put("emojis", this::sendEmojis);
helpers.put("icons.svg", this::sendIconsSprite);
helpers.put("icons", this::sendIcons);
helpers.put("themes", this::sendThemes);
}
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
final String requestItem = request.getParameter("request");
final HelperConsumer requestHelper = this.helpers.get(requestItem);
String errorMsg = null;
if (requestItem == null) {
errorMsg = "Request item not set.";
} else if (requestHelper == null) {
errorMsg = "Unknown requested item: " + requestItem;
}
if (errorMsg != null) {
setDefaultHeader(response, FileFormat.UTXT);
response.getWriter().write(errorMsg);
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return;
}
requestHelper.accept(request, response);
}
private void setDefaultHeader(HttpServletResponse response, FileFormat fileFormat) {
setDefaultHeader(response, fileFormat.getMimeType());
}
private HttpServletResponse setDefaultHeader(HttpServletResponse response, String contentType) {
response.addHeader("Access-Control-Allow-Origin", "*");
response.setContentType(contentType);
return response;
}
private void sendJson(HttpServletResponse response, String json) throws IOException {
setDefaultHeader(response, "application/json;charset=UTF-8");
response.getWriter().write(json);
}
private String[] getIcons() throws IOException {
InputStream in = DummyIcon.class.getResourceAsStream("all.txt");
try (BufferedReader br = new BufferedReader(new InputStreamReader(in))) {
return br.lines().toArray(String[]::new);
}
}
private void sendIcons(HttpServletRequest request, HttpServletResponse response) throws IOException {
sendJson(response, Json.array(getIcons()).toString());
}
private void sendIconsSprite(HttpServletRequest request, HttpServletResponse response) throws IOException {
if (svgIconsSpriteCache == null) {
// NOTE: all icons has the following svg tag attributes: width="8" height="8" viewBox="0 0 8 8"
String[] iconNames = getIcons();
StringBuilder sprite = new StringBuilder();
sprite.append("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"8\" height=\"8\" viewBox=\"0 0 8 8\">\n");
sprite.append("<defs>\n");
sprite.append(" <style><![CDATA[\n");
sprite.append(" .sprite { display: none; }\n");
sprite.append(" .sprite:target { display: inline; }\n");
sprite.append(" ]]></style>\n");
sprite.append("</defs>\n");
for (String name : iconNames) {
try (InputStream in = DummyIcon.class.getResourceAsStream(name + ".svg")) {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
DocumentBuilder db = docFactory.newDocumentBuilder();
Document doc = db.parse(in);
Writer out = new StringWriter();
out.write("<g class=\"sprite\" id=\"" + name + "\">");
TransformerFactory tfFactory = TransformerFactory.newInstance();
tfFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
tfFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
Transformer tf = tfFactory.newTransformer();
tf.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
tf.setOutputProperty(OutputKeys.INDENT, "no");
NodeList svgInnerNodes = doc.getElementsByTagName("svg").item(0).getChildNodes();
for (int index = 0; index < svgInnerNodes.getLength(); index++) {
tf.transform(new DOMSource(svgInnerNodes.item(index)), new StreamResult(out));
}
out.write("</g>");
sprite.append(out.toString() + "\n");
} catch (ParserConfigurationException | SAXException | TransformerException ex) {
// skip icons which can not be parsed/read
Logger logger = Logger.getLogger("com.plantuml");
logger.log(Level.WARNING, "SVG icon \"{0}\" could not be parsed. Skip!", name);
}
}
sprite.append("</svg>\n");
svgIconsSpriteCache = sprite.toString();
}
setDefaultHeader(response, FileFormat.SVG);
response.getWriter().write(svgIconsSpriteCache);
}
private String[][] getEmojis() throws IOException {
InputStream in = Dummy.class.getResourceAsStream("emoji.txt");
try (BufferedReader br = new BufferedReader(new InputStreamReader(in))) {
return br.lines().map(line -> line.split(";")).toArray(String[][]::new);
}
}
private void sendEmojis(HttpServletRequest request, HttpServletResponse response) throws IOException {
String[][] emojis = getEmojis();
JsonArray json = new JsonArray();
for (String[] emojiUnicodeNamePair : emojis) {
json.add(Json.array(emojiUnicodeNamePair));
}
sendJson(response, json.toString());
}
private void sendThemes(HttpServletRequest request, HttpServletResponse response) throws IOException {
String[] themes = ThemeUtils.getAllThemeNames().toArray(new String[0]);
sendJson(response, Json.array(themes).toString());
}
}

View File

@ -2,7 +2,7 @@
* PlantUML : a free UML diagram generator
* ========================================================================
*
* Project Info: http://plantuml.sourceforge.net
* Project Info: https://plantuml.com
*
* This file is part of PlantUML.
*
@ -29,160 +29,170 @@ import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sourceforge.plantuml.BlockUml;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.OptionFlags;
import net.sourceforge.plantuml.SourceStringReader;
import net.sourceforge.plantuml.core.Diagram;
import net.sourceforge.plantuml.core.UmlSource;
import java.security.cert.Certificate;
import java.util.List;
import java.util.stream.Collectors;
import javax.imageio.IIOException;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLPeerUnverifiedException;
/*
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.sourceforge.plantuml.FileFormat;
/**
* Proxy servlet of the webapp.
* This servlet retrieves the diagram source of a web resource (web html page)
* and renders it.
*/
@SuppressWarnings("serial")
@SuppressWarnings("SERIAL")
public class ProxyServlet extends HttpServlet {
static {
OptionFlags.ALLOW_INCLUDE = false;
if ("true".equalsIgnoreCase(System.getenv("ALLOW_PLANTUML_INCLUDE"))) {
OptionFlags.ALLOW_INCLUDE = true;
public static boolean forbiddenURL(String full) {
if (full == null) {
return true;
}
if (full.contains("@")) {
return true;
}
if (full.startsWith("https://") == false && full.startsWith("http://") == false) {
return true;
}
if (full.matches("^https?://[-#.0-9:\\[\\]+]+/.*")) {
return true;
}
if (full.matches("^https?://[^.]+/.*")) {
return true;
}
if (full.matches("^https?://[^.]+$")) {
return true;
}
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");
final URL srcUrl;
// Check if the src URL is valid
try {
srcUrl = new URL(source);
} catch (MalformedURLException mue) {
mue.printStackTrace();
return;
final int idx = index == null ? 0 : Integer.parseInt(index);
final URL srcUrl = validateURL(source, response);
if (srcUrl == null) {
return; // error is already set/handled inside `validateURL`
}
// generate the response
String diagmarkup = getSource(srcUrl);
SourceStringReader reader = new SourceStringReader(diagmarkup);
int n = index == null ? 0 : Integer.parseInt(index);
List<BlockUml> blocks = reader.getBlocks();
BlockUml block = blocks.get(n);
Diagram diagram = block.getDiagram();
UmlSource umlSrc = diagram.getSource();
String uml = umlSrc.getPlainString();
//System.out.println("uml=" + uml);
// fetch diagram from URL
final String uml = getSource(srcUrl);
// generate the response
DiagramResponse dr = new DiagramResponse(response, getOutputFormat(fmt), request);
try {
dr.sendDiagram(uml, 0);
} catch (IIOException iioe) {
// special handling for the MAP since it's not using "#sendDiagram()" like the other types
if ("map".equals(fmt)) {
dr.sendMap(uml, idx);
} else {
dr.sendDiagram(uml, idx);
}
} catch (IIOException e) {
// Browser has closed the connection, so the HTTP OutputStream is closed
// Silently catch the exception to avoid annoying log
}
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;
StringBuilder sb;
try {
HttpURLConnection con = getConnection(url);
rd = new BufferedReader(new InputStreamReader(con.getInputStream()));
sb = new StringBuilder();
while ((line = rd.readLine()) != null) {
sb.append(line + '\n');
}
rd.close();
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
} finally {
rd = null;
HttpURLConnection conn = getConnection(url);
try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
return br.lines().collect(Collectors.joining("\n"));
}
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;
}
if (format.equals("svg")) {
return FileFormat.SVG;
switch (format.toLowerCase()) {
case "png": return FileFormat.PNG;
case "svg": return FileFormat.SVG;
case "eps": return FileFormat.EPS;
case "epstext": return FileFormat.EPS_TEXT;
case "txt": return FileFormat.UTXT;
case "map": return FileFormat.UTXT;
case "pdf": return FileFormat.PDF;
default: return FileFormat.PNG;
}
if (format.equals("eps")) {
return FileFormat.EPS;
}
if (format.equals("epstext")) {
return FileFormat.EPS_TEXT;
}
if (format.equals("txt")) {
return FileFormat.UTXT;
}
return FileFormat.PNG;
}
private HttpURLConnection getConnection(final URL url) throws IOException {
final HttpURLConnection con = (HttpURLConnection) url.openConnection();
if (con instanceof HttpsURLConnection) {
// printHttpsCert((HttpsURLConnection) con);
}
con.setRequestMethod("GET");
String token = System.getenv("HTTP_AUTHORIZATION");
if (token != null) {
con.setRequestProperty("Authorization", token);
}
con.setReadTimeout(10000); // 10 seconds
con.connect();
return con;
}
/**
* Debug method used to dump the certificate info
* @param con the https connection
* 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
*/
@SuppressWarnings("unused")
private void printHttpsCert(final HttpsURLConnection con) {
if (con != null) {
try {
System.out.println("Response Code : " + con.getResponseCode());
System.out.println("Cipher Suite : " + con.getCipherSuite());
System.out.println("\n");
Certificate[] certs = con.getServerCertificates();
for (Certificate cert : certs) {
System.out.println("Cert Type : " + cert.getType());
System.out.println("Cert Hash Code : " + cert.hashCode());
System.out.println("Cert Public Key Algorithm : " + cert.getPublicKey().getAlgorithm());
System.out.println("Cert Public Key Format : " + cert.getPublicKey().getFormat());
System.out.println("\n");
}
} catch (SSLPeerUnverifiedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
public static HttpURLConnection getConnection(final URL url) throws IOException {
final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
String token = System.getenv("HTTP_AUTHORIZATION");
if (token != null) {
conn.setRequestProperty("Authorization", token);
}
final String timeoutString = System.getenv("HTTP_PROXY_READ_TIMEOUT");
int timeout = 10000; // 10 seconds as default
if (timeoutString != null && timeoutString.matches("^\\d+$")) {
timeout = Integer.parseInt(timeoutString);
}
conn.setReadTimeout(timeout);
conn.connect();
return conn;
}
}

View File

@ -2,7 +2,7 @@
* PlantUML : a free UML diagram generator
* ========================================================================
*
* Project Info: http://plantuml.sourceforge.net
* Project Info: https://plantuml.com
*
* This file is part of PlantUML.
*
@ -25,13 +25,19 @@ package net.sourceforge.plantuml.servlet;
import net.sourceforge.plantuml.FileFormat;
/*
/**
* SVG servlet of the webapp.
* This servlet produces the UML diagram in SVG format.
*/
@SuppressWarnings("serial")
@SuppressWarnings("SERIAL")
public class SvgServlet extends UmlDiagramService {
/**
* Gives the wished output format of the diagram.
* This value is used by the DiagramResponse class.
*
* @return the format for svg responses
*/
@Override
public FileFormat getOutputFormat() {
return FileFormat.SVG;

View File

@ -2,7 +2,7 @@
* PlantUML : a free UML diagram generator
* ========================================================================
*
* Project Info: http://plantuml.sourceforge.net
* Project Info: https://plantuml.com
*
* This file is part of PlantUML.
*
@ -23,42 +23,36 @@
*/
package net.sourceforge.plantuml.servlet;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.OptionFlags;
import net.sourceforge.plantuml.servlet.utility.UmlExtractor;
import javax.imageio.IIOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.imageio.IIOException;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.servlet.utility.UmlExtractor;
import net.sourceforge.plantuml.servlet.utility.UrlDataExtractor;
/**
* Common service servlet to produce diagram from compressed UML source contained in the end part of the requested URI.
*/
@SuppressWarnings("serial")
@SuppressWarnings("SERIAL")
public abstract class UmlDiagramService extends HttpServlet {
static {
OptionFlags.ALLOW_INCLUDE = false;
if ("true".equalsIgnoreCase(System.getenv("ALLOW_PLANTUML_INCLUDE"))) {
OptionFlags.ALLOW_INCLUDE = true;
}
}
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
final String url = request.getRequestURI();
final String encoded = UrlDataExtractor.getEncodedDiagram(url, "");
final int idx = UrlDataExtractor.getIndex(url, 0);
// build the UML source from the compressed request parameter
final String[] sourceAndIdx = getSourceAndIdx(request);
final int idx = Integer.parseInt(sourceAndIdx[1]);
final String uml;
try {
uml = UmlExtractor.getUmlSource(sourceAndIdx[0]);
uml = UmlExtractor.getUmlSource(encoded);
} catch (Exception e) {
e.printStackTrace();
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Bad Request");
@ -70,69 +64,44 @@ public abstract class UmlDiagramService extends HttpServlet {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
final int idx = UrlDataExtractor.getIndex(request.getRequestURI(), 0);
// build the UML source from the compressed request parameter
final String[] sourceAndIdx = getSourceAndIdx(request);
final int idx = Integer.parseInt(sourceAndIdx[1]);
// read textual diagram source from request body
final StringBuilder uml = new StringBuilder();
final BufferedReader in = request.getReader();
while (true) {
final String line = in.readLine();
if (line == null) {
break;
try (BufferedReader in = request.getReader()) {
String line;
while ((line = in.readLine()) != null) {
uml.append(line).append('\n');
}
uml.append(line).append('\n');
}
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 {
dr.sendDiagram(uml, idx);
} catch (IIOException iioe) {
} catch (IIOException e) {
// 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 compressed UML source from the HTTP URI.
*
* @param uri
* the complete URI as returned by request.getRequestURI()
* @return the compressed UML source
*/
public final String[] getSourceAndIdx(HttpServletRequest request) {
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(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" };
}
/**

View File

@ -6,26 +6,33 @@ hide empty members
hide empty methods
hide empty fields
abstract class UmlDiagramService {
public void doGet(HttpServletRequest rq, HttpServletResponse rsp)
abstract public String getSource( String uri)
abstract FileFormat getOutputFormat()
+ doGet(request: HttpServletRequest, response: HttpServletResponse) : void
+ doPost(request: HttpServletRequest, response: HttpServletResponse) : void
+ {abstract} getOutputFormat() : FileFormat
}
class DiagramResponse {
DiagramResponse( HttpServletResponse r, FileFormat f)
void sendDiagram( String uml)
void sendMap( String uml)
+ DiagramResponse(res: HttpServletResponse, fmt: FileFormat, req: HttpServletRequest)
+ sendDiagram(uml: String, idx: int) : void
+ sendMap(uml: String, idx: int) : void
+ sendCheck(uml: String) : void
}
abstract HttpServlet <|-- UmlDiagramService
abstract HttpServlet <|-- MapServlet
abstract HttpServlet <|-- ProxyServlet
UmlDiagramService <|-- PngServlet
UmlDiagramService <|-- SvgServlet
HttpServlet <|-- CheckSyntaxServlet
HttpServlet <|-- LanguageServlet
HttpServlet <|-- MapServlet
HttpServlet <|-- PlantUmlServlet
HttpServlet <|-- ProxyServlet
HttpServlet <|-- OldProxyServlet
HttpServlet <|-- UmlDiagramService
UmlDiagramService <|-- AsciiServlet
UmlDiagramService <|-- Base64Servlet
UmlDiagramService <|-- EpsServlet
UmlDiagramService <|-- EpsTextServlet
UmlDiagramService <|-- AsciiServlet
UmlDiagramService o- DiagramResponse
MapServlet o-- DiagramResponse
ProxyServlet o-- DiagramResponse
UmlDiagramService <|-- ImgServlet
UmlDiagramService <|-- SvgServlet
UmlDiagramService o-- DiagramResponse
DiagramResponse --o CheckSyntaxServlet
DiagramResponse --o MapServlet
DiagramResponse --o ProxyServlet
@enduml
## Sequence diagram ##
@ -33,14 +40,17 @@ ProxyServlet o-- DiagramResponse
@startuml
title Generation of a PNG image illustrated
PngServlet -> PngServlet : getSource()
PngServlet -> UmlExtractor : getUmlSource()
UmlExtractor --> PngServlet
PngServlet -> PngServlet : getOutputFormat()
PngServlet -> DiagramResponse : <<create>>
PngServlet -> DiagramResponse : sendDiagram()
ImgServlet -> UrlDataExtractor : getEncodedDiagram()
UrlDataExtractor --> ImgServlet : encoded
ImgServlet -> UrlDataExtractor : getIndex()
UrlDataExtractor --> ImgServlet : index
ImgServlet -> UmlExtractor : getUmlSource()
UmlExtractor --> ImgServlet : decoded
ImgServlet -> ImgServlet : getOutputFormat()
ImgServlet -> "dr:DiagramResponse" as dr ** : <<create>>
ImgServlet -> dr : sendDiagram()
participant "PlantUML library" as Lib #99FF99
DiagramResponse -> Lib : generateImage()
Lib --> DiagramResponse
DiagramResponse --> PngServlet
@enduml
dr -> Lib : generateImage()
Lib --> dr
dr --> ImgServlet
@enduml

View File

@ -1,15 +1,21 @@
<html>
<body>
<p>This package is in charge of the JEE PlantUml Server.</p>
<p>there are 2 kind of servlets in this package :<br>
- Interactive servlets : Welcome, PlantUmlServlet that are in charge of the web pages dedicated to human users.<br>
- Service servlets : ImgServlet, SvgServlet, EpsServlet, EpsTextServlet, AsciiServlet, ProxyServlet that only produce a diagram as output.<br>
<br>
Structure of the service part of the PlantUmlServer: <br>
<img src="http://www.plantuml.com/plantuml/img/XP51ReCm44Ntd6AMH0etwAPIbNPJjIhg0OoPm4WsTiPZrAZDtGk5913IP3b_dlx_7jTK8g3riWUBja0EIJsLf7RbJDeIcavHHH1MMa0R5G9yMlD4gc9bS-IMDC9t0k1ZOKX3wwY4qZsZf2yYlYSCoWVk8WO1tgrX9WVlce30mQywZrFGQ9OBKrD1XPAxo1hJenAPPlo636uSMoKz_1R5HndcT9KSag7tMFeKshU-qDBhxTRJW6sV_FVCW4qv6foRMJFRloe_tntEvvnamSDFbYqlUuFjZCVv1lJExcj_n9R_DZ1DTOV8stl4Oz14_pCkkpnqSgxVRPVhQV5hm2y0" />
</p>
<p>
<img src="http://www.plantuml.com/plantuml/img/XP1DZi8m38NtdCA23RFe0OfGLr24n4y5uW2cU2fBQL8vBeBRup8ZHEc2LPJtNhuNMraTmOey2Ie73-4N48hT2hZ6Ye2TQwEQHvTHuQiZoTMHGfB1ssq65Uanj5BIzESZTghTycQ0KeFy1KrvPNjkqgD-gTktshIQ1wbH1wKBnagmFb1iWezaB-RpKiYcoBAlqKZ-ygyQk45HBhb1hp0kd1sdxGOSdmNbFWQCiE4pJD8qpzDqz4cpWixkVlpSCAsxhHgsKvDX_H3G6_q1" />
</p>
<p>This package is in charge of the JEE PlantUml Server.</p>
<p>There are 2 kind of servlets in this package:<br>
- Interactive servlets: Welcome, PlantUmlServlet that are in charge of the web pages dedicated to human users.<br>
- Service servlets: ImgServlet, SvgServlet, EpsServlet, EpsTextServlet, AsciiServlet, ProxyServlet that only produce a diagram as output.<br>
<br>
Structure of the service part of the PlantUmlServer: <br>
<img
alt="Class diagram of the service part of the PlantUmlServer"
src="https://www.plantuml.com/plantuml/svg/XP31Ri8m44Jl_eez1Wd-e0SgLRGNrAfHFy3OIx1ansQzHaKj_zv4mKfCYZXwvcbclEl8aZWvAmv68w0BV0Q7ReSKIuaFNXVItg3j5BcBJ58nl3676kbaaKTHMHaZV3dxOcH3qlM0KGW_0Y2adJKAJjveqFuLkPf4VE8nOMIWun8AEGRVHWIAOI40Sb4EgvbCsq23NFj42gki9385lp4MDvwSv1v-JnmI3-zg8IvYs7qTdKlxrRTQzV-wvRHWtpKFtupOwcl0kCpPmj_AK7eNCQc0fz_L2hOol-VU1_dlStRdbn-Ojdb0rAT7n7DKnjnd_EhsL69StRbpEm-_2wonrSdPFm00"
>
</p>
<p>
<img
alt="Generation of a PNG image illustrated"
src="https://www.plantuml.com/plantuml/svg/XP1DZi8m38NtdCA23RFe0OfGLr24n4y5uW2cU2fBQL8vBeBRup8ZHEc2LPJtNhuNMraTmOey2Ie73-4N48hT2hZ6Ye2TQwEQHvTHuQiZoTMHGfB1ssq65Uanj5BIzESZTghTycQ0KeFy1KrvPNjkqgD-gTktshIQ1wbH1wKBnagmFb1iWezaB-RpKiYcoBAlqKZ-ygyQk45HBhb1hp0kd1sdxGOSdmNbFWQCiE4pJD8qpzDqz4cpWixkVlpSCAsxhHgsKvDX_H3G6_q1"
>
</p>
</body>
</html>
</html>

View File

@ -2,7 +2,7 @@
* PlantUML : a free UML diagram generator
* ========================================================================
*
* Project Info: http://plantuml.sourceforge.net
* Project Info: https://plantuml.com
*
* This file is part of PlantUML.
*
@ -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,15 +78,17 @@ 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"
*/
public static boolean get(final String key) {
if (instance.config.getProperty(key) == null) {
if (get().getProperty(key) == null) {
return false;
}
return instance.config.getProperty(key).startsWith("on");
return get().getProperty(key).startsWith("on");
}
}

View File

@ -2,7 +2,7 @@
* PlantUML : a free UML diagram generator
* ========================================================================
*
* Project Info: http://plantuml.sourceforge.net
* Project Info: https://plantuml.com
*
* This file is part of PlantUML.
*
@ -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
}
}

View File

@ -2,7 +2,7 @@
* PlantUML : a free UML diagram generator
* ========================================================================
*
* Project Info: http://plantuml.sourceforge.net
* Project Info: https://plantuml.com
*
* This file is part of PlantUML.
*
@ -27,32 +27,29 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import net.sourceforge.plantuml.OptionFlags;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.SourceStringReader;
import net.sourceforge.plantuml.code.Transcoder;
import net.sourceforge.plantuml.code.TranscoderUtil;
import net.sourceforge.plantuml.core.Diagram;
import net.sourceforge.plantuml.core.ImageData;
/**
* Utility class to extract the UML source from the compressed UML source contained in the end part
* of the requested URI.
*/
public class UmlExtractor {
static {
OptionFlags.ALLOW_INCLUDE = false;
if ("true".equalsIgnoreCase(System.getenv("ALLOW_PLANTUML_INCLUDE"))) {
OptionFlags.ALLOW_INCLUDE = true;
}
}
public abstract class UmlExtractor {
/**
* Build the complete UML source from the compressed source extracted from the HTTP URI.
* Build the complete UML source from the compressed source extracted from the
* HTTP URI.
*
* @param source the last part of the URI containing the compressed UML
*
* @param source
* the last part of the URI containing the compressed UML
* @return the textual UML source
*/
static public String getUmlSource(String source) {
// build the UML source from the compressed part of the URL
String text;
try {
@ -84,9 +81,36 @@ public class UmlExtractor {
return uml;
}
protected UmlExtractor() {
// prevents calls from subclass
throw new UnsupportedOperationException();
/**
* Get image map from uml.
*
* @param uml textual diagram source
*
* @return image map of the diagram in HTML format if the image has some position information; otherwise `null`
*
* @throws IOException if an input or output exception occurred
*/
public static String extractMap(final String uml) throws IOException {
return extractMap(uml, FileFormat.PNG);
}
}
/**
* Get image map from uml.
*
* @param uml textual diagram source
* @param fileFormat underlying file format of uml image
*
* @return image map of the diagram in HTML format if the image has some position information; otherwise `null`
*
* @throws IOException if an input or output exception occurred
*/
public static String extractMap(final String uml, final FileFormat fileFormat) throws IOException {
Diagram diagram = new SourceStringReader(uml).getBlocks().get(0).getDiagram();
ImageData map = diagram.exportDiagram(new NullOutputStream(), 0, new FileFormatOption(fileFormat, false));
if (map.containsCMapData()) {
return map.getCMapData("plantuml");
}
return null;
}
}

View File

@ -0,0 +1,102 @@
/* ========================================================================
* 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.utility;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Utility class to extract the index and diagram source from an URL, e.g., returned by `request.getRequestURI()`.
*/
public abstract class UrlDataExtractor {
/**
* URL regex pattern to easily extract index and encoded diagram.
*/
private static final Pattern URL_PATTERN = Pattern.compile(
"/\\w+(?:/(?<idx>\\d+))?(?:/(?<encoded>[^/]+))?/?$"
);
/**
* Get diagram index from URL.
*
* @param url URL to analyse, e.g., returned by `request.getRequestURI()`
*
* @return if exists diagram index; otherwise -1
*/
public static int getIndex(final String url) {
return getIndex(url, -1);
}
/**
* Get diagram index from URL.
*
* @param url URL to analyse, e.g., returned by `request.getRequestURI()`
* @param fallback fallback index if no index exists in {@code url}
*
* @return if exists diagram index; otherwise {@code fallback}
*/
public static int getIndex(final String url, final int fallback) {
final Matcher matcher = URL_PATTERN.matcher(url);
if (!matcher.find()) {
return fallback;
}
String idx = matcher.group("idx");
if (idx == null) {
return fallback;
}
return Integer.parseInt(idx);
}
/**
* Get encoded diagram source from URL.
*
* @param url URL to analyse, e.g., returned by `request.getRequestURI()`
*
* @return if exists diagram index; otherwise `null`
*/
public static String getEncodedDiagram(final String url) {
return getEncodedDiagram(url, null);
}
/**
* Get encoded diagram source from URL.
*
* @param url URL to analyse, e.g., returned by `request.getRequestURI()`
* @param fallback fallback if no encoded diagram source exists in {@code url}
*
* @return if exists diagram index; otherwise {@code fallback}
*/
public static String getEncodedDiagram(final String url, final String fallback) {
final Matcher matcher = URL_PATTERN.matcher(url);
if (!matcher.find()) {
return fallback;
}
String encoded = matcher.group("encoded");
if (encoded == null) {
return fallback;
}
return encoded;
}
}

View File

@ -1,9 +1,9 @@
<html>
<body>
<p>This package contains utility classes of the JEE PlantUml Server.</p>
<ul>
<li>NullOutputStream is used by the Map feature.</li>
<li>UmlExtractor encapsulates the PlantUML library to decode the UML compressed source.</li>
</ul>
<p>This package contains utility classes of the JEE PlantUml Server.</p>
<ul>
<li>NullOutputStream is used by the Map feature.</li>
<li>UmlExtractor encapsulates the PlantUML library to decode the UML compressed source.</li>
</ul>
</body>
</html>
</html>

View File

@ -0,0 +1,6 @@
# Logger configuration file
#
log4j.rootCategory=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yy-MM-dd HH:mm:ss:SSS} %5p %t %c{2}:%L - %m%n

View File

@ -1,150 +1,255 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name>plantuml</display-name>
<context-param>
<param-name>org.eclipse.jetty.servlet.Default.welcomeServlets</param-name>
<param-value>exact</param-value>
</context-param>
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>compilerSourceVM</param-name>
<param-value>1.7</param-value>
</init-param>
<init-param>
<param-name>compilerTargetVM</param-name>
<param-value>1.7</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>plantumlservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.PlantUmlServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>welcome</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.Welcome</servlet-class>
</servlet>
<servlet>
<servlet-name>imgservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.ImgServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>svgservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.SvgServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>epsservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.EpsServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>epstextservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.EpsTextServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>base64servlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.Base64Servlet</servlet-class>
</servlet>
<servlet>
<servlet-name>asciiservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.AsciiServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>proxyservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.ProxyServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>oldproxyservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.OldProxyServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>mapservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.MapServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>checkservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.CheckSyntaxServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>languageservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.LanguageServlet</servlet-class>
</servlet>
<!-- Patterns of the servlet -->
<servlet-mapping>
<servlet-name>plantumlservlet</servlet-name>
<url-pattern>/welcome</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>plantumlservlet</servlet-name>
<url-pattern>/uml/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>plantumlservlet</servlet-name>
<url-pattern>/form</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>imgservlet</servlet-name>
<url-pattern>/png/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>imgservlet</servlet-name>
<url-pattern>/img/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>svgservlet</servlet-name>
<url-pattern>/svg/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>epsservlet</servlet-name>
<url-pattern>/eps/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>epstextservlet</servlet-name>
<url-pattern>/epstext/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>base64servlet</servlet-name>
<url-pattern>/base64/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>asciiservlet</servlet-name>
<url-pattern>/txt/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>checkservlet</servlet-name>
<url-pattern>/check/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>mapservlet</servlet-name>
<url-pattern>/map/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>plantumlservlet</servlet-name>
<url-pattern>/start/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>oldproxyservlet</servlet-name>
<url-pattern>/proxy/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>proxyservlet</servlet-name>
<url-pattern>/proxy</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>languageservlet</servlet-name>
<url-pattern>/language</url-pattern>
</servlet-mapping>
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/error.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error.jsp</location>
</error-page>
<welcome-file-list>
<welcome-file>welcome</welcome-file>
</welcome-file-list>
</web-app>
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xmlns:web="https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0"
>
<!-- ========================================================== -->
<!-- General -->
<!-- ========================================================== -->
<!-- Name the application -->
<display-name>PlantUML</display-name>
<description>PlantUML Online Server</description>
<!-- This app is cluster-ready -->
<distributable />
<!-- Set timeout to 120 minutes -->
<session-config>
<session-timeout>120</session-timeout>
</session-config>
<!-- ========================================================== -->
<!-- Custom Tag Libraries -->
<!-- ========================================================== -->
<!-- Taglib declarations are no longer required since JSP 2.0, see Removing taglib from web.xml -->
<!-- The <taglib> did not need to be a child of <jsp-config> in earlier versions but is required as of Tomcat 7 -->
<!-- Note that you can only have one <jsp-config> element per web.xml -->
<!--
<jsp-config>
<taglib>
<taglib-uri>http://java.sun.com/jsp/jstl/core</taglib-uri>
<taglib-location>/WEB-INF/lib/c.tld</taglib-location>
</taglib>
</jsp-config>
-->
<!-- ========================================================== -->
<!-- Context Parameters -->
<!-- ========================================================== -->
<context-param>
<param-name>org.eclipse.jetty.servlet.Default.welcomeServlets</param-name>
<param-value>exact</param-value>
</context-param>
<!-- ========================================================== -->
<!-- Servlets -->
<!-- ========================================================== -->
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.eclipse.jetty.jsp.JettyJspServlet</servlet-class>
<init-param>
<param-name>compilerSourceVM</param-name>
<param-value>1.8</param-value>
</init-param>
<init-param>
<param-name>compilerTargetVM</param-name>
<param-value>1.8</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>plantumlservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.PlantUmlServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>plantumlservlet</servlet-name>
<url-pattern>/welcome</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>plantumlservlet</servlet-name>
<url-pattern>/uml/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>plantumlservlet</servlet-name>
<url-pattern>/form</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>plantumlservlet</servlet-name>
<url-pattern>/start/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>imgservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.ImgServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>imgservlet</servlet-name>
<url-pattern>/png/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>imgservlet</servlet-name>
<url-pattern>/img/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>svgservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.SvgServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>svgservlet</servlet-name>
<url-pattern>/svg/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>pdfservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.PdfServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>pdfservlet</servlet-name>
<url-pattern>/pdf/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>epsservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.EpsServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>epsservlet</servlet-name>
<url-pattern>/eps/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>epstextservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.EpsTextServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>epstextservlet</servlet-name>
<url-pattern>/epstext/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>base64servlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.Base64Servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>base64servlet</servlet-name>
<url-pattern>/base64/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>asciiservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.AsciiServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>asciiservlet</servlet-name>
<url-pattern>/txt/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>proxyservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.ProxyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>proxyservlet</servlet-name>
<url-pattern>/proxy</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>oldproxyservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.OldProxyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>oldproxyservlet</servlet-name>
<url-pattern>/proxy/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>mapservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.MapServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>mapservlet</servlet-name>
<url-pattern>/map/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>checkservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.CheckSyntaxServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>checkservlet</servlet-name>
<url-pattern>/check/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>languageservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.LanguageServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>languageservlet</servlet-name>
<url-pattern>/language</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>asciicoderservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.AsciiCoderServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>asciicoderservlet</servlet-name>
<url-pattern>/coder/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>plantumluihelperservlet</servlet-name>
<servlet-class>net.sourceforge.plantuml.servlet.PlantUmlUIHelperServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>plantumluihelperservlet</servlet-name>
<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 -->
<!-- ========================================================== -->
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/error.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error.jsp</location>
</error-page>
<!-- ========================================================== -->
<!-- Welcome Files -->
<!-- ========================================================== -->
<welcome-file-list>
<welcome-file>welcome</welcome-file>
</welcome-file-list>
</web-app>

View File

@ -0,0 +1 @@
<svg height="24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M16 16v4c0 1.152-.848 2-2 2H4c-1.152 0-2-.848-2-2V10c0-1.152.848-2 2-2h4V4c0-1.152.848-2 2-2h10c1.152 0 2 .848 2 2v10c0 1.152-.848 2-2 2h-4zm-2 0h-4c-1.152 0-2-.848-2-2v-4H4v10h10v-4zM10 4v10h10V4H10z" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 301 B

View File

@ -0,0 +1 @@
<svg height="512" width="512" xmlns="http://www.w3.org/2000/svg"><path d="M384 224v184a40 40 0 01-40 40H104a40 40 0 01-40-40V168a40 40 0 0140-40h167.48" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"/><path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="32" d="M216 184v112h112m-104-8L440 72"/></svg>

After

Width:  |  Height:  |  Size: 384 B

View File

@ -0,0 +1 @@
<svg fill="none" height="24" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg>

After

Width:  |  Height:  |  Size: 240 B

View File

@ -0,0 +1 @@
<svg height="512" width="512" xmlns="http://www.w3.org/2000/svg"><path d="M262.29 192.31a64 64 0 1057.4 57.4 64.13 64.13 0 00-57.4-57.4zM416.39 256a154.34 154.34 0 01-1.53 20.79l45.21 35.46a10.81 10.81 0 012.45 13.75l-42.77 74a10.81 10.81 0 01-13.14 4.59l-44.9-18.08a16.11 16.11 0 00-15.17 1.75A164.48 164.48 0 01325 400.8a15.94 15.94 0 00-8.82 12.14l-6.73 47.89a11.08 11.08 0 01-10.68 9.17h-85.54a11.11 11.11 0 01-10.69-8.87l-6.72-47.82a16.07 16.07 0 00-9-12.22 155.3 155.3 0 01-21.46-12.57 16 16 0 00-15.11-1.71l-44.89 18.07a10.81 10.81 0 01-13.14-4.58l-42.77-74a10.8 10.8 0 012.45-13.75l38.21-30a16.05 16.05 0 006-14.08c-.36-4.17-.58-8.33-.58-12.5s.21-8.27.58-12.35a16 16 0 00-6.07-13.94l-38.19-30A10.81 10.81 0 0149.48 186l42.77-74a10.81 10.81 0 0113.14-4.59l44.9 18.08a16.11 16.11 0 0015.17-1.75A164.48 164.48 0 01187 111.2a15.94 15.94 0 008.82-12.14l6.73-47.89A11.08 11.08 0 01213.23 42h85.54a11.11 11.11 0 0110.69 8.87l6.72 47.82a16.07 16.07 0 009 12.22 155.3 155.3 0 0121.46 12.57 16 16 0 0015.11 1.71l44.89-18.07a10.81 10.81 0 0113.14 4.58l42.77 74a10.8 10.8 0 01-2.45 13.75l-38.21 30a16.05 16.05 0 00-6.05 14.08c.33 4.14.55 8.3.55 12.47z" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1 @@
<svg height="512" width="512" xmlns="http://www.w3.org/2000/svg"><path d="M384 224v184a40 40 0 01-40 40H104a40 40 0 01-40-40V168a40 40 0 0140-40h167.48M336 64h112v112M224 288L440 72" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"/></svg>

After

Width:  |  Height:  |  Size: 281 B

View File

@ -0,0 +1 @@
<svg fill="none" height="24" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M17 8l-5-5-5 5M12 3v12"/></svg>

After

Width:  |  Height:  |  Size: 241 B

View File

@ -0,0 +1 @@
<svg class="bi bi-filetype-ascii" fill="currentColor" height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M14 4.5V11h-1V4.5h-2A1.5 1.5 0 019.5 3V1H4a1 1 0 00-1 1v9H2V2a2 2 0 012-2h5.5zM2.404 14.903l-.313 1.028h-.8l1.342-3.999h.926l1.335 4h-.84l-.314-1.03H2.404zm1.178-.59l-.49-1.616h-.034l-.49 1.617h1.014zm1.782.977a1.178 1.178 0 01-.111-.449h.764a.58.58 0 00.255.384c.07.049.154.087.25.114.095.028.2.041.319.041.164 0 .3-.023.413-.07a.558.558 0 00.255-.193.507.507 0 00.085-.29.387.387 0 00-.153-.326c-.101-.08-.256-.144-.463-.193l-.618-.143a1.72 1.72 0 01-.54-.214 1.002 1.002 0 01-.35-.367 1.068 1.068 0 01-.123-.524c0-.244.063-.457.19-.639.127-.181.303-.322.527-.422.225-.1.484-.149.777-.149.304 0 .564.05.779.152.217.102.384.239.5.41.12.17.186.359.2.566h-.75a.56.56 0 00-.12-.258.623.623 0 00-.246-.181.923.923 0 00-.37-.068c-.216 0-.387.05-.512.152a.472.472 0 00-.184.384c0 .121.047.22.143.3a.97.97 0 00.404.175l.62.143c.218.05.407.12.567.211.16.09.285.21.375.358.09.148.135.335.135.56 0 .247-.063.466-.188.656-.133.196-.32.348-.54.439-.233.105-.52.158-.857.158a2.191 2.191 0 01-.665-.09 1.404 1.404 0 01-.478-.252 1.131 1.131 0 01-.29-.375zm4.383-2.246a1.732 1.732 0 00-.103.633v.495c0 .246.035.455.103.627a.834.834 0 00.299.393c.142.09.308.136.477.13a.872.872 0 00.402-.087.699.699 0 00.272-.248.8.8 0 00.117-.364h.765v.076c-.01.241-.088.475-.226.674-.136.194-.32.345-.55.454a1.81 1.81 0 01-.785.164c-.36 0-.665-.072-.915-.216a1.424 1.424 0 01-.57-.627c-.13-.272-.194-.597-.194-.976v-.498c0-.38.065-.705.196-.978.13-.274.32-.485.57-.633.253-.15.557-.223.913-.223.218 0 .42.032.606.097.187.062.35.153.49.272.283.241.452.591.465.964v.073h-.765a.85.85 0 00-.12-.38.7.7 0 00-.272-.261.802.802 0 00-.4-.097.814.814 0 00-.473.138.868.868 0 00-.302.398zm3.628-1.106v4h-.79v-4h.79zm1.337.005v3.999h-.791v-4h.79z" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1 @@
<svg class="bi bi-filetype-map" fill="currentColor" height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M14 4.5V14a2 2 0 01-2 2v-1a1 1 0 001-1V4.5h-2A1.5 1.5 0 019.5 3V1H4a1 1 0 00-1 1v9H2V2a2 2 0 012-2h5.5zM.706 15.849v-2.66h.038l.952 2.16h.516l.946-2.16h.038v2.66h.715V11.85h-.8l-1.14 2.596h-.026L.805 11.85H0v3.999zm7.31-3.999h1.6c.289 0 .533.06.732.179.201.117.355.276.46.477.106.201.158.427.158.677 0 .25-.053.476-.16.677-.106.199-.26.357-.464.474a1.46 1.46 0 01-.732.173h-.803v1.342h-.79V11.85zm2.06 1.714a.795.795 0 00.085-.381c0-.226-.062-.4-.185-.521-.123-.122-.294-.182-.513-.182h-.659v1.406h.66a.794.794 0 00.374-.082.574.574 0 00.238-.24zm-5.12 2.306l.313-1.028h1.336l.314 1.028h.84l-1.336-3.999h-.925l-1.329 3.96m1.79-3.195l.488 1.617H5.433l.49-1.617z" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 808 B

View File

@ -0,0 +1 @@
<svg class="bi bi-filetype-pdf" fill="currentColor" height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M14 4.5V14a2 2 0 01-2 2h-1v-1h1a1 1 0 001-1V4.5h-2A1.5 1.5 0 019.5 3V1H4a1 1 0 00-1 1v9H2V2a2 2 0 012-2h5.5L14 4.5zM1.6 11.85H0v3.999h.791v-1.342h.803c.287 0 .531-.057.732-.173.203-.117.358-.275.463-.474a1.42 1.42 0 00.161-.677c0-.25-.053-.476-.158-.677a1.176 1.176 0 00-.46-.477c-.2-.12-.443-.179-.732-.179zm.545 1.333a.795.795 0 01-.085.38.574.574 0 01-.238.241.794.794 0 01-.375.082H.788V12.48h.66c.218 0 .389.06.512.181.123.122.185.296.185.522zm1.217-1.333v3.999h1.46c.401 0 .734-.08.998-.237a1.45 1.45 0 00.595-.689c.13-.3.196-.662.196-1.084 0-.42-.065-.778-.196-1.075a1.426 1.426 0 00-.589-.68c-.264-.156-.599-.234-1.005-.234H3.362zm.791.645h.563c.248 0 .45.05.609.152a.89.89 0 01.354.454c.079.201.118.452.118.753a2.3 2.3 0 01-.068.592 1.14 1.14 0 01-.196.422.8.8 0 01-.334.252 1.298 1.298 0 01-.483.082h-.563v-2.707zm3.743 1.763v1.591h-.79V11.85h2.548v.653H7.896v1.117h1.606v.638H7.896z" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1 @@
<svg class="bi bi-filetype-png" fill="currentColor" height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M14 4.5V14a2 2 0 01-2 2v-1a1 1 0 001-1V4.5h-2A1.5 1.5 0 019.5 3V1H4a1 1 0 00-1 1v9H2V2a2 2 0 012-2h5.5L14 4.5zm-3.76 8.132c.076.153.123.317.14.492h-.776a.797.797 0 00-.097-.249.689.689 0 00-.17-.19.707.707 0 00-.237-.126.96.96 0 00-.299-.044c-.285 0-.506.1-.665.302-.156.201-.234.484-.234.85v.498c0 .234.032.439.097.615a.881.881 0 00.304.413.87.87 0 00.519.146.967.967 0 00.457-.096.67.67 0 00.272-.264c.06-.11.091-.23.091-.363v-.255H8.82v-.59h1.576v.798c0 .193-.032.377-.097.55a1.29 1.29 0 01-.293.458 1.37 1.37 0 01-.495.313c-.197.074-.43.111-.697.111a1.98 1.98 0 01-.753-.132 1.447 1.447 0 01-.533-.377 1.58 1.58 0 01-.32-.58 2.482 2.482 0 01-.105-.745v-.506c0-.362.067-.678.2-.95.134-.271.328-.482.582-.633.256-.152.565-.228.926-.228.238 0 .45.033.636.1.187.066.348.158.48.275.133.117.238.253.314.407zm-8.64-.706H0v4h.791v-1.343h.803c.287 0 .531-.057.732-.172.203-.118.358-.276.463-.475a1.42 1.42 0 00.161-.677c0-.25-.053-.475-.158-.677a1.176 1.176 0 00-.46-.477c-.2-.12-.443-.179-.732-.179zm.545 1.333a.795.795 0 01-.085.381.574.574 0 01-.238.24.794.794 0 01-.375.082H.788v-1.406h.66c.218 0 .389.06.512.182.123.12.185.295.185.521zm1.964 2.666V13.25h.032l1.761 2.675h.656v-3.999h-.75v2.66h-.032l-1.752-2.66h-.662v4h.747z" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1 @@
<svg class="bi bi-filetype-svg" fill="currentColor" height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M14 4.5V14a2 2 0 01-2 2v-1a1 1 0 001-1V4.5h-2A1.5 1.5 0 019.5 3V1H4a1 1 0 00-1 1v9H2V2a2 2 0 012-2h5.5L14 4.5zM0 14.841a1.13 1.13 0 00.401.823c.13.108.288.192.478.252.19.061.411.091.665.091.338 0 .624-.053.858-.158.237-.105.416-.252.54-.44a1.17 1.17 0 00.187-.656c0-.224-.045-.41-.135-.56a1 1 0 00-.375-.357 2.027 2.027 0 00-.565-.21l-.621-.144a.97.97 0 01-.405-.176.37.37 0 01-.143-.299c0-.156.061-.284.184-.384.125-.101.296-.152.513-.152.143 0 .266.023.37.068a.625.625 0 01.245.181.56.56 0 01.12.258h.75a1.092 1.092 0 00-.199-.566 1.21 1.21 0 00-.5-.41 1.813 1.813 0 00-.78-.152c-.293 0-.552.05-.776.15-.225.099-.4.24-.528.421-.127.182-.19.395-.19.639 0 .201.04.376.123.524.082.149.199.27.351.367.153.095.332.167.54.213l.618.144c.207.049.36.113.462.193a.387.387 0 01.153.326.512.512 0 01-.085.29.559.559 0 01-.256.193c-.111.047-.249.07-.413.07-.117 0-.224-.013-.32-.04a.837.837 0 01-.248-.115.578.578 0 01-.255-.384H0zm4.575 1.09h.952l1.327-3.999h-.879l-.887 3.138H5.05l-.897-3.138h-.917l1.339 4zm5.483-3.293c.076.152.123.316.14.492h-.776a.797.797 0 00-.096-.249.689.689 0 00-.17-.19.707.707 0 00-.237-.126.963.963 0 00-.3-.044c-.284 0-.506.1-.664.302-.157.2-.235.484-.235.85v.497c0 .235.033.44.097.616a.881.881 0 00.305.413.87.87 0 00.518.146.965.965 0 00.457-.097.67.67 0 00.273-.263c.06-.11.09-.23.09-.364v-.254h-.823v-.59h1.576v.798c0 .193-.032.377-.096.55a1.29 1.29 0 01-.293.457 1.37 1.37 0 01-.495.314c-.198.074-.43.111-.698.111a1.98 1.98 0 01-.752-.132 1.447 1.447 0 01-.534-.377 1.58 1.58 0 01-.319-.58 2.482 2.482 0 01-.105-.745v-.507c0-.36.066-.677.199-.949.134-.271.329-.482.583-.633.256-.152.564-.228.926-.228.238 0 .45.033.635.1.188.066.348.158.48.275.134.117.238.253.314.407z" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1 @@
<svg class="bi bi-filetype-txt" fill="currentColor" height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M14 4.5V14a2 2 0 01-2 2h-2v-1h2a1 1 0 001-1V4.5h-2A1.5 1.5 0 019.5 3V1H4a1 1 0 00-1 1v9H2V2a2 2 0 012-2h5.5L14 4.5zM1.928 15.849v-3.337h1.136v-.662H0v.662h1.134v3.337h.794zm4.689-3.999h-.894L4.9 13.289h-.035l-.832-1.439h-.932l1.228 1.983-1.24 2.016h.862l.853-1.415h.035l.85 1.415h.907l-1.253-1.992 1.274-2.007zm1.93.662v3.337h-.794v-3.337H6.619v-.662h3.064v.662H8.546z" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 516 B

Some files were not shown because too many files have changed in this diff Show More