├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── plugin-report-issue.md ├── README.md └── workflows │ ├── deploy.yml │ └── pr-previews.yml ├── .gitignore ├── 404.md ├── CNAME ├── Dockerfile ├── Gemfile ├── _config.yml ├── _data ├── contributors.json └── update-contributors.js ├── _includes ├── forkMe.html ├── googleAnalytics.html ├── inlineCss.css ├── landing.css ├── landing │ ├── section-1-title-and-get-started.html │ ├── section-2-small-used-by.html │ ├── section-3-why-javalin.html │ ├── section-4-server-and-api.html │ ├── section-5-community.html │ ├── section-6-whos-using-javalin.html │ ├── section-7-survey.html │ └── section-sponsors.html ├── macros │ ├── docsSnippet.html │ ├── docsSnippetKotlinFirst.html │ ├── gettingStarted.md │ ├── importingDocsSnippet.html │ ├── mavenDep.md │ ├── mavenLink.html │ ├── newsSummary.md │ ├── sponsor-button.html │ ├── tutorialPost.html │ └── vueDocsSnippet.html ├── notificationBanner.html ├── plugins │ ├── pluginCard.css │ ├── pluginCard.html │ └── pluginCard.js ├── socialButtons.html ├── sponsorOrStar.html ├── starBegging.html └── tutorialNav.html ├── _layouts ├── blogpost.html ├── default.html ├── news.html └── tutorial.html ├── _posts ├── blog │ ├── 2019-11-03-moving-from-travis-to-github-actions.md │ ├── 2021-07-17-static-methods-within-lambdas.md │ ├── 2021-08-02-number-of-imports-and-library-usability.md │ └── survey │ │ ├── 2018-09-07-javalin-user-survey-2018.md │ │ ├── 2020-03-20-javalin-user-survey-2020.md │ │ └── 2021-08-31-javalin-user-survey-2021.md ├── news │ ├── pre-3.0 │ │ ├── 2017-05-24-javalin-0.0.1-released.md │ │ ├── 2017-05-26-javalin-0.0.2-released.md │ │ ├── 2017-05-28-javalin-0.1.1-released.md │ │ ├── 2017-06-05-javalin-0.2.0-released.md │ │ ├── 2017-06-11-javalin-0.3.0-released.md │ │ ├── 2017-06-18-javalin-0.3.1-released.md │ │ ├── 2017-06-24-javalin-0.3.2-released.md │ │ ├── 2017-07-03-javalin-0.3.3-released.md │ │ ├── 2017-07-07-javalin-0.3.4-released.md │ │ ├── 2017-07-20-javalin-0.3.5-released.md │ │ ├── 2017-07-28-javalin-0.3.6-released.md │ │ ├── 2017-08-06-javalin-0.3.7-released.md │ │ ├── 2017-08-12-javalin-0.4.0-released.md │ │ ├── 2017-08-19-javalin-0.4.1-released.md │ │ ├── 2017-08-30-javalin-0.4.2-released.md │ │ ├── 2017-09-09-javalin-0.5.0-released.md │ │ ├── 2017-09-22-javalin-0.5.1-released.md │ │ ├── 2017-10-05-javalin-0.5.2-released.md │ │ ├── 2017-10-16-javalin-0.5.3-released.md │ │ ├── 2017-10-22-javalin-0.5.4-released.md │ │ ├── 2017-11-06-javalin-1.0.0-released.md │ │ ├── 2017-11-11-javalin-1.0.1-released.md │ │ ├── 2017-11-18-javalin-1.1.0-released.md │ │ ├── 2017-11-26-javalin-1.1.1-released.md │ │ ├── 2017-12-02-javalin-1.2.0-released.md │ │ ├── 2018-01-03-javalin-1.2.1-released.md │ │ ├── 2018-02-04-javalin-1.3.0-released.md │ │ ├── 2018-03-03-javalin-1.4.0-released.md │ │ ├── 2018-03-04-javalin-1.4.1-released.md │ │ ├── 2018-03-23-javalin-1.5.0-released.md │ │ ├── 2018-04-14-javalin-1.6.0-released.md │ │ ├── 2018-05-01-javalin-1.6.1-released.md │ │ ├── 2018-05-21-javalin-1.7.0-released.md │ │ ├── 2018-08-19-javalin-2.0.0-released.md │ │ ├── 2018-08-27-javalin-2.1.0-released.md │ │ ├── 2018-09-02-javalin-2.1.1-released.md │ │ ├── 2018-09-16-javalin-2.2.0-released.md │ │ ├── 2018-10-06-javalin-2.3.0-released.md │ │ ├── 2018-11-04-javalin-2.4.0-released.md │ │ ├── 2019-01-04-javalin-2.5.0-released.md │ │ ├── 2019-01-17-javalin-2.6.0-released.md │ │ ├── 2019-03-02-javalin-2.7.0-released.md │ │ └── 2019-03-26-javalin-2.8.0-released.md │ ├── pre-4.0 │ │ ├── 2019-06-11-javalin-3.0.0-released.md │ │ ├── 2019-06-23-javalin-3.1.0-released.md │ │ ├── 2019-07-21-javalin-3.2.0-released.md │ │ ├── 2019-07-28-javalin-3.3.0-released.md │ │ ├── 2019-08-11-javalin-3.4.1-released.md │ │ ├── 2019-09-15-javalin-3.5.0-released.md │ │ ├── 2019-11-03-javalin-3.6.0-released.md │ │ ├── 2020-01-01-javalin-3.7.0-released.md │ │ ├── 2020-03-27-javalin-3.8.0-released.md │ │ ├── 2020-06-07-javalin-3.9.0-released.md │ │ ├── 2020-06-21-javalin-3.9.1-released.md │ │ ├── 2020-08-27-javalin-3.10.0-released.md │ │ ├── 2020-09-02-javalin-3.10.1-released.md │ │ ├── 2020-10-04-javalin-3.11.0-released.md │ │ ├── 2020-11-14-javalin-3.12.0-released.md │ │ └── 2021-01-19-javalin-3.13.0-released.md │ ├── pre-5.0 │ │ ├── 2021-02-17-javalin-4.0.0-alpha.md │ │ ├── 2021-07-28-javalin-4.0.0-rc.md │ │ ├── 2021-09-13-javalin-4.0.0-released.md │ │ ├── 2021-09-25-javalin-4.0.1-released.md │ │ ├── 2021-10-02-javalin-4.1.0-released.md │ │ ├── 2021-10-10-javalin-4.1.1-released.md │ │ ├── 2022-01-02-javalin-4.2.0-released.md │ │ ├── 2022-01-13-javalin-4.3.0-released.md │ │ ├── 2022-03-19-javalin-4.4.0-released.md │ │ ├── 2022-04-09-javalin-4.5.0.RC0-released.md │ │ ├── 2022-04-24-javalin-4.5.0-released.md │ │ ├── 2022-05-10-javalin-4.6.0-released.md │ │ ├── 2022-06-05-javalin-4.6.1-released.md │ │ └── 2022-06-16-javalin-4.6.X-released.md │ ├── pre-6.0 │ │ ├── 2022-10-01-javalin-5.0.0-released.md │ │ ├── 2022-10-16-javalin-5.1.0-released.md │ │ ├── 2022-11-20-javalin-5.2.0-released.md │ │ ├── 2023-01-01-javalin-5.3.0-released.md │ │ ├── 2023-03-04-javalin-5.4.0-released.md │ │ ├── 2023-05-01-javalin-5.5.0-released.md │ │ └── 2023-06-10-javalin-5.6.0-released.md │ └── pre-7.0 │ │ └── 2024-01-10-javalin-6.0.0-released.md └── tutorials │ ├── 2017-05-24-javalin-gradle-example.md │ ├── 2017-05-24-javalin-maven-example.md │ ├── 2017-05-25-javalin-heroku-example.md │ ├── 2017-05-25-javalin-kotlin-example.md │ ├── 2017-05-27-javalin-vuejs-example.md │ ├── 2017-07-28-javalin-html-forms-example.md │ ├── 2017-08-06-javalin-email-example.md │ ├── 2017-08-24-javalin-auth-example.md │ ├── 2017-09-22-javalin-websocket-example.md │ ├── 2017-12-02-javalin-prometheus-example.md │ ├── 2018-04-22-javalin-realtime-collaboration-example.md │ ├── 2018-09-02-jetty-session-handling-java.md │ ├── 2019-07-31-simple-frontends-with-javalin-and-vue.md │ ├── 2020-01-03-javalin-openapi-example.md │ ├── 2020-01-18-javalin-testing-example.md │ ├── 2022-10-30-javalin-docker-example.md │ ├── 2023-11-13-javalin-bazel-example.md │ ├── 2024-01-13-omeglin │ ├── 2023-12-30-building-omegle-in-javalin.md │ └── snippets │ │ └── main │ │ ├── java │ │ └── io │ │ │ └── javalin │ │ │ └── omeglin │ │ │ ├── Matchmaking.java │ │ │ └── OmeglinMain.java │ │ ├── kotlin │ │ └── io │ │ │ └── javalin │ │ │ └── omeglin │ │ │ ├── Matchmaker.kt │ │ │ └── OmeglinMain.kt │ │ └── resources │ │ └── public │ │ ├── index.html │ │ ├── js │ │ ├── app.js │ │ ├── chat.js │ │ └── peer-connection.js │ │ └── styles.css │ └── community │ ├── 2017-05-26-javalin-website-example.md │ ├── 2017-12-11-javalin-influxdb-example.md │ ├── 2018-04-29-javalin-java-10-guice.md │ ├── 2018-09-11-javalin-jwt-example.md │ ├── 2018-09-27-javalin-graalvm-example.md │ ├── 2018-11-15-javalin-embedded-example.md │ ├── 2019-10-20-javalin-mockito-example.md │ ├── 2020-08-23-javalin-jte-example.md │ ├── 2020-09-05-javalin-raspberry-pi-example.md │ ├── 2021-04-16-javalin-sureness-example.md │ ├── 2021-05-12-javalin-tracing-example.md │ ├── 2021-05-20-frontends-with-javalinmithril.md │ ├── 2021-07-11-using-javalin-as-http-simulator.md │ ├── 2022-03-25-serving-protobuf-with-javalin.md │ ├── 2022-04-06-javalin-and-minecraft-servers.md │ ├── 2022-08-03-javalin-with-jpms-and-gradle.md │ ├── 2022-11-17-javalin-ssl-tutorial.md │ ├── 2023-01-14-mtls-with-javalin-ssl.md │ ├── 2023-07-30-javalin-logging.md │ └── 2024-10-20-javalin-hibernate.md ├── _sass ├── _frame.scss ├── _main.scss ├── _normalize.scss ├── _post.scss ├── _prism.scss ├── _syntaxHighlighting.scss └── _tutorial.scss ├── css └── main.scss ├── gfx-src ├── javalin-logo.sketch └── summary-infographic.sketch ├── img ├── background.jpg ├── favicon.png ├── github.svg ├── javalin-stats-aug-2023.svg ├── javalin-stats-june-2021.svg ├── javalin-stats-mar-2022.svg ├── javalin-stats-sep-2022.svg ├── javalin.png ├── juke.png ├── logo.svg ├── logo_inv.svg ├── news │ ├── javalin6.png │ └── route-overview.png ├── omeglin.png ├── pages │ ├── for-educators-discoverability.png │ └── pull_req_site_preview.png ├── plugins │ └── routeoverview.png ├── posts │ ├── apmExample │ │ ├── apm-architecture.png │ │ ├── apm-console.png │ │ ├── apm-correlations.png │ │ ├── apm-intellij.png │ │ ├── apm-menu.png │ │ ├── apm-observability.png │ │ ├── apm-overview.png │ │ └── apm-secret-token.png │ ├── javalinSureness │ │ ├── test1.PNG │ │ ├── test2.PNG │ │ ├── test3.PNG │ │ ├── test4.PNG │ │ ├── test5.PNG │ │ ├── test6.PNG │ │ ├── test7.PNG │ │ └── test8.PNG │ ├── javalinvue │ │ └── performance.png │ ├── jteExample │ │ └── jte-intellij.gif │ ├── mtlsTutorial │ │ ├── insomnia-conf.png │ │ ├── insomnia.png │ │ └── mtls.png │ ├── openapi │ │ ├── get-users-endpoint.png │ │ ├── no-annotations.png │ │ └── one-annotation.png │ ├── prometheusExample │ │ ├── grafana.png │ │ └── prometheus.png │ ├── protobufExample │ │ ├── directory-tree.png │ │ ├── json-response.png │ │ └── pbuf-response.png │ ├── sslTutorial │ │ ├── reverse-proxy-diagram.png │ │ ├── tls-diagram.png │ │ └── your-connection-is-not-private.png │ └── websiteExample │ │ ├── packageOverview.fw.png │ │ └── packageOverview.png ├── sponsors │ ├── feature-upvote.png │ └── kabcash.svg └── used-by │ ├── apexar.png │ ├── briar.png │ ├── c6bank.png │ ├── carilion-clinic.png │ ├── celer.png │ ├── datawire.png │ ├── dkb.png │ ├── essential.png │ ├── expressscripts.png │ ├── jhu.png │ ├── measuresforjustice.png │ ├── microsoft.png │ ├── nav.png │ ├── nordstrom.png │ ├── ntnu.png │ ├── pleo.png │ ├── redhat.png │ ├── revolut.png │ ├── swatt.png │ ├── talanlabs.png │ ├── telenor.png │ ├── twosigma.png │ ├── uber.png │ ├── virgilsecurity.png │ ├── wgtwo.png │ └── wit.png ├── js ├── _partials │ ├── code.js │ ├── main.js │ ├── scrolling.js │ ├── tutorials.js │ └── vendor │ │ ├── anchor.min.js │ │ ├── clipboard.min.js │ │ ├── gumshoe.min.js │ │ ├── prism.min.js │ │ └── smoothscroll.min.js └── scripts.js ├── pages ├── about.md ├── community.md ├── comparison-to-spark.md ├── docs │ ├── docs-past-1-7-0.md │ ├── docs-past-2-8-0.md │ ├── docs-past-3-13-X.md │ ├── docs-past-4-6-X.md │ ├── docs-past-5-6-x.md │ ├── docs.md │ ├── migration-guide-1-2.md │ ├── migration-guide-2-3.md │ ├── migration-guide-3-4.md │ ├── migration-guide-4-5.md │ └── migration-guide-5-6.md ├── download.md ├── for-educators.md ├── index.md ├── news.md ├── plugins.md ├── plugins │ ├── cors.md │ ├── devlogging.md │ ├── graphql.md │ ├── how-to.md │ ├── javalinvue.md │ ├── micrometer.md │ ├── openapi.md │ ├── rendering.md │ ├── routeoverview.md │ └── ssl-helpers.md └── tutorials.md └── run_win.bat /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/plugin-report-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Plugin reporting issue 3 | about: Use this template to generate a reporting-issue for a Javalin plugin 4 | title: "[PLUGIN REPORTS] - " 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## !!! The comments on this issue are reflected on https://javalin.io/plugins !!! 11 | 12 | This is intended as safety mechanism to warn other users if a plugin is not behaving as it should. 13 | 14 | Please be elaborate in your reports, and explain why there is a problem with the plugin. 15 | -------------------------------------------------------------------------------- /.github/README.md: -------------------------------------------------------------------------------- 1 | # javalin.io [![Chat at https://discord.gg/sgak4e5NKv](https://img.shields.io/badge/chat-on%20Discord-%234cb697)](https://discord.gg/sgak4e5NKv) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 2 | 3 | This repo contains the source code for [javalin.io](https://javalin.io). 4 | 5 | Pull requests for adding tutorials and fixing errors in docs are very welcome. 6 | 7 | ## Quickstart 8 | 9 | This website is built with Jekyll. To run it locally, you need to have Ruby and Bundler installed. 10 | 11 | ```bash 12 | # Install dependencies 13 | bundle install 14 | # Run the server 15 | bundle exec jekyll serve --port 4000 --future --incremental 16 | ``` 17 | 18 | Alternatively, you can use Docker to run the site without installing Ruby and Bundler. 19 | 20 | ```bash 21 | # Build the Docker image 22 | docker build -t javalin-web . 23 | # Run the Docker container 24 | docker run -p 4000:4000 -v ${PWD}:/app javalin-web 25 | ``` 26 | 27 | The site will be available at `http://localhost:4000` 28 | 29 | 30 | The [contributing guidelines](CONTRIBUTING.md) have additional set-up information. 31 | 32 | ## Contributing 33 | 34 | Contributions are welcome! Please read the [contributing guidelines](CONTRIBUTING.md) before submitting a pull request. 35 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a Jekyll site to GitHub Pages 2 | name: Deploy Jekyll site to Pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: [master] 8 | # Allows you to run this workflow manually from the Actions tab 9 | workflow_dispatch: 10 | 11 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 12 | permissions: 13 | contents: read 14 | pages: write 15 | id-token: write 16 | 17 | # Allow one concurrent deployment 18 | concurrency: 19 | group: "pages" 20 | cancel-in-progress: true 21 | 22 | jobs: 23 | # Build job 24 | build: 25 | if: github.repository == 'javalin/website' 26 | runs-on: ubuntu-24.04 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v3 30 | 31 | - name: Setup Node.js environment 32 | uses: actions/setup-node@v3.5.1 33 | with: 34 | node-version: 18 35 | 36 | - name: Setup Ruby and Gems 37 | uses: ruby/setup-ruby@v1 38 | with: 39 | ruby-version: 3.3.4 40 | bundler-cache: true # runs 'bundle install' and caches installed gems automatically 41 | 42 | - name: Update Bundler 43 | run: gem install bundler 44 | 45 | - name: Install Gems 46 | run: | 47 | rm -rf vendor/bundle 48 | bundle install 49 | 50 | - name: Setup Pages 51 | id: pages 52 | uses: actions/configure-pages@v2 53 | 54 | - name: Update contributor list 55 | run: | 56 | echo "Generating contributor list" 57 | cd _data 58 | node update-contributors.js 59 | cd .. 60 | 61 | - name: Build with Jekyll 62 | # Outputs to the './_site' directory by default 63 | run: bundle exec jekyll build 64 | env: 65 | JEKYLL_ENV: production 66 | 67 | - name: Upload artifact 68 | # Automatically uploads an artifact from the './_site' directory by default 69 | uses: actions/upload-pages-artifact@v3 70 | 71 | # Deployment job 72 | deploy: 73 | if: github.repository == 'javalin/website' 74 | environment: 75 | name: github-pages 76 | url: ${{ steps.deployment.outputs.page_url }} 77 | runs-on: ubuntu-24.04 78 | needs: build 79 | steps: 80 | - name: Deploy to GitHub Pages 81 | id: deployment 82 | uses: actions/deploy-pages@v4 83 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### Jekyll ### 4 | _site/ 5 | .sass-cache/ 6 | .jekyll-metadata 7 | Gemfile.lock 8 | 9 | ### Intellij ### 10 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 11 | 12 | *.iml 13 | 14 | ## Directory-based project format: 15 | .idea/ 16 | # if you remove the above rule, at least ignore the following: 17 | 18 | # User-specific stuff: 19 | # .idea/workspace.xml 20 | # .idea/tasks.xml 21 | # .idea/dictionaries 22 | 23 | # Sensitive or high-churn files: 24 | # .idea/dataSources.ids 25 | # .idea/dataSources.xml 26 | # .idea/sqlDataSources.xml 27 | # .idea/dynamic.xml 28 | # .idea/uiDesigner.xml 29 | 30 | # Gradle: 31 | # .idea/gradle.xml 32 | # .idea/libraries 33 | 34 | # Mongo Explorer plugin: 35 | # .idea/mongoSettings.xml 36 | 37 | ## File-based project format: 38 | *.ipr 39 | *.iws 40 | 41 | ## Plugin-specific files: 42 | 43 | # IntelliJ 44 | /out/ 45 | 46 | # mpeltonen/sbt-idea plugin 47 | .idea_modules/ 48 | 49 | # JIRA plugin 50 | atlassian-ide-plugin.xml 51 | 52 | # Crashlytics plugin (for Android Studio and IntelliJ) 53 | com_crashlytics_export_strings.xml 54 | crashlytics.properties 55 | crashlytics-build.properties 56 | 57 | 58 | ### Windows ### 59 | # Windows image file caches 60 | Thumbs.db 61 | ehthumbs.db 62 | 63 | # Folder config file 64 | Desktop.ini 65 | 66 | # Recycle Bin used on file shares 67 | $RECYCLE.BIN/ 68 | 69 | # Windows Installer files 70 | *.cab 71 | *.msi 72 | *.msm 73 | *.msp 74 | 75 | # Windows shortcuts 76 | *.lnk 77 | 78 | 79 | ### OSX ### 80 | .DS_Store 81 | .AppleDouble 82 | .LSOverride 83 | 84 | # Icon must end with two \r 85 | Icon 86 | 87 | 88 | # Thumbnails 89 | ._* 90 | 91 | # Files that might appear in the root of a volume 92 | .DocumentRevisions-V100 93 | .fseventsd 94 | .Spotlight-V100 95 | .TemporaryItems 96 | .Trashes 97 | .VolumeIcon.icns 98 | 99 | # Directories potentially created on remote AFP share 100 | .AppleDB 101 | .AppleDesktop 102 | Network Trash Folder 103 | Temporary Items 104 | .apdisk 105 | 106 | # Vscode 107 | .vscode 108 | -------------------------------------------------------------------------------- /404.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Page not found 4 | rightmenu: false 5 | --- 6 | 7 |
8 | Decorative illustration 9 |

10 | Well... the bad news is that the page you requested isn't here.
11 | The good news is that you found Javalin's secret mascot, Juke. 12 |

13 |

You can use the top menu to find what you're looking for.

