├── .devcontainer
├── Dockerfile
└── devcontainer.json
├── .dockerignore
├── .github
├── CODEOWNERS
├── FUNDING.yml
├── README.md
├── copilot-instructions.md
├── dependabot.yml
└── workflows
│ ├── integration-tests.yml
│ ├── quick-build.yml
│ └── release.yml
├── .gitignore
├── .vscode
├── extensions.json
├── settings.json
└── workspace.code-workspace
├── Dockerfile
├── LICENSE
├── pom.xml
└── src
├── main
├── java
│ └── us
│ │ └── fatehi
│ │ └── schemacrawler
│ │ └── webapp
│ │ ├── AsyncConfiguration.java
│ │ ├── MultipartFileConverter.java
│ │ ├── SchemaCrawlerWebApplication.java
│ │ ├── controller
│ │ ├── DiagramRequestController.java
│ │ ├── DiagramResultController.java
│ │ ├── ErrorController.java
│ │ └── URIConstants.java
│ │ ├── model
│ │ ├── DiagramKey.java
│ │ ├── DiagramRequest.java
│ │ └── DiagramRequestUtility.java
│ │ └── service
│ │ ├── WebappConfig.java
│ │ ├── notification
│ │ ├── AmazonSESNotificationConfig.java
│ │ ├── AmazonSESNotificationService.java
│ │ └── NotificationService.java
│ │ ├── processing
│ │ └── ProcessingService.java
│ │ └── storage
│ │ ├── AWSConfig.java
│ │ ├── AmazonS3StorageConfig.java
│ │ ├── AmazonS3StorageService.java
│ │ ├── FileExtensionType.java
│ │ └── StorageService.java
└── resources
│ ├── api
│ └── schemacrawler-web-application.yaml
│ ├── application.yaml
│ ├── banner.txt
│ ├── static
│ ├── css
│ │ └── style.css
│ └── images
│ │ └── schemacrawler_logo.svg
│ └── templates
│ ├── SchemaCrawlerDiagram.html
│ ├── SchemaCrawlerDiagramForm.html
│ ├── SchemaCrawlerDiagramResult.html
│ ├── error.html
│ └── fragments
│ ├── footer.html
│ ├── head.html
│ └── header.html
└── test
├── java
└── us
│ └── fatehi
│ └── schemacrawler
│ └── webapp
│ └── test
│ ├── ControllerRoundtripTest.java
│ ├── DiagramRequestUtilityTest.java
│ ├── LocalStackS3BucketTest.java
│ ├── RequestControllerAPITest.java
│ ├── RequestControllerTest.java
│ ├── RequestControllerWithS3Test.java
│ ├── ResultControllerAPITest.java
│ ├── ResultControllerTest.java
│ ├── service
│ ├── notification
│ │ └── LogNotificationService.java
│ └── storage
│ │ ├── FileSystemStorageConfig.java
│ │ └── FileSystemStorageService.java
│ └── utility
│ ├── S3ServiceControllerTestConfig.java
│ └── TestUtility.java
├── postman
├── how-to-run-locally.md
├── schemacrawler-web-application.postman_collection.json
├── test.data
└── test.db
└── resources
└── test.db
/.devcontainer/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/vscode/devcontainers/java:0-8
2 |
3 | ARG MAVEN_VERSION=""
4 | RUN \
5 | su vscode -c "umask 0002 && . /usr/local/sdkman/bin/sdkman-init.sh && sdk install maven \"${MAVEN_VERSION}\""
6 |
7 | RUN \
8 | apt-get update && \
9 | export DEBIAN_FRONTEND=noninteractive && \
10 | apt-get -y install --no-install-recommends gnupg2 graphviz fonts-freefont-ttf && \
11 | apt-get clean && \
12 | apt-get autoremove --purge && \
13 | rm -rf /var/lib/apt/lists/*
14 |
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "SchemaCrawler - Web Application",
3 | "build": {
4 | "dockerfile": "Dockerfile",
5 | "args": {
6 | "INSTALL_MAVEN": "true",
7 | "INSTALL_GRADLE": "true",
8 | "NODE_VERSION": "none"
9 | }
10 | },
11 | "settings": {
12 | "workbench.startupEditor": "none",
13 | "java.home": "/docker-java-home",
14 | "java.import.gradle.java.home": "/usr/local/sdkman/candidates/java/current",
15 | "java.configuration.runtimes": [
16 | {
17 | "default": true,
18 | "name": "JavaSE-1.8",
19 | "path": "/usr/local/sdkman/candidates/java/current"
20 | }
21 | ],
22 | "files.exclude": {
23 | "workspace": true
24 | },
25 | "java.configuration.updateBuildConfiguration": "automatic",
26 | "java.format.settings.profile": "GoogleStyle",
27 | "java.format.settings.url": "https://raw.githubusercontent.com/google/styleguide/gh-pages/eclipse-java-google-style.xml",
28 | "editor.tabSize": 2,
29 | "editor.foldingImportsByDefault": true,
30 | "files.trimTrailingWhitespace": true,
31 | "git.enableCommitSigning": true
32 | },
33 | "extensions": [
34 | "vscjava.vscode-java-pack",
35 | "shengchen.vscode-checkstyle",
36 | "editorconfig.editorconfig",
37 | "pivotal.vscode-boot-dev-pack",
38 | "pivotal.vscode-spring-boot",
39 | "redhat.vscode-xml",
40 | "jebbs.markdown-extended",
41 | "davidanson.vscode-markdownlint"
42 | ],
43 | "remoteUser": "vscode"
44 | }
45 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | **
2 | !target
3 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @sualeh @schemacrawler
2 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: sualeh
2 | custom: 'https://www.paypal.me/sualeh'
3 |
--------------------------------------------------------------------------------
/.github/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/schemacrawler/SchemaCrawler-Web-Application/actions?query=workflow%3A%22Quick+Build%22)
2 | [](https://github.com/schemacrawler/SchemaCrawler-Web-Application/actions/workflows/integration-tests.yml)
3 | [](https://app.codecov.io/gh/schemacrawler/SchemaCrawler-Web-Application)
4 |
5 |
6 |
7 | # SchemaCrawler Web Application
8 |
9 | > **Note**: Please see the [SchemaCrawler website](https://www.schemacrawler.com/) for more details.
10 |
11 |
12 | ## Technologies
13 |
14 | This is a Spring Boot web application with a Bootstrap user interface, with source code control in GitHub, which is automatically built on every commit by GitHub Actions using a Maven build, tests are run, and coverage measured with JaCoCo and Codecov.io, and then immediately deployed to Heroku using a Docker image, which generates an crows-foot ERD of a SQLite database.
15 |
16 |
17 | ## Build and Run
18 |
19 | ### Build
20 |
21 | - Install [Graphviz](https://www.graphviz.org), which is a prerequisite for SchemaCrawler
22 | - Install Docker
23 | - Build application from Maven, run `mvn clean package`
24 |
25 |
26 | ### Build Docker Image
27 |
28 | - Follow the steps above
29 | - Install Docker
30 | - Build application and Docker image from Maven, run `mvn -Ddocker.skip=false clean package`
31 |
32 |
33 | ### Start the Server
34 |
35 | - Set the following environmental variables locally
36 | - AWS_ACCESS_KEY_ID
37 | - AWS_SECRET_ACCESS_KEY
38 | - AWS_S3_BUCKET
39 | - Do one of the steps below to start the web application locally on your system
40 | - Start the application from Maven, run
41 | `mvn -Dspring-boot.run.fork=false spring-boot:run`
42 | - Start application from the jar file, run
43 | `java -jar target/schemacrawler-webapp-16.26.1-1.jar`
44 | - Start the application from the local image in a Docker container, run
45 | `docker run -d --rm --env AWS_ACCESS_KEY_ID=xxxxx --env AWS_SECRET_ACCESS_KEY=xxxxx --env AWS_S3_BUCKET=xxxxx -p 8080:8080 -t schemacrawler/schemacrawler-webapp`
46 |
47 |
48 | ### Use the Application
49 |
50 | Then, after you ensure that the web server is running, either from the command-line,
51 | or the Docker container, open a browser to
52 | [https://localhost:8080](https://localhost:8080)
53 |
--------------------------------------------------------------------------------
/.github/copilot-instructions.md:
--------------------------------------------------------------------------------
1 | # Instructions for Java Projects Using Apache Maven
## General Coding Guidelines
- Prefer **immutability** and use the `final` keyword for fields, parameters, and local variables when appropriate.
- Follow **Java 21 best practices**, including usage of `Optional`, `Streams`, and functional programming where applicable.
- Ensure **thread safety** by avoiding mutable shared state and using synchronized wrappers or concurrency utilities when necessary.
- Use **meaningful names** for classes, methods, and variables to improve code readability.
- Follow **SOLID principles** to enhance maintainability and scalability.
- Write meaningful **javadocs** for functions and classes.
## Project Structure
- Organize packages based on functionality (e.g., `service`, `repository`, `controller`).
- Use **Apache Maven** for build management and dependency resolution.
- Maintain a **consistent project structure**, following standard conventions.
## Dependencies and Versions
- Define **explicit versions** for dependencies to prevent compatibility issues.
- Prefer **dependency management** using `dependencyManagement` in `pom.xml` for centralized version control.
- Use **dependency exclusions** where necessary to avoid unwanted transitive dependencies.
## Testing and Quality
- Write **unit tests** for business logic using JUnit 5 with Hamcrest matchers.
- Use **Mockito** for mocking dependencies in tests.
- Maintain **high test coverage** to ensure reliability.
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: maven
4 | directory: "/"
5 | schedule:
6 | interval: "weekly"
7 | - package-ecosystem: "github-actions"
8 | directory: "/"
9 | schedule:
10 | interval: "weekly"
11 |
--------------------------------------------------------------------------------
/.github/workflows/integration-tests.yml:
--------------------------------------------------------------------------------
1 | name: Integration Tests
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | schedule:
7 | - cron: '12 0/11 * * *'
8 |
9 | concurrency:
10 | group: ${{ github.workflow }}-${{ github.ref }}
11 | cancel-in-progress: true
12 |
13 | jobs:
14 | build:
15 | name: Integration Tests
16 | runs-on: ubuntu-latest
17 | steps:
18 |
19 | # SETUP BUILD ENVIRONMENT
20 | - id: prepare-maven-build
21 | name: Prepare Maven build
22 | uses: sualeh/prepare-maven-build@v1.4.0
23 | with:
24 | java-version: 21
25 | - id: install-graphviz
26 | name: Install Graphviz
27 | uses: sualeh/install-graphviz@v1.0.3
28 | - id: setup-node
29 | name: Set up Node.js
30 | uses: actions/setup-node@v4
31 | with:
32 | node-version: '20.x'
33 | - id: install-newman
34 | name: Install newman
35 | shell: bash
36 | run: |
37 | # Install newman
38 | npm install -g newman
39 | newman -v
40 |
41 | # BUILD DEPENDENCIES
42 | - id: checkout-schemacrawler
43 | name: Checkout SchemaCrawler
44 | uses: actions/checkout@v4
45 | with:
46 | repository: schemacrawler/SchemaCrawler
47 | path: SchemaCrawler
48 | - id: build-schemacrawler
49 | name: Build SchemaCrawler for local Maven repository
50 | shell: bash
51 | run: |
52 | # Build SchemaCrawler
53 | cd SchemaCrawler
54 | mvn \
55 | --no-transfer-progress \
56 | --batch-mode \
57 | -DskipTests=true \
58 | clean install
59 |
60 | # BUILD AND TEST
61 | - id: build-test
62 | name: Build and run tests
63 | shell: bash
64 | run: |
65 | # Build
66 | mvn \
67 | --no-transfer-progress \
68 | --batch-mode \
69 | package
70 |
71 | # RUN INTEGRATION TESTS WITH NEWMAN
72 | - id: run-postman
73 | name: Run Postman tests for API
74 | env:
75 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
76 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
77 | AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }}
78 | shell: bash
79 | run: |
80 | # Build
81 | timeout 120s mvn spring-boot:run > application.log 2>&1 &
82 | sleep 15
83 | newman run \
84 | --verbose \
85 | --color on \
86 | --delay-request 1000 \
87 | --working-dir src/test/postman \
88 | --env-var "url=http://localhost:8080" \
89 | src/test/postman/schemacrawler-web-application.postman_collection.json
90 | - id: upload-application-log
91 | name: Upload application log
92 | uses: actions/upload-artifact@v4
93 | if: always()
94 | with:
95 | name: application-log
96 | path: ./application.log
97 | retention-days: 5
98 |
--------------------------------------------------------------------------------
/.github/workflows/quick-build.yml:
--------------------------------------------------------------------------------
1 | name: Quick Build
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | pull_request:
7 | branches:
8 | - main
9 |
10 | concurrency:
11 | group: ${{ github.workflow }}-${{ github.ref }}
12 | cancel-in-progress: true
13 |
14 | permissions: read-all
15 |
16 | jobs:
17 | build:
18 | name: Quick build
19 | runs-on: ubuntu-latest
20 |
21 | # Service containers to run with workflow
22 | services:
23 | swagger-editor:
24 | image: swaggerapi/swagger-editor
25 | ports:
26 | - 80:8080
27 |
28 |
29 | steps:
30 |
31 | # SETUP BUILD ENVIRONMENT
32 | - id: prepare-maven-build
33 | name: Prepare Maven build
34 | uses: sualeh/prepare-maven-build@v1.4.0
35 | with:
36 | java-version: 21
37 | - id: install-graphviz
38 | name: Install Graphviz
39 | uses: sualeh/install-graphviz@v1.0.3
40 |
41 | # BUILD DEPENDENCIES
42 | - id: checkout-schemacrawler
43 | name: Checkout SchemaCrawler
44 | uses: actions/checkout@v4
45 | with:
46 | repository: schemacrawler/SchemaCrawler
47 | path: SchemaCrawler
48 | - id: build-schemacrawler
49 | name: Build SchemaCrawler for local Maven repository
50 | shell: bash
51 | run: |
52 | # Build SchemaCrawler
53 | cd SchemaCrawler
54 | mvn \
55 | --no-transfer-progress \
56 | --batch-mode \
57 | -DskipTests=true \
58 | clean install
59 |
60 | # VALIDATE OPENAPI SPECIFICATION
61 | - id: validate-oas
62 | name: Validate OpenAPI definition
63 | uses: swaggerexpert/swagger-editor-validate@v1
64 | with:
65 | swagger-editor-url: http://localhost/
66 | definition-file: src/main/resources/api/schemacrawler-web-application.yaml
67 |
68 | # BUILD AND TEST
69 | - id: build-test
70 | name: Build and run tests
71 | shell: bash
72 | run: |
73 | # Build
74 | mvn \
75 | --no-transfer-progress \
76 | --batch-mode \
77 | clean package
78 | - id: publish-test-results
79 | name: Upload results and coverage
80 | if: contains(github.ref, 'main')
81 | env:
82 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
83 | CODECOV_TOKEN: 350b716c-8f65-451b-b438-072f0662637d
84 | shell: bash
85 | run: |
86 | bash <(curl -s https://codecov.io/bash)
87 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Create Release
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | tags:
7 | - 'v*'
8 |
9 | concurrency:
10 | group: ${{ github.workflow }}-${{ github.ref }}
11 | cancel-in-progress: true
12 |
13 | jobs:
14 | build:
15 | name: Create Release
16 | runs-on: ubuntu-latest
17 | steps:
18 |
19 | # VALIDATE TAGS
20 | - id: validate-semver
21 | name: Validate tag against semantic versioning
22 | if: startsWith(github.ref, 'refs/tags/')
23 | shell: bash
24 | run: |
25 | SEMVER_PATTERN="^refs/tags/v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$"
26 | if [[ ! $GITHUB_REF =~ $SEMVER_PATTERN ]]
27 | then
28 | echo "Tag $GITHUB_REF does not follow semantic versioning"
29 | exit 1
30 | fi
31 |
32 | # SETUP BUILD ENVIRONMENT
33 | - id: prepare-maven-build
34 | name: Prepare Maven build
35 | uses: sualeh/prepare-maven-build@v1.4.0
36 | with:
37 | java-version: 21
38 | - id: install-graphviz
39 | name: Install Graphviz
40 | uses: sualeh/install-graphviz@v1.0.3
41 |
42 | # BUILD FOR DISTRIBUTION
43 | # To build an image using Cloud Native Buildpacks for Spring Boot, provide
44 | # and additional -Ddocker.skip=false argument to the Maven build.
45 | - id: build
46 | name: Build and test for distribution
47 | shell: bash
48 | run: |
49 | # Build
50 | mvn \
51 | --no-transfer-progress \
52 | --batch-mode \
53 | clean install
54 |
55 | # CREATE GITHUB RELEASE AND ADD ASSETS
56 | - id: create-release
57 | name: Create GitHub release
58 | uses: actions/create-release@latest
59 | if: startsWith(github.ref, 'refs/tags/')
60 | env:
61 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
62 | with:
63 | tag_name: ${{ github.ref }}
64 | release_name: ${{ github.ref }}
65 | body: |
66 | SchemaCrawler Webapp Release ${{ github.sha }}
67 | draft: false
68 | prerelease: false
69 | - id: upload-release-zip
70 | name: Upload SchemaCrawler Webapp distribution
71 | uses: actions/upload-release-asset@latest
72 | if: startsWith(github.ref, 'refs/tags/')
73 | env:
74 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
75 | with:
76 | upload_url: ${{ steps.create-release.outputs.upload_url }}
77 | asset_path: ./target/schemacrawler-webapp-16.26.1-1.jar
78 | asset_name: schemacrawler-webapp-16.26.1-1.jar
79 | asset_content_type: application/zip
80 |
81 | # BUILD AND PUBLISH DOCKER IMAGE
82 | - id: setup-qemu
83 | name: Setup QEMU
84 | uses: docker/setup-qemu-action@v3
85 |
86 | - id: setup-buildx
87 | name: Setup Docker Buildx
88 | uses: docker/setup-buildx-action@v3
89 |
90 | - name: Log into Docker Hub
91 | uses: docker/login-action@v3
92 | with:
93 | username: ${{ secrets.DOCKER_USERNAME }}
94 | password: ${{ secrets.DOCKER_PASSWORD }}
95 |
96 | - name: Build and push Docker image
97 | uses: docker/build-push-action@v6
98 | with:
99 | file: ./Dockerfile
100 | context: .
101 | platforms: |-
102 | linux/amd64
103 | linux/arm64
104 | tags: |-
105 | schemacrawler/schemacrawler-webapp:v16.26.1-1
106 | schemacrawler/schemacrawler-webapp:latest
107 | sbom: true
108 | provenance: true
109 | push: true
110 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 | ~/
4 | *.versionsBackup
5 | tree.txt
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 |
15 | ### IntelliJ IDEA ###
16 | .idea
17 | *.iws
18 | **/*.iml
19 | *.ipr
20 |
21 | ### NetBeans ###
22 | nbproject/private/
23 | build/
24 | nbbuild/
25 | dist/
26 | nbdist/
27 | .nb-gradle/
28 | /uploaded-files/
29 | keys.txt
30 | *.db
31 | *.png
32 | **/launch.json
33 |
34 | /src/test/postman/production.postman_environment.json
35 | application.log
36 | tree.txt
37 | /src/test/postman/api-*.json
38 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "vscjava.vscode-java-debug"
4 | ]
5 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "scm.showInputActionButton": false,
3 | "git.showActionButton": {
4 | "commit": false,
5 | "publish": false,
6 | "sync": false
7 | },
8 | "git.postCommitCommand": "sync",
9 | "java.configuration.updateBuildConfiguration": "automatic",
10 | "java.compile.nullAnalysis.mode": "automatic",
11 | "editor.formatOnSave": false,
12 | "editor.formatOnType": true
13 | }
--------------------------------------------------------------------------------
/.vscode/workspace.code-workspace:
--------------------------------------------------------------------------------
1 | {
2 | "folders": [
3 | {
4 | "path": ".."
5 | }
6 | ],
7 | "settings": {
8 | "workbench.startupEditor": "none",
9 | "files.exclude": {
10 | "workspace": true
11 | },
12 | "java.configuration.updateBuildConfiguration": "automatic",
13 | "java.format.settings.profile": "GoogleStyle",
14 | "java.format.settings.url": "https://raw.githubusercontent.com/google/styleguide/gh-pages/eclipse-java-google-style.xml",
15 | "java.test.config": [
16 | {
17 | "name": "Standard",
18 | "workingDirectory": "${workspaceFolder}"
19 | },
20 | {
21 | "name": "Run Heavy Database Tests",
22 | "vmargs": [
23 | "-Dheavydb"
24 | ]
25 | }
26 | ],
27 | "editor.tabSize": 2,
28 | "editor.foldingImportsByDefault": true,
29 | "files.trimTrailingWhitespace": true,
30 | "git.enableCommitSigning": true
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # ========================================================================
2 | # SchemaCrawler
3 | # http://www.schemacrawler.com
4 | # Copyright (c) 2000-2025, Sualeh Fatehi .
5 | # All rights reserved.
6 | # ------------------------------------------------------------------------
7 | #
8 | # SchemaCrawler is distributed in the hope that it will be useful, but
9 | # WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 | #
12 | # SchemaCrawler and the accompanying materials are made available under
13 | # the terms of the Eclipse Public License v1.0, GNU General Public License
14 | # v3 or GNU Lesser General Public License v3.
15 | #
16 | # You may elect to redistribute this code under any of these licenses.
17 | #
18 | # The Eclipse Public License is available at:
19 | # http://www.eclipse.org/legal/epl-v10.html
20 | #
21 | # The GNU General Public License v3 and the GNU Lesser General Public
22 | # License v3 are available at:
23 | # http://www.gnu.org/licenses/
24 | #
25 | # ========================================================================
26 |
27 | FROM eclipse-temurin:21-jdk-alpine
28 |
29 | ARG SCHEMACRAWLER_VERSION=16.26.1
30 | ARG SCHEMACRAWLER_WEBAPP_VERSION=16.26.1-1
31 |
32 | LABEL \
33 | "maintainer"="Sualeh Fatehi " \
34 | "org.opencontainers.image.authors"="Sualeh Fatehi " \
35 | "org.opencontainers.image.title"="SchemaCrawler Web Application" \
36 | "org.opencontainers.image.description"="Free database schema discovery and comprehension tool" \
37 | "org.opencontainers.image.url"="https://www.schemacrawler.com/" \
38 | "org.opencontainers.image.source"="https://github.com/schemacrawler/SchemaCrawler-Web-Application" \
39 | "org.opencontainers.image.vendor"="SchemaCrawler" \
40 | "org.opencontainers.image.license"="(GPL-3.0 OR OR LGPL-3.0+ EPL-1.0)"
41 |
42 |
43 | # Install Graphviz as root user
44 | RUN \
45 | apk add --update --no-cache \
46 | bash \
47 | bash-completion \
48 | graphviz \
49 | ttf-freefont
50 |
51 | # Copy SchemaCrawler Web Application files for the current user
52 | COPY \
53 | ./target/schemacrawler-webapp-${SCHEMACRAWLER_WEBAPP_VERSION}.jar \
54 | schemacrawler-webapp.jar
55 |
56 | # Expose the port the application will run on
57 | EXPOSE 8080
58 |
59 | # Run the web-application. Define default port and java options as environment variables
60 | ENV JAVA_OPTS="-Djava.security.egd=file:/dev/./urandom"
61 | ENV PORT=8080
62 |
63 | # Default command to run the application
64 | ENTRYPOINT java $JAVA_OPTS -Dserver.port=$PORT -jar schemacrawler-webapp.jar
65 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Eclipse Public License - v 2.0
2 |
3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
4 | PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
5 | OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
6 |
7 | 1. DEFINITIONS
8 |
9 | "Contribution" means:
10 |
11 | a) in the case of the initial Contributor, the initial content
12 | Distributed under this Agreement, and
13 |
14 | b) in the case of each subsequent Contributor:
15 | i) changes to the Program, and
16 | ii) additions to the Program;
17 | where such changes and/or additions to the Program originate from
18 | and are Distributed by that particular Contributor. A Contribution
19 | "originates" from a Contributor if it was added to the Program by
20 | such Contributor itself or anyone acting on such Contributor's behalf.
21 | Contributions do not include changes or additions to the Program that
22 | are not Modified Works.
23 |
24 | "Contributor" means any person or entity that Distributes the Program.
25 |
26 | "Licensed Patents" mean patent claims licensable by a Contributor which
27 | are necessarily infringed by the use or sale of its Contribution alone
28 | or when combined with the Program.
29 |
30 | "Program" means the Contributions Distributed in accordance with this
31 | Agreement.
32 |
33 | "Recipient" means anyone who receives the Program under this Agreement
34 | or any Secondary License (as applicable), including Contributors.
35 |
36 | "Derivative Works" shall mean any work, whether in Source Code or other
37 | form, that is based on (or derived from) the Program and for which the
38 | editorial revisions, annotations, elaborations, or other modifications
39 | represent, as a whole, an original work of authorship.
40 |
41 | "Modified Works" shall mean any work in Source Code or other form that
42 | results from an addition to, deletion from, or modification of the
43 | contents of the Program, including, for purposes of clarity any new file
44 | in Source Code form that contains any contents of the Program. Modified
45 | Works shall not include works that contain only declarations,
46 | interfaces, types, classes, structures, or files of the Program solely
47 | in each case in order to link to, bind by name, or subclass the Program
48 | or Modified Works thereof.
49 |
50 | "Distribute" means the acts of a) distributing or b) making available
51 | in any manner that enables the transfer of a copy.
52 |
53 | "Source Code" means the form of a Program preferred for making
54 | modifications, including but not limited to software source code,
55 | documentation source, and configuration files.
56 |
57 | "Secondary License" means either the GNU General Public License,
58 | Version 2.0, or any later versions of that license, including any
59 | exceptions or additional permissions as identified by the initial
60 | Contributor.
61 |
62 | 2. GRANT OF RIGHTS
63 |
64 | a) Subject to the terms of this Agreement, each Contributor hereby
65 | grants Recipient a non-exclusive, worldwide, royalty-free copyright
66 | license to reproduce, prepare Derivative Works of, publicly display,
67 | publicly perform, Distribute and sublicense the Contribution of such
68 | Contributor, if any, and such Derivative Works.
69 |
70 | b) Subject to the terms of this Agreement, each Contributor hereby
71 | grants Recipient a non-exclusive, worldwide, royalty-free patent
72 | license under Licensed Patents to make, use, sell, offer to sell,
73 | import and otherwise transfer the Contribution of such Contributor,
74 | if any, in Source Code or other form. This patent license shall
75 | apply to the combination of the Contribution and the Program if, at
76 | the time the Contribution is added by the Contributor, such addition
77 | of the Contribution causes such combination to be covered by the
78 | Licensed Patents. The patent license shall not apply to any other
79 | combinations which include the Contribution. No hardware per se is
80 | licensed hereunder.
81 |
82 | c) Recipient understands that although each Contributor grants the
83 | licenses to its Contributions set forth herein, no assurances are
84 | provided by any Contributor that the Program does not infringe the
85 | patent or other intellectual property rights of any other entity.
86 | Each Contributor disclaims any liability to Recipient for claims
87 | brought by any other entity based on infringement of intellectual
88 | property rights or otherwise. As a condition to exercising the
89 | rights and licenses granted hereunder, each Recipient hereby
90 | assumes sole responsibility to secure any other intellectual
91 | property rights needed, if any. For example, if a third party
92 | patent license is required to allow Recipient to Distribute the
93 | Program, it is Recipient's responsibility to acquire that license
94 | before distributing the Program.
95 |
96 | d) Each Contributor represents that to its knowledge it has
97 | sufficient copyright rights in its Contribution, if any, to grant
98 | the copyright license set forth in this Agreement.
99 |
100 | e) Notwithstanding the terms of any Secondary License, no
101 | Contributor makes additional grants to any Recipient (other than
102 | those set forth in this Agreement) as a result of such Recipient's
103 | receipt of the Program under the terms of a Secondary License
104 | (if permitted under the terms of Section 3).
105 |
106 | 3. REQUIREMENTS
107 |
108 | 3.1 If a Contributor Distributes the Program in any form, then:
109 |
110 | a) the Program must also be made available as Source Code, in
111 | accordance with section 3.2, and the Contributor must accompany
112 | the Program with a statement that the Source Code for the Program
113 | is available under this Agreement, and informs Recipients how to
114 | obtain it in a reasonable manner on or through a medium customarily
115 | used for software exchange; and
116 |
117 | b) the Contributor may Distribute the Program under a license
118 | different than this Agreement, provided that such license:
119 | i) effectively disclaims on behalf of all other Contributors all
120 | warranties and conditions, express and implied, including
121 | warranties or conditions of title and non-infringement, and
122 | implied warranties or conditions of merchantability and fitness
123 | for a particular purpose;
124 |
125 | ii) effectively excludes on behalf of all other Contributors all
126 | liability for damages, including direct, indirect, special,
127 | incidental and consequential damages, such as lost profits;
128 |
129 | iii) does not attempt to limit or alter the recipients' rights
130 | in the Source Code under section 3.2; and
131 |
132 | iv) requires any subsequent distribution of the Program by any
133 | party to be under a license that satisfies the requirements
134 | of this section 3.
135 |
136 | 3.2 When the Program is Distributed as Source Code:
137 |
138 | a) it must be made available under this Agreement, or if the
139 | Program (i) is combined with other material in a separate file or
140 | files made available under a Secondary License, and (ii) the initial
141 | Contributor attached to the Source Code the notice described in
142 | Exhibit A of this Agreement, then the Program may be made available
143 | under the terms of such Secondary Licenses, and
144 |
145 | b) a copy of this Agreement must be included with each copy of
146 | the Program.
147 |
148 | 3.3 Contributors may not remove or alter any copyright, patent,
149 | trademark, attribution notices, disclaimers of warranty, or limitations
150 | of liability ("notices") contained within the Program from any copy of
151 | the Program which they Distribute, provided that Contributors may add
152 | their own appropriate notices.
153 |
154 | 4. COMMERCIAL DISTRIBUTION
155 |
156 | Commercial distributors of software may accept certain responsibilities
157 | with respect to end users, business partners and the like. While this
158 | license is intended to facilitate the commercial use of the Program,
159 | the Contributor who includes the Program in a commercial product
160 | offering should do so in a manner which does not create potential
161 | liability for other Contributors. Therefore, if a Contributor includes
162 | the Program in a commercial product offering, such Contributor
163 | ("Commercial Contributor") hereby agrees to defend and indemnify every
164 | other Contributor ("Indemnified Contributor") against any losses,
165 | damages and costs (collectively "Losses") arising from claims, lawsuits
166 | and other legal actions brought by a third party against the Indemnified
167 | Contributor to the extent caused by the acts or omissions of such
168 | Commercial Contributor in connection with its distribution of the Program
169 | in a commercial product offering. The obligations in this section do not
170 | apply to any claims or Losses relating to any actual or alleged
171 | intellectual property infringement. In order to qualify, an Indemnified
172 | Contributor must: a) promptly notify the Commercial Contributor in
173 | writing of such claim, and b) allow the Commercial Contributor to control,
174 | and cooperate with the Commercial Contributor in, the defense and any
175 | related settlement negotiations. The Indemnified Contributor may
176 | participate in any such claim at its own expense.
177 |
178 | For example, a Contributor might include the Program in a commercial
179 | product offering, Product X. That Contributor is then a Commercial
180 | Contributor. If that Commercial Contributor then makes performance
181 | claims, or offers warranties related to Product X, those performance
182 | claims and warranties are such Commercial Contributor's responsibility
183 | alone. Under this section, the Commercial Contributor would have to
184 | defend claims against the other Contributors related to those performance
185 | claims and warranties, and if a court requires any other Contributor to
186 | pay any damages as a result, the Commercial Contributor must pay
187 | those damages.
188 |
189 | 5. NO WARRANTY
190 |
191 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
192 | PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS"
193 | BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
194 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF
195 | TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
196 | PURPOSE. Each Recipient is solely responsible for determining the
197 | appropriateness of using and distributing the Program and assumes all
198 | risks associated with its exercise of rights under this Agreement,
199 | including but not limited to the risks and costs of program errors,
200 | compliance with applicable laws, damage to or loss of data, programs
201 | or equipment, and unavailability or interruption of operations.
202 |
203 | 6. DISCLAIMER OF LIABILITY
204 |
205 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
206 | PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
207 | SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
208 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
209 | PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
210 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
211 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
212 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
213 | POSSIBILITY OF SUCH DAMAGES.
214 |
215 | 7. GENERAL
216 |
217 | If any provision of this Agreement is invalid or unenforceable under
218 | applicable law, it shall not affect the validity or enforceability of
219 | the remainder of the terms of this Agreement, and without further
220 | action by the parties hereto, such provision shall be reformed to the
221 | minimum extent necessary to make such provision valid and enforceable.
222 |
223 | If Recipient institutes patent litigation against any entity
224 | (including a cross-claim or counterclaim in a lawsuit) alleging that the
225 | Program itself (excluding combinations of the Program with other software
226 | or hardware) infringes such Recipient's patent(s), then such Recipient's
227 | rights granted under Section 2(b) shall terminate as of the date such
228 | litigation is filed.
229 |
230 | All Recipient's rights under this Agreement shall terminate if it
231 | fails to comply with any of the material terms or conditions of this
232 | Agreement and does not cure such failure in a reasonable period of
233 | time after becoming aware of such noncompliance. If all Recipient's
234 | rights under this Agreement terminate, Recipient agrees to cease use
235 | and distribution of the Program as soon as reasonably practicable.
236 | However, Recipient's obligations under this Agreement and any licenses
237 | granted by Recipient relating to the Program shall continue and survive.
238 |
239 | Everyone is permitted to copy and distribute copies of this Agreement,
240 | but in order to avoid inconsistency the Agreement is copyrighted and
241 | may only be modified in the following manner. The Agreement Steward
242 | reserves the right to publish new versions (including revisions) of
243 | this Agreement from time to time. No one other than the Agreement
244 | Steward has the right to modify this Agreement. The Eclipse Foundation
245 | is the initial Agreement Steward. The Eclipse Foundation may assign the
246 | responsibility to serve as the Agreement Steward to a suitable separate
247 | entity. Each new version of the Agreement will be given a distinguishing
248 | version number. The Program (including Contributions) may always be
249 | Distributed subject to the version of the Agreement under which it was
250 | received. In addition, after a new version of the Agreement is published,
251 | Contributor may elect to Distribute the Program (including its
252 | Contributions) under the new version.
253 |
254 | Except as expressly stated in Sections 2(a) and 2(b) above, Recipient
255 | receives no rights or licenses to the intellectual property of any
256 | Contributor under this Agreement, whether expressly, by implication,
257 | estoppel or otherwise. All rights in the Program not expressly granted
258 | under this Agreement are reserved. Nothing in this Agreement is intended
259 | to be enforceable by any entity that is not a Contributor or Recipient.
260 | No third-party beneficiary rights are created under this Agreement.
261 |
262 | Exhibit A - Form of Secondary Licenses Notice
263 |
264 | "This Source Code may also be made available under the following
265 | Secondary Licenses when the conditions for such availability set forth
266 | in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),
267 | version(s), and exceptions or additional permissions here}."
268 |
269 | Simply including a copy of this Agreement, including this Exhibit A
270 | is not sufficient to license the Source Code under Secondary Licenses.
271 |
272 | If it is not possible or desirable to put the notice in a particular
273 | file, then You may include the notice in a location (such as a LICENSE
274 | file in a relevant directory) where a recipient would be likely to
275 | look for such a notice.
276 |
277 | You may add additional accurate notices of copyright ownership.
278 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | us.fatehi
8 | schemacrawler-webapp
9 | 16.26.1-1
10 | jar
11 |
12 | SchemaCrawler Web Application
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter-parent
17 | 3.4.1
18 |
19 |
20 |
21 |
22 | UTF-8
23 | UTF-8
24 | true
25 | 21
26 |
27 |
28 |
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-dependencies
33 | 3.5.0
34 | pom
35 | import
36 |
37 |
38 | org.junit
39 | junit-bom
40 | 5.13.0
41 | pom
42 | import
43 |
44 |
45 | software.amazon.awssdk
46 | bom
47 | 2.31.54
48 | pom
49 | import
50 |
51 |
52 | com.atlassian.oai
53 | swagger-request-validator
54 | 2.44.8
55 | pom
56 | import
57 |
58 |
59 | us.fatehi
60 | schemacrawler-parent
61 | 16.26.1
62 | import
63 | pom
64 |
65 |
66 | org.testcontainers
67 | testcontainers-bom
68 | 1.21.1
69 | pom
70 | import
71 |
72 |
73 |
74 |
75 |
76 |
77 | org.springframework.boot
78 | spring-boot-starter-web
79 |
80 |
81 | org.springframework.boot
82 | spring-boot-starter-thymeleaf
83 |
84 |
85 | org.springframework.boot
86 | spring-boot-starter-validation
87 |
88 |
89 | org.springframework.boot
90 | spring-boot-starter-actuator
91 |
92 |
93 |
94 | org.springframework.boot
95 | spring-boot-devtools
96 | runtime
97 | true
98 |
99 |
100 | org.springframework.boot
101 | spring-boot-starter-test
102 | test
103 |
104 |
105 |
106 | commons-io
107 | commons-io
108 | 2.19.0
109 |
110 |
111 | com.google.code.gson
112 | gson
113 |
114 |
115 | software.amazon.awssdk
116 | s3
117 |
118 |
119 | com.sun.mail
120 | jakarta.mail
121 | 2.0.1
122 |
123 |
124 |
125 | org.eclipse.angus
126 | jakarta.mail
127 |
128 |
129 | software.amazon.awssdk
130 | ses
131 |
132 |
133 | org.apache.commons
134 | commons-lang3
135 |
136 |
137 |
138 | us.fatehi
139 | schemacrawler-sqlite
140 | 16.26.1
141 |
142 |
143 |
144 |
145 | org.apache.tika
146 | tika-core
147 | 3.2.0
148 |
149 |
150 |
151 | org.testcontainers
152 | junit-jupiter
153 | test
154 |
155 |
156 | nl.jqno.equalsverifier
157 | equalsverifier
158 | 4.0
159 | test
160 |
161 |
162 | org.testcontainers
163 | localstack
164 | test
165 |
166 |
167 |
168 |
169 | com.atlassian.oai
170 | swagger-request-validator-mockmvc
171 | test
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 | true
183 | org.apache.maven.plugins
184 | maven-enforcer-plugin
185 | 3.5.2
186 |
187 |
188 | org.apache.maven.plugins
189 | maven-clean-plugin
190 | 3.5.0
191 |
192 |
193 | org.apache.maven.plugins
194 | maven-resources-plugin
195 | 3.3.1
196 |
197 |
198 | org.apache.maven.plugins
199 | maven-source-plugin
200 | 3.3.1
201 |
202 |
203 | org.apache.maven.plugins
204 | maven-compiler-plugin
205 | 3.14.0
206 |
207 |
208 | org.apache.maven.plugins
209 | maven-surefire-plugin
210 | 3.5.3
211 |
212 |
213 | org.apache.maven.plugins
214 | maven-jar-plugin
215 | 3.4.2
216 |
217 |
218 | org.apache.maven.plugins
219 | maven-javadoc-plugin
220 | 3.11.2
221 |
222 | 21
223 |
224 |
225 |
226 | org.apache.maven.plugins
227 | maven-install-plugin
228 | 3.1.4
229 |
230 |
231 | org.apache.maven.plugins
232 | maven-gpg-plugin
233 | 3.2.7
234 |
235 |
236 | org.apache.maven.plugins
237 | maven-assembly-plugin
238 | 3.7.1
239 |
240 |
241 | org.apache.maven.plugins
242 | maven-shade-plugin
243 | 3.6.0
244 |
245 |
246 | org.apache.maven.plugins
247 | maven-deploy-plugin
248 | 3.1.4
249 |
250 |
251 | org.sonatype.plugins
252 | nexus-staging-maven-plugin
253 | 1.7.0
254 |
255 |
256 | org.codehaus.mojo
257 | exec-maven-plugin
258 | 3.5.1
259 |
260 |
261 |
262 |
263 |
264 | org.apache.maven.plugins
265 | maven-toolchains-plugin
266 | 3.2.0
267 |
268 |
269 |
270 | toolchain
271 |
272 |
273 |
274 |
275 |
276 |
277 | 21
278 | temurin
279 |
280 |
281 |
282 |
283 |
284 | org.apache.maven.plugins
285 | maven-compiler-plugin
286 |
287 | 21
288 | 21
289 |
290 |
291 |
292 | org.springframework.boot
293 | spring-boot-maven-plugin
294 |
295 |
296 | repackage
297 | package
298 |
299 | repackage
300 |
301 |
302 |
303 |
304 |
305 | org.jacoco
306 | jacoco-maven-plugin
307 | 0.8.13
308 |
309 |
310 |
311 | prepare-agent
312 |
313 |
314 |
315 | report
316 | test
317 |
318 | report
319 |
320 |
321 |
322 |
323 |
324 | org.codehaus.mojo
325 | exec-maven-plugin
326 |
327 |
328 | docker-build
329 | package
330 |
331 | exec
332 |
333 |
334 | ${docker.skip}
335 | docker
336 | ${project.basedir}
337 |
338 | build
339 | -f
340 | ${project.basedir}/Dockerfile
341 | -t
342 | schemacrawler/schemacrawler-webapp:v${project.version}
343 | -t
344 | schemacrawler/schemacrawler-webapp:latest
345 | ${project.basedir}
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
--------------------------------------------------------------------------------
/src/main/java/us/fatehi/schemacrawler/webapp/AsyncConfiguration.java:
--------------------------------------------------------------------------------
1 | /*
2 | ========================================================================
3 | SchemaCrawler
4 | http://www.schemacrawler.com
5 | Copyright (c) 2000-2025, Sualeh Fatehi .
6 | All rights reserved.
7 | ------------------------------------------------------------------------
8 |
9 | SchemaCrawler is distributed in the hope that it will be useful, but
10 | WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 |
13 | SchemaCrawler and the accompanying materials are made available under
14 | the terms of the Eclipse Public License v1.0, GNU General Public License
15 | v3 or GNU Lesser General Public License v3.
16 |
17 | You may elect to redistribute this code under any of these licenses.
18 |
19 | The Eclipse Public License is available at:
20 | http://www.eclipse.org/legal/epl-v10.html
21 |
22 | The GNU General Public License v3 and the GNU Lesser General Public
23 | License v3 are available at:
24 | http://www.gnu.org/licenses/
25 |
26 | ========================================================================
27 | */
28 |
29 | package us.fatehi.schemacrawler.webapp;
30 |
31 | import org.apache.commons.lang3.exception.ExceptionUtils;
32 | import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
33 | import org.springframework.context.annotation.Configuration;
34 | import org.springframework.scheduling.annotation.AsyncConfigurer;
35 | import org.springframework.scheduling.annotation.EnableAsync;
36 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
37 | import java.util.Arrays;
38 | import java.util.concurrent.Executor;
39 | import java.util.logging.Logger;
40 |
41 | @Configuration
42 | @EnableAsync
43 | public class AsyncConfiguration implements AsyncConfigurer {
44 |
45 | private static final Logger logger = Logger.getLogger(AsyncConfiguration.class.getName());
46 |
47 | @Override
48 | public Executor getAsyncExecutor() {
49 | ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
50 | executor.setCorePoolSize(2);
51 | executor.setMaxPoolSize(5);
52 | executor.setQueueCapacity(500);
53 | executor.setThreadNamePrefix("AsyncExecutor-");
54 | executor.initialize();
55 | return executor;
56 | }
57 |
58 | @Override
59 | public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
60 | return (throwable, method, parameters) -> {
61 | final StringBuilder buffer = new StringBuilder();
62 | buffer.append("Thread, " + Thread.currentThread().getName()).append(System.lineSeparator());
63 | buffer.append("Method, " + method).append(System.lineSeparator());
64 | buffer.append("Parameters, " + Arrays.asList(parameters)).append(System.lineSeparator());
65 | buffer.append(ExceptionUtils.getStackTrace(throwable)).append(System.lineSeparator());
66 |
67 | logger.warning(buffer.toString());
68 | };
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/us/fatehi/schemacrawler/webapp/MultipartFileConverter.java:
--------------------------------------------------------------------------------
1 | /*
2 | ========================================================================
3 | SchemaCrawler
4 | http://www.schemacrawler.com
5 | Copyright (c) 2000-2025, Sualeh Fatehi .
6 | All rights reserved.
7 | ------------------------------------------------------------------------
8 |
9 | SchemaCrawler is distributed in the hope that it will be useful, but
10 | WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 |
13 | SchemaCrawler and the accompanying materials are made available under
14 | the terms of the Eclipse Public License v1.0, GNU General Public License
15 | v3 or GNU Lesser General Public License v3.
16 |
17 | You may elect to redistribute this code under any of these licenses.
18 |
19 | The Eclipse Public License is available at:
20 | http://www.eclipse.org/legal/epl-v10.html
21 |
22 | The GNU General Public License v3 and the GNU Lesser General Public
23 | License v3 are available at:
24 | http://www.gnu.org/licenses/
25 |
26 | ========================================================================
27 | */
28 | package us.fatehi.schemacrawler.webapp;
29 |
30 | import org.springframework.core.convert.converter.Converter;
31 | import org.springframework.web.multipart.MultipartFile;
32 |
33 | /**
34 | * Converts a multi-part file to a string, which is the name of the uploaded file. This is needed
35 | * for input form validation, and also for saving details of the request.
36 | */
37 | public class MultipartFileConverter implements Converter {
38 |
39 | /** Converts a multi-part file to a string, which is the name of the uploaded file. */
40 | @Override
41 | public String convert(final MultipartFile file) {
42 | if (file == null) {
43 | return null;
44 | } else {
45 | return file.getOriginalFilename();
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/us/fatehi/schemacrawler/webapp/SchemaCrawlerWebApplication.java:
--------------------------------------------------------------------------------
1 | /*
2 | ========================================================================
3 | SchemaCrawler
4 | http://www.schemacrawler.com
5 | Copyright (c) 2000-2025, Sualeh Fatehi .
6 | All rights reserved.
7 | ------------------------------------------------------------------------
8 |
9 | SchemaCrawler is distributed in the hope that it will be useful, but
10 | WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 |
13 | SchemaCrawler and the accompanying materials are made available under
14 | the terms of the Eclipse Public License v1.0, GNU General Public License
15 | v3 or GNU Lesser General Public License v3.
16 |
17 | You may elect to redistribute this code under any of these licenses.
18 |
19 | The Eclipse Public License is available at:
20 | http://www.eclipse.org/legal/epl-v10.html
21 |
22 | The GNU General Public License v3 and the GNU Lesser General Public
23 | License v3 are available at:
24 | http://www.gnu.org/licenses/
25 |
26 | ========================================================================
27 | */
28 | package us.fatehi.schemacrawler.webapp;
29 |
30 | import org.springframework.boot.CommandLineRunner;
31 | import org.springframework.boot.SpringApplication;
32 | import org.springframework.boot.autoconfigure.SpringBootApplication;
33 | import org.springframework.format.FormatterRegistry;
34 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
35 |
36 | @SpringBootApplication
37 | public class SchemaCrawlerWebApplication implements WebMvcConfigurer, CommandLineRunner {
38 |
39 | /**
40 | * Spring Boot entry point.
41 | *
42 | * @param args Spring Boot arguments.
43 | */
44 | public static void main(final String[] args) {
45 | SpringApplication.run(SchemaCrawlerWebApplication.class, args);
46 | }
47 |
48 | @Override
49 | public void addFormatters(final FormatterRegistry registry) {
50 | registry.addConverter(new MultipartFileConverter());
51 | }
52 |
53 | @Override
54 | public void run(final String... args) throws Exception {
55 | // Do something, if needed
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/us/fatehi/schemacrawler/webapp/controller/DiagramRequestController.java:
--------------------------------------------------------------------------------
1 | /*
2 | ========================================================================
3 | SchemaCrawler
4 | http://www.schemacrawler.com
5 | Copyright (c) 2000-2025, Sualeh Fatehi .
6 | All rights reserved.
7 | ------------------------------------------------------------------------
8 |
9 | SchemaCrawler is distributed in the hope that it will be useful, but
10 | WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 |
13 | SchemaCrawler and the accompanying materials are made available under
14 | the terms of the Eclipse Public License v1.0, GNU General Public License
15 | v3 or GNU Lesser General Public License v3.
16 |
17 | You may elect to redistribute this code under any of these licenses.
18 |
19 | The Eclipse Public License is available at:
20 | http://www.eclipse.org/legal/epl-v10.html
21 |
22 | The GNU General Public License v3 and the GNU Lesser General Public
23 | License v3 are available at:
24 | http://www.gnu.org/licenses/
25 |
26 | ========================================================================
27 | */
28 |
29 | package us.fatehi.schemacrawler.webapp.controller;
30 |
31 | import static java.nio.charset.StandardCharsets.UTF_8;
32 | import static java.nio.file.StandardOpenOption.READ;
33 | import static org.apache.commons.io.IOUtils.toInputStream;
34 | import static us.fatehi.schemacrawler.webapp.controller.URIConstants.API_PREFIX;
35 | import static us.fatehi.schemacrawler.webapp.controller.URIConstants.UI_PREFIX;
36 | import static us.fatehi.schemacrawler.webapp.service.storage.FileExtensionType.DATA;
37 | import static us.fatehi.schemacrawler.webapp.service.storage.FileExtensionType.JSON;
38 | import static us.fatehi.schemacrawler.webapp.service.storage.FileExtensionType.LOG;
39 | import static us.fatehi.schemacrawler.webapp.service.storage.FileExtensionType.SQLITE_DB;
40 | import java.io.IOException;
41 | import java.io.InputStream;
42 | import java.io.PrintWriter;
43 | import java.io.StringWriter;
44 | import java.net.URI;
45 | import java.nio.file.Files;
46 | import java.nio.file.Path;
47 | import java.util.Collections;
48 | import java.util.List;
49 | import java.util.Optional;
50 | import java.util.stream.Collectors;
51 | import org.apache.tika.Tika;
52 | import org.apache.tika.mime.MimeType;
53 | import org.apache.tika.mime.MimeTypeException;
54 | import org.apache.tika.mime.MimeTypes;
55 | import org.slf4j.Logger;
56 | import org.slf4j.LoggerFactory;
57 | import org.springframework.http.MediaType;
58 | import org.springframework.http.ResponseEntity;
59 | import org.springframework.stereotype.Controller;
60 | import org.springframework.ui.Model;
61 | import org.springframework.util.DigestUtils;
62 | import org.springframework.validation.BindingResult;
63 | import org.springframework.web.bind.annotation.GetMapping;
64 | import org.springframework.web.bind.annotation.ModelAttribute;
65 | import org.springframework.web.bind.annotation.PostMapping;
66 | import org.springframework.web.bind.annotation.RequestParam;
67 | import org.springframework.web.bind.annotation.ResponseBody;
68 | import org.springframework.web.multipart.MultipartFile;
69 | import static us.fatehi.utility.Utility.isBlank;
70 | import jakarta.validation.Valid;
71 | import jakarta.validation.constraints.NotNull;
72 | import schemacrawler.schemacrawler.exceptions.ExecutionRuntimeException;
73 | import us.fatehi.schemacrawler.webapp.model.DiagramKey;
74 | import us.fatehi.schemacrawler.webapp.model.DiagramRequest;
75 | import us.fatehi.schemacrawler.webapp.service.notification.NotificationService;
76 | import us.fatehi.schemacrawler.webapp.service.processing.ProcessingService;
77 | import us.fatehi.schemacrawler.webapp.service.storage.StorageService;
78 |
79 | @Controller
80 | public class DiagramRequestController {
81 |
82 | private static final Logger LOGGER = LoggerFactory.getLogger(DiagramRequestController.class);
83 |
84 | private final StorageService storageService;
85 | private final ProcessingService processingService;
86 | private final NotificationService notificationService;
87 |
88 | public DiagramRequestController(
89 | @NotNull(message = "Storage service not provided") final StorageService storageService,
90 | @NotNull(message = "Processing service not provided")
91 | final ProcessingService processingService,
92 | @NotNull(message = "Notification service not provided")
93 | final NotificationService notificationService) {
94 | this.storageService = storageService;
95 | this.processingService = processingService;
96 | this.notificationService = notificationService;
97 | }
98 |
99 | @GetMapping(UI_PREFIX)
100 | public String diagramRequestForm(@NotNull(message = "Model not provided") final Model model) {
101 | model.addAttribute("diagramRequest", new DiagramRequest());
102 | return "SchemaCrawlerDiagramForm";
103 | }
104 |
105 | // http://stackoverflow.com/questions/30297719/cannot-get-validation-working-with-spring-boot-and-thymeleaf
106 | @PostMapping(value = UI_PREFIX)
107 | public String diagramRequestFormSubmit(
108 | @ModelAttribute("diagramRequest") @NotNull(message = "Diagram request not provided") @Valid
109 | final DiagramRequest diagramRequest,
110 | final BindingResult bindingResult,
111 | @RequestParam("file") final MultipartFile file)
112 | throws Exception {
113 |
114 | if (bindingResult.hasErrors()) {
115 | // Save validation errors
116 | saveBindingResultLogFile(diagramRequest.getKey(), bindingResult);
117 | saveDiagramRequest(diagramRequest);
118 | return "SchemaCrawlerDiagramForm";
119 | }
120 |
121 | generateSchemaCrawlerDiagram(diagramRequest, file);
122 | notificationService.notify(diagramRequest);
123 |
124 | return "SchemaCrawlerDiagramResult";
125 | }
126 |
127 | @PostMapping(value = API_PREFIX, produces = MediaType.APPLICATION_JSON_VALUE)
128 | @ResponseBody
129 | public ResponseEntity diagramRequestFormSubmitApi(
130 | @ModelAttribute("diagramRequest") @NotNull(message = "Diagram request not provided") @Valid
131 | final DiagramRequest diagramRequest,
132 | final BindingResult bindingResult,
133 | @RequestParam("file") final Optional file) {
134 |
135 | // Check for bad requests
136 | if (!file.isPresent()) {
137 | diagramRequest.setError("No SQLite file upload provided");
138 | // Save validation errors
139 | saveDiagramRequest(diagramRequest);
140 | } else if (bindingResult.hasErrors()) {
141 | final List errors =
142 | bindingResult.getFieldErrors().stream()
143 | .map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage())
144 | .collect(Collectors.toList());
145 | Collections.sort(errors);
146 | diagramRequest.setError(String.join("; ", errors));
147 | // Save validation errors
148 | saveBindingResultLogFile(diagramRequest.getKey(), bindingResult);
149 | saveDiagramRequest(diagramRequest);
150 | }
151 |
152 | if (diagramRequest.hasLogMessage()) {
153 | return ResponseEntity.badRequest().body(diagramRequest);
154 | }
155 |
156 | // Generate diagram
157 | URI location = null;
158 | try {
159 | generateSchemaCrawlerDiagram(diagramRequest, file.get());
160 | location = new URI("./" + diagramRequest.getKey());
161 | } catch (final Exception e) {
162 | LOGGER.error(String.format("%s%n%s", e.getMessage(), diagramRequest));
163 | LOGGER.trace(e.getMessage(), e);
164 | diagramRequest.setError(e.getMessage());
165 | }
166 |
167 | if (diagramRequest.hasLogMessage()) {
168 | return ResponseEntity.internalServerError().body(diagramRequest);
169 | }
170 | return ResponseEntity.created(location).body(diagramRequest);
171 | }
172 |
173 | @GetMapping(value = "/")
174 | public String index() {
175 | return "redirect:/schemacrawler";
176 | }
177 |
178 | private void checkMimeType(final DiagramRequest diagramRequest, final Path localPath)
179 | throws ExecutionRuntimeException {
180 | try {
181 | final String detectedMimeType = new Tika().detect(localPath);
182 | if (!"application/x-sqlite3".equals(detectedMimeType)) {
183 | final MimeType mimeType = MimeTypes.getDefaultMimeTypes().forName(detectedMimeType);
184 |
185 | final StringBuffer exceptionMessage = new StringBuffer();
186 | exceptionMessage.append("Expected a SQLite database file, but got a ");
187 | if (!isBlank(mimeType.getDescription())) {
188 | exceptionMessage.append(mimeType.getDescription()).append(" file");
189 | } else if (isBlank(mimeType.getDescription()) && !isBlank(detectedMimeType)) {
190 | exceptionMessage.append("file of type ").append(detectedMimeType);
191 | } else {
192 | exceptionMessage.append("file of an unknown type");
193 | }
194 | throw new ExecutionRuntimeException(exceptionMessage.toString());
195 | }
196 | } catch (final MimeTypeException | IOException | NullPointerException e) {
197 | LOGGER.error(String.format("%s%n%s", e.getMessage(), diagramRequest));
198 | LOGGER.trace(e.getMessage(), e);
199 | }
200 | }
201 |
202 | private void generateSchemaCrawlerDiagram(
203 | final DiagramRequest diagramRequest, final MultipartFile file) throws Exception {
204 |
205 | final DiagramKey key = diagramRequest.getKey();
206 | try {
207 |
208 | // Store the uploaded database file locally, so it can be processed
209 | final Path localPath = storageService.storeLocal(file, key, SQLITE_DB);
210 | try (InputStream is = Files.newInputStream(localPath, READ)) {
211 | final String md5DigestHex = DigestUtils.md5DigestAsHex(is);
212 | diagramRequest.setFileHash(md5DigestHex);
213 | }
214 |
215 | checkMimeType(diagramRequest, localPath);
216 |
217 | // Make asynchronous call to generate diagram
218 | processingService.generateSchemaCrawlerDiagram(diagramRequest, localPath);
219 | } catch (final Exception e) {
220 | LOGGER.error(String.format("%s%n%s", e.getMessage(), diagramRequest));
221 | LOGGER.warn(e.getMessage(), e);
222 | saveExceptionLogFile(key, e);
223 | diagramRequest.setError(e.getMessage());
224 | // Save a copy of the uploaded file, which may not be a SQLite database
225 | storageService.store(file, key, DATA);
226 | throw e;
227 | } finally {
228 | // Diagram request may contain an error message due to exceptions thrown
229 | saveDiagramRequest(diagramRequest);
230 | }
231 | }
232 |
233 | private void saveBindingResultLogFile(final DiagramKey key, final BindingResult bindingResult) {
234 | try {
235 | // Write out stack trace to a log file, and save it
236 | final StringWriter stackTraceWriter = new StringWriter();
237 | stackTraceWriter.write(bindingResult.toString());
238 | final String stackTrace = stackTraceWriter.toString();
239 | storageService.store(() -> toInputStream(stackTrace, UTF_8), key, LOG);
240 | } catch (final Exception e) {
241 | LOGGER.error(String.format("<%s>: %s", key, e.getMessage()));
242 | LOGGER.warn(e.getMessage(), e);
243 | }
244 | }
245 |
246 | private void saveDiagramRequest(final DiagramRequest diagramRequest) {
247 | if (diagramRequest == null) {
248 | return;
249 | }
250 | final DiagramKey key = diagramRequest.getKey();
251 | try {
252 | storageService.store(() -> toInputStream(diagramRequest.toJson(), UTF_8), key, JSON);
253 | } catch (final Exception e) {
254 | LOGGER.error(
255 | String.format("Could not save diagram request%n%s%n%s", e.getMessage(), diagramRequest));
256 | }
257 | }
258 |
259 | private void saveExceptionLogFile(final DiagramKey key, final Exception exception) {
260 | try {
261 | // Write out stack trace to a log file, and save it
262 | final StringWriter stackTraceWriter = new StringWriter();
263 | exception.printStackTrace(new PrintWriter(stackTraceWriter));
264 | final String stackTrace = stackTraceWriter.toString();
265 | storageService.store(() -> toInputStream(stackTrace, UTF_8), key, LOG);
266 | } catch (final Exception e) {
267 | LOGGER.error(String.format("<%s>: %s", key, e.getMessage()));
268 | LOGGER.warn(e.getMessage(), e);
269 | }
270 | }
271 | }
272 |
--------------------------------------------------------------------------------
/src/main/java/us/fatehi/schemacrawler/webapp/controller/DiagramResultController.java:
--------------------------------------------------------------------------------
1 | /*
2 | ========================================================================
3 | SchemaCrawler
4 | http://www.schemacrawler.com
5 | Copyright (c) 2000-2025, Sualeh Fatehi .
6 | All rights reserved.
7 | ------------------------------------------------------------------------
8 |
9 | SchemaCrawler is distributed in the hope that it will be useful, but
10 | WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 |
13 | SchemaCrawler and the accompanying materials are made available under
14 | the terms of the Eclipse Public License v1.0, GNU General Public License
15 | v3 or GNU Lesser General Public License v3.
16 |
17 | You may elect to redistribute this code under any of these licenses.
18 |
19 | The Eclipse Public License is available at:
20 | http://www.eclipse.org/legal/epl-v10.html
21 |
22 | The GNU General Public License v3 and the GNU Lesser General Public
23 | License v3 are available at:
24 | http://www.gnu.org/licenses/
25 |
26 | ========================================================================
27 | */
28 |
29 | package us.fatehi.schemacrawler.webapp.controller;
30 |
31 | import static java.nio.charset.StandardCharsets.UTF_8;
32 | import static java.nio.file.Files.newBufferedReader;
33 | import static us.fatehi.schemacrawler.webapp.controller.URIConstants.API_PREFIX;
34 | import static us.fatehi.schemacrawler.webapp.controller.URIConstants.UI_RESULTS_PREFIX;
35 | import static us.fatehi.schemacrawler.webapp.service.storage.FileExtensionType.JSON;
36 | import static us.fatehi.schemacrawler.webapp.service.storage.FileExtensionType.PNG;
37 | import java.nio.file.Path;
38 | import org.slf4j.Logger;
39 | import org.slf4j.LoggerFactory;
40 | import org.springframework.core.io.PathResource;
41 | import org.springframework.core.io.Resource;
42 | import org.springframework.http.MediaType;
43 | import org.springframework.http.ResponseEntity;
44 | import org.springframework.stereotype.Controller;
45 | import org.springframework.ui.Model;
46 | import org.springframework.web.bind.annotation.GetMapping;
47 | import org.springframework.web.bind.annotation.PathVariable;
48 | import org.springframework.web.bind.annotation.ResponseBody;
49 | import jakarta.validation.constraints.NotNull;
50 | import schemacrawler.schemacrawler.exceptions.ExecutionRuntimeException;
51 | import us.fatehi.schemacrawler.webapp.model.DiagramKey;
52 | import us.fatehi.schemacrawler.webapp.model.DiagramRequest;
53 | import us.fatehi.schemacrawler.webapp.service.processing.ProcessingService;
54 | import us.fatehi.schemacrawler.webapp.service.storage.StorageService;
55 |
56 | @Controller
57 | public class DiagramResultController {
58 |
59 | private static final Logger LOGGER = LoggerFactory.getLogger(DiagramResultController.class);
60 |
61 | private final StorageService storageService;
62 |
63 | public DiagramResultController(
64 | @NotNull(message = "StorageService not provided") final StorageService storageService,
65 | @NotNull(message = "ProcessingService not provided")
66 | final ProcessingService processingService) {
67 | this.storageService = storageService;
68 | }
69 |
70 | @GetMapping(
71 | value = {API_PREFIX + "/{key}/diagram", UI_RESULTS_PREFIX + "/{key}/diagram"},
72 | produces = MediaType.IMAGE_PNG_VALUE)
73 | @ResponseBody
74 | public Resource diagramImage(
75 | @PathVariable @NotNull(message = "Key not provided") final DiagramKey key) throws Exception {
76 | return retrieveDiagramLocal(key);
77 | }
78 |
79 | /**
80 | * Retrieve results as HTML using a rendered Thymeleaf template.
81 | *
82 | * @param key Diagram key for the results.
83 | * @return Diagram request data, including the key
84 | * @throws Exception On an exception
85 | */
86 | @GetMapping(value = UI_RESULTS_PREFIX + "/{key}")
87 | public String retrieveResults(
88 | final Model model, @PathVariable @NotNull(message = "Key not provided") final DiagramKey key)
89 | throws Exception {
90 |
91 | final DiagramRequest diagramRequest = retrieveResults(key);
92 | model.addAttribute("diagramRequest", diagramRequest);
93 |
94 | if (diagramRequest.hasLogMessage()) {
95 | throw new ExecutionRuntimeException(diagramRequest.toString());
96 | }
97 |
98 | return "SchemaCrawlerDiagram";
99 | }
100 |
101 | /**
102 | * Retrieve results as a JSON object.
103 | *
104 | * @param key Diagram key for the results.
105 | * @return Diagram request data, including the key
106 | * @throws Exception On an exception
107 | */
108 | @GetMapping(value = API_PREFIX + "/{key}", produces = MediaType.APPLICATION_JSON_VALUE)
109 | @ResponseBody
110 | public ResponseEntity retrieveResultsApi(
111 | @PathVariable @NotNull(message = "Key not provided") final DiagramKey key) throws Exception {
112 |
113 | final DiagramRequest diagramRequest;
114 | try {
115 | diagramRequest = retrieveResults(key);
116 | } catch (final Exception e) {
117 | LOGGER.error(String.format("<%s>: %s", key, e.getMessage()));
118 | LOGGER.trace(e.getMessage(), e);
119 | return ResponseEntity.notFound().build();
120 | }
121 |
122 | return ResponseEntity.ok(diagramRequest);
123 | }
124 |
125 | private Resource retrieveDiagramLocal(final DiagramKey key) throws Exception {
126 | return storageService
127 | .retrieveLocal(key, PNG)
128 | .map(PathResource::new)
129 | .orElseThrow(
130 | () -> new ExecutionRuntimeException(String.format("Cannot find key <%s>", key)));
131 | }
132 |
133 | private DiagramRequest retrieveResults(final DiagramKey key) throws Exception {
134 | final Path jsonFile =
135 | storageService
136 | .retrieveLocal(key, JSON)
137 | .orElseThrow(
138 | () ->
139 | new ExecutionRuntimeException(
140 | String.format("Cannot find request for <%s>", key)));
141 | final DiagramRequest diagramRequest =
142 | DiagramRequest.fromJson(newBufferedReader(jsonFile, UTF_8));
143 | return diagramRequest;
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/main/java/us/fatehi/schemacrawler/webapp/controller/ErrorController.java:
--------------------------------------------------------------------------------
1 | /*
2 | ========================================================================
3 | SchemaCrawler
4 | http://www.schemacrawler.com
5 | Copyright (c) 2000-2025, Sualeh Fatehi .
6 | All rights reserved.
7 | ------------------------------------------------------------------------
8 |
9 | SchemaCrawler is distributed in the hope that it will be useful, but
10 | WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 |
13 | SchemaCrawler and the accompanying materials are made available under
14 | the terms of the Eclipse Public License v1.0, GNU General Public License
15 | v3 or GNU Lesser General Public License v3.
16 |
17 | You may elect to redistribute this code under any of these licenses.
18 |
19 | The Eclipse Public License is available at:
20 | http://www.eclipse.org/legal/epl-v10.html
21 |
22 | The GNU General Public License v3 and the GNU Lesser General Public
23 | License v3 are available at:
24 | http://www.gnu.org/licenses/
25 |
26 | ========================================================================
27 | */
28 | package us.fatehi.schemacrawler.webapp.controller;
29 |
30 | import org.apache.commons.lang3.exception.ExceptionUtils;
31 | import org.slf4j.Logger;
32 | import org.slf4j.LoggerFactory;
33 | import org.springframework.http.ResponseEntity;
34 | import org.springframework.stereotype.Controller;
35 | import org.springframework.web.bind.annotation.ControllerAdvice;
36 | import org.springframework.web.bind.annotation.ExceptionHandler;
37 | import org.springframework.web.context.request.WebRequest;
38 | import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
39 | import org.springframework.web.servlet.mvc.support.RedirectAttributes;
40 |
41 | @Controller
42 | @ControllerAdvice
43 | public class ErrorController {
44 |
45 | private static final Logger LOGGER = LoggerFactory.getLogger(ErrorController.class);
46 |
47 | @ExceptionHandler(Throwable.class)
48 | public String handleException(
49 | final Throwable throwable, final RedirectAttributes redirectAttributes) {
50 | LOGGER.error(throwable.getMessage(), throwable);
51 |
52 | final String errorMessage = ExceptionUtils.getRootCauseMessage(throwable);
53 | redirectAttributes.addFlashAttribute("errorMessage", errorMessage);
54 |
55 | return "redirect:/error";
56 | }
57 |
58 | @ExceptionHandler({MethodArgumentTypeMismatchException.class})
59 | public ResponseEntity