14 | 33 | 34 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | javalin.io -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:2.7.4-alpine 2 | 3 | WORKDIR /app 4 | 5 | COPY Gemfile ./ 6 | 7 | RUN apk add ruby-dev build-base && bundle install 8 | 9 | ENTRYPOINT ["bundle", "exec"] 10 | 11 | CMD ["jekyll", "serve", "--host=0.0.0.0", "--future", "--incremental"] -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gem 'github-pages' 3 | gem 'wdm', '>= 0.1.0' if Gem.win_platform? 4 | gem "webrick", "~> 1.8" 5 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | javalinThreeVersion: 3.13.13 2 | javalinFourVersion: 4.6.7 3 | javalinFiveVersion: 5.6.4 4 | javalinversion: 6.6.0 5 | slf4jversion: 2.0.17 6 | repourl: https://github.com/javalin/website 7 | description: Javalin - A lightweight Java and Kotlin web framework. Create REST APIs in Java or Kotlin easily. 8 | baseurl: "" # the subpath of your site, e.g. /blog 9 | 10 | # Build settings 11 | markdown: kramdown 12 | sass: 13 | sass_dir: _sass 14 | style: compressed 15 | plugins: 16 | - jekyll-sitemap 17 | -------------------------------------------------------------------------------- /_data/contributors.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "username": "tipsy", 4 | "contributions": 2090, 5 | "avatar": "https://avatars.githubusercontent.com/u/1521451?v=4" 6 | }, 7 | { 8 | "username": "dzikoysk", 9 | "contributions": 150, 10 | "avatar": "https://avatars.githubusercontent.com/u/4235722?v=4" 11 | }, 12 | { 13 | "username": "Playacem", 14 | "contributions": 50, 15 | "avatar": "https://avatars.githubusercontent.com/u/3692093?v=4" 16 | }, 17 | { 18 | "username": "TobiasWalle", 19 | "contributions": 32, 20 | "avatar": "https://avatars.githubusercontent.com/u/9933601?v=4" 21 | }, 22 | { 23 | "username": "TareqK", 24 | "contributions": 31, 25 | "avatar": "https://avatars.githubusercontent.com/u/18736962?v=4" 26 | }, 27 | { 28 | "username": "vn7n24fzkq", 29 | "contributions": 27, 30 | "avatar": "https://avatars.githubusercontent.com/u/20241522?v=4" 31 | }, 32 | { 33 | "username": "zugazagoitia", 34 | "contributions": 26, 35 | "avatar": "https://avatars.githubusercontent.com/u/23284823?v=4" 36 | }, 37 | { 38 | "username": "mpe85", 39 | "contributions": 15, 40 | "avatar": "https://avatars.githubusercontent.com/u/23398713?v=4" 41 | }, 42 | { 43 | "username": "oharaandrew314", 44 | "contributions": 14, 45 | "avatar": "https://avatars.githubusercontent.com/u/894152?v=4" 46 | }, 47 | { 48 | "username": "ShikaSD", 49 | "contributions": 13, 50 | "avatar": "https://avatars.githubusercontent.com/u/6198744?v=4" 51 | }, 52 | { 53 | "username": "casid", 54 | "contributions": 12, 55 | "avatar": "https://avatars.githubusercontent.com/u/4077354?v=4" 56 | }, 57 | { 58 | "username": "7agustibm", 59 | "contributions": 12, 60 | "avatar": "https://avatars.githubusercontent.com/u/8149332?v=4" 61 | }, 62 | { 63 | "username": "ClemensElflein", 64 | "contributions": 12, 65 | "avatar": "https://avatars.githubusercontent.com/u/2864655?v=4" 66 | }, 67 | { 68 | "username": "maxemann96", 69 | "contributions": 9, 70 | "avatar": "https://avatars.githubusercontent.com/u/4399206?v=4" 71 | }, 72 | { 73 | "username": "nixxcode", 74 | "contributions": 8, 75 | "avatar": "https://avatars.githubusercontent.com/u/46871088?v=4" 76 | } 77 | ] -------------------------------------------------------------------------------- /_data/update-contributors.js: -------------------------------------------------------------------------------- 1 | // run by doing `node update-contributors.js` in this dir 2 | // requires node18 3 | 4 | (async () => { 5 | const fileSystem = require("fs"); 6 | 7 | let repos = (await (await fetch("https://api.github.com/orgs/javalin/repos")).json()) 8 | .filter(it => it.fork !== true) // we only care about sources/transferred repos 9 | .map(it => it.name) 10 | .filter(it => it !== ".github"); // this one isn't all that interesting, it's just meta info for the org 11 | 12 | let contributions = new Map(); 13 | let avatars = new Map(); 14 | 15 | for (let repo of repos) { 16 | console.log(`Getting contributors for ${repo} ...`); 17 | const response = await fetch(`https://api.github.com/repos/javalin/${repo}/contributors`); 18 | const repoContributors = await response.json(); 19 | console.log(`${repo} has ${repoContributors.length} contributors ...`); 20 | for (let user of repoContributors) { 21 | console.log(`Adding contributions of ${user.login} ...`); 22 | avatars.set(user.login, user.avatar_url); 23 | if (contributions.has(user.login)) { 24 | contributions.set(user.login, contributions.get(user.login) + user.contributions); 25 | } else { 26 | contributions.set(user.login, user.contributions); 27 | } 28 | } 29 | console.log("------------------------------------------------------") 30 | } 31 | 32 | let contributorArray = Array.from(contributions.keys()).map(user => ({ 33 | username: user, 34 | contributions: contributions.get(user), 35 | avatar: avatars.get(user), 36 | })); 37 | 38 | let sortedContributorArray = contributorArray 39 | .sort((a, b) => b.contributions - a.contributions) 40 | .filter(it => it.username !== "dependabot[bot]") 41 | .slice(0, 15); 42 | 43 | fileSystem.writeFileSync("contributors.json", JSON.stringify(sortedContributorArray, null, 2)); 44 | })(); 45 | -------------------------------------------------------------------------------- /_includes/forkMe.html: -------------------------------------------------------------------------------- 1 | 2 | Fork me on GitHub 5 | 6 | -------------------------------------------------------------------------------- /_includes/googleAnalytics.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /_includes/inlineCss.css: -------------------------------------------------------------------------------- 1 | /* 2 | Styles that affect the length of the document, 3 | having these inline prevents reflow due to size changes. 4 | */ 5 | p code, 6 | li code { 7 | font-size: 90%; 8 | letter-spacing: -0.5px; 9 | border: 1px solid #ccd; 10 | background: #f5f5f7; 11 | padding: 1px 3px; 12 | } 13 | 14 | .multitab-code pre { 15 | margin-top: 0; 16 | border-radius: 0 0 5px 5px; 17 | } 18 | 19 | .multitab-code div[data-tab] { 20 | display: none; 21 | } 22 | 23 | :target { 24 | scroll-margin-top: 108px; 25 | } 26 | 27 | .anchorjs-link { 28 | transition: all .25s linear; 29 | } 30 | 31 | *:hover > .anchorjs-link { 32 | margin-left: -1.125em !important; 33 | } 34 | -------------------------------------------------------------------------------- /_includes/landing/section-1-title-and-get-started.html: -------------------------------------------------------------------------------- 1 |
2 | {{include.title}} 3 | {% include macros/gettingStarted.md %} 4 | 10 |
11 | -------------------------------------------------------------------------------- /_includes/landing/section-2-small-used-by.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Microsoft 4 | Redhat 5 | Uber 6 | Telenor 7 | Revolut 8 | C6 Bank 9 | Nordstrom 10 | Deutsche Kreditbank 11 |
12 |
13 | -------------------------------------------------------------------------------- /_includes/landing/section-3-why-javalin.html: -------------------------------------------------------------------------------- 1 | {% comment %} 2 | Liquid only allows passing strings - splitting on "---" gives us an array of reasons 3 | Splitting on -- gives title/paragraph pairs 4 | {% endcomment %} 5 |
6 | {{ include.title }} 7 |
8 | {% assign reasons = include.reasonRow1 | split: "---" %} 9 | {% for reason in reasons %} 10 |
11 |

{{ reason | split: "--" | first }}

12 |

{{ reason | split: "--" | last }}

13 |
14 | {% endfor %} 15 |
16 |
17 | {% assign reasons = include.reasonRow2 | split: "---" %} 18 | {% for reason in reasons %} 19 |
20 |

{{ reason | split: "--" | first }}

21 |

{{ reason | split: "--" | last }}

22 |
23 | {% endfor %} 24 |
25 |
26 | -------------------------------------------------------------------------------- /_includes/landing/section-4-server-and-api.html: -------------------------------------------------------------------------------- 1 |
2 | {{ include.title }} 3 | {% capture java %} 4 | import io.javalin.Javalin; 5 | import static io.javalin.apibuilder.ApiBuilder.*; 6 | 7 | public static void main(String[] args) { 8 | var app = Javalin.create(config -> { 9 | config.useVirtualThreads = true; 10 | config.http.asyncTimeout = 10_000L; 11 | config.staticFiles.add("/public"); 12 | config.staticFiles.enableWebjars(); 13 | config.router.apiBuilder(() -> { 14 | path("/users", () -> { 15 | get(UserController::getAll); 16 | post(UserController::create); 17 | path("/{userId}", () -> { 18 | get(UserController::getOne); 19 | patch(UserController::update); 20 | delete(UserController::delete); 21 | }); 22 | ws("/events", userController::webSocketEvents); 23 | }); 24 | }); 25 | }).start(7070); 26 | } 27 | {% endcapture %} 28 | {% capture kotlin %} 29 | import io.javalin.Javalin 30 | import io.javalin.apibuilder.ApiBuilder.* 31 | 32 | fun main() { 33 | val app = Javalin.create { config -> 34 | config.useVirtualThreads = true 35 | config.http.asyncTimeout = 10_000L 36 | config.staticFiles.add("/public") 37 | config.staticFiles.enableWebjars() 38 | config.router.apiBuilder { 39 | path("/users") { 40 | get(UserController::getAll) 41 | post(UserController::create) 42 | path("/{userId}") { 43 | get(UserController::getOne) 44 | patch(UserController::update) 45 | delete(UserController::delete) 46 | } 47 | ws("/events", userController::webSocketEvents) 48 | } 49 | } 50 | }.start(7070) 51 | } 52 | {% endcapture %} 53 | {% include macros/docsSnippet.html java=java kotlin=kotlin %} 54 | 55 |

{{ include.brag }}

56 | 57 | 61 |
62 | -------------------------------------------------------------------------------- /_includes/landing/section-5-community.html: -------------------------------------------------------------------------------- 1 |
2 | {{ include.title }} 3 | Javalin Infographic 4 |

{{ include.paragraph }}

5 |
6 | -------------------------------------------------------------------------------- /_includes/landing/section-6-whos-using-javalin.html: -------------------------------------------------------------------------------- 1 |
2 | {{ include.title }} 3 |
4 | JHU 5 | Microsoft 6 | Redhat 7 | NTNU 8 | Pleo 9 | Uber 10 | Measures for Justice 11 | WIT 12 | Briar 13 | Two Sigma 14 | nav 15 | Virgil Security 16 | Working Group Two 17 | Talan Labs 18 | Datawire 19 | Swatt 20 | Telenor 21 | Revolut 22 | Express Scripts 23 | Deutsche Kreditbank 24 | C6 Bank 25 | Nordstrom 26 | Apexar 27 | Celer 28 | Essential.gg 29 | Carilion Clinic 30 |
31 |

{{ include.paragraph }}

32 |
33 | -------------------------------------------------------------------------------- /_includes/landing/section-7-survey.html: -------------------------------------------------------------------------------- 1 |
2 | {{ include.title }} 3 |

{{ include.paragraph }}

4 |
5 | -------------------------------------------------------------------------------- /_includes/landing/section-sponsors.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{include.title}} 4 | 8 | 15 |
16 |
17 | 93 | -------------------------------------------------------------------------------- /_includes/macros/docsSnippet.html: -------------------------------------------------------------------------------- 1 |
2 |
    3 |
  • Java
  • 4 |
  • Kotlin
  • 5 |
6 |
7 | ~~~java{{include.java}}~~~ 8 |
9 |
10 | ~~~kotlin{{include.kotlin}}~~~ 11 |
12 |
13 | -------------------------------------------------------------------------------- /_includes/macros/docsSnippetKotlinFirst.html: -------------------------------------------------------------------------------- 1 |
2 |
    3 |
  • Kotlin
  • 4 |
  • Java
  • 5 |
6 |
7 | ~~~kotlin{{include.kotlin}}~~~ 8 |
9 |
10 | ~~~java{{include.java}}~~~ 11 |
12 |
13 | -------------------------------------------------------------------------------- /_includes/macros/gettingStarted.md: -------------------------------------------------------------------------------- 1 | {% capture java %} 2 | import io.javalin.Javalin; 3 | 4 | public class HelloWorld { 5 | public static void main(String[] args) { 6 | var app = Javalin.create(/*config*/) 7 | .get("/", ctx -> ctx.result("Hello World")) 8 | .start(7070); 9 | } 10 | } 11 | {% endcapture %} 12 | 13 | {% capture kotlin %} 14 | import io.javalin.Javalin 15 | 16 | fun main() { 17 | val app = Javalin.create(/*config*/) 18 | .get("/") { ctx -> ctx.result("Hello World") } 19 | .start(7070) 20 | } 21 | {% endcapture %} 22 | {% include macros/docsSnippet.html java=java kotlin=kotlin %} 23 | -------------------------------------------------------------------------------- /_includes/macros/importingDocsSnippet.html: -------------------------------------------------------------------------------- 1 | {% capture java %} 2 | {% include_relative {{include.java}} %} 3 | {% endcapture %} 4 | {% capture kotlin %} 5 | {% include_relative {{include.kotlin}} %} 6 | {% endcapture %} 7 | {% include macros/docsSnippet.html java=java kotlin=kotlin %} 8 | -------------------------------------------------------------------------------- /_includes/macros/mavenDep.md: -------------------------------------------------------------------------------- 1 |
2 |
    3 |
  • Maven
  • 4 |
  • Gradle
  • 5 |
  • SBT
  • 6 |
  • Grape
  • 7 |
  • Leiningen
  • 8 |
  • Buildr
  • 9 |
  • Ivy
  • 10 |
11 | 12 |
13 | ~~~markup 14 | 15 | io.javalin 16 | javalin 17 | {{javalinVersion | default: site.javalinversion}} 18 | 19 | ~~~ 20 | Not familiar with Maven? Read our [Maven tutorial](/tutorials/maven-setup). 21 |
22 | 23 |
24 | ~~~java 25 | implementation("io.javalin:javalin:{{javalinVersion | default: site.javalinversion }}") 26 | ~~~ 27 | Not familiar with Gradle? Read our [Gradle tutorial](/tutorials/gradle-setup). 28 |
29 | 30 |
31 | ~~~java 32 | libraryDependencies += "io.javalin" % "javalin" % "{{javalinVersion | default: site.javalinversion }}" 33 | ~~~ 34 |
35 | 36 |
37 | ~~~java 38 | @Grab(group='io.javalin', module='javalin', version='{{javalinVersion | default: site.javalinversion }}') 39 | ~~~ 40 |
41 | 42 |
43 | ~~~java 44 | [io.javalin/javalin "{{javalinVersion | default: site.javalinversion }}"] 45 | ~~~ 46 |
47 | 48 |
49 | ~~~java 50 | 'io.javalin:javalin:jar:{{javalinVersion | default: site.javalinversion }}' 51 | ~~~ 52 |
53 | 54 |
55 | ~~~markup 56 | 57 | ~~~ 58 |
59 |
60 | 61 |
62 | If you want Javalin with testing tools, Jackson and Logback, 63 | you can use the artifact id `javalin-bundle` instead of `javalin`. 64 |
65 | 71 | -------------------------------------------------------------------------------- /_includes/macros/mavenLink.html: -------------------------------------------------------------------------------- 1 | Javalin {{include.version}} is available for download on 2 | Maven Central. -------------------------------------------------------------------------------- /_includes/macros/newsSummary.md: -------------------------------------------------------------------------------- 1 | ## Other changes introduced from {{ include.from }}.0 to {{ include.to }}.0 2 | {% assign newsposts = (site.posts | where: "category" , "news") | sort: 'date' %} 3 | 4 | 12 | -------------------------------------------------------------------------------- /_includes/macros/sponsor-button.html: -------------------------------------------------------------------------------- 1 | 7 | 51 | -------------------------------------------------------------------------------- /_includes/macros/tutorialPost.html: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /_includes/macros/vueDocsSnippet.html: -------------------------------------------------------------------------------- 1 |
2 |
    3 |
  • Vue 2
  • 4 |
  • Vue 3
  • 5 |
6 |
7 | ~~~html{{include.vue2}}~~~ 8 |
9 |
10 | ~~~html{{include.vue3}}~~~ 11 |
12 |
13 | -------------------------------------------------------------------------------- /_includes/notificationBanner.html: -------------------------------------------------------------------------------- 1 | 23 | 93 | -------------------------------------------------------------------------------- /_includes/plugins/pluginCard.css: -------------------------------------------------------------------------------- 1 | .plugin-box { 2 | position: relative; 3 | color: #444; 4 | display: block; 5 | padding: 20px; 6 | background: #fff; 7 | border-radius: 5px; 8 | box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08); 9 | margin-top: 20px; 10 | font-size: 14px; 11 | } 12 | 13 | .plugin-box-header { 14 | display: flex; 15 | align-items: center; 16 | justify-content: space-between; 17 | } 18 | 19 | .plugin-box-header .plugin-tag { 20 | text-transform: capitalize; 21 | background: #d6f0f9; 22 | border-radius: 5px; 23 | padding: 2px 6px 1px; 24 | display: inline-block; 25 | font-size: 14px; 26 | } 27 | 28 | .plugin-box h2 { 29 | font-size: 22px; 30 | margin-top: 0; 31 | } 32 | 33 | .plugin-box p { 34 | margin-top: 0; 35 | } 36 | 37 | .plugin-box h2 .plugin-authors { 38 | font-size: 70%; 39 | font-weight: 400; 40 | } 41 | 42 | .plugin-authors a + a { 43 | margin-left: 3px; 44 | } 45 | 46 | .plugin-authors a + a:before { 47 | margin-right: 3px; 48 | content: "/"; 49 | } 50 | 51 | .plugin-box .is-bundled { 52 | font-size: 13px; 53 | } 54 | 55 | .plugin-box-footer { 56 | font-size: 13px; 57 | margin-top: 8px; 58 | display: flex; 59 | justify-content: space-between; 60 | align-items: center; 61 | } 62 | 63 | .plugin-box-footer .view-docs-btn { 64 | background: #f5f5f5; 65 | border-radius: 40px; 66 | padding: 8px 16px; 67 | display: inline-flex; 68 | align-items: center; 69 | color: black; 70 | } 71 | 72 | .plugin-box-footer .view-docs-btn svg { 73 | margin-left: 4px; 74 | } 75 | 76 | .plugin-box-footer .plugin-reports { 77 | display: inline-flex; 78 | align-items: center; 79 | } 80 | 81 | .plugin-box-footer .r-icon { 82 | display: inline-flex; 83 | align-items: center; 84 | background: 1px #f5f5f5; 85 | padding: 4px 8px; 86 | border-radius: 5px; 87 | line-height: 1; 88 | } 89 | 90 | .plugin-box-footer .r-icon svg { 91 | display: block; 92 | height: 20px; 93 | width: 20px; 94 | margin-right: 4px; 95 | } 96 | 97 | .plugin-box-footer .plugin-reports .r-check, 98 | .plugin-box-footer .plugin-reports .r-check svg { 99 | fill: #676767; 100 | } 101 | 102 | .plugin-box-footer .plugin-reports .r-alert, 103 | .plugin-box-footer .plugin-reports .r-alert svg { 104 | fill: #c02c2c; 105 | } 106 | 107 | .plugin-box-footer .plugin-reports a { 108 | color: #676767; 109 | text-decoration: underline; 110 | } 111 | 112 | .plugin-reports .make-report { 113 | margin-left: 12px; 114 | } 115 | 116 | .plugin-reports .view-reports { 117 | margin-left: 12px; 118 | display: none; 119 | } 120 | 121 | .plugin-reports .r-alert + .view-reports { 122 | display: inline-block; 123 | } 124 | -------------------------------------------------------------------------------- /_includes/plugins/pluginCard.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | {{ include.title }} 5 | 6 | {% assign authors = include.author | split: "," %} 7 | (by {% for author in authors %}@{{ author }}{% endfor %}) 8 | 9 |

10 | 11 | {% if include.bundled == "true" %} 12 | Bundled 13 | {% else %} 14 | Third party 15 | {% endif %} 16 | 17 |
18 |

19 | {{ include.description }} 20 |

21 | 40 | 48 |
49 | -------------------------------------------------------------------------------- /_includes/plugins/pluginCard.js: -------------------------------------------------------------------------------- 1 | let reactionIcons = {}; 2 | reactionIcons.alert = ``; 3 | reactionIcons.check = ``; 4 | 5 | function setPluginReports(pluginName, apiUrl) { 6 | let cached = JSON.parse(sessionStorage.getItem(apiUrl)); 7 | if (cached?.url) { // check if we have a valid response cached 8 | setReports(cached.comments); 9 | } else { 10 | fetch(apiUrl) 11 | .then(res => res.json()) 12 | .then(data => { 13 | if (!data?.url) return; // check if we got a valid response 14 | sessionStorage.setItem(apiUrl, JSON.stringify(data)); 15 | setReports(data.comments); 16 | }); 17 | } 18 | 19 | function setReports(comments) { 20 | document.querySelector(`.${pluginName} .plugin-reports`).insertAdjacentHTML("afterbegin", ` 21 | ${comments > 0 22 | ? `${reactionIcons.alert} This plugin has been reported ${comments} times` 23 | : `${reactionIcons.check} This plugin has never been reported`} 24 | `) 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /_includes/socialButtons.html: -------------------------------------------------------------------------------- 1 | 46 | 53 | -------------------------------------------------------------------------------- /_includes/sponsorOrStar.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | If you want to support Javalin, consider sponsoring or starring: 4 | Support Javalin? 5 |
6 | {% include macros/sponsor-button.html %} 7 | 11 |
12 | 55 | -------------------------------------------------------------------------------- /_includes/starBegging.html: -------------------------------------------------------------------------------- 1 |
2 | 42 |

43 | Like Javalin? 44 |
Star us 😊 45 |

46 | × 47 | 51 | 62 |
63 | -------------------------------------------------------------------------------- /_includes/tutorialNav.html: -------------------------------------------------------------------------------- 1 | {% assign tutorials = (site.posts | where: "layout" , "tutorial") %} 2 |
3 | 8 |
9 | -------------------------------------------------------------------------------- /_layouts/blogpost.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 5 |
6 |
7 |

{{ page.title }}

8 |

9 | 10 | • Written by 11 | Reading time: 0-0 min 12 |

13 |
14 |
15 | {{ content }} 16 |
17 |

Thanks for reading

18 | If you find any errors, please edit this page on GitHub. 19 |
20 |
21 | {% include socialButtons.html %} 22 | -------------------------------------------------------------------------------- /_layouts/news.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 5 |
6 |

Javalin {{ page.version }} released

7 |
{{ page.title }} ()
8 | {% unless page.hidewhatsjavalin %} 9 |
10 | What's Javalin? Javalin is a very lightweight Java and Kotlin web framework which focuses on simplicity and Java/Kotlin interoperability. 11 | Read more on our landing page. 12 |
13 | {% endunless %} 14 |
15 | {% assign thisMajor = page.version | split: "." | first %} 16 | {% assign currentMajor = site.javalinversion | split: "." | first %} 17 | {% if thisMajor < currentMajor %} 18 |
19 | Important: This news article covers an old version of Javalin (v{{page.version}}). 20 | The current version is v{{site.javalinversion}}. 21 |
22 | See the documentation page for up-to-date information. 23 |
24 | {% endif %} 25 | {{ content }} 26 |
27 |
28 | {% include socialButtons.html %} 29 | 30 | 44 | -------------------------------------------------------------------------------- /_layouts/tutorial.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | istutorial: true 4 | --- 5 | 6 |
7 |
8 |

{{ page.title }}

9 |

10 | 11 | {% if page.author %} 12 | • Written by 13 | {% endif %} 14 | Reading time: 0-0 min 15 | {% if page.github %} 16 |
17 | 18 | The source code for this tutorial can be found on 19 | GitHub. 20 | Please fork/clone and look while you read. 21 | 22 | {% endif %} 23 |

24 | {% if page.notice %} 25 |

26 | {{ page.notice }} 27 |

28 | {% endif %} 29 |
30 |
31 | {{ content }} 32 |
33 |

Thanks for reading

34 | If you find any errors, please edit this page on GitHub. 35 |
36 | {% if page.github %} 37 | The full source code for this tutorial can also be found on GitHub. 38 | {% endif %} 39 |
40 | {% include socialButtons.html %} 41 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-05-24-javalin-0.0.1-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-05-24 5 | version: 0.0.1 6 | title: Hello world! 7 | summary: Javalin is now available on Maven Central! 8 | --- 9 | 10 | The first Javalin version is now available (0.0.1). 11 | Javalin will try to follow semantic versioning, but be prepared for breaking changes. 12 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-05-26-javalin-0.0.2-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-05-26 5 | version: 0.0.2 6 | title: Introducing Translators 7 | summary: Add Jackson object-mapping and template engines 8 | --- 9 | 10 | Javalin 0.0.2 introduces automatic json-mapping and template engines. 11 | 12 | * Jackson 13 | * Velocity 14 | * Freemarker 15 | * Mustache 16 | * Thymeleaf 17 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-05-28-javalin-0.1.1-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-05-28 5 | version: 0.1.1 6 | title: Fixing the package structure 7 | summary: javalin -> io.javalin 8 | --- 9 | 10 | Javalin didn't follow the convention of `tld.name`, and only used `javalin` as the top package. 11 | This was fixed in 0.1.1, so you'll have to fix your imports. 12 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-06-05-javalin-0.2.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-06-05 5 | version: 0.2.0 6 | title: Javalin rewritten in Kotlin 7 | summary: Kotlin love! 8 | --- 9 | 10 | Most of Javalin was re-written to Kotlin in version 0.2.0. The SAM interfaces were left 11 | as Java, as well as the main Javalin-class which has method-declarations with SAM parameters. 12 | This had to be done due to limitations in Kotlin itself ([https://youtrack.jetbrains.com/issue/KT-14151](https://youtrack.jetbrains.com/issue/KT-14151), 13 | [https://devnet.jetbrains.com/thread/461516](https://devnet.jetbrains.com/thread/461516))\\ 14 | The rest of the library will be ported to Kotlin if this issue is resolved, or and alternative solution is discovered. 15 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-06-11-javalin-0.3.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-06-11 5 | version: 0.3.0 6 | title: Introducing Context 7 | summary: (request, response) -> { ... } is now ctx -> { ... } 8 | --- 9 | 10 | Javalin 0.3.0 changes the core API from dealing with `request`/`response` pairs, to just dealing with 11 | a single `context`(or `ctx`) object. This context-object contains all the methods that belonged 12 | to `request` and `response` previously, with a few adjustments: 13 | 14 | Comparison: 15 | {% capture java %} 16 | app.get("/", ctx -> ctx.result("Hello World")); // new syntax 17 | app.get("/", (req, res) -> res.body("Hello World")); // old syntax 18 | {% endcapture %} 19 | {% capture kotlin %} 20 | app.get("/") { ctx -> ctx.result("Hello World") } // new syntax 21 | app.get("/") { req, res -> res.body("Hello World") } // old syntax 22 | {% endcapture %} 23 | {% include macros/docsSnippet.html java=java kotlin=kotlin %} 24 | 25 | Most getters (`header(name)`, `cookie(name)`, etc) operate on the underlying request, 26 | while all setters (`header(name, value)`, `cookie(name, value)`, etc) operate on the underlying response. 27 | Since `request` and `response` don't share any methods, this approach seems to work well. 28 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-06-18-javalin-0.3.1-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-06-18 5 | version: 0.3.1 6 | title: External static resources 7 | summary: Javalin now supports external static resources 8 | --- 9 | 10 | You can now have external static resources by doing `app.enableStaticFiles("/folder", Location.EXTERNAL)`. 11 | 12 | Exciting, I know. 13 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-06-24-javalin-0.3.2-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-06-24 5 | version: 0.3.2 6 | title: Embedded server refactoring 7 | summary: Start/stop is now sync 8 | --- 9 | 10 | Most of the changes in this release (~20 commits) won't be noticed, except that 11 | `app.start()` and `app.stop()` are now synchronous methods 12 | (`app.awaitStart()` and `app.awaitStop()` have been removed). 13 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-07-03-javalin-0.3.3-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-07-03 5 | version: 0.3.3 6 | title: Configuring Jackson 7 | summary: You can now configure the Jackson ObjectMapper 8 | --- 9 | 10 | Very small change this time, but you can now configure the Jackson ObjectMapper. 11 | 12 | Pretty useful, I guess.. if you're into that sort of thing. 13 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-07-07-javalin-0.3.4-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-07-07 5 | version: 0.3.4 6 | title: ApiBuilder improvements 7 | summary: ApiBuilder improvements 8 | --- 9 | 10 | You no longer need to supply a path to verbs (`get`, `post`, etc) in the ApiBuilder. 11 | 12 | It makes for pretty neat APIs: 13 | 14 | {% capture java %} 15 | app.routes(() -> { 16 | path("users", () -> { 17 | get(UserController::getAllUsers); 18 | post(UserController::createUser); 19 | path(":id", () -> { 20 | get(UserController::getUser); 21 | patch(UserController::updateUser); 22 | delete(UserController::deleteUser); 23 | }); 24 | }); 25 | }); 26 | {% endcapture %} 27 | {% capture kotlin %} 28 | app.routes { 29 | path("users") { 30 | get(userController::getAllUsers); 31 | post(userController::createUser); 32 | path(":id") { 33 | get(userController::getUser); 34 | patch(userController::updateUser); 35 | delete(userController::deleteUser); 36 | } 37 | } 38 | } 39 | {% endcapture %} 40 | {% include macros/docsSnippet.html java=java kotlin=kotlin %} 41 | 42 | Some verbs (`trace`, `head`, `options`, `connect`) were also removed from `ApiBuilder`. 43 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-07-20-javalin-0.3.5-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-07-20 5 | version: 0.3.5 6 | title: Query-param improvements 7 | summary: Added two helpful methods for query params 8 | --- 9 | 10 | ### ctx.mapQueryParams() 11 | This feature is mostly useful for Kotlin users, 12 | as it allows for very neat query-param declaration with destructuring and elvis-throws: 13 | 14 | ```kotlin 15 | app.post("/new-user") { ctx -> 16 | val (name, email) = ctx.mapQueryParams("name", "email") ?: throw MissingQueryParamException() 17 | } 18 | ``` 19 | 20 | ### ctx.anyQueryParamNull() 21 | This is band-aid for Java developers who can't use `ctx.mapQueryParams()`, 22 | at least you can make sure that your query-params are present: 23 | ```java 24 | app.post("/new-user", ctx -> 25 | if (ctx.anyQueryParamNull("name", "email")) { 26 | throw new MissingQueryParamException(); 27 | } 28 | }); 29 | ``` 30 | 31 | #### Minor change in `before`/`after` 32 | Previously `before` and `after` used `/*` as path when called pathless (like `before(handler)`). 33 | This has been changed to `*`, in order to include the root path (`/`). 34 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-07-28-javalin-0.3.6-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-07-28 5 | version: 0.3.6 6 | title: Simple uploads! 7 | summary: Added easy to use file uploads, fixed form-param decoding 8 | --- 9 | 10 | ## Uploads, uploads everywhere! 11 | Uploaded files are now easily accessible via: `ctx.uploadedFiles()` 12 | {% capture java %} 13 | app.post("/upload", ctx -> { 14 | ctx.uploadedFiles("files").forEach(file -> { 15 | FileUtils.copyInputStreamToFile(file.getContent(), new File("upload/" + file.getName())); 16 | }); 17 | }); 18 | {% endcapture %} 19 | {% capture kotlin %} 20 | app.post("/upload") { ctx -> 21 | ctx.uploadedFiles("files").forEach { (contentType, content, name, extension) -> 22 | FileUtils.copyInputStreamToFile(content, File("upload/" + name)) 23 | } 24 | } 25 | {% endcapture %} 26 | {% include macros/docsSnippet.html java=java kotlin=kotlin %} 27 | 28 | ## Other changes: 29 | * `ctx.formParam()` now uses same decoding as `ctx.queryParam()` 30 | * `.start()` is now required in order to start the server (previously declaring routes would also start the server) 31 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-08-06-javalin-0.3.7-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-08-06 5 | version: 0.3.7 6 | title: Some minor adjustments 7 | summary: Trailing slashes, quickstart, overloads, @JvmStatic 8 | --- 9 | 10 | ## Some minor adjustments 11 | 12 | * Trailing slashes can now be un-ignored by calling `app.dontIgnoreTrailingSlashes()` 13 | * Added `mapFormParams()` and `anyFormParamNull()` functions 14 | * Added log-message for static-file configuration 15 | * Added `start(port)` method for quick-starting the server 16 | * Added render-overloads for template engines that don't require a map 17 | * Added `@JvmStatic` annotations to template engines, so `INSTANCE` is no longer required when calling methods from Java 18 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-08-12-javalin-0.4.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-08-12 5 | version: 0.4.0 6 | title: CORS and recap 7 | summary: Added cors and changed plugin-config 8 | --- 9 | 10 | ## CORS 11 | Javalin 0.4.0 adds built-in CORS support with `app.enableCorsForOrigin("origin")`. 12 | There were also some breaking changes made to Jackson and the template-engines. If 13 | you want to configure Jackson now, you'll have to call `JavalinJacksonPlugin.configure()` 14 | instead of just `Jackson.configure()`. 15 | 16 | ### 0.3.0 to 0.4.0 recap 17 | * Added simple file-upload api: 18 | ```java 19 | app.post("/upload") { ctx -> 20 | ctx.uploadedFiles("files").forEach { (contentType, content, name, extension) -> 21 | FileUtils.copyInputStreamToFile(content, File("upload/" + name)) 22 | } 23 | } 24 | ``` 25 | * Added `ctx.mapQueryParams()` and `ctx.mapFormParams()`, which adds neat destructuring in Kotlin: 26 | ```kotlin 27 | app.post("/new-user") { ctx -> 28 | val (name, email) = ctx.mapFormParams("name", "email") ?: throw MissingFormParamException() 29 | } 30 | ``` 31 | * Added `ctx.anyQueryParamNull()` and `ctx.anyFormParamNull()` for Java devs who can't destructure 32 | * Added option to declare routes without paths, relying on the `path()` method: 33 | ```java 34 | app.routes { 35 | path("users") { 36 | get(userController::getAllUsers); 37 | post(userController::createUser); 38 | path(":id") { 39 | get(userController::getUser); 40 | patch(userController::updateUser); 41 | delete(userController::deleteUser); 42 | } 43 | } 44 | } 45 | ``` 46 | * Added support for external static files via `app.enableStaticFiles("/folder", Location.EXTERNAL)` 47 | * `app.start()` and `app.stop()` are now synchronous methods (`app.awaitStart()` and `app.awaitStop()` have been removed) 48 | * Added `@JvmStatic` annotations, so `INSTANCE` can be omitted when calling configuration-methods from Java 49 | * Added option to `dontIgnoreTrailingSlashes()` 50 | * Added some convenience methods and overloads (like `start(port)` and `render(templatePath)`) 51 | 52 | There were also other minor adjustments and bugfixes, see the [news overview](/news) for a complete list. 53 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-08-19-javalin-0.4.1-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-08-19 5 | version: 0.4.1 6 | title: Request-logs and form-params 7 | summary: Added request-logging and improved form-params with three more functions 8 | --- 9 | 10 | ## Added more `ctx.formParam`-functions 11 | Previously there was only `ctx.formParam()` to get a single form-param, 12 | now form-params have the same functionality as query-params: 13 | 14 | ```kotlin 15 | formParam("key") // get one form-params 16 | formParamOrDefault("key", "default") // get one form-params (or default if null) 17 | formParams("key") // get multiple form-paramss 18 | formParamMap() // get map of all form-params key/values 19 | ``` 20 | 21 | ## Added request-logging 22 | You can enable request-logging by calling `ctx.enableStandardRequestLogging()`, 23 | or by calling `requestLogLevel(LogLevel logLevel)`. The current LogLevels are: 24 | 25 | ### LogLevel.OFF 26 | Request-logging is off by default. 27 | 28 | ### LogLevel.MINIMAL 29 | ```markdown 30 | INFO - POST -> 200 (0.16 ms) 31 | ``` 32 | 33 | ### LogLevel.STANDARD 34 | ```markdown 35 | INFO - POST /endpoint-path -> 200 [text/plain;charset=utf-8] (took 0.15 ms) 36 | ``` 37 | 38 | ### LogLevel.EXTENSIVE 39 | The `EXTENSIVE` level wraps the `ServletResponse` and uses a custom `PrintWriter` to copy the content. 40 | This operation is somewhat expensive, so you shouldn't use it if you need performance. 41 | 42 | ```markdown 43 | INFO - JAVALIN EXTENSIVE REQUEST LOG (this clones the response, which is an expensive operation): 44 | Request: POST [/some-endpoint] 45 | Headers: {User-Agent=unirest-java/1.3.11, Connection=keep-alive, Host=localhost:51958, Accept-Encoding=gzip, Content-Length=4, Content-Type=text/plain; charset=UTF-8} 46 | Cookies: {} 47 | Body: body=mybody 48 | QueryString: qp=queryparam 49 | QueryParams: {qp=[queryparam]} 50 | FormParams: {body=[mybody]} 51 | Response: [200], execution took 0.27 ms 52 | Headers: {Server=Javalin, Date=Sat, 19 Aug 2017 09:11:12 GMT, Content-Type=text/plain;charset=utf-8} 53 | Body: (starts on next line) 54 | response-body-content 55 | ---------------------------------------------------------------------------------- 56 | ``` 57 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-08-30-javalin-0.4.2-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-08-30 5 | version: 0.4.2 6 | title: Internal refactoring and Jetty-changes 7 | summary: Improved logging and static file handling 8 | --- 9 | 10 | ## Improved response handling and logging 11 | 12 | Javalin previously used a `PrintWriter` to write responses, 13 | while Jetty copied to the response `OutputStream`. 14 | For logging, Javalin used a custom `PrintWriter` that would copy the response, 15 | but this would not catch responses written by Jetty (static files). 16 | Javalin now uses a custom `OutputStream` for response-logging, 17 | so every response (both Javalin and Jetty-handled) is logged. 18 | 19 | Request-logger no longer includes the body of multipart-requests. 20 | 21 | ## Static resource improvements 22 | 23 | * GZIP has been enabled 24 | * Cache-control is now set to `max-age=0` by default, which means that 25 | clients will always ask Jetty if files have been updated - unless: 26 | * If you put files in a dir called `immutable`, then `max-age` will be set to one year. 27 | This should only be used for versioned library files, like `vue-2.4.2.min.js` 28 | 29 | ## New functions on Context 30 | * `ctx.renderMarkdown("/path/to/markdown-file.md")` - render and serve a markdown-file 31 | * `ctx.isMultipart()` check if request is multipart 32 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-09-09-javalin-0.5.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-09-09 5 | version: 0.5.0 6 | title: Opening up the Cookie Store 7 | summary: Added ctx.cookieStore(), Headers.kt, and removed some old functions 8 | --- 9 | 10 | ## Introducing the cookieStore 11 | The new `ctx.cookieStore()` functions provide a convenient way for sharing information between handlers, request, or even servers: 12 | ```java 13 | ctx.cookieStore(key, value) // store any type of value 14 | ctx.cookieStore(key) // read any type of value 15 | ctx.clearCookieStore() // clear the cookie-store 16 | ``` 17 | The cookieStore works like this: 18 | 1. The first handler that matches the incoming request will populate the cookie-store-map with the data currently stored in the cookie (if any). 19 | 2. This map can now be used as a state between handlers on the same request-cycle, pretty much in the same way as `ctx.attribute()` 20 | 3. At the end of the request-cycle, the cookie-store-map is serialized, base64-encoded and written to the response as a cookie. 21 | This allows you to share the map between requests and servers (in case you're running multiple servers behind a load-balancer) 22 | 23 | ### Example: 24 | ```kotlin 25 | serverOneApp.post("/cookie-storer") { ctx -> 26 | ctx.cookieStore("string", "Hello world!") 27 | ctx.cookieStore("i", 42) 28 | ctx.cookieStore("list", listOf("One", "Two", "Three")) 29 | } 30 | 31 | serverTwoApp.get("/cookie-reader") { ctx -> // runs on a different server than serverOneApp 32 | val string = ctx.cookieStore("string") 33 | val i = ctx.cookieStore("i") 34 | val list = ctx.cookieStore>("list") 35 | } 36 | ``` 37 | 38 | Since the client stores the cookie, the `get` request to `serverTwoApp` 39 | will be able to retrieve the information that was passed in the `post` to `serverOneApp`. 40 | 41 | ## Added Header.kt 42 | A constant-class holding all common headers has been added: 43 | ```kotlin 44 | object Header { 45 | const val ACCEPT = "Accept" 46 | const val ACCEPT_CHARSET = "Accept-Charset" 47 | etc... 48 | } 49 | ``` 50 | 51 | `const val` in Kotlin behaves like `public static final` in Java, and can be called like `Header.ACCEPT` from both languages. 52 | 53 | ## Added `ctx.basicAuthCredentials()` 54 | Added a function which returns a `BasicAuthCredentials` object, which has username and password used for basic-auth. 55 | 56 | ## Removed two functions 57 | * `Javalin#ipAddress` has been removed. The IP can still be configured via `Javalin#embeddedServer`, 58 | but the functionality is not used often enough to deserve its own method. 59 | * `Context#bodyParam` has been removed. It was just an alias for `Context#formParam`, which is still there. 60 | 61 | {% include macros/newsSummary.md from="0.4" to="0.5" %} 62 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-09-22-javalin-0.5.1-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-09-22 5 | version: 0.5.1 6 | title: WebSockets 7 | summary: Added WebSocket support! 8 | --- 9 | 10 | ## Functional WebSockets 11 | 12 | There is only one change in this release, but it's a big one. 13 | Version 0.5.1 introduces a WebSocket API, built on top of Jetty. 14 | 15 | WebSockets can be enabled in three different ways: 16 | 17 | ### Lambda approach 18 | {% capture java %} 19 | app.ws("/websocket", ws -> { 20 | ws.onConnect(session -> System.out.println("Connected")); 21 | ws.onMessage((session, message) -> { 22 | System.out.println("Received: " + message); 23 | session.getRemote().sendString("Echo: " + message); 24 | }); 25 | ws.onClose((session, statusCode, reason) -> System.out.println("Closed")); 26 | ws.onError((session, throwable) -> System.out.println("Errored")); 27 | }); 28 | {% endcapture %} 29 | {% capture kotlin %} 30 | app.ws("/websocket") { ws -> 31 | ws.onConnect { session -> println("Connected") } 32 | ws.onMessage { session, message -> 33 | println("Received: " + message) 34 | session.remote.sendString("Echo: " + message) 35 | } 36 | ws.onClose { session, statusCode, reason -> println("Closed") } 37 | ws.onError { session, throwable -> println("Errored") } 38 | } 39 | {% endcapture %} 40 | {% include macros/docsSnippet.html java=java kotlin=kotlin %} 41 | 42 | ### Annotated class 43 | You can pass an annotated class to the `ws()` function: 44 | ```java 45 | app.ws("/websocket", WebSocketClass.class); 46 | ``` 47 | 48 | Annotation API can be found on [Jetty's docs page](http://www.eclipse.org/jetty/documentation/9.4.x/jetty-websocket-api-annotations.html) 49 | 50 | ### WebSocket object 51 | You can pass any object that fulfills Jetty's requirements (annotated/implementing `WebSocketListener`, etc): 52 | ```java 53 | app.ws("/websocket", new WebSocketObject()); 54 | ``` 55 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-10-05-javalin-0.5.2-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-10-05 5 | version: 0.5.2 6 | title: Jetty upgrade 7 | summary: Migrated to latest version of Jetty 8 | --- 9 | 10 | ## Jetty migration 11 | The latest version of Jetty introduced a small bug in static file handling, 12 | but the Jetty people were very helpful in coming up with a workaround. 13 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-10-16-javalin-0.5.3-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-10-16 5 | version: 0.5.3 6 | title: Context paths 7 | summary: Added context path support and some Kotlin improvements 8 | --- 9 | 10 | ## Context paths 11 | You can now configure the context path by calling `app.contextPath("/context-path")`.\\ 12 | Any requests made below the context path will result in a 404. For example: 13 | 14 | {% capture java %} 15 | Javalin app = Javalin.create() 16 | .contextPath("/my-path") 17 | .enabledStaticFiles("public") 18 | .port(1234) 19 | .start() 20 | 21 | app.get("/", ctx -> ctx.result("Hello, World")); 22 | {% endcapture %} 23 | {% capture kotlin %} 24 | val app = Javalin.create().apply { 25 | contextPath("/my-path") 26 | enabledStaticFiles("public") 27 | port(1234) 28 | }.start() 29 | 30 | app.get("/") { ctx -> ctx.result("Hello, World") } 31 | {% endcapture %} 32 | {% include macros/docsSnippet.html java=java kotlin=kotlin %} 33 | 34 | ``` 35 | GET localhost:1234/ // 404 36 | GET localhost:1234/my-path // 200 (Hello, World) 37 | GET localhost:1234/my-path/ // 200 (Hello, World) 38 | GET localhost:1234/script.js // 404 39 | GET localhost:1234/my-path/script.js // 200 (if there is a script.js file in your /public folder) 40 | ``` 41 | 42 | Any argument to `contextPath()` will be normalized to the form `/path` (slash, path, no-slash) 43 | 44 | ## @NotNull annotations 45 | All the Java API's have had `@NotNull` added to their method signatures, which should provide better IDE support 46 | when programming in Kotlin. 47 | 48 | ## Simple safety refactoring 49 | * Always check if response is committed before trying to write to it 50 | * Wrap `doHandle()` in try-catch 51 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-10-22-javalin-0.5.4-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-10-22 5 | version: 0.5.4 6 | title: Session convenience 7 | summary: Added sessionAttribute() functions and improved logging 8 | --- 9 | 10 | ## Session attributes 11 | You can now access session attributes more easily. 12 | 13 | ```kotlin 14 | ctx.sessionAttribute("foo", "bar") // set session-attribute "foo" to "bar" 15 | val foo = ctx.sessionAttribute("foo") // get session-attribute "foo" as string 16 | val sessionAttributeMap = ctx.sessionAttributeMap() // {foo=bar} 17 | ``` 18 | 19 | ## Improved request logging 20 | 21 | `LogLevel.EXTENSIVE` now includes `matching endpoint-handlers` for a request. Given this app config: 22 | 23 | ```java 24 | app.before(ctx -> {...}); 25 | app.get("/matched/:param", ctx -> ctx.result(ctx.matchedPath())); 26 | app.after(ctx -> {...}); 27 | ``` 28 | 29 | A GET request to `/matched/p1` gives this log output: 30 | ```bash 31 | [qtp319977154-18] INFO io.javalin.core.JavalinServlet - JAVALIN EXTENSIVE REQUEST LOG (this clones the response, which is an expensive operation): 32 | Request: GET [/matched/p1] 33 | Matching endpoint-handlers: [BEFORE=*, GET=/matched/:param, AFTER=*] 34 | Headers: {User-Agent=unirest-java/1.3.11, Connection=keep-alive, Host=localhost:7777, Accept-Encoding=gzip} 35 | Cookies: {} 36 | Body: 37 | QueryString: null 38 | QueryParams: {} 39 | FormParams: {=[]} 40 | Response: [200], execution took 1.65 ms 41 | Headers: {Server=Javalin, Date=Sun, 22 Oct 2017 09:56:27 GMT, Content-Type=text/plain;charset=utf-8} 42 | Body: (starts on next line) 43 | /matched/:param 44 | ``` 45 | 46 | ## ctx.matchedPath() 47 | Added a function for getting the current matched path (from the Javalin router) from the context. 48 | ```java 49 | app.get("/matched/:param", ...); // ctx.matchedPath() = "/matched/:param" 50 | ``` 51 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-11-11-javalin-1.0.1-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-11-11 5 | version: 1.0.1 6 | title: Three small bugfixes 7 | summary: Fixed a few bugs found after release 8 | --- 9 | 10 | ## Bugfixes 11 | 12 | * The `SERVER_STARTED` event is now fired **after** the `started` flag is set to true 13 | * `port()` now returns the port even if the server is not started (used to return -1) 14 | * Changed to `CompletionStage` for `async`, and marked `async` as experimental 15 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-11-18-javalin-1.1.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-11-18 5 | version: 1.1.0 6 | title: GZIP, CORS and WebSocket improvements 7 | summary: Added dynamic GZIP and new features for CORS and WebSockets 8 | --- 9 | 10 | ## New features 11 | 12 | * Added `enableDynamicGzip()` option to gzip any response over 1500 bytes 13 | * Added `*` as possible origin so you can do `enableCorsForOrigin("*")` 14 | * Added `enableCorsForAllOrigins()` which just calls the above ^ 15 | * Added `session.send()` and different `session.queryParam()` methods to websocket `session` object 16 | 17 | ## Bugfixes 18 | 19 | * `isMultipart()` now checks for any type of multipart (previously just `multipart/form-data`) 20 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-11-26-javalin-1.1.1-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-11-26 5 | version: 1.1.1 6 | title: WebSocket improvements/bugfix 7 | summary: Allows WebSockets without defining all handlers 8 | --- 9 | 10 | ## Changes 11 | In Javalin 1.1.0 and before, you had to define handlers for `onConnect`, `onMessage` and `onClose` for WebSockets to work. 12 | As of 1.1.1, all handlers are optional. 13 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2017-12-02-javalin-1.2.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2017-12-02 5 | version: 1.2.0 6 | title: Custom jetty handlers 7 | summary: Allows you to configure the embedded jetty server with custom handlers 8 | --- 9 | 10 | ## Custom jetty handlers 11 | Javalin 1.2.0 introduces the possibility of adding custom jetty handlers, such as `StatisticsHandler` and `RequestLogHandler`.\\ 12 | You can configure your embedded jetty-server with a handler-chain 13 | ([example](https://github.com/javalin/javalin/blob/master/src/test/java/io/javalin/TestCustomJetty.java#L66-L82)), 14 | and Javalin will attach it's own handlers to the end of this chain. 15 | 16 | {% capture java %} 17 | StatisticsHandler statisticsHandler = new StatisticsHandler(); 18 | 19 | Javalin.create() 20 | .embeddedServer(new EmbeddedJettyFactory(() -> { 21 | Server server = new Server(); 22 | server.setHandler(statisticsHandler); 23 | return server; 24 | })) 25 | .start(); 26 | {% endcapture %} 27 | {% capture kotlin %} 28 | val statisticsHandler = StatisticsHandler() 29 | 30 | Javalin.create().apply { 31 | embeddedServer(EmbeddedJettyFactory({ 32 | Server(queuedThreadPool).apply { 33 | handler = statisticsHandler 34 | } 35 | })) 36 | }.start(); 37 | {% endcapture %} 38 | {% include macros/docsSnippetKotlinFirst.html java=java kotlin=kotlin %} 39 | 40 | Doing this will allow you to integrate Javalin with for example prometheus easily, 41 | by exposing statistics collected by the `StatisticsHandler`.\\ 42 | There's even a tutorial to show you how that's done: [/tutorials/prometheus-example](/tutorials/prometheus-example) 43 | 44 | ## EventManager cleanup 45 | There was a bug in the `Event` data class, where properties were declared as `var` instead of `val`. 46 | The `Javalin` val was nullable for no apparent reason, so that's been fixed too. 47 | 48 | ## Kotlin 1.2.0 49 | Kotlin has been bumped to 1.2.0 50 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2018-01-03-javalin-1.2.1-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2018-01-03 5 | version: 1.2.1 6 | title: Bumps, bugfixes, cleanup 7 | summary: Updated Jetty and Kotlin and fixed bug in TemplateUtil 8 | --- 9 | 10 | ## Dependency bumps 11 | Jetty has been bumped to `9.4.8.v20171121`. Kotlin has been bumped to `1.2.10`. 12 | There were no breaking changes in either bump. 13 | 14 | ## TemplateUtil#model bugfix 15 | The `model` function used to be `vararg args: Any`, but has been changed to `vararg args: Any?`. 16 | Nullables are perfectly fine for template-models. 17 | 18 | ## Cleanup 19 | Some minor code cleanup was performed, but it shouldn't affect anyone. 20 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2018-02-04-javalin-1.3.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2018-02-04 5 | version: 1.3.0 6 | title: WebSocket and static files improvements 7 | summary: Added id to WsSession and option to add multiple static files locations 8 | --- 9 | 10 | ## WebSocket improvements 11 | * Jetty WebSocket module is now bundled with Javalin, there is no need to add it separately. 12 | If your project already has the WebSocket dependency declared this can now be removed. 13 | * Each WsSession now has an ID that you can access via `session.id` 14 | 15 | ## Multiple Static Files locations 16 | You can now call `enableStaticFiles` as many times as you want. Each call will add a new handler: 17 | 18 | ```kotlin 19 | val app = Javalin.create().apply { 20 | enableStaticFiles("/public/a") 21 | enableStaticFiles("/public/b") 22 | enableStaticFiles("src/test/external/", Location.EXTERNAL) 23 | }.start() 24 | ``` 25 | 26 | Handlers will be executed in the order they're declared. 27 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2018-03-03-javalin-1.4.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2018-03-03 5 | version: 1.4.0 6 | title: Configuration options and bugfixes! 7 | summary: Cache-settings, regex matching, redirect/encoding bugfixes 8 | --- 9 | 10 |
11 | 1.4.0 introduced a couple of bugs. The good news is we've 12 | fixed them in [1.4.1](/news/2018/03/04/javalin-1.4.1-released.html). 13 |
14 | 15 | ## Features 16 | GitHub user [ShikaSD](https://github.com/ShikaSD) has contributed a lot of new config options and features: 17 | 18 | ### Config options 19 | ```kotlin 20 | val app = Javalin.create().apply { 21 | defaultContentType(string) // set a default content-type for responses 22 | defaultCharacterEncoding(string) // set a default character-encoding for responses 23 | maxBodySizeForRequestCache(long) // set max body size for request cache 24 | disableRequestCache() // disable request caching 25 | } 26 | ``` 27 | 28 | ### Other features 29 | * Route declarations can now accept regex patterns (`app.get("/:param/[0-9]+/") { ... }`) 30 | 31 | ## Bugfixes 32 | * `ctx.redirect(path, status)` used to ignore status-code, always using `302`. This has been fixed. 33 | * `CacheRequestWrapper` used to not provide access to the `InputStream` for requests if `Transfer-Encoding` was chunked. This has been fixed. 34 | * Dynamic GZIP only worked for `ctx.result(string)` before, now it also works for `ctx.result(stream)` 35 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2018-03-04-javalin-1.4.1-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2018-03-04 5 | version: 1.4.1 6 | title: Some bugfixes and bumps 7 | summary: Fixes bug in redirects and body-reading 8 | --- 9 | 10 | ## Bugfixes 11 | 1.4.0 attempted to fix some bugs, and introduced new bugs in the process (as is tradition). 12 | Hopefully the bugfixes in 1.4.1 will prove less buggy. 13 | 14 | ### Redirects 15 | Redirects were broken in certain cases by the 1.4.0 release. We switched away from 16 | `HttpServletRequest#sendRedirect()` to handling them manually, but we didn't halt execution. 17 | This resulted in 404's for redirects from before-filters in certain cases. This should be fixed now. 18 | 19 | ### Query-params and reading request body 20 | 1.4.0 introduced lazy caching of the request body. This resulted in some bugs when using `Context#queryParam` 21 | (which uses `HttpServletRequest#getParameter`), which caused the body to be drained before it could be cached. 22 | Query-params are now parsed from `HttpServletRequest#getQueryString`, which should be a 23 | better solution all around. 24 | 25 | ## Bumps 26 | * Kotlin has been bumped to 1.2.30 27 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2018-03-23-javalin-1.5.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2018-03-23 5 | version: 1.5.0 6 | title: WebSocket paths, route overview, multipart improvements 7 | summary: Enhanced WebSocket lambdas, added an automatically generated route overview, 8 | made improvements to multipart-forms and added Javadoc to public API 9 | --- 10 | 11 | ## WebsSocket improvements 12 | One of the most requests WebSocket features has been the ability to use dynamic paths, like `/:param`. 13 | This has finally been added: 14 | 15 | ```kotlin 16 | ws("/chat/:channel") { ws -> 17 | ws.onMessage { session, message -> 18 | val channel = session.param("channel") 19 | broadcastMessage(channel, message) 20 | } 21 | } 22 | ``` 23 | 24 | More convenience methods for extracting information from the upgrade-request were also added: 25 | ```kotlin 26 | session.paramMap() // get all param key/values as map 27 | session.header("key") // get a header 28 | session.headerMap() // get all header key/values as map 29 | session.host() // get request host 30 | ``` 31 | 32 | ## Route overview 33 | You can now generate an overview of all the mapped paths in your application and host it on a path of your choice: 34 | 35 | ```kotlin 36 | val app = Javalin.create().apply { 37 | enableRouteOverview("route-overview") 38 | } 39 | ``` 40 | 41 | The route-overview will show the verb, the path, the function/field/class handling the request, and any roles attached to the handler: 42 | 43 | Route overview 44 | 45 | ## Multipart fields 46 | 47 | Previously the Apache FileUpload dependency was required in order to receive uploaded files, and multipart-fields simply didn't work. 48 | You can now access multipart-fields via the normal form-param function (`ctx.formParam("name")`), and Apache FileUpload 49 | is no longer necessary in order to receive uploaded files (the public API remains unchanged). 50 | 51 | ### Misc/Bugfixes 52 | * Jetty was bumped to `9.4.9.v20180320` which was released very recently. There were no breaking changes. 53 | * Fixed some minor bugs in path-prefixing in `ApiBuilder` 54 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2018-04-14-javalin-1.6.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2018-04-14 5 | version: 1.6.0 6 | title: Async requests and performance improvements 7 | summary: Added ctx.result(completableFuture) and rewrote path matching to be more efficient 8 | --- 9 | 10 | ## Javalin goes async 11 | 12 | We've been working on async on and off for a long time. We've always ended up postponing it due to its trickiness, 13 | but we finally decided to sit down and spend some time on it. 14 | The API has been through several iterations, and we've landed on something that is both simple enough 15 | for end-users to use, and for us to implement (it's also backwards compatible!). 16 | 17 | ### So how does it work? 18 | 19 | Just set a `CompletableFuture` or `CompletableFuture` as your result: 20 | 21 | ```kotlin 22 | import io.javalin.Javalin 23 | 24 | fun main(args: Array) { 25 | val app = Javalin.start(7000) 26 | app.get("/") { ctx -> ctx.result(getFuture()) } 27 | } 28 | 29 | // hopefully your future is less pointless than this: 30 | private fun getFuture() = CompletableFuture().apply { 31 | Executors.newSingleThreadScheduledExecutor().schedule({ this.complete("Hello World!") }, 1, TimeUnit.SECONDS) 32 | } 33 | ``` 34 | 35 | You can only set future results in endpoint handlers (get/post/put/etc).\\ 36 | After-handlers, exception-handlers and error-handlers run like you'd expect them to after 37 | the future has been resolved or rejected. 38 | 39 | A lot has changed behind the scenes, but everything is backwards compatible, so existing users 40 | who don't care about asynchronicity shouldn't be affected at all. 41 | 42 | ### Example project 43 | We made a small example project for illustrating the effects of using futures: 44 | [https://github.com/tipsy/javalin-async-example](https://github.com/tipsy/javalin-async-example). 45 | 46 | Please note, the tool is only intended for illustration. Use a proper benchmarking tool (like [wrk](https://github.com/wg/wrk)) 47 | if you want to actually measure performance. 48 | 49 | ## Performance improvements 50 | Path matching now uses an `EnumMap` to split the search through the different http-methods (get/post/put/etc). 51 | This increases performance slightly for apps with a lot of routes. 52 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2018-05-01-javalin-1.6.1-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2018-05-01 5 | version: 1.6.1 6 | title: Some minor bugfixes and bumps 7 | summary: Fixed bug in redirect, json-mapping and log-util 8 | --- 9 | 10 | ## Bugfixes 11 | 12 | * You can now redirect inside exception mappers 13 | * Jackson now automatically register the Kotlin module if you're using it 14 | * The `LogUtil` broke in 1.6.0 and stopped being able to measure time, this has been fixed 15 | 16 | ## Bumps 17 | 18 | * Kotlin has been bumped to 1.2.41 19 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2018-08-27-javalin-2.1.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2018-08-27 5 | version: 2.1.0 6 | title: Sessions, testing, and default responses 7 | summary: Added support for Jetty SessionHandlers, easier unit testing from Java, more default responses, and the option to throw Exceptions in EventListeners 8 | --- 9 | 10 | ## Custom SessionHandler 11 | Javalin 2.1.0 will let you supply your own `SessionHandler` which will 12 | be attached to HTTP handler in Jetty. 13 | 14 | This will allow multiple Javalin instances to share the same session data, 15 | as well as persist sessions after server restart/redeploy. 16 | 17 | You can configure the `SessionHandler` by calling `app.sessionHandler(...)`. 18 | 19 | If you just want to persist sessions to the file system, you could use a `FileSessionDataStore`: 20 | 21 | ```kotlin 22 | private fun fileSessionHandler() = SessionHandler().apply { 23 | httpOnly = true 24 | sessionCache = DefaultSessionCache(this).apply { 25 | sessionDataStore = FileSessionDataStore().apply { 26 | val baseDir = File(System.getProperty("java.io.tmpdir")) 27 | storeDir = File(baseDir, "javalin-session-store").apply { mkdir() } 28 | } 29 | } 30 | } 31 | ``` 32 | 33 | If you want to use MongoDB, Jetty has a built in `MongoSessionDataStore`: 34 | 35 | ```kotlin 36 | fun mongoSessionHandler() = SessionHandler().apply { 37 | httpOnly = true 38 | sessionCache = DefaultSessionCache(this).apply { 39 | sessionDataStore = MongoSessionDataStoreFactory().apply { 40 | connectionString = "mongodb://:@:/session_db" 41 | dbName = "session_db" 42 | collectionName = "sessions" 43 | }.getSessionDataStore(sessionHandler) 44 | } 45 | } 46 | ``` 47 | 48 | Jetty also has built in functionality for JDBC more. Check the 49 | [Jetty docs](https://www.eclipse.org/jetty/documentation/9.4.x/session-management.html) for a full list. 50 | 51 | ## Easier testing in Java 52 | Version 2.0 of Javalin removed the all-args constructor from `Context`. 53 | This caused some problems for some of our Java users, 54 | so we've added a `init` method to the `ContextUtil` which 55 | can be used to construct `Context` objects for testing. 56 | 57 | ## More default responses 58 | Default responses for 409, 410, 502 and 504 have been added. 59 | 60 | ## Exceptional events 61 | You can now throw Exceptions in an `EventListener`. 62 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2018-09-02-javalin-2.1.1-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2018-09-02 5 | version: 2.1.1 6 | title: Post release bugfixes 7 | summary: Nullable attributes, improved exception handling, encoding fixes 8 | --- 9 | 10 | ## Nullable attributes 11 | The following functions on the `Context` class are now correctly marked as nullable: 12 | 13 | ```kotlin 14 | fun attribute(attribute: String): T? 15 | fun attributeMap(): Map 16 | fun sessionAttribute(attribute: String): T? 17 | fun sessionAttributeMap(): Map 18 | ``` 19 | 20 | This change only affects Kotlin users. 21 | 22 | ## Improved exception handling in async handlers 23 | Javalin 2.0.0 introduced an exception handler for the `CompletionException` class. 24 | Unfortunately, this handler interfered with user-defined exception handlers. This has been fixed in 2.1.1. The 25 | default exception handler only handles `CompletionException` if the cause is a `HttpResponseExceptions` now. 26 | 27 | ## Fixed encoding issues for `json()` and `html()` functions 28 | The `json()` and `html()` functions on `Context` used to set the result stream before setting the content-type. 29 | This would lead to the result being written with the wrong encoding in some environments. This has been fixed. 30 | 31 | ## Opening the Context class 32 | The `Context` class is now `open` (not final anymore). This should make mocking easier for some mocking frameworks. 33 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2018-10-06-javalin-2.3.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2018-10-06 5 | version: 2.3.0 6 | title: App attributes, Jetty HandlerCollections, and a lot of improvements 7 | summary: Added app attributes, Jetty HandlerCollection support, and an automatic module name. 8 | Fixed and improved upon the Validator and JavalinJackson features. Fixed bugs in static files and HttpResponseException. 9 | --- 10 | 11 | ### App attributes 12 | You can now register attributes on the Javalin instances by calling `app.attribute(Class, Object)`. 13 | These attributes can be retrieved on the instance by calling `app.attribute(Class)`, 14 | or inside Handlers by calling `ctx.appAttribute(Class)`. 15 | 16 | As an example, here's how to make a `ConnectionPool` available to every Handler: 17 | 18 | ```java 19 | Javalin app = Javalin.create() 20 | .attribute(ConnectionPool.class, new ConnectionPool()); 21 | .get("/some-path", SomeOtherClass::controller) 22 | .start(7070); 23 | 24 | class SomeOtherClass { 25 | static void controller(Context ctx) { 26 | Connection c = ctx.appAttribute(ConnectionPool.class).getConnection() 27 | } 28 | } 29 | ``` 30 | 31 | ### Custom Jetty HandlerCollection 32 | It's now possible to add a custom `HandlerCollection` to the Jetty instance that Javalin uses. 33 | Javalin is added at the end of the collection. 34 | 35 | ### Improved Java 9-11 support 36 | Javalin now uses the automatic module name `io.javalin` for Java9+ projects. 37 | All of Javalin's tests run against JDK8, JDK9, JDK10, and JDK11. 38 | 39 | ### Jackson improvements 40 | * The Jackson `ObjectMapper` is now available through `JavalinJackson.getObjectMapper()`. 41 | * The dependency checker will now advice you to add the `jackson-module-kotlin` dependency 42 | if you're using Jackson from Kotlin. 43 | 44 | ### Validator fixes 45 | * Initially, the `notNullOrEmpty` check was performed in the constructor. 46 | It has now been moved to the `getOrThrow()`/`asClass()` methods. 47 | * The `Validator` used to swallow exceptions for unregistered converters. This has been fixed. 48 | * Added overloads to the `check` method. 49 | 50 | ### Misc bugfixes 51 | * When handling static files, Javalin used to mark any file with a path starting with "/immutable" as immutable. 52 | Javalin now requires files to be in a directory named exactly "immutable" as the docs specify. 53 | * The response exceptions (`HttpResponseException`) used to have a `msg` property, this has been 54 | deprecated in favor of the standard `message` property on exceptions. 55 | * It's no longer possible to set/replace an `AccessManager` after the Server has started. 56 | 57 | 58 | ## Thanks to all contributors 59 | This release was created with the help of seven different contributors. 60 | If you want to get involved, head on over to [https://github.com/javalin/javalin](https://github.com/javalin/javalin) 61 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2018-11-04-javalin-2.4.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2018-11-04 5 | version: 2.4.0 6 | title: JavalinServlet exposed! 7 | summary: Made it possible to use Javalin without Jetty. Added logger for WebSockets. 8 | Made improvements to exception mapping. Made a lot of other smaller fixes. 9 | --- 10 | 11 | ## Javalin without Jetty 12 | If you want to use Javalin with an application server or a servlet container, such as Tomcat, WebLocic, etc, 13 | you can use `EmbeddedJavalin.createServlet()`: 14 | 15 | ```kotlin 16 | @WebServlet(urlPatterns = ["/rest/*"], name = "MyServlet") 17 | class MyServlet : HttpServlet() { 18 | val javalin = EmbeddedJavalin() 19 | .get("/rest") { ctx -> ctx.result("Hello!") } 20 | .createServlet() 21 | 22 | override fun service(req: HttpServletRequest, resp: HttpServletResponse) { 23 | javalin.service(req, resp) 24 | } 25 | } 26 | ``` 27 | 28 | The `createServlet()` method is the same method that Javalin uses internally when attaching itself to Jetty. 29 | Jetty server methods like `app.contextPath()`, `app.start()`, etc, will throw exceptions if called on `EmbeddedJavalin`. 30 | You have to manually exclude Jetty from your build files if you want to use this approach. 31 | 32 | ## WebSockets 33 | - You can now easily add WebSocket loggers by calling `app.wsLogger()`. The method takes a `WsHandler`, 34 | (the same interface as a normal `app.ws()` call), and can be used to log events of all types. 35 | The logger runs after the WebSocket handler for the endpoint 36 | - `app.enableDebugLogging()` now includes extensive WebSocket logging. 37 | - Fixed a NPE if calling `queryParamMap` when there was no query string. 38 | 39 | ## Exception mapping 40 | - You can now add a custom exception handler to override the built-in handler for `HttpResponseException` 41 | - The default "Internal server error" for uncaught exceptions now uses the `InternalServerErrorResponse` instead of a plaintext String 42 | - ExceptionMapper now takes `defaultContentType` into account 43 | 44 | ## Jetty 45 | - Extended support for wrapped and nested Jetty handlers 46 | - The `SessionHandler` is now validated before starting the Server 47 | - "Jetty is listening on {...}" message now includes the context path 48 | 49 | ## Misc 50 | - You can now get the matched path of an endpoint in an `after` filter by using `ctx.endpointHandlerPath()` 51 | - You can now use the `head` verb in the `ApiBuilder` 52 | - Added support for double extensions for templates (like `.html.ext`) 53 | - Added missing @Nullable/@NotNull annotations 54 | - The `onBinaryMessage` WebSocket handler now throws `Exception` 55 | - The CORS options method is no longer managed by the `AccessManager` 56 | 57 | ## Thanks to all contributors 58 | This release was created with the help of ten different contributors (!) 59 | If you want to get involved, head on over to [https://github.com/javalin/javalin](https://github.com/javalin/javalin) 60 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2019-01-04-javalin-2.5.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2019-01-04 5 | version: 2.5.0 6 | title: App extensions and single-page improvements 7 | summary: Added extensions to Javalin instances. Made single-page mode work with external files. Some minor bugfixes. 8 | --- 9 | 10 | ## App extensions 11 | 12 | You can now create app-extensions by calling `app.register(appExtension)`. 13 | The `Extension` interface is a simple lambda with access to the current Javalin app: 14 | 15 | ```java 16 | Javalin.create() 17 | .register(app -> { ... }) // register lambda 18 | .register(new ExtensionClass(...)); // ExtensionClass must implement Extension 19 | ``` 20 | 21 | You can (for example) create a `DosFilter` that you use across multiple Javalin apps, and add it to each app by calling `app.register(new DosFilter())`. 22 | 23 | The feature is inspired by the `register` function in Sinatra: [http://sinatrarb.com/extensions.html](http://sinatrarb.com/extensions.html#extending-the-dsl-class-context-with-sinatraregister) 24 | 25 | ## Single-page mode improvements 26 | 27 | * You can now use external (non classpath) files for your single-page entrypoint (`index.html`) 28 | * Single-page entry point (`index.html`) is no longer cached if host is localhost 29 | 30 | ## Bugfixes and misc 31 | 32 | * You can now call `ctx.resultString()` in request loggers (after the response has been written). 33 | * You can now set the context-path in `EmbeddedJavalin` 34 | * Static resource handling now has a `ResourceHandler` interface, so you can use your own resource handler if you're extending Javalin 35 | * An error is now logged if user attempts to read request body twice, and body is not cached 36 | * Improved custom handling of `HttpResponseException` 37 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2019-01-17-javalin-2.6.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2019-01-17 5 | version: 2.6.0 6 | title: Server-sent events support! 7 | summary: Javalin finally has a Server-sent events implementation. 8 | --- 9 | 10 | ## Server-sent events 11 | Server-sent events has been requested a lot in Javalin, and thanks to two contributors 12 | ([@7agustibm](https://github.com/7agustibm) and [@firxworx](https://github.com/firxworx)), 13 | we now have an implementation. As with most Javalin APIs, the syntax is lambda based: 14 | 15 | 16 | ```kotlin 17 | val clients = ConcurrentLinkedQueue() 18 | 19 | app.sse("/sse") { client -> 20 | clients.add(client) 21 | client.sendEvent("connected", "Hello, SSE") 22 | client.onClose { clients.remove(client) } 23 | } 24 | 25 | while (true) { 26 | for (client in clients) { 27 | client.sendEvent("PING") 28 | } 29 | Thread.sleep(1000) 30 | } 31 | ``` 32 | 33 | A corresponding JavaScript client would look something like: 34 | ```js 35 | const eventSource = new EventSource("http://localhost:7000/sse"); 36 | eventSource.addEventListener("connected", msg => console.log(msg); 37 | ``` 38 | 39 | Server-sent events is also known as "EventSource", and is useful for when you need to push events to clients (to avoid polling). 40 | 41 | ## Misc 42 | * Fixed a bug introduced in `2.5.0` which made "catch-all" exception-mappers interfere with `HttpResponseException` exceptions 43 | * Added method for retrieving the request context path from `Context` 44 | * `Javalin#addHandler` is now public, this is the internal method that get/post/put/etc call 45 | * Bumped Jetty 46 | -------------------------------------------------------------------------------- /_posts/news/pre-3.0/2019-03-02-javalin-2.7.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2019-03-02 5 | version: 2.7.0 6 | title: WebSocket improvements and Jetty bump 7 | summary: You can now configure the WebSocketServletFactory. 8 | --- 9 | 10 | ## Configure your WebSocketServletFactory 11 | 12 | It's now possible to configure the object which creates WebSocket connections in Javalin. 13 | This can be done through `app.wsFactoryConfig()`: 14 | 15 | ```kotlin 16 | app.wsFactoryConfig { wsFactory -> 17 | wsFactory.policy.maxTextMessageSize = 1234; 18 | wsFactory.policy.idleTimeout = 1234; 19 | wsFactory.extensionFactory.register(...) 20 | wsFactory.register(...) 21 | } 22 | ``` 23 | 24 | This gives you access to a lot of low level settings for WebSockets. 25 | For details, see the [Jetty docs](https://www.eclipse.org/jetty/javadoc/9.4.15.v20190215/org/eclipse/jetty/websocket/servlet/WebSocketServletFactory.html). 26 | 27 | ## Misc 28 | * Added some missing overrides in `EmbeddedJavalin` 29 | * Made it possible to use nested paths in `CrudHandler` 30 | * Bumped Jetty to the latest version 31 | -------------------------------------------------------------------------------- /_posts/news/pre-4.0/2019-06-23-javalin-3.1.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2019-06-23 5 | version: 3.1.0 6 | title: Improvements to validation and plugins 7 | summary: Validation now supports nullable results, and a few bugs have been fixed in OpenApi and JavalinVue. 8 | --- 9 | 10 | ## Validation improvements 11 | The validator now supports nullable values through `.getOrNull()`: 12 | 13 | ```kotlin 14 | val optionalInstant = ctx.queryParam("instant").getOrNull() 15 | ``` 16 | 17 | ## Plugin improvements 18 | * Minor fixes and changes to OpenAPI plugin (interface name, CORS, default values) 19 | * Fixed a `NullPointerException` in the Micrometer plugin 20 | 21 | ## Misc 22 | * Fixed a bug in static file handling causing wrong content type for wrapped responses 23 | * Added support for logging of binary responses when `config.devLogging = true` 24 | * Re-introduced the `EventHandler` interface to allow events to throw checked exceptions in Java 25 | * Fixed a bug in JavalinVue where it would fail to walk file system in jars 26 | * Bumped Jetty 27 | -------------------------------------------------------------------------------- /_posts/news/pre-4.0/2019-07-21-javalin-3.2.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2019-07-21 5 | version: 3.2.0 6 | title: Brotli and bugfixes! 7 | summary: You can now enable Brotli compression (in addition to GZIP), and response writing has been refactored. 8 | --- 9 | 10 | ## Introducing Brotli compression! 11 | 12 | Thanks to the hard work of [@nixxcode](https://github.com/nixxcode) 13 | ([LinkedIn](https://www.linkedin.com/in/dominik-petrovic/)), 14 | Javalin now has support for Brotli compression (in addition to the existing GZIP support). 15 | 16 | The new setting deprecates the old `dynamicGzip` setting. 17 | You also get the option to change the compression level for both Brotli and Gzip. 18 | This can be done by adding an int specifying the compression level: 19 | 20 | ```kotlin 21 | Javalin.create { config -> 22 | config.dynamicGzip = true // deprecated 23 | config.compressionStrategy(Brotli(4), Gzip(6)) 24 | } 25 | ``` 26 | 27 | The change also moves compression handling for static files from Jetty to Javalin, 28 | so static files compression can also be configured with the new setting. 29 | 30 | To make all of this possible, the way responses are written in Javalin has been completely refactored. 31 | Please let us know if you notice anything out of the ordinary. 32 | 33 | ## Misc fixes 34 | 35 | * MicroMeter is now easier to configure *(thanks to [@jon-ruckwood](https://github.com/jon-ruckwood))* 36 | * Fixed a bug where Pebble (template engine) would crash because of immutable maps *(thanks to [@hex-agon](https://github.com/hex-agon))* 37 | * Fixed a bug in the `getOrNull` function on `Validator` *(thanks to [@oharaandrew314](https://github.com/oharaandrew314))* 38 | * Fixed a bug in OpenAPI docs for Java method references *(thanks to [@TobiasWalle](https://github.com/TobiasWalle))* 39 | * Made cookie-store cookie use root domain 40 | * Made cookie-store cookie renamable 41 | * Removed confusing log message when `SessionHandler` was configured with database 42 | * Fixed a bug where basic auth credentials couldn't contain colons *(thanks to [@mikexliu](https://github.com/mikexliu))* 43 | * A state variable for sharing state between the server and client has been introduced to VueComponent 44 | * Caching of VueComponents has been disabled 45 | 46 | This release wouldn't have been possible without contributions from the community, 47 | please head over to [github.com/javalin/javalin](https://github.com/javalin/javalin) 48 | if you're interested in contributing! 49 | -------------------------------------------------------------------------------- /_posts/news/pre-4.0/2019-07-28-javalin-3.3.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2019-07-28 5 | version: 3.3.0 6 | title: Simplified error handling, optional Brotli and bugfixes! 7 | summary: Easier error mapping (based on content-type), optional Brotli, and fixes for compression of large files. 8 | --- 9 | 10 | ## Simplified error handling 11 | 12 | Since the introduction of the standardized HTTP exceptions (like `BadRequestResponse()`), 13 | the `app.error(status, handler)` method has been less useful. 14 | Its main use now seems to be to render HTML error pages, so we've added an optional content-type to the method: 15 | 16 | ```kotlin 17 | app.error(404, "html", my404PageHandler) 18 | ``` 19 | 20 | We've also made the `ErrorHandler` interface extend `Handler`, as it's really the same interface. 21 | Because of backwards compatibility it can't be removed, but all `ErrorHandler` instances 22 | are now valid `Handler` instances, and `app.error()` now takes a `Handler`. 23 | 24 | ## Optional Brotli 25 | In 3.2.0, we included include Brotli as a non-optional dependency, but due to how 26 | the Brotli artifact we depended on is published, this caused issues for some users (anyone using gradle). 27 | 28 | As of 3.3.0, Brotli is an optional dependency, and we've also started work on creating a better 29 | Brotli wrapper for the JVM. This will hopefully be included in the next version of Javalin. 30 | 31 | ## Bugfixes and test improvements 32 | 33 | * Fixed a bug in how responses are written, which caused issues when compressing responses larger than 64kb 34 | * Fixed a bug where certain files (images/audio/etc) were being compressed when they shouldn't have been 35 | * Added Selenium and Chrome to the development test suite 36 | -------------------------------------------------------------------------------- /_posts/news/pre-4.0/2019-08-11-javalin-3.4.1-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2019-08-11 5 | version: 3.4.1 6 | title: JSON RouteOverview and fixes 7 | summary: The RouteOverviewPlugin can now produce JSON, Brotli is using Jvm-Brotli, JavalinVue reads files in UTF-8, ++. 8 | --- 9 | 10 | ## RouteOverviewPlugin JSON 11 | 12 | The `RouteOverviewPlugin` has been with Javalin since version 1.5.0. 13 | It's a plugin which listens for `routeAdded` events and creates a HTML page displaying all the mapped routes: 14 | 15 | Route overview 16 | 17 | As of 3.4.1, this will be served as a JSON object if the client accepts json. 18 | You can enable the `RouteOverviewPlugin` like this: 19 | 20 | ```java 21 | Javalin.create(config -> 22 | config.registerPlugin(new RouteOverviewPlugin(path)); // show all routes on specified path 23 | config.registerPlugin(new RouteOverviewPlugin(path, roles)); // show all routes on specified path (with auth) 24 | ) 25 | ``` 26 | 27 | Thanks to [@dherges](https://github.com/dherges) for adding this feature. 28 | 29 | ## Fixes 30 | 31 | * We're now using the newly created [Jvm-Brotli](https://github.com/nixxcode/jvm-brotli) instead of 32 | [jBrotli](https://github.com/meteogroup/jbrotli) (thanks to [@nixxcode](https://github.com/nixxcode)). 33 | * The `JavalinVue` plugin now always uses UTF-8 for reading files. This fixes a bug when 34 | running on distroless docker images (or any OS which doesn't use UTF-8 by default). 35 | * The `JavalinVue#stateFunction` is now a `@JvmField`. 36 | * If an `EofException` occurs in `JavalinServlet` or `JettyResourceHandler` it's no longer logged. 37 | These exceptions occur if the client closes a connection before the response has been fully written (which is fine). 38 | * The `WsContext` class now has `queryParam(key: String)` (thanks to [@bluedevil2k](https://github.com/bluedevil2k)). 39 | -------------------------------------------------------------------------------- /_posts/news/pre-4.0/2019-09-15-javalin-3.5.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2019-09-15 5 | version: 3.5.0 6 | title: A few features and a lot of fixes to plugins 7 | summary: Validation for headers, public WsServlet, and fixes to OpenAPI and JavalinVue. 8 | --- 9 | 10 | ## First - Thanks for contributing! 11 | 12 | This release consists mainly of code contributed by the Javalin community. 13 | Some features were written by the core team, but the majority were not. Each community contribution is tagged with who contributed it. 14 | Thank to everyone for taking part in the community and helping to make Javalin a better project! 15 | 16 | ## Core improvements 17 | * The `wsServlet` is now publicly available on Javalin (thanks to [@MrRamych](https://github.com/MrRamych)). 18 | * Validation and casting is now available for headers: `ctx.header("my-header")` (thanks to [@Peroniada](https://github.com/Peroniada)). 19 | * The `ctx.basicAuthCredentials()` method now either returns valid credentials, or throws. 20 | * Misc dependencies have been bumped (thanks to [@mpe85](https://github.com/mpe85)). 21 | 22 | ## OpenAPI improvements 23 | * Default documentation are now applied first (thanks to [@TobiasWalle](https://github.com/TobiasWalle)). 24 | * Warnings are now logged in case of path mismatches (thanks to [@TobiasWalle](https://github.com/TobiasWalle)). 25 | * Fixed some reflection issues with extended classes (thanks to [@TobiasWalle](https://github.com/TobiasWalle)). 26 | * Fixed security scheme (thanks to [@maxemann96](https://github.com/maxemann96)). 27 | * Fixed bug with Swagger UI and ReDoc now being handled correctly by `AccessManager`. 28 | * Removed `CorsPlugin` paths from docs (thanks to [@maxemann96](https://github.com/maxemann96)). 29 | * Improved default responses (thanks to [@maxemann96](https://github.com/maxemann96)). 30 | * Added option to ignore certain paths and path-groups (thanks to [@maxemann96](https://github.com/maxemann96)). 31 | 32 | ## JavalinVue improvements 33 | * You can now set the vue directory (thanks to [@jorunfa](https://github.com/jorunfa)). 34 | * You can now use a tag shorthands. `VueComponent("my-tag")` instead of `VueComponent("")`. 35 | * The plugin will now crash on unknown components (catches typos). 36 | * Files on localhost are now properly closed after being read (thanks to [@jorunfa](https://github.com/jorunfa)). 37 | -------------------------------------------------------------------------------- /_posts/news/pre-4.0/2019-11-03-javalin-3.6.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2019-11-03 5 | version: 3.6.0 6 | title: Small fixes and a couple of new features 7 | summary: Form params in OpenAPI, basic auth filter, multiple fixes and dependencies bumps. 8 | --- 9 | 10 | ## Features 11 | * You can now document form params in the OpenAPI plugin (thanks to [@TobiasWalle](https://github.com/TobiasWalle)). 12 | * Added a simple Basic auth plugin (before-filter) 13 | 14 | ## Fixes 15 | * Added a debug log message for deserialization failures in `Context#body()` (thanks to [@ksmith97](https://github.com/ksmith97)). 16 | * Fixed error in logging missing path-params in OpenAPI plugin (thanks to [@rafalsiwiec](https://github.com/rafalsiwiec)). 17 | * Javalin no longer warns about a missing logger if you have a higher version of SLF4J (thanks to [@m-rossini](https://github.com/m-rossini)). 18 | * Query-params can now contain equals signs (thanks to [@pawel-piecyk](https://github.com/pawel-piecyk)). 19 | * Use `byte[]` instead of `Byte[]` in binary WebSocket messages (thanks to [@dvtsants-alar](https://github.com/dvtsants-alar)). 20 | * Uploaded files are no longer buffered in RAM (thanks to [@ryansusana](https://github.com/ryansusana)). 21 | * Fixed a bug in JavalinVue when running from IDE using classpath. 22 | 23 | ## Misc 24 | * Moved from Travis to GitHub actions for CI. All tests now run against Java 8 to 13 on OSX, Ubuntu and Windows. 25 | * Upgraded the Dokka (Java doc generator) plugin, should fix a bug where documentation for certain classes wasn't generated 26 | * Bumped SLF4J and Jackson to the latest version 27 | -------------------------------------------------------------------------------- /_posts/news/pre-4.0/2020-01-01-javalin-3.7.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2020-01-01 5 | version: 3.7.0 6 | title: Rate limiting, case insensitive matching, and more! 7 | summary: New utils and plugins, and improvements to Context, Javalin/JavalinConfig, OpenAPI, and more. 8 | --- 9 | 10 | ## Rate limiting 11 | A very simple util class for rate limiting has been added. 12 | You can call it in the beginning of your endpoint `Handler` functions: 13 | 14 | ```kotlin 15 | app.get("/") { ctx -> 16 | RateLimit(ctx).requestPerTimeUnit(5, TimeUnit.MINUTES) // throws if rate limit is exceeded 17 | ctx.status("Hello, rate-limited World!") 18 | } 19 | ``` 20 | 21 | Every rate limiter is independent (IP and `Handler` based), so different endpoints can have different rate limits. It works as follows: 22 | 23 | * A map of maps holds one IP/counter map per method/path combination (`Handler`). 24 | * On each request the counter for that IP is incremented. 25 | * If the counter exceeds the number of requests specified, an exception is thrown. 26 | * All counters are cleared periodically on every timeunit that you specified. 27 | 28 | ## Case insensitive path matching 29 | There is a new plugin, `RedirectToLowercasePathPlugin`, which fills the void 30 | left by removing case-insensitive matching that was present in Javalin 2.X. 31 | 32 | The plugin redirects requests with uppercase/mixcase paths to lowercase paths. 33 | For example, `/Users/John` redirects to `/users/John` (if endpoint is `/users/:userId`). 34 | It does not affect the casing of path-params and query-params, only static 35 | URL fragments (`Users` becomes `users` above, but `John` remains `John`).\\ 36 | When using this plugin, you can only add paths with lowercase URL fragments. 37 | 38 | It can be added like any other plugin: 39 | 40 | ```kotlin 41 | config.registerPlugin(RedirectToLowercasePathPlugin()) 42 | ``` 43 | 44 | ## Misc new features 45 | 46 | * Added a `basicAuthCredentialsExists()` method to `Context` (thanks to [@mikexliu](https://github.com/mikexliu)). 47 | * Added option to specify host (`start(port, host)` overload) to `Javalin` (thanks to [@rbygrave](https://github.com/rbygrave)). 48 | * Added support for dynamic single page handlers (thanks to [@pgross41](https://github.com/pgross41)). 49 | * Added support for `anyOf`/`oneOf` to OpenAPI plugin (thanks to [@sealedtx](https://github.com/sealedtx)). 50 | * Added a `Path` overload to `JavalinVue#rootDirectory` method 51 | 52 | ## Fixes 53 | * Fixed `Instant` serialization in OpenAPI plugin (thanks to [@TobiasWalle](https://github.com/TobiasWalle)). 54 | * Fixed bug where `endpointHandlerPath()` method would throw if called in after/requestLogger on 404s. 55 | * Fixed bug in `config.enforceSsl` (now takes load balancers into account) 56 | * Fixed bug in content decoding, will now try specified charset instead of always UTF-8 (thanks to [@thibaultmeyer](https://github.com/thibaultmeyer)). 57 | -------------------------------------------------------------------------------- /_posts/news/pre-4.0/2020-06-07-javalin-3.9.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2020-06-07 5 | version: 3.9.0 6 | title: Javalin goes multi-module! 7 | summary: Multi-module, new Micrometer plugin, static file path-prefixes and pre-compression, Validator improvements. 8 | --- 9 | 10 | ## Multi module 11 | Thanks to [chsfleury](https://github.com/chsfleury), Javalin is now a multi-module project. The available modules are 12 | 13 | * `javalin` - the standard Javalin dependency, just as before 14 | * `javalin-bundle` - `javalin`, `javalin-openapi`, `jackson` and `logback` 15 | * `javalin-openapi` - the OpenAPI plugin and all required dependencies 16 | * `javalin-graphql` - the new GraphQL plugin (thanks to [7agustibm](https://github.com/7agustibm)) 17 | * `javalin-without-jetty` - `javalin` with all `jetty` dependencies excluded 18 | and Jetty specific methods removed (useful for running on e.g. Tomcat) 19 | 20 | The modules themselves can be considered stable, but which dependencies they pull in might change. 21 | 22 | ## New Micrometer plugin 23 | The [Micrometer](https://micrometer.io/) team wrote a new implementation for the Micrometer plugin, 24 | which according to them is on par with their Spring Boot integration. The previous implementation was very minimal. 25 | 26 | ## Static file improvements 27 | * You can now pass a path-prefix to static files (`config.addStaticFiles("/hosted-path", "/directory")`) 28 | * You can now pre-compress static files by setting `config.precompressStaticFiles = true`. 29 | This will store your compressible static files as highly compressed bytearrays (in memory), 30 | and will ensure that content-length is always set (thanks to [vn7n24fzkq](https://github.com/vn7n24fzkq)) 31 | 32 | ## Validator improvements 33 | Thanks to [thibaultmeyer](https://github.com/thibaultmeyer) and [MrThreepwood](https://github.com/MrThreepwood) 34 | the [Validator](https://javalin.io/documentation#validation) API is a lot more flexible. You can now get a list 35 | showing all errors (if there were multiple errors) so you can form a nicer response to the client. 36 | 37 | ## Misc fixes and improvements 38 | * Fixed OpenAPI annotations for fields (thanks to [RalphSteinhagen](https://github.com/RalphSteinhagen)) 39 | * Fixed JSON escaping of `title` during `HttpException` mapping (thanks to [krig](https://github.com/krig)) 40 | * Added an exception for restarting a stopped server (thanks to [ashwinbhaskar](https://github.com/ashwinbhaskar)) 41 | * Made CORS plugin more strict (check full origin, not just startsWith) (thanks to [Jerbell](https://github.com/Jerbell)) 42 | -------------------------------------------------------------------------------- /_posts/news/pre-4.0/2020-06-21-javalin-3.9.1-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2020-06-21 5 | version: 3.9.1 6 | title: A quick bugfix release 7 | summary: Fixes to static files, single-page-handler and query/form params in main javalin module. 8 | Also minor fixes for javalin-openapi, javalin-bundle, javalin-graphql, and javalin-without-jetty. 9 | --- 10 | 11 | ## A few fixes 12 | 13 | * Added Jetty HTTP2 modules to `javalin-bundle` 14 | (thanks to [RalphSteinhagen](https://github.com/RalphSteinhagen)) 15 | * Added Context to GraphQL plugin 16 | (thanks to [7agustibm](https://github.com/7agustibm)) 17 | * Fix a bug related to old IE in single-page-handler 18 | (thanks to [ligasgr](https://github.com/ligasgr)) 19 | * Welcome files will now be served if trailing slash is omitted `/mydir` -> `/mydir/index.html` 20 | (thanks to [AlexGustafsson](https://github.com/AlexGustafsson)) 21 | * Fixed verbose logging in `javalin-openapi` 22 | (thanks to [RalphSteinhagen](https://github.com/RalphSteinhagen)) 23 | * Fixed missing decoding of query/form param *keys* 24 | (thanks to [grzegorzbor](https://github.com/grzegorzbor)) 25 | * Fixed `NoSuchMethodError` bug in `javalin-without-jetty` 26 | -------------------------------------------------------------------------------- /_posts/news/pre-4.0/2020-08-27-javalin-3.10.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2020-08-27 5 | version: 3.10.0 6 | title: A brand new template engine (and some fixes) 7 | summary: Added a new template engine with compile time type-safety and IntelliJ plugin. 8 | Also fixed minor issues with CORS plugin, and added a new feature to JavalinVue. 9 | --- 10 | 11 | ## JTE integration 12 | [Jte](https://jte.gg/) is a new JVM template engine with compile time checking and an auto-complete plugin for IntelliJ. 13 | The author [casid](https://github.com/casid) has also written a [tutorial](/tutorials/jte) on it. It's worth checking out if you're doing 14 | server side templates. 15 | 16 | ## Misc fixes 17 | * Fixed a bug in CORS plugin (`app.options("*")` has been replaced by a `before`) 18 | * Exposed the default `ObjectMapper` from `JavalinJackson` 19 | * Added the option to set local state for `VueComponent` instances 20 | 21 | -------------------------------------------------------------------------------- /_posts/news/pre-4.0/2020-09-02-javalin-3.10.1-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2020-09-02 5 | version: 3.10.1 6 | title: Vue plugin security patch 7 | summary: Fixed an issue in the JavalinVue plugin which made it possible to inject XSS via query parameters 8 | --- 9 | 10 | ## JavalinVue security patch 11 | 12 | The JavalinVue plugin includes query-parameters in the DOM, which made it possible to do XSS 13 | injection. If you are using JavalinVue you should update to 3.10.1 as soon as possible. 14 | 15 | If you are not using JavalinVue, then you can skip upgrading to 3.10.1, as there are no other fixes. 16 | -------------------------------------------------------------------------------- /_posts/news/pre-4.0/2020-10-04-javalin-3.11.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2020-10-04 5 | version: 3.11.0 6 | title: Don't ignore trailing slashes, static file fixes and JavalinVue improvements. 7 | summary: Added an option to not ignore trailing slashes, fixed a concurrency bug in static file handling, and added new features to JavalinVue. 8 | --- 9 | 10 | ## Don't ignore trailing slashes 11 | You can now instruct Javalin to not ignore trailing slashes by doing `config.ignoreTrailingSlashes = false`. 12 | This functionality used to exist in Javalin 2, but it was removed when refactoring the routing for Javalin 3. 13 | Thanks to [NPi2Loup](https://github.com/NPi2Loup) for bringing it back. 14 | 15 | ## Static files bugfix 16 | There used to be a bug in pre-compressed static files (disabled by default), which could lead to a concurrency bug. 17 | Thanks to [vn7n24fzkq](https://github.com/vn7n24fzkq) for fixing it. 18 | 19 | ## JavalinVue improvements 20 | * You can now set an `isDevFunction` function (previously this was just determined by checking if the request was on localhost) 21 | * Added three new methods for inlining files in the layout template `@inlineFile`, `@inlineFileDev` and `@inlineFileNotDev` 22 | * Added `JavalinVue.optimizeDependencies` config option. If set to true, JavalinVue will only load the dependencies required for your route component 23 | 24 | ## MultipartUtil configuration 25 | You can now change the function that runs before files are uploaded through `MultipartUtil.preUploadFunction`. 26 | This means you can specify your own `MultipartConfigElement`. 27 | -------------------------------------------------------------------------------- /_posts/news/pre-4.0/2020-11-14-javalin-3.12.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2020-11-14 5 | version: 3.12.0 6 | title: New routing options! Improvements to ApiBuilder, static files, and OpenAPI. 7 | summary: Added support for non sub-path wildcards, pathless crud calls in ApiBuilder, symlinks/aliases for static files, and more! 8 | --- 9 | 10 | ## Non sub-path wildcards 11 | Javalin has always supported wildcard routing with `/path/*`. As of `3.12.0`, there's also support for doing 12 | `/path*` and `/path/:param*`, which will match both `/path`+`/path/*` and `/path/:param`+`/path/:param/*` respectively. 13 | This is particularly useful when you want to run a `before`/`after` handler for a resource (and all sub-paths of the resource). 14 | 15 | ## Pathless calls to `crud()` in `ApiBuilder` 16 | You can now call `crud()` without a path argument, similar to the other methods in the `ApiBuilder`. 17 | This means you can finally use `crud(MyHandler)` inside a `path()` call. 18 | 19 | ## Support for static file symlinks 20 | You can now configure alias checking for resolving symlinks for static files. 21 | This can be done through a `ContextHandler.AliasCheck` field, which can be configure on `config.aliasCheckForStaticFiles`: 22 | 23 | ```java 24 | config.aliasCheckForStaticFiles = ContextHandler.AliasCheck { path, resource -> 25 | /* Your predicate here */ 26 | } 27 | ``` 28 | 29 | To allow all aliases, you can do `config.aliasCheckForStaticFiles = new ContextHandler.ApproveAliases();` 30 | 31 | Thanks to [sealedtx](https://github.com/sealedtx) for implementing this. 32 | 33 | ## Redoc configuration 34 | 35 | [LeoColman](https://github.com/LeoColman) has created a fully typed API for configuring Redoc in the OpenAPI plugin, big thanks! 36 | 37 | ```kotlin 38 | .reDoc(ReDocOptions("/redoc", RedocOptionsObject( 39 | hideDownloadButton = true, 40 | theme = RedocOptionsTheme( 41 | spacingUnit = 10, 42 | isTypographyOptimizeSpeed = true 43 | ) 44 | )) 45 | ``` 46 | 47 | ## Misc fixes 48 | * The OpenAPI plugin now skips `exampleSetFlag` on serialization (thanks to [pawel-piecyk](https://github.com/pawel-piecyk)) 49 | * Fixed issues with enums in OpenAPI plugin (thanks to [sealedtx](https://github.com/sealedtx)) 50 | * Fixed a bug in `ignoreTrailingSlashes` (thanks to [NPi2Loup](https://github.com/NPi2Loup)) 51 | * `Context#body` is now a lazy property (thanks to [ghn1712](https://github.com/ghn1712)) 52 | * The static instance in `ApiBuilder` is now thread-local (thanks to [kwbc](https://github.com/kwbc)) 53 | -------------------------------------------------------------------------------- /_posts/news/pre-4.0/2021-01-19-javalin-3.13.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2021-01-19 5 | version: 3.13.0 6 | title: Plugin improvements and splats! 7 | summary: We've improved the Micrometer, OpenAPI and JavalinVue plugins, and reintroduced Context#splats. 8 | --- 9 | 10 | ## Plugin improvements 11 | * You can now host multiple OpenAPI specs from the same Javalin app (thanks to [sauterl](https://github.com/sauterl)) 12 | * You can now specify a "base" model for template engines (thanks to [TareqK](https://github.com/TareqK)) 13 | * JavalinVue now automatically HTML decodes URL parameters (thanks to [otron](https://github.com/otron)) 14 | * Micrometer now correctly tracks Jetty connections (thanks to [gi-wg2](https://github.com/gi-wg2)) 15 | 16 | ## Splats 17 | After a lot of back and forth, Splats have returned (thanks to [oharaandrew314](https://github.com/oharaandrew314)): 18 | 19 | Handler-paths can include wildcard parameters (splats). These are available via `Context.splat()` 20 | 21 | {% capture java %} 22 | app.get("/hello/*/and/*", ctx -> { 23 | ctx.result("Hello: " + ctx.splat(0) + " and " + ctx.splat(1)); 24 | }); 25 | {% endcapture %} 26 | {% capture kotlin %} 27 | app.get("/hello/*/and/*") { ctx -> 28 | ctx.result("Hello: " + ctx.splat(0) + " and " + ctx.splat(1)) 29 | } 30 | {% endcapture %} 31 | {% include macros/docsSnippet.html java=java kotlin=kotlin %} 32 | -------------------------------------------------------------------------------- /_posts/news/pre-5.0/2021-09-25-javalin-4.0.1-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2021-09-25 5 | version: 4.0.1 6 | title: A couple of bugfixes ! 7 | summary: Small fixes have been added to SSE terminators, async results, validation, and gzip. 8 | --- 9 | 10 | ## Bugfixes 11 | * A bug was introduced in Javalin 4, which made Javalin fail to terminate SSE messages properly. This has been fixed. 12 | * A possible race condition in async handling has been fixed 13 | * The `ValidationException` class is now a `RuntimeException` rather than an `Exception` 14 | * There was a memory leak in GZIP handling which has been plugged 15 | -------------------------------------------------------------------------------- /_posts/news/pre-5.0/2021-10-02-javalin-4.1.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2021-10-02 5 | version: 4.1.0 6 | title: Validator improvements and and latest Jetty 7 | summary: You can now call the Validator manually with an object (not just a string) 8 | --- 9 | 10 | ## Changes 11 | * The option to call `Validator("name", myObject)` has been added 12 | * Jetty has been bumped to the latest version 13 | -------------------------------------------------------------------------------- /_posts/news/pre-5.0/2021-10-10-javalin-4.1.1-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2021-10-10 5 | version: 4.1.1 6 | title: Plugged memory leak in GZIP handling 7 | summary: There was an issue with method naming which resulted in a memory leak 8 | --- 9 | 10 | ## Plugged memory leak in GZIP handling 11 | There was an issue with method naming which resulted in a memory leak. This has now been fixed. 12 | -------------------------------------------------------------------------------- /_posts/news/pre-5.0/2022-01-02-javalin-4.2.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | hidewhatsjavalin: true 4 | category: news 5 | date: 2022-01-02 6 | version: 4.2.0 7 | title: New year, new Javalin 🎉 8 | summary: We're starting 2022 with a couple of new features 9 | (new Context resolvers, new lifecycle events, log improvements, SseClient comments) and some minor fixes (openapi/json/loom/++). 10 | --- 11 | 12 |
Happy 2022 🎉
13 | 14 | ### New features 15 | * Added option to mute Javalin startup messages 16 | * Added `SERVER_STOP_FAILED` lifecycle event 17 | * Added context resolvers for `url`, `scheme`, and `fullUrl` 18 | * Added option to send comments using `SseClient` 19 | * The Java time module is now included when using `javalin-bundle` 20 | 21 | ### Fixes 22 | * Improved Javadoc in OpenAPI module 23 | * Improved dev-logging to log handler-added events 24 | * Updated loom implementation to work with latest JDK preview 25 | * Ensured UTF-8 is always used when encoding JSON 26 | * Fixed bug to allow literal colons in paths (ex: "/path:colon") 27 | * Changed default threadpool threadnames from `qtp` to `JettyServerThreadPool` 28 | 29 | 36 | -------------------------------------------------------------------------------- /_posts/news/pre-5.0/2022-01-13-javalin-4.3.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2022-01-13 5 | version: 4.3.0 6 | title: Small async and WebSocket fixes 7 | summary: Exception handling in future callback, and convenience methods for WebSockets 8 | --- 9 | 10 | ## Changes 11 | * Exceptions thrown in `ctx.future()` are now handled by the exception mapper 12 | * You can now call `ctx.closeSession()` on any `WsContext` 13 | -------------------------------------------------------------------------------- /_posts/news/pre-5.0/2022-03-19-javalin-4.4.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2022-03-19 5 | version: 4.4.0 6 | title: Fixes to SSE, multipart forms, seekable stream, and more! 7 | summary: A minor update with fixes, bumps of all major dependencies, and a few new features 8 | --- 9 | 10 | ## New features 11 | * The Javalin `Cookie` class now has a lot more constructors when used from Java 12 | * The `Context#seekableStream` method now supports files larger than 2GB. 13 | 14 | ## Fixes 15 | * The `SseHandler` now sets `"X-Accel-Buffering"` to `no`, preventing buffering 16 | * All Java context resolvers are now JVM fields 17 | * File descriptors are now closed in `JavalinVue#walkPaths` 18 | * Trying to access a session attribute no longer creates a new session 19 | * JPG is now an alias to JPEG for mime types 20 | * Uploads will now be cached to disk from the first byte 21 | 22 | ## Bumps 23 | * Kotlin, Jetty, Jackson, and SLF4J have all been bumped to their latest versions 24 | -------------------------------------------------------------------------------- /_posts/news/pre-5.0/2022-05-10-javalin-4.6.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2022-05-10 5 | version: 4.6.0 6 | title: Fixing SSE and adding some small features 7 | summary: SSE (broken by 4.5.0) have now been unbroken, and the validator has some new features! 8 | --- 9 | 10 | ## Changes 11 | * Server-sent events were broken by 4.5.0, but they should be working like before now 12 | * An option to programmatically close the SSE session has been added 13 | * You can now collect errors from `NullableValidator` 14 | * You can now call `getOrThrow` on all `Validator`s 15 | * The `TestUtil` class in the `javalin-testtools` module has been deprecated in favor of `JavalinTest` 16 | -------------------------------------------------------------------------------- /_posts/news/pre-5.0/2022-06-05-javalin-4.6.1-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2022-06-05 5 | version: 4.6.1 6 | title: More SSE fixes, as well as some JavalinVue improvements 7 | summary: The closing of SSE connections is now more consistent, and reloading data in JavalinVue is easier 8 | --- 9 | 10 | ## Changes 11 | * The closing of SSE connections is now more consistent. 12 | The close-callback is now called every time the connection is closed, 13 | including when it is closed manually by the user. 14 | * The `LoadableData` JavaScript class in the `JavalinVue` plugin 15 | now has more options for reloading itself 16 | * Bump Jackson (optional), Logback (bundle) and GSON (test) to latest versions 17 | * Jetty `EofException` and `TimeoutException` are no longer logged for async handlers 18 | * A new plugin, `HttpAllowedMethodsOnRoutesUtil` has been added 19 | -------------------------------------------------------------------------------- /_posts/news/pre-5.0/2022-06-16-javalin-4.6.X-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2022-06-16 5 | version: 4.6.X 6 | title: Last feature release of Javalin 4x! 7 | summary: This is last feature release of 4x. Bugs in 4x will be fixed, but new features will be added in Javalin 5! 8 | --- 9 | 10 | This is last feature release of 4x. Bugs in 4x will be fixed, but new features will be added in Javalin 5! 11 | 12 | ## Changes (4.6.4) 13 | * Bumped Jetty to the latest version. This is most likely the last Jetty 9.X version. 14 | 15 | ## Changes (4.6.3) 16 | * Fixed issue with OSGi bundle release 17 | 18 | ## Changes (4.6.2) 19 | * Added a `sendPing` method to `WsContext` 20 | * Made `JavalinTest` print logs on assertion errors 21 | * Fix issue with manual call to `HttpServletRequest#startAsync` 22 | -------------------------------------------------------------------------------- /_posts/news/pre-6.0/2022-10-16-javalin-5.1.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2022-10-16 5 | version: 5.1.0 6 | title: Javalin 5.1.0 has been released 7 | summary: Async, static-files, http-status and slf4j fixes! 8 | --- 9 | 10 | ## Cleaning up some async problems 11 | The `Context#async` signature released in 5.0.0 used the Kotlin `Result` type, which does not work from Java. 12 | We caught this a bit late, and had to rework the method. Our apologies! 13 | 14 | ## Static files after server start 15 | We've made it possible to add more static file location after the server has been started. 16 | 17 | ## Fixing HttpStatus.forStatus 18 | The `HttpStatus.forStatus(int)` method was not directly available from Java 19 | (you had to do `HttpStatus.companion.forStatus(int)`). This has been fixed. 20 | 21 | ## SLF4J v2 22 | Jetty moved to SLF4J v2 in v11, which caused dependency conflicts in some cases. 23 | We've also moved Javalin SLF4J to v2 now. 24 | 25 | -------------------------------------------------------------------------------- /_posts/news/pre-6.0/2022-11-20-javalin-5.2.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | category: news 4 | date: 2022-11-20 5 | version: 5.2.0 6 | title: Javalin 5.2.0 has been released 7 | summary: Micrometer and SSE fixes 8 | --- 9 | 10 | ## Micrometer is back (baby)! 11 | 12 | The Micrometer team has managed to get Micrometer ready for Jetty 11, 13 | which allows us to bring back the plugin. This is distributed in the `javalin-micrometer` artifact. 14 | 15 | The plugin can be enabled like this: 16 | 17 | {% capture java %} 18 | Javalin.create(config -> { 19 | config.plugins.register( 20 | MicrometerPlugin.create(metrics -> { 21 | metrics.registry = meterRegistry; 22 | metrics.tags = Tags.empty(); 23 | metrics.tagExceptionName = true; 24 | metrics.tagRedirectPaths = tagRedirectPaths; 25 | metrics.tagNotFoundMappedPaths = tagNotFoundMappedPaths; 26 | }) 27 | ); 28 | }); 29 | {% endcapture %} 30 | {% capture kotlin %} 31 | Javalin.create { config -> 32 | config.plugins.register( 33 | MicrometerPlugin.create { metrics -> 34 | metrics.registry = meterRegistry 35 | metrics.tags = Tags.empty() 36 | metrics.tagExceptionName = true 37 | metrics.tagRedirectPaths = tagRedirectPaths 38 | metrics.tagNotFoundMappedPaths = tagNotFoundMappedPaths 39 | } 40 | ) 41 | } 42 | {% endcapture %} 43 | {% include macros/docsSnippet.html java=java kotlin=kotlin %} 44 | 45 | ## SseClient features and fixes 46 | We've added a `terminated()` function to the `SseClient`. This function will return true 47 | if the connection has been terminated, either by the user calling `SseClient#close()`, or by 48 | the remote client disconnecting. 49 | 50 | We've also fixed a bug where `SseClient#close` was called every time `SseClient#send` 51 | was called on a closed connection. 52 | 53 | ## Other changes since 5.1.0 54 | 55 | * Fix welcome files for static files when pre-compression is enabled 56 | * Support order in `submitTask` API (request lifecycle) 57 | * Support "prefer405over404" option in `CorsPlugin` 58 | * Use `127.0.0.1` instead of `localhost` in `testtools` 59 | * Use latest version of logback 60 | * Include brotli in `javalin-bundle` 61 | * Expose fields and methods from `ConcurrencyUtil` 62 | * Support plugins initialization in `Javalin#updateConfig` 63 | * Move "Created but not started" to `JavalinLogger#startup` 64 | * Fix issue with double compression when pre-compression enabled 65 | -------------------------------------------------------------------------------- /_posts/news/pre-6.0/2023-03-04-javalin-5.4.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | hidewhatsjavalin: false 4 | category: news 5 | date: 2023-03-04 6 | version: 5.4.0 7 | title: Javalin 5.4.0 is out! 8 | summary: Mimetypes for static files, Jetty and Jackson updates, SSL port support, and various fixes and improvements. 9 | --- 10 | 11 | ## Javalin 5.4.0 is here! 12 | Notable additions include custom mimetypes for static files, support for consuming `Stream<*>` in `JsonMapper`, 13 | custom SSL ports in the SSL Redirect plugin, and implementation of all `HttpResponseException` classes. 14 | We've also fixed some bugs with routing and request writing, and updated core dependencies. 15 | 16 | ## Added 17 | - [staticfiles] Add mimeTypes option to staticFiles and switch to text/javascript ([#1824](https://github.com/tipsy/javalin/pull/1824)) 18 | - [async] Disable default timeout system for all async requests ([#1823](https://github.com/tipsy/javalin/pull/1823)) 19 | - [jsonmapper] Add function for consuming Stream<*>, and Jackson implementation ([f75d72b](https://github.com/tipsy/javalin/commit/f75d72b1b85db6e63673da458a80ed6fb20812f2)) 20 | - [ssl-redirect-plugin] Support custom SSL port & add tests ([#1835](https://github.com/tipsy/javalin/pull/1835)) 21 | - [default-responses] Implement all HttpResponseException classes (Fix #1811) ([#8314ce7](https://github.com/tipsy/javalin/commit/8314ce7574cbd7aa6fc422b609f8d2706d42edb8)) 22 | - [json] Implement writeToOutputStream for JavalinGson ([#1815](https://github.com/tipsy/javalin/pull/1815)) 23 | 24 | ## Fixed 25 | - [routing] Add special consideration for root path ([#1808](https://github.com/tipsy/javalin/pull/1808)) 26 | - [servlet] Replace invalid `isReady` call with `isInitialized()` (Fix #1752) ([#1831](https://github.com/tipsy/javalin/pull/1831)) 27 | - [tests] Update Unix socket test for Jetty 11 ([#1833](https://github.com/tipsy/javalin/pull/1833)) 28 | - [deps] Fix dependency visibility in parent pom ([#1827](https://github.com/tipsy/javalin/pull/1827)) 29 | - [tests] Fix RouteOverviewTest by actually iterating over all entries ([#1809](https://github.com/tipsy/javalin/pull/1809)) 30 | 31 | ## Miscellaneous 32 | - [deps] Bump Jetty and Jackson ([54e142a](https://github.com/tipsy/javalin/commit/54e142ad613912d4bab6fdc50b5b5a6c26c6d8c8)) 33 | - [pom] Remove old oss parent ([7b3c0e0](https://github.com/tipsy/javalin/commit/7b3c0e0006b4939245086aedd6dd9ed60d8f4d26)) 34 | -------------------------------------------------------------------------------- /_posts/news/pre-6.0/2023-05-01-javalin-5.5.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | hidewhatsjavalin: false 4 | category: news 5 | date: 2023-05-01 6 | version: 5.5.0 7 | title: Javalin 5.5.0 has been released! 8 | summary: Multi-instance plugins, Error handlers, dependency updates, and some fixes and improvements. 9 | --- 10 | 11 | ## Javalin 5.5.0 has been released! 12 | 13 | This release adds support for multi-instance plugins, and a private config option to handle `java.lang.Error` in handlers. 14 | Some fixes include a fix for incorrect logging for missing access manager, eof/timeout handling in main exception handler path, and a fix for the Java api and naming of the Micrometer plugin. 15 | We've also moved to the latest version of Kotlin, Jetty, Jackson, slf4j, okhttp and logback. 16 | 17 | ## Added 18 | 19 | - [plugins] Support multi-instance plugins with RepeatablePlugin interface ([38ab828](https://github.com/javalin/javalin/commit/38ab82867bfa8e88e79a2c25937faf4d7ca25580)) 20 | - [config] Add a private config option to handle `java.lang.Error` in handlers ([#1866](https://github.com/javalin/javalin/pull/1866)) 21 | 22 | ## Fixed 23 | 24 | - [content-type] Change deprecation level of APPLICATION_JS to WARNING ([#1838](https://github.com/javalin/javalin/pull/1838)) 25 | - [accessmanager] Fix incorrect logging for missing access manager ([#1842](https://github.com/javalin/javalin/pull/1842)) 26 | - [exceptions] Add eof/timeout handling in main exception handler path ([#1845](https://github.com/javalin/javalin/pull/1845)) 27 | - [tests] Fix Vue & Selenium tests ([#1870](https://github.com/javalin/javalin/pull/1870)) 28 | - [micrometer] Fix Java api and naming ([c77e580](https://github.com/javalin/javalin/commit/c77e5802b4d296fe65f76610ca025edca897b306)) 29 | 30 | ## Miscellaneous 31 | 32 | - [deps] Move to latest version of Kotlin, Jetty, Jackson, slf4j, okhttp and logback ([99e94f9](https://github.com/javalin/javalin/commit/99e94f945eefdb7f01856cb97894b8b22ba314eb)) 33 | - [pom] Move test dependencies from parent to submodule ([#1843](https://github.com/javalin/javalin/pull/1843)) 34 | - [staticfiles] Remove mimetype workaround for text/javascript ([#1850](https://github.com/javalin/javalin/pull/1850)) 35 | - [tests] Reorganize JavalinJackson, JavalinGson, and JsonMapper test classes ([#1830](https://github.com/javalin/javalin/pull/1830)) 36 | -------------------------------------------------------------------------------- /_posts/news/pre-6.0/2023-06-10-javalin-5.6.0-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: news 3 | hidewhatsjavalin: false 4 | category: news 5 | date: 2023-06-10 6 | version: 5.6.0 7 | title: Javalin 5.6.0 is now available! 8 | summary: Support for Java 9 modules, improvements to Jetty, static files, compression and routing! 9 | --- 10 | 11 | ## Javalin 5.6.0 has been released! 12 | Javalin now supports Java 9 modules! We've also added various improvements, 13 | including the ability to configure HttpConfiguration for Jetty, and a method to clear 14 | cache of pre-compressed static files. 15 | 16 | ## Added 17 | - [jetty] Add ability to configure HttpConfiguration [#1910](https://github.com/javalin/javalin/pull/1910) 18 | - [static-files] Add method to clear cache of pre-compressed files [#1903](https://github.com/javalin/javalin/pull/1903) 19 | - [meta] Support Java 9 modules 20 | - [websocket] Add missing headerAsClass to WsContext [#1878](https://github.com/javalin/javalin/pull/1878) 21 | - [corsplugin] Add maxAge parameter to config [#1880](https://github.com/javalin/javalin/pull/1880) 22 | - [content-types] Support AVIF [#1879](https://github.com/javalin/javalin/pull/1879) 23 | - [routing] Add case insensitive matching [#1892](https://github.com/javalin/javalin/pull/1892) 24 | 25 | ## Fixed 26 | - [javalinvue] Fix filesystem scanning issue [#1905](https://github.com/javalin/javalin/pull/1905) 27 | - [gson] Fix JavalinGson#toJsonStream [#1885](https://github.com/javalin/javalin/pull/1885) 28 | 29 | ## Improved 30 | - [compression] Add brotli4j and make it default in javalin-bundle [#1904](https://github.com/javalin/javalin/pull/1904) 31 | - [compression] Introduce Compressor interface [#1897](https://github.com/javalin/javalin/pull/1897) 32 | - [websocket] Add method level docs to WsContext [#1877](https://github.com/javalin/javalin/pull/1877) 33 | 34 | ## Test Improvement 35 | - [tests] Improve tests using selenium/webdriver [#1886](https://github.com/javalin/javalin/pull/1886) 36 | - [tests] Improve custom jetty filter test 37 | -------------------------------------------------------------------------------- /_posts/tutorials/2017-05-24-javalin-gradle-example.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: tutorial 3 | official: true 4 | title: "Setting up Javalin with Gradle" 5 | author: David Åse 6 | date: 2017-05-24 7 | permalink: /tutorials/gradle-setup 8 | summarytitle: Gradle setup 9 | summary: Set up a Javalin project using Gradle in IntelliJ IDEA 10 | language: kotlin 11 | --- 12 | 13 | When you're done with this tutorial, your `build.gradle` file 14 | should look like this: 15 | 16 | ~~~java 17 | group 'io.javalin' // your group id 18 | version '1.0-SNAPSHOT' 19 | 20 | apply plugin: 'java' 21 | 22 | sourceCompatibility = 11 23 | 24 | repositories { 25 | mavenCentral() 26 | } 27 | 28 | dependencies { 29 | implementation 'io.javalin:javalin:{{site.javalinversion}}' 30 | } 31 | ~~~ 32 | 33 |

Step by step instructions

34 | 35 | * `File` `->` `New` `->` `Project` 36 | * Select `Gradle` then `Java` (or `Kotlin`), click `Next` 37 | * Enter `groupId` and `artifactId`, click `Next` 38 | * Check `Use auto-import`, click `Next` 39 | 40 | Open the newly generated `build.gradle` file and add the gradle-dependency \\ 41 | `implementation 'io.javalin:javalin:{{site.javalinversion}}'` to the `dependencies {}` scope. 42 | See the full `build.gradle` example above if you're not sure where to put it. 43 | 44 | Finally, create a file `src/main/java/HelloWorld.java` or `src/main/kotlin/HelloWorld.kt`\\ 45 | and paste the Hello World example: 46 | 47 | {% include macros/gettingStarted.md %} 48 | 49 | Now everything is ready for you to start programming. Enjoy! 50 | -------------------------------------------------------------------------------- /_posts/tutorials/2017-05-27-javalin-vuejs-example.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: tutorial 3 | official: true 4 | title: "TODO MVC with Vue.js and Kotlin" 5 | author: David Åse 6 | date: 2017-05-27 7 | permalink: /tutorials/kotlin-vuejs-example 8 | github: https://github.com/javalin/javalin-samples/tree/main/javalin5/javalin-vuejs-example 9 | summarytitle: Single-page app with Kotlin and Vue.js 10 | summary: Use Vue.js and Kotlin to create the famous TODO MVC app 11 | language: kotlin 12 | --- 13 | 14 | ## This is a very short tutorial 15 | 16 | If you need to learn how to setup Kotlin with Maven, please 17 | follow the beginning of our [Kotlin CRUD REST API tutorial](/tutorials/simple-kotlin-example) 18 | 19 | ## Dependencies 20 | ~~~markup 21 | 22 | io.javalin 23 | javalin-bundle 24 | {{site.javalinversion}} 25 | 26 | ~~~ 27 | 28 | ## Our main class 29 | 30 | ~~~kotlin 31 | import io.javalin.http.staticfiles.Location 32 | import io.javalin.http.bodyAsClass 33 | 34 | data class Todo(val id: Long, val title: String, val completed: Boolean) 35 | 36 | fun main() { 37 | 38 | var todos = arrayOf(Todo(123123123, "My very first todo", false)) 39 | 40 | val app = Javalin.create { 41 | it.staticFiles.add("/public", Location.CLASSPATH) 42 | it.router.mount { 43 | it.get("/todos") { ctx -> 44 | ctx.json(todos) 45 | } 46 | it.put("/todos") { ctx -> 47 | todos = ctx.bodyAsClass>() 48 | ctx.status(204) 49 | } 50 | } 51 | }.start(7070) 52 | 53 | } 54 | ~~~ 55 | 56 | We're use Javalin to serve our static files, as well as 57 | handle two endpoints: `get` and `put`. 58 | 59 | Most of the work here is being done by `ctx.json` and `ctx.bodyAsClass`, 60 | which map a Todo data-class: 61 | 62 | ~~~kotlin 63 | data class Todo(val id: Long = -1, val title: String = "", val completed: Boolean = false) 64 | ~~~ 65 | 66 | That's it. The rest of the logic is in `index.html` (vue template) 67 | and `todomvc.js` (vue logic). \\ 68 | This is not a JavaScript tutorial, so please have a look at those files for yourself. 69 | -------------------------------------------------------------------------------- /_posts/tutorials/2024-01-13-omeglin/snippets/main/java/io/javalin/omeglin/OmeglinMain.java: -------------------------------------------------------------------------------- 1 | package io.javalin.omeglin; 2 | 3 | import io.javalin.Javalin; 4 | import io.javalin.http.staticfiles.Location; 5 | 6 | public class OmeglinMain { 7 | public static void main(String[] args) { 8 | Javalin.create(config -> { 9 | config.staticFiles.add("src/main/resources/public", Location.EXTERNAL); 10 | config.router.mount(router -> { 11 | router.ws("/api/matchmaking", Matchmaking::websocket); 12 | }); 13 | }).start(7070); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /_posts/tutorials/2024-01-13-omeglin/snippets/main/kotlin/io/javalin/omeglin/Matchmaker.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.omeglin 2 | 3 | import io.javalin.websocket.WsConfig 4 | import io.javalin.websocket.WsContext 5 | import org.slf4j.LoggerFactory 6 | import java.util.concurrent.ConcurrentLinkedQueue 7 | 8 | data class Message(val name: String, val data: String? = null) 9 | 10 | data class Exchange(var a: WsContext?, var b: WsContext?, var doneCount: Int = 0) { 11 | fun otherUser(user: WsContext) = if (user == a) b else a 12 | } 13 | 14 | object Matchmaker { 15 | 16 | private val logger = LoggerFactory.getLogger(Matchmaker::class.java) 17 | private val queue = ConcurrentLinkedQueue() 18 | 19 | fun websocket(ws: WsConfig) { 20 | ws.onConnect { user -> user.enableAutomaticPings() } 21 | ws.onClose { user -> pairingAbort(user) } 22 | ws.onMessage { user -> 23 | logger.info("Received message: ${user.message()}") 24 | val message = user.messageAsClass(Message::class.java) 25 | when (message.name) { 26 | "PAIRING_START" -> pairingStart(user) 27 | "PAIRING_ABORT" -> pairingAbort(user) 28 | "PAIRING_DONE" -> pairingDone(user) 29 | "SDP_OFFER", "SDP_ANSWER", "SDP_ICE_CANDIDATE" -> { // should only happen when two users are paired 30 | val exchange = queue.find { it.a == user || it.b == user } 31 | if (exchange?.a == null || exchange.b == null) { 32 | logger.warn("Received SDP message from unpaired user") 33 | return@onMessage 34 | } 35 | exchange.otherUser(user)?.send(message) // forward message to other user 36 | } 37 | } 38 | } 39 | } 40 | 41 | private fun pairingStart(user: WsContext) { 42 | queue.removeAll { it.a == user || it.b == user } // prevent double queueing 43 | val waitingUser = queue.find { it.b == null }?.let { exchange -> 44 | exchange.b = user 45 | exchange.a?.send(Message("PARTNER_FOUND", "GO_FIRST")) 46 | exchange.b?.send(Message("PARTNER_FOUND")) 47 | } 48 | if (waitingUser == null) { 49 | queue.add(Exchange(a = user, b = null)) 50 | } 51 | } 52 | 53 | private fun pairingAbort(user: WsContext) { 54 | queue.find { it.a == user || it.b == user }?.let { ex -> 55 | ex.otherUser(user)?.send(Message("PARTNER_LEFT")) 56 | queue.remove(ex) 57 | } 58 | } 59 | 60 | private fun pairingDone(user: WsContext) { 61 | queue.find { it.a == user || it.b == user }?.let { it.doneCount++ } 62 | queue.removeAll { it.doneCount == 2 } // remove exchanges where both users are done 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /_posts/tutorials/2024-01-13-omeglin/snippets/main/kotlin/io/javalin/omeglin/OmeglinMain.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.omeglin 2 | 3 | import io.javalin.Javalin 4 | import io.javalin.http.staticfiles.Location 5 | 6 | fun main() { 7 | Javalin.create { 8 | it.staticFiles.add("src/main/resources/public", Location.EXTERNAL) 9 | it.router.mount{ 10 | it.ws("/api/matchmaking", Matchmaker::websocket) 11 | } 12 | }.start(7070) 13 | } 14 | -------------------------------------------------------------------------------- /_posts/tutorials/2024-01-13-omeglin/snippets/main/resources/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Omeglin - Talk to strangers 7 | 8 | 9 | 10 | 11 |
12 | 13 |
14 |
15 |
16 |
17 | 18 | 19 |
20 |
21 | 22 |
23 |
24 |
25 |
26 |
27 |
28 | 29 | 30 | 31 |
32 |
33 | 34 | 35 |
36 |
37 |
38 |
39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /_posts/tutorials/2024-01-13-omeglin/snippets/main/resources/public/js/app.js: -------------------------------------------------------------------------------- 1 | import {Chat} from './chat.js'; 2 | import {PeerConnection} from "./peer-connection.js"; 3 | 4 | const peerConnection = new PeerConnection({ 5 | onLocalMedia: stream => document.getElementById("localVideo").srcObject = stream, 6 | onRemoteMedia: stream => document.getElementById("remoteVideo").srcObject = stream, 7 | onChatMessage: message => chat.addRemoteMessage(message), 8 | onStateChange: state => { 9 | document.body.dataset.state = state; 10 | chat.updateUi(state); 11 | } 12 | }); 13 | 14 | let chat = new Chat(peerConnection); 15 | 16 | document.getElementById("startPairing").addEventListener("click", async () => { 17 | peerConnection.setState("CONNECTING"); 18 | peerConnection.sdpExchange.send(JSON.stringify({name: "PAIRING_START"})) 19 | }); 20 | 21 | document.getElementById("abortPairing").addEventListener("click", () => { 22 | peerConnection.sdpExchange.send(JSON.stringify({name: "PAIRING_ABORT"})) 23 | peerConnection.disconnect("LOCAL"); 24 | }) 25 | 26 | document.getElementById("leavePairing").addEventListener("click", () => { 27 | peerConnection.sendBye(); 28 | }); 29 | 30 | window.addEventListener("beforeunload", () => { 31 | if (peerConnection.state === "CONNECTED") { 32 | peerConnection.sendBye(); 33 | } 34 | }); 35 | -------------------------------------------------------------------------------- /_posts/tutorials/2024-01-13-omeglin/snippets/main/resources/public/js/chat.js: -------------------------------------------------------------------------------- 1 | export class Chat { 2 | 3 | #input = document.getElementById("chatInput"); 4 | #sendBtn = document.getElementById("chatSend"); 5 | #log = document.getElementById("chatLog"); 6 | #peerConnection; 7 | 8 | constructor(peerConnection) { 9 | this.#peerConnection = peerConnection; 10 | this.updateUi("NOT_CONNECTED"); 11 | this.#sendBtn.addEventListener("click", () => { 12 | if (this.#peerConnection.dataChannel === null) return console.log("No data channel"); 13 | if (this.#input.value.trim() === "") return this.#input.value = ""; 14 | this.#addToLog("local", this.#input.value); 15 | this.#peerConnection.dataChannel.send(JSON.stringify({chat: this.#input.value})); 16 | this.#input.value = ""; 17 | }); 18 | 19 | this.#input.addEventListener("keyup", event => { 20 | if (event.key !== "Enter") return; 21 | this.#sendBtn.click(); // reuse the click handler 22 | }); 23 | } 24 | 25 | updateUi(state) { 26 | if (["NOT_CONNECTED", "CONNECTING", "CONNECTED"].includes(state)) { 27 | this.#log.innerHTML = ""; 28 | } 29 | if (state === "NOT_CONNECTED") this.#addToLog("server", "Click 'Find Stranger' to connect with a random person!"); 30 | if (state === "CONNECTING") this.#addToLog("server", "Finding a stranger for you to chat with..."); 31 | if (state === "CONNECTED") this.#addToLog("server", "You're talking to a random person. Say hi!"); 32 | if (state === "DISCONNECTED_LOCAL") this.#addToLog("server", "You disconnected"); 33 | if (state === "DISCONNECTED_REMOTE") this.#addToLog("server", "Stranger disconnected"); 34 | } 35 | 36 | addRemoteMessage = (message) => this.#addToLog("remote", message) 37 | 38 | #addToLog(owner, message) { 39 | this.#log.insertAdjacentHTML("beforeend", `
${message}
`); 40 | this.#log.scrollTop = this.#log.scrollHeight; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /_posts/tutorials/community/2018-11-15-javalin-embedded-example.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: tutorial 3 | official: false 4 | title: "Embed Javalin Into Servlet Container" 5 | author: Martin Vyšný 6 | github: https://github.com/tipsy/javalin-tomcat-embed-example 7 | date: 2018-11-15 8 | summarytitle: Embed Javalin Into Servlet Container 9 | summary: Running Javalin Embedded In Tomcat Without Jetty 10 | language: kotlin 11 | --- 12 | 13 | ## What You'll Create 14 | A WAR application which will contain Javalin without Jetty. You can drop 15 | this WAR file into any Servlet 3.0 container. 16 | 17 | ## Getting Started 18 | 19 | The easiest way to get started is to clone the [javalin-tomcat-embed-example](https://github.com/tipsy/javalin-tomcat-embed-example) 20 | example application: 21 | 22 | ```bash 23 | git clone https://github.com/tipsy/javalin-tomcat-embed-example 24 | cd javalin-tomcat-embed-example 25 | ./gradlew clean appRun 26 | ``` 27 | 28 | This will run Gradle Gretty plugin which in turn launches this WAR app in Tomcat. 29 | When the server boots, you can access the REST endpoint simply by typing 30 | this in your terminal, or opening http://localhost:8080/rest : 31 | 32 | ```bash 33 | curl localhost:8080/rest/ 34 | ``` 35 | 36 | ## Looking At The Sources 37 | 38 | The project is using Gradle to do standard stuff: declare the project as WAR and 39 | uses the [Gradle Gretty Plugin](https://github.com/gretty-gradle-plugin/gretty) 40 | to easily launch the WAR app in Tomcat (using the `appRun` task). 41 | 42 | The interesting bit is the `dependencies` stanza which includes Javalin but omits 43 | the Jetty dependency: 44 | 45 | ```kotlin 46 | dependencies { 47 | compile(kotlin("stdlib-jdk8")) 48 | compile("io.javalin:javalin:{{site.javalinThreeVersion}}") { 49 | exclude(mapOf("group" to "org.eclipse.jetty")) 50 | exclude(mapOf("group" to "org.eclipse.jetty.websocket")) 51 | } 52 | compile("org.slf4j:slf4j-simple:1.7.30") 53 | } 54 | ``` 55 | 56 | The servlet itself is very simple: 57 | 58 | ```kotlin 59 | @WebServlet(urlPatterns = ["/rest/*"], name = "MyRestServlet", asyncSupported = false) 60 | class MyRestServlet : HttpServlet() { 61 | val javalin: JavalinServlet = Javalin.createStandalone() 62 | .get("/rest") { ctx -> ctx.result("Hello!") } 63 | .servlet() 64 | 65 | override fun service(req: HttpServletRequest, resp: HttpServletResponse) { 66 | javalin.service(req, resp) 67 | } 68 | } 69 | ``` 70 | 71 | > Note: You must remember to use the `createStandalone()` function, which has been carefully 72 | designed to make Javalin not to depend on Jetty. Using `Javalin.create()` 73 | will make the WAR app fail to start with `java.lang.ClassNotFoundException: org.eclipse.jetty.server.Server`. 74 | 75 | 76 | The Servlet container will automatically auto-discover the servlet (since it's annotated with `@WebServlet`); 77 | any requests to the servlet will be directed straight to Javalin which will then take care 78 | of handling the request properly. Annotate your class with @MultipartConfig in order to populate UploadedFile or UploadedFiles servlet-request getters. 79 | -------------------------------------------------------------------------------- /_sass/_post.scss: -------------------------------------------------------------------------------- 1 | .posts-overview { 2 | margin: 24px 0; 3 | } 4 | 5 | .posts-header, 6 | .posts-footer { 7 | clear: both; 8 | p { 9 | margin: 0; 10 | } 11 | } 12 | 13 | ul.post-list { 14 | list-style: none; 15 | padding: 0; 16 | margin: 0; 17 | h2 { 18 | margin-top: 0; 19 | } 20 | li { 21 | + li { 22 | margin-top: 20px; 23 | } 24 | a { 25 | position: relative; 26 | color: #444; 27 | display: block; 28 | padding: 20px; 29 | background: #fff; 30 | border-radius: 5px; 31 | box-shadow: $card-shadow; 32 | &:hover { 33 | transform: scale(1.01); 34 | } 35 | h2 { 36 | font-size: 22px; 37 | font-weight: 500; 38 | max-width: calc(100% - 120px); 39 | margin: 0; 40 | white-space: nowrap; 41 | overflow: hidden; 42 | text-overflow: ellipsis; 43 | } 44 | p { 45 | margin: 12px 0 0; 46 | } 47 | .date { 48 | font-size: 14px; 49 | color: #666; 50 | position: absolute; 51 | top: 16px; 52 | right: 16px; 53 | } 54 | h3 { 55 | margin-top: 5px; 56 | font-size: 20px; 57 | font-weight: 500; 58 | } 59 | } 60 | p { 61 | font-size: 14px; 62 | } 63 | } 64 | } 65 | 66 | .subtitle { 67 | font-style: italic; 68 | margin-top: 16px; 69 | font-size: 18px; 70 | } 71 | 72 | @media (max-width: 480px) { 73 | ul.post-list { 74 | &.half { 75 | width: 100%; 76 | &:first-of-type, 77 | &:last-of-type { 78 | margin: 0; 79 | } 80 | + .half { 81 | margin-top: 24px; 82 | } 83 | } 84 | li a { 85 | h2 { 86 | font-size: 18px; 87 | .date { 88 | margin-top: 3px; 89 | } 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /_sass/_tutorial.scss: -------------------------------------------------------------------------------- 1 | .tutorials .post-summary { 2 | position: relative; 3 | 4 | .tutorial-languages { 5 | position: absolute; 6 | right: 24px; 7 | top: 24px; 8 | 9 | .tutorial-language { 10 | text-transform: capitalize; 11 | background: #d6f0f9; 12 | border-radius: 5px; 13 | padding: 1px 6px; 14 | display: inline-block; 15 | font-size: 14px; 16 | + .tutorial-language { 17 | margin-left: 4px; 18 | } 19 | } 20 | } 21 | } 22 | 23 | .tutorial { 24 | #timeToRead { 25 | float: right; 26 | } 27 | 28 | .tutorial-header .notification { 29 | font-size: 14px; 30 | margin-bottom: 30px; 31 | 32 | .github-link { 33 | display: block; 34 | width: 100%; 35 | padding-top: 8px; 36 | margin-top: 8px; 37 | border-top: 1px solid $light-border; 38 | } 39 | } 40 | } 41 | 42 | @media (max-width: 600px) { 43 | .tutorial #timeToRead { 44 | white-space: nowrap; 45 | margin-top: 4px; 46 | display: inline-block; 47 | float: none; 48 | } 49 | } 50 | 51 | /* Tutorial overview tabs */ 52 | .tutorial-tabs { 53 | margin-top: 40px; 54 | margin-bottom: 20px; 55 | display: flex; 56 | 57 | .tutorial-tab { 58 | font-size: 24px; 59 | font-family: 'Roboto Slab', arial, sans-serif; 60 | font-weight: 500; 61 | cursor: pointer; 62 | 63 | + .tutorial-tab { 64 | margin-left: 20px; 65 | } 66 | } 67 | } 68 | 69 | .all-tutorials[data-tab=official] { 70 | [data-tutorial-tab=official] { 71 | border-bottom: 3px solid $javalin-brand-primary; 72 | } 73 | 74 | [data-tutorial-content=community] { 75 | display: none; 76 | } 77 | } 78 | 79 | .all-tutorials[data-tab=community] { 80 | [data-tutorial-tab=community] { 81 | border-bottom: 3px solid $javalin-brand-primary; 82 | } 83 | 84 | [data-tutorial-content=official] { 85 | display: none; 86 | } 87 | } 88 | 89 | @media (max-width: 600px) { 90 | .tutorial-tabs .tutorial-tab { 91 | font-size: 18px; 92 | } 93 | } 94 | 95 | @media (max-width: 480px) { 96 | .tutorial-tabs .tutorial-tab { 97 | font-size: 16px; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /css/main.scss: -------------------------------------------------------------------------------- 1 | --- 2 | # Only the main Sass file needs front matter (the dashes are enough) 3 | --- 4 | 5 | @import 'normalize'; 6 | @import 'prism'; 7 | @import 'main'; 8 | @import 'syntaxHighlighting'; 9 | @import 'frame'; 10 | @import 'tutorial'; 11 | @import 'post'; 12 | -------------------------------------------------------------------------------- /gfx-src/javalin-logo.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/gfx-src/javalin-logo.sketch -------------------------------------------------------------------------------- /gfx-src/summary-infographic.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/gfx-src/summary-infographic.sketch -------------------------------------------------------------------------------- /img/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/background.jpg -------------------------------------------------------------------------------- /img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/favicon.png -------------------------------------------------------------------------------- /img/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 11 | 12 | -------------------------------------------------------------------------------- /img/javalin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/javalin.png -------------------------------------------------------------------------------- /img/juke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/juke.png -------------------------------------------------------------------------------- /img/news/javalin6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/news/javalin6.png -------------------------------------------------------------------------------- /img/news/route-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/news/route-overview.png -------------------------------------------------------------------------------- /img/omeglin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/omeglin.png -------------------------------------------------------------------------------- /img/pages/for-educators-discoverability.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/pages/for-educators-discoverability.png -------------------------------------------------------------------------------- /img/pages/pull_req_site_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/pages/pull_req_site_preview.png -------------------------------------------------------------------------------- /img/plugins/routeoverview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/plugins/routeoverview.png -------------------------------------------------------------------------------- /img/posts/apmExample/apm-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/apmExample/apm-architecture.png -------------------------------------------------------------------------------- /img/posts/apmExample/apm-console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/apmExample/apm-console.png -------------------------------------------------------------------------------- /img/posts/apmExample/apm-correlations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/apmExample/apm-correlations.png -------------------------------------------------------------------------------- /img/posts/apmExample/apm-intellij.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/apmExample/apm-intellij.png -------------------------------------------------------------------------------- /img/posts/apmExample/apm-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/apmExample/apm-menu.png -------------------------------------------------------------------------------- /img/posts/apmExample/apm-observability.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/apmExample/apm-observability.png -------------------------------------------------------------------------------- /img/posts/apmExample/apm-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/apmExample/apm-overview.png -------------------------------------------------------------------------------- /img/posts/apmExample/apm-secret-token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/apmExample/apm-secret-token.png -------------------------------------------------------------------------------- /img/posts/javalinSureness/test1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/javalinSureness/test1.PNG -------------------------------------------------------------------------------- /img/posts/javalinSureness/test2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/javalinSureness/test2.PNG -------------------------------------------------------------------------------- /img/posts/javalinSureness/test3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/javalinSureness/test3.PNG -------------------------------------------------------------------------------- /img/posts/javalinSureness/test4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/javalinSureness/test4.PNG -------------------------------------------------------------------------------- /img/posts/javalinSureness/test5.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/javalinSureness/test5.PNG -------------------------------------------------------------------------------- /img/posts/javalinSureness/test6.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/javalinSureness/test6.PNG -------------------------------------------------------------------------------- /img/posts/javalinSureness/test7.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/javalinSureness/test7.PNG -------------------------------------------------------------------------------- /img/posts/javalinSureness/test8.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/javalinSureness/test8.PNG -------------------------------------------------------------------------------- /img/posts/javalinvue/performance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/javalinvue/performance.png -------------------------------------------------------------------------------- /img/posts/jteExample/jte-intellij.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/jteExample/jte-intellij.gif -------------------------------------------------------------------------------- /img/posts/mtlsTutorial/insomnia-conf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/mtlsTutorial/insomnia-conf.png -------------------------------------------------------------------------------- /img/posts/mtlsTutorial/insomnia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/mtlsTutorial/insomnia.png -------------------------------------------------------------------------------- /img/posts/mtlsTutorial/mtls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/mtlsTutorial/mtls.png -------------------------------------------------------------------------------- /img/posts/openapi/get-users-endpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/openapi/get-users-endpoint.png -------------------------------------------------------------------------------- /img/posts/openapi/no-annotations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/openapi/no-annotations.png -------------------------------------------------------------------------------- /img/posts/openapi/one-annotation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/openapi/one-annotation.png -------------------------------------------------------------------------------- /img/posts/prometheusExample/grafana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/prometheusExample/grafana.png -------------------------------------------------------------------------------- /img/posts/prometheusExample/prometheus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/prometheusExample/prometheus.png -------------------------------------------------------------------------------- /img/posts/protobufExample/directory-tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/protobufExample/directory-tree.png -------------------------------------------------------------------------------- /img/posts/protobufExample/json-response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/protobufExample/json-response.png -------------------------------------------------------------------------------- /img/posts/protobufExample/pbuf-response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/protobufExample/pbuf-response.png -------------------------------------------------------------------------------- /img/posts/sslTutorial/reverse-proxy-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/sslTutorial/reverse-proxy-diagram.png -------------------------------------------------------------------------------- /img/posts/sslTutorial/tls-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/sslTutorial/tls-diagram.png -------------------------------------------------------------------------------- /img/posts/sslTutorial/your-connection-is-not-private.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/sslTutorial/your-connection-is-not-private.png -------------------------------------------------------------------------------- /img/posts/websiteExample/packageOverview.fw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/websiteExample/packageOverview.fw.png -------------------------------------------------------------------------------- /img/posts/websiteExample/packageOverview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/posts/websiteExample/packageOverview.png -------------------------------------------------------------------------------- /img/sponsors/feature-upvote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/sponsors/feature-upvote.png -------------------------------------------------------------------------------- /img/used-by/apexar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/apexar.png -------------------------------------------------------------------------------- /img/used-by/briar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/briar.png -------------------------------------------------------------------------------- /img/used-by/c6bank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/c6bank.png -------------------------------------------------------------------------------- /img/used-by/carilion-clinic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/carilion-clinic.png -------------------------------------------------------------------------------- /img/used-by/celer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/celer.png -------------------------------------------------------------------------------- /img/used-by/datawire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/datawire.png -------------------------------------------------------------------------------- /img/used-by/dkb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/dkb.png -------------------------------------------------------------------------------- /img/used-by/essential.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/essential.png -------------------------------------------------------------------------------- /img/used-by/expressscripts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/expressscripts.png -------------------------------------------------------------------------------- /img/used-by/jhu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/jhu.png -------------------------------------------------------------------------------- /img/used-by/measuresforjustice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/measuresforjustice.png -------------------------------------------------------------------------------- /img/used-by/microsoft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/microsoft.png -------------------------------------------------------------------------------- /img/used-by/nav.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/nav.png -------------------------------------------------------------------------------- /img/used-by/nordstrom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/nordstrom.png -------------------------------------------------------------------------------- /img/used-by/ntnu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/ntnu.png -------------------------------------------------------------------------------- /img/used-by/pleo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/pleo.png -------------------------------------------------------------------------------- /img/used-by/redhat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/redhat.png -------------------------------------------------------------------------------- /img/used-by/revolut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/revolut.png -------------------------------------------------------------------------------- /img/used-by/swatt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/swatt.png -------------------------------------------------------------------------------- /img/used-by/talanlabs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/talanlabs.png -------------------------------------------------------------------------------- /img/used-by/telenor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/telenor.png -------------------------------------------------------------------------------- /img/used-by/twosigma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/twosigma.png -------------------------------------------------------------------------------- /img/used-by/uber.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/uber.png -------------------------------------------------------------------------------- /img/used-by/virgilsecurity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/virgilsecurity.png -------------------------------------------------------------------------------- /img/used-by/wgtwo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/wgtwo.png -------------------------------------------------------------------------------- /img/used-by/wit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javalin/website/f8f78640c43dd798eb94cecea6a9dc291e45d88c/img/used-by/wit.png -------------------------------------------------------------------------------- /js/_partials/code.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | // trim blank lines around code 3 | let codeBlocks = document.getElementsByTagName("code"); 4 | for (let i = 0; i < codeBlocks.length; i++) { 5 | codeBlocks[i].innerHTML = codeBlocks[i].innerHTML.trim(); 6 | } 7 | 8 | // multi-tab code 9 | document.addEventListener("click", function (e) { 10 | const targetTab = e.target.getAttribute("data-tab"); 11 | if (targetTab == null) { 12 | return; 13 | } 14 | const grandParent = e.target.parentElement.parentElement; 15 | if (grandParent == null || !grandParent.classList.contains("multitab-code")) { 16 | return; 17 | } 18 | grandParent.setAttribute("data-tab", targetTab); 19 | if (!grandParent.classList.contains("dependencies")) { 20 | localStorage.setItem("language", e.target.textContent.toLowerCase()); 21 | document.querySelectorAll(".multitab-code:not(.dependencies)").forEach(multitab => { 22 | multitab.setAttribute("data-tab", targetTab); 23 | }); 24 | } 25 | }); 26 | 27 | // set language for multi-tab code 28 | function setLanguage() { 29 | const queryParams = new URLSearchParams(window.location.search); 30 | const language = (queryParams.get("language") || localStorage.getItem("language") || "").toLowerCase(); 31 | const languages = new Map(); 32 | const codeTabs = document.querySelectorAll(".multitab-code:not(.dependencies) li") || []; 33 | Array.from(codeTabs).forEach(it => { 34 | languages.set(it.textContent.toLowerCase(), it.getAttribute("data-tab")); // language and index 35 | }); 36 | if (languages.get(language) !== undefined) { 37 | codeTabs[0].parentElement.querySelector(`[data-tab='${languages.get(language)}']`).click(); 38 | } 39 | } 40 | 41 | const intervalId = setInterval(() => { 42 | if (document.querySelector(".multitab-code") !== null) { 43 | clearInterval(intervalId); 44 | setLanguage(); 45 | } 46 | }, 10); 47 | })(); 48 | -------------------------------------------------------------------------------- /js/_partials/main.js: -------------------------------------------------------------------------------- 1 | // add target blank to all external links 2 | for (let i = 0; i < document.links.length; i++) { 3 | if (document.links[i].hostname !== window.location.hostname) { 4 | document.links[i].target = "_blank"; 5 | document.links[i].rel = "noopener"; 6 | } 7 | } 8 | 9 | (() => { 10 | let collapsibleLimit = 4; 11 | let subMenus = Array.from(document.querySelectorAll(".right-menu li > ul")).filter(ul => ul.children.length > collapsibleLimit); 12 | subMenus.forEach(ul => ul.style.display = "none"); 13 | let subMenuParents = subMenus.map(ul => ul.parentElement); 14 | 15 | // opening the menu 16 | document.addEventListener("click", e => tryOpenSubMenu(e.target.parentElement)); 17 | document.addEventListener("menu-scroll-enter", e => tryOpenSubMenu(e.detail.nav.parentElement.parentElement.parentElement)); 18 | 19 | function tryOpenSubMenu(element) { 20 | if (!subMenuParents.includes(element) || openingDisabled) return; 21 | if (element.querySelector(".submenu-close") === null) { 22 | element.querySelector("a").insertAdjacentHTML("afterend", `×`); 23 | } 24 | element.querySelector("ul").style.display = "block"; 25 | } 26 | 27 | // we don't want to trigger the open when scrolling "through" as submenu 28 | // by clicking menu elements, so we disable opening on click 29 | let openingDisabled = false; 30 | document.addEventListener("click", () => { 31 | openingDisabled = true; 32 | setTimeout(() => openingDisabled = false, 250); // scroll duration 33 | }); 34 | 35 | // closing the menu 36 | document.addEventListener("click", e => { 37 | if (!e.target.classList.contains("submenu-close")) return; 38 | let submenuLi = e.target.parentElement; 39 | submenuLi.removeChild(submenuLi.querySelector(".submenu-close")); 40 | submenuLi.querySelector("ul").style.display = "none"; 41 | }); 42 | })(); 43 | 44 | (() => { 45 | let navLinks = document.querySelectorAll(".top-nav li a"); 46 | navLinks.forEach(link => { 47 | const href = link.getAttribute("href") 48 | if (window.location.pathname.startsWith(href)) { 49 | link.parentElement.classList.add("active"); 50 | } 51 | }); 52 | })(); 53 | -------------------------------------------------------------------------------- /js/_partials/scrolling.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | let fixedHeaderSelector = '.top-nav-header'; 3 | smoothScroll.init({ // https://github.com/cferdinandi/smooth-scroll 4 | selector: 'a[href^="#"]', // Selector for links (must be a class, ID, data attribute, or element tag) 5 | selectorHeader: fixedHeaderSelector, // Selector for fixed headers [optional] 6 | speed: 250, // Integer. How fast to complete the scroll in milliseconds 7 | easing: 'easeInOutCubic', // Easing pattern to use 8 | offset: 24, // Integer. How far to offset the scrolling anchor location in pixels 9 | callback: function (anchor, toggle) { 10 | // Function to run after scrolling 11 | } 12 | }); 13 | try { 14 | gumshoe.init({ // https://github.com/cferdinandi/gumshoe (scrollspy) 15 | selector: '#spy-nav ul a', // Default link selector 16 | selectorHeader: fixedHeaderSelector, // Fixed header selector 17 | container: window, // The element to spy on scrolling in (must be a valid DOM Node) 18 | offset: 128, // Distance in pixels to offset calculations 19 | activeClass: 'active', // Class to apply to active navigation link and its parent list item 20 | scrollDelay: false, // Wait until scrolling has stopped before updating the navigation 21 | callback: function (nav) { 22 | try { 23 | document.querySelector(".right-menu .active").scrollIntoView({block: "start"}); 24 | window.history.replaceState({}, "", location.pathname + "#" + nav.target.id); 25 | document.dispatchEvent(new CustomEvent("menu-scroll-enter", {detail: nav})); 26 | } catch (e) { /* Doesn't matter */ 27 | } 28 | } 29 | }); 30 | } catch (e) { 31 | console.error("Failed to initialize gumshoe"); 32 | } 33 | })(); 34 | -------------------------------------------------------------------------------- /js/_partials/tutorials.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | // calculate time to read 3 | if (document.getElementById("timeToRead") !== null) { 4 | function getText(a){for(var d="",c=0;c"; 5 | } 6 | 7 | // build tutorial tabs 8 | const allTutorials = document.querySelector(".all-tutorials") 9 | if (!allTutorials) return; 10 | document.querySelectorAll(".tutorial-tab").forEach(tab => { 11 | tab.addEventListener("click", e => { 12 | const tab = e.target.getAttribute("data-tutorial-tab"); 13 | allTutorials.setAttribute("data-tab", tab); // set active tab 14 | history.replaceState({}, '', `${location.pathname}?tab=${tab}`); 15 | }) 16 | }) 17 | allTutorials.setAttribute("data-tab", "official"); // set default tab 18 | 19 | // set active tab from query param 20 | const urlParams = new URLSearchParams(location.search); 21 | const tab = urlParams.get("tab"); 22 | if (tab) { 23 | allTutorials.setAttribute("data-tab", tab); 24 | } 25 | })(); 26 | -------------------------------------------------------------------------------- /js/scripts.js: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | {% include_relative _partials/vendor/anchor.min.js %} 5 | {% include_relative _partials/vendor/clipboard.min.js %} 6 | {% include_relative _partials/vendor/prism.min.js %} 7 | {% include_relative _partials/vendor/smoothscroll.min.js %} 8 | {% include_relative _partials/vendor/gumshoe.min.js %} 9 | {% include_relative _partials/tutorials.js %} 10 | {% include_relative _partials/code.js %} 11 | {% include_relative _partials/scrolling.js %} 12 | {% include_relative _partials/main.js %} 13 | -------------------------------------------------------------------------------- /pages/about.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: About 4 | rightmenu: false 5 | permalink: /about 6 | --- 7 | 8 |

About Javalin

9 | Javalin started as a fork of the Java and Kotlin web framework [Spark](http://sparkjava.com), but quickly 10 | turned into a ground-up rewrite influenced by [koa.js](http://koajs.com/#application). 11 | Both of these web frameworks are inspired by the modern micro web framework 12 | grandfather: [Sinatra](http://www.sinatrarb.com/), so if you're coming from Ruby then 13 | Javalin shouldn't feel *too* unfamiliar. 14 | 15 | ## Philosophy 16 | Like Sinatra, Javalin is not aiming to be a full web framework, but rather 17 | just a lightweight REST API library (or a micro framework, if you must). There is no concept of MVC, 18 | but there is support for template engines, WebSockets, and static file serving for convenience. 19 | This allows you to use Javalin for both creating your RESTful API backend, as well as serving 20 | an `index.html` with static resources (in case you're creating an SPA). This is practical 21 | if you don't want to deploy an apache or nginx server in addition to your Javalin service. 22 | If you wish to use Javalin to create a more traditional website instead of a REST APIs, 23 | there are several template engine wrappers available for a quick and easy setup. 24 | 25 | ### API design 26 | All the methods on the Javalin instance return `this`, making the API fully fluent. 27 | This will let you create a declarative and predictive REST API, 28 | that will be very easy to reason about for new developers joining your project. 29 | 30 | ### Java and Kotlin interoperability 31 | Javalin is both a Kotlin web framework and a Java web framework, meaning the API is 32 | being developed with focus on great interoperability between the two languages. 33 | The library itself is written primarily in Kotlin, but has a few 34 | core classes written in Java to achieve the best interoperability between the two languages. 35 | Javalin is intended as a "foot in the door" to Kotlin development for companies 36 | that already write a lot of Java. 37 | 38 | When moving a Javalin project from Java to Kotlin, you shouldn’t need to learn a new way of doing things. 39 | To maintain this consistent API for both languages is an important goal of the project. 40 | 41 | ## Contact 42 | If you want to get in touch, please create an issue on our [GitHub tracker](https://github.com/javalin/javalin/issues). 43 | -------------------------------------------------------------------------------- /pages/docs/migration-guide-2-3.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Migration guide, v2 to v3 4 | rightmenu: false 5 | permalink: /migration-guide-javalin-2-to-3 6 | --- 7 | 8 |

Javalin 2 to 3 migration guide

9 | 10 | ## Configuration 11 | One of the biggest new things in Javalin 3 is the configuration setup. 12 | Server config has been moved away from `Javalin` and into a `JavalinConfig` class, which is 13 | available inside `Javalin.create()`: 14 | 15 | ```java 16 | Javalin.create(config -> { 17 | config.addStaticFiles(directory) 18 | config.autogenerateEtags = true/false 19 | config.dynamicGzip = true/false 20 | config.enableDevLogging() 21 | config.enforceSsl = true/false 22 | config.requestCacheSize = sizeInBytes 23 | config.sessionHandler { ... } 24 | config.server { ... } 25 | config.accessManager { ... } 26 | }).start() 27 | ``` 28 | 29 | A full list of the config options can be found [in the docs](/documentation#configuration). 30 | 31 | ## WebSockets 32 | WebSockets used to have 5 separate interfaces with different signatures. 33 | To make the WebSocket API more like the HTTP API, we've introduced `WsContext` in Javalin 3. 34 | Each WebSocket event now takes a `WsContext` (ctx), which also has access to the underlying `Context` 35 | which was used to upgrade from HTTP to WebSocket. We've also added `wsBefore`, `wsAfter`, and `wsException`, and made the 36 | `AccessManager` aware of WebSocket upgrade requests. 37 | 38 | You can read more about the new WebSocket API [in the docs](/documentation#websockets). 39 | 40 | ## Events 41 | Events have been changed from Enum to Config object: 42 | 43 | ```java 44 | // BEFORE 45 | Javalin app = Javalin.create() 46 | .event(JavalinEvent.SERVER_STARTING, () -> { ... }) 47 | .event(JavalinEvent.SERVER_STARTED, () -> { ... }) 48 | .event(JavalinEvent.SERVER_START_FAILED, () -> { ... }) 49 | // AFTER 50 | Javalin app = Javalin.create().events(event -> { 51 | event.serverStarting(() -> { ... }); 52 | event.serverStarted(() -> { ... }); 53 | event.serverStartFailed(() -> { ... }); 54 | }); 55 | ``` 56 | 57 | ## Package structure 58 | Javalin 3 has some changes to the package structure. The only class that's still on root (`io.javalin`) is `Javalin`. 59 | Most of what used to be on the root has been moved into `io.javalin.http` (this includes pacakges such as `staticfiles` and `serversentevents`) 60 | Everything related to WebSockets has been moved into `io.javalin.websocket`. 61 | The `io.javalin.security` package has been moved into `io.javalin.core.security`. Plugins have been moved into `io.javalin.plugin`. 62 | -------------------------------------------------------------------------------- /pages/download.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Download 4 | rightmenu: false 5 | permalink: /download 6 | --- 7 | 8 | {% include notificationBanner.html %} 9 | 10 |

A lightweight REST API library

11 | 12 | Javalin is a true microframework with only one required dependency: SLF4J (logging). 13 | By default Javalin also depends on Jetty, but you can exclude 14 | if you want to use a different Webserver (like Tomcat, etc). 15 | Javalin also has plugins for JSON mapping, template rendering, and OpenAPI (Swagger), but they're 16 | optional dependencies that you have to add manually. 17 | See the [plugins](/plugins) page for more information. 18 | 19 | ## Download Javalin 20 | {% include macros/mavenDep.md %} 21 | 22 | ## Javalin modules 23 | 24 | You can see a list of all available modules on [https://search.maven.org/search?q=g:io.javalin](https://search.maven.org/search?q=g:io.javalin) 25 | 26 | ## Manual downloads 27 | You can get prebuilt jars from [Maven Central](https://repo1.maven.org/maven2/io/javalin/javalin/).\\ 28 | You can get the source on [GitHub](https://github.com/javalin/javalin), or [download it as a zip](https://github.com/javalin/javalin/archive/master.zip). 29 | -------------------------------------------------------------------------------- /pages/for-educators.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: For educators 4 | rightmenu: false 5 | permalink: /for-educators 6 | --- 7 | 8 |

For educators

9 | Javalin is well suited for programming courses and for demos/prototypes. This page explains why. 10 | 11 | ## Simple setup and configuration 12 | A lot of universities still use application servers such as Glassfish or Tomcat when teaching Java web development. 13 | Setting up and configuring these servers for each student requires a lot of effort, and that effort 14 | could be spent teaching students about HTTP and programming instead. 15 | 16 | Javalin runs on an embedded Jetty server, and you only need to add the dependency 17 | and write a single line of code to create and start a server. A full "Hello World" app looks like this: 18 | ```java 19 | import io.javalin.Javalin; 20 | 21 | public class HelloWorld { 22 | public static void main(String[] args) { 23 | Javalin app = Javalin.create().start(7000); // create and launch server 24 | app.get("/", ctx -> ctx.result("Hello World")); // add root endpoint 25 | } 26 | } 27 | ``` 28 | 29 | This app can be packaged and launched with `java -jar hello-world.jar`, no further configuration required. 30 | This lets you focus your classes on core principles rather than specifics for setting up an application server. 31 | 32 | ## Small and unopinionated 33 | Javalin is just a couple of thousands lines of code running on top of Jetty. There is very little magic, 34 | making it easy to fully understand the control-flow of your program. 35 | 36 | * No annotations 37 | * No global static state 38 | * No reflection 39 | * No configuration files 40 | * Servlet based 41 | 42 | Javalin doesn't care how you build your app, so any knowledge obtained while working 43 | with a Javalin project should transfer easily to other (non Javalin) projects. 44 | 45 | ## API discoverability 46 | Javalin’s API is built with discoverability in mind. 47 | The server configuration object has a fluent/chainable API, 48 | and the context object has everything needed for handling a HTTP-request. 49 | 50 | This lets new users discover the API with their IDE: 51 | 52 | Discoverability 53 | 54 | ## Good documentation and tutorials 55 | Javalin's documentation is example-based rather than technical, which allows new users to copy snippets and experiment with them. 56 | Javalin also has tutorials for most common tasks that developers have to solve when starting web-programming. 57 | 58 | ## Active development 59 | A new (backwards compatible) version of Javalin has been released every month since the first version. 60 | Pull requests and issues are reviewed swiftly, normally every week. 61 | -------------------------------------------------------------------------------- /pages/news.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: News 4 | rightmenu: false 5 | permalink: /news/ 6 | --- 7 | 8 | {% include notificationBanner.html %} 9 | 10 |

News

11 | Javalin is released frequently (once or twice a month). Please follow us on [Twitter](https://twitter.com/javalin_io) 12 | or [Facebook](https://www.facebook.com/javalin.io) to get notified about new releases. 13 | 14 | {% assign newsposts = site.posts | where: "category" , "news" | sort: 'date' | reverse %} 15 | 16 | 31 | -------------------------------------------------------------------------------- /pages/plugins/devlogging.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: DevLoggingPlugin documentation 4 | rightmenu: false 5 | permalink: /plugins/devlogging 6 | --- 7 | 8 |

DevLoggingPlugin

9 | 10 | When you enable the DevLoggingPlugin, the following output is logged 11 | when the server receives a HTTP request: 12 | 13 | ``` 14 | [JettyServerThreadPool-81] INFO io.javalin.Javalin - JAVALIN REQUEST DEBUG LOG: 15 | Request: GET [/hello-world] 16 | Matching endpoint-handlers: [GET=/hello-world] 17 | Headers: {User-Agent=unirest-java/3.1.00, Accept-Encoding=gzip, ... } 18 | Cookies: {} 19 | Body: 20 | QueryString: null 21 | QueryParams: {} 22 | FormParams: {} 23 | Response: [200 OK], execution took 0.19 ms 24 | Headers: {Date=Fri, 19 Aug 2022 16:28:16 GMT, Content-Type=text/plain} 25 | Body is 12 bytes (starts on next line): 26 | Hello World! 27 | ``` 28 | 29 | When the server receives a WebSocket request, all the relevant events are logged: 30 | 31 | ``` 32 | [JettyServerThreadPool-75] INFO io.javalin.Javalin - JAVALIN WEBSOCKET DEBUG LOG 33 | WebSocket Event: onConnect 34 | Session Id: ca762465-a30d-4f9d-97a3-ce24e9515135 35 | Host: localhost 36 | Matched Path: /path/{param} 37 | PathParams: {param=1} 38 | QueryParams: {test=[banana], hi=[1, 2]} 39 | 40 | [JettyServerThreadPool-80] INFO io.javalin.Javalin - JAVALIN WEBSOCKET DEBUG LOG 41 | WebSocket Event: onClose 42 | Session Id: ca762465-a30d-4f9d-97a3-ce24e9515135 43 | Host: localhost 44 | Matched Path: /path/{param} 45 | PathParams: {param=1} 46 | QueryParams: {test=[banana], hi=[1, 2]} 47 | StatusCode: 1000 48 | Reason: No reason was provided 49 | ``` 50 | 51 | You can enable dev logging through `config`: 52 | 53 | ```java 54 | Javalin.create(config -> { 55 | config.bundledPlugins.enableDevLogging(); 56 | }); 57 | ``` 58 | -------------------------------------------------------------------------------- /pages/plugins/openapi.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: OpenAPI documentation 4 | rightmenu: false 5 | permalink: /plugins/openapi 6 | --- 7 | 8 | This plugin has been moved out of the main repo, 9 | it now lives at [https://github.com/javalin/javalin-openapi](https://github.com/javalin/javalin-openapi) 10 | 11 | 14 | -------------------------------------------------------------------------------- /pages/plugins/routeoverview.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: RouteOverview documentation 4 | rightmenu: false 5 | permalink: /plugins/routeoverview 6 | --- 7 | 8 |

RouteOverview Plugin

9 | 10 | The route-overview shows the verb, the path, the function/field/class handling the request, 11 | and any roles attached to the handler. 12 | If the clients accepts JSON, this is served as JSON. Otherwise it's served as HTML: 13 | 14 | Route overview 15 | 16 | You can enable the route-overview either through the `config.plugins` or by registering it manually: 17 | 18 | {% capture java %} 19 | Javalin.create(config -> { 20 | config.plugins.enableRouteOverview(path); // show all routes on specified path 21 | config.plugins.enableRouteOverview(path, roles); // show all routes on specified path (with auth) 22 | config.plugins.register(new RouteOverviewPlugin(path)); // show all routes on specified path 23 | config.plugins.register(new RouteOverviewPlugin(path, roles)); // show all routes on specified path (with auth) 24 | }); 25 | {% endcapture %} 26 | {% capture kotlin %} 27 | Javalin.create { config -> 28 | config.plugins.enableRouteOverview(path) // show all routes on specified path 29 | config.plugins.enableRouteOverview(path, roles) // show all routes on specified path (with auth) 30 | config.plugins.register(RouteOverviewPlugin(path)) // show all routes on specified path 31 | config.plugins.register(RouteOverviewPlugin(path, roles)) // show all routes on specified path (with auth) 32 | }} 33 | {% endcapture %} 34 | {% include macros/docsSnippet.html java=java kotlin=kotlin %} 35 | -------------------------------------------------------------------------------- /pages/tutorials.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Tutorials 4 | permalink: /tutorials/ 5 | --- 6 | 7 | {% include notificationBanner.html %} 8 | 9 |

Tutorials

10 | 11 | {% assign tutorials = site.posts | where: "layout" , "tutorial" | sort: 'date' | reverse %} 12 | {% assign communityTuts = tutorials | where: "official", false %} 13 | {% assign officialTuts = tutorials | where: "official", true %} 14 | 15 |
16 | Some of the tutorials have code examples in both Kotlin and Java (check the blue tags), 17 | but it should be easy to follow along even if they don't. The official tutorials are simple and 18 | mainly focus on one core concept at the time, while the community tutorials are usually more complex. 19 |
20 | 21 |
22 |
23 |
Official tutorials
24 |
Community tutorials
25 |
26 |
27 |
28 | {% include macros/tutorialPost.html tutorials=officialTuts %} 29 |
30 |
31 |
32 | These community tutorials are written by Javalin users and posted at their request and/or 33 | with their permission. If you have have a tutorial you want to submit, 34 | please create a pull request on GitHub. 35 |
36 | {% include macros/tutorialPost.html tutorials=communityTuts %} 37 |
38 |
39 |
40 | -------------------------------------------------------------------------------- /run_win.bat: -------------------------------------------------------------------------------- 1 | bundle exec jekyll serve --port 4000 --future --incremental 2 | REM --verbose 3 | --------------------------------------------------------------------------------