惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Security Archives - TechRepublic
Security Archives - TechRepublic
罗磊的独立博客
T
The Blog of Author Tim Ferriss
The GitHub Blog
The GitHub Blog
Apple Machine Learning Research
Apple Machine Learning Research
The Register - Security
The Register - Security
J
Java Code Geeks
V2EX - 技术
V2EX - 技术
Vercel News
Vercel News
N
News and Events Feed by Topic
腾讯CDC
P
Proofpoint News Feed
N
News | PayPal Newsroom
www.infosecurity-magazine.com
www.infosecurity-magazine.com
爱范儿
爱范儿
O
OpenAI News
酷 壳 – CoolShell
酷 壳 – CoolShell
月光博客
月光博客
Martin Fowler
Martin Fowler
Engineering at Meta
Engineering at Meta
D
Docker
Y
Y Combinator Blog
博客园 - 聂微东
G
Google Developers Blog
S
Security @ Cisco Blogs
Simon Willison's Weblog
Simon Willison's Weblog
S
Schneier on Security
H
Hackread – Cybersecurity News, Data Breaches, AI and More
S
SegmentFault 最新的问题
云风的 BLOG
云风的 BLOG
阮一峰的网络日志
阮一峰的网络日志
C
CXSECURITY Database RSS Feed - CXSecurity.com
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
C
CERT Recently Published Vulnerability Notes
I
Intezer
G
GRAHAM CLULEY
有赞技术团队
有赞技术团队
Attack and Defense Labs
Attack and Defense Labs
V
Visual Studio Blog
博客园 - Franky
博客园 - 三生石上(FineUI控件)
W
WeLiveSecurity
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Hugging Face - Blog
Hugging Face - Blog
Scott Helme
Scott Helme
T
Troy Hunt's Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
L
LINUX DO - 最新话题
C
Cybersecurity and Infrastructure Security Agency CISA

Show HN

GitHub - steveking-gh/firmion: Firmion is DSL and engine for firmware image generation. GitHub - villagesql/villagesql-skills: Agent skills for VillageSQL - gemini-cli-extension; claude-code-plugin GitHub - flightdeckhq/flightdeck: Observability and control plane for AI agents. CSP Radar GitHub - Light-Heart-Labs/DreamServer: Turn your PC, Mac, or Linux box into an AI server. LLM inference, chat UI, voice, agents, workflows, RAG, and image generation. GitHub - Diplomat-ai/diplomat-agent-ts: What can your TypeScript AI agent do to the real world? Scan your code. See which tool calls have zero checks Code Block Selector - Visual Studio Marketplace Prometheus dependency graph — interactive showcase | Riftmap Show HN: I made a vi-like modal keyboard plugin for Figma GitHub - run-llama/liteparse: A fast, helpful, and open-source document parser GitHub - dalemyers/Roar: A macOS CLI tool for notifications GitHub - district-solutions/open-agent-tools-coder: Enables small-to-large self-hosted ai models to use local source code when running tool-calling agentic workloads. We actively data mine 20,900+ (2+ TB) popular github repos using large and small ai models to create reuseable: json, markdown and parquet files for local-first tool-calling models. GitHub - progapandist/stripeek: A local TUI proxy for real-time Stripe API debugging, built for navigating complex payloads fast. GitHub - sir1st/hermes-desktop: All-in-one cross-platform desktop app for Hermes Agent — bundles Python + hermes-agent + hermes-web-ui GitHub - astefanutti/shaderbang: Shebang for Shaders Show HN: Generate Claude Code Workflows using Spec Driven Development approach GitHub - nixys/nxs-universal-chart: The Helm chart you can use to install any of your applications into Kubernetes/OpenShift Show HN: AI agents for UK GDAD PCF roles and their skills The Two Pillars: Mixer Mode and Meta-Software in the Reorganization of Software Work After AI GitHub - JaiCode08/teleport-env What 1,000+ Harness Experiments Taught Me About Self-Improving Agents Show HN: Liiists, a Markdown-first, iOS and CLI list app SwiperTab – Get this Extension for 🦊 Firefox (en-US) GitHub - kouhxp/fftext: Summarize, explain, fact-check, or translate any text, URL, or file. No GPU. No cloud. One command GitHub - sweetpad-dev/sweetpad: Develop Swift/iOS projects using VSCode GitHub - dogmaticdev/IRON: IRON a.k.a. Intermediate Representation Object Notation is a Interpreter/Database that is used to create Programming Languages. GitHub - sjhalani7/vaen: Package your AI coding harness into a portable .agent file, and share it across repos, teams, & the community without ever having to copy-paste instructions, skills, MCP config, or secrets. Show HN: Gandalf the Grader Show HN: Citadeld – replay any CI failure locally from a single file GitHub - tdortman/cuSBF: High-Performance GPU Super Bloom Filter coral-ai/claude-code-token-xray at main · Coral-Bricks-AI/coral-ai GitHub - ulyssestenn/funes: Funes is a Git-based framework for LLM-managed knowledge work: an AI Librarian ingests raw sources, builds an interlinked Markdown knowledge base, and uses it to produce cited reports, analyses, and other outputs. GitHub - ThatXliner/gah: Git Add Hunk, built for agents to use GitHub - harmont-dev/harmont-cli: Command-line client for the Harmont CI platform GitHub - brooksmcmillin/mcp-authflow: OAuth 2.0 Authorization Server framework for MCP servers GitHub - javaid-codes/audit-supply-chain-agents GitHub - amorey/gochan: A small library of common channel architectures for Go, inspired by Rust GitHub - arifozgun/OpenGem: Free, Open-Source AI API Gateway with Gemini, OpenAI & Anthropic Compatibility in 1 file GitHub - Pranesh950/BioPetals: 🌸 Run BIOxAI models at home, BitTorrent-style. Fine-tuning and inference up to 10x faster than offloading GitHub - cnguyen14/bounty-doctor: Diagnose a GitHub bounty issue before you waste hours: detects honeypot scam repos, AI-bot attempt swarms, and stale contests. Show HN: CoreMCP – MCP Server for On-Prem DBs Show HN: KittyHTML – Render HTML/CSS as an inline image in your terminal GitHub - bingud/filemat: Web-based file manager Show HN: TruthLens – Free multi-signal deepfake image detector GitHub - apexlocal-jz/claude-usage-tray: Windows system-tray app showing your Claude Code rate-limit usage at a glance. Zero deps, ~300 lines of PowerShell. Cross-IDE (works regardless of VS Code, Cursor, plain terminal). Release v0.1.2.1 · kouhxp/yapsnap GitHub - noopolis/moltnet: Self-hostable chat network for AI agents. Pre-built bridges for Claude Code, Codex, and the Claws. Rooms, DMs, history. No Slack bots, no Matrix, no glue code. GitHub - tamerh/enju: Coordinating Humans, AI Agents, and Compute as Peers on a Shared Workflow Graph Show HN: Continuity-auth – Respect-weighted rate limits for the open web GitHub - luml-ai/luml: AI lifecycle platform where engineers and agents track experiments, train models, and ship to production. GitHub - mrdanielcasper/CoreTex: A UNIX-inspired, biomimetic, flat-file AI harness and knowledge engine. GitHub - clemg/pierre-github: Pierre's diffs.com and trees.software for Github GitHub - lyriks-io/unspaghettit: Behavior-driven AI development without prompt spaghetti. GitHub - sofumel/claude-handoff-revive: Resume Claude Code work after rate/usage/context limits without replaying the prior transcript. Auto-saves at 90%/95% usage. Plugin-installable, 10 languages. GitHub - dotexorg/saferpc: Typed, end-to-end encrypted RPC over any bidirectional channel. GitHub - BeeZeeAgent/beezee: Agent harness orchestration Legato Next.js Boilerplate for Internal Tools · CoreUI GitHub - clark-labs-inc/clark-hash: Clark Hash, 32x smaller searchable sketches for embeddings GitHub - ZeroPointRepo/youtube-mcp: The fastest YouTube transcript + YouTube search MCP for AI agents. Try for free. Typing Mastery — climb toward 100+ WPM, deliberately GitHub - Andebugulin/Awareen GitHub - fayzan123/claude-workflow-composer: Visual desktop app for composing multi-agent coding workflows. Drag agents, attach skills and MCPs, wire handoffs, export to .claude/ GitHub - harshaneel/humanize: Best static AI text humanizer. Two research-grounded skills that work in any LLM (Claude, ChatGPT, Gemini, Codex): humanize beats perplexity-based detectors, ai-check produces forensic scoring with evidence-quoted flags. Nine levers, 50+ peer-reviewed sources, 2024-2026 detection literature. GitHub - StackOneHQ/stack-nudge GitHub - nodes-app/swift-markdown-engine: A native AppKit Markdown editor for macOS, built on TextKit 2 and bridged to SwiftUI. We hardened an LLM agent. Each defense we added made it more exploitable. GitHub - alkait/WhatsKept: Agent-queryable WhatsApp history from an iOS backup — a single Go binary. GitHub - octelium/cordium: Open-source, general-purpose sandbox platform for devs and AI agents that provides identity-based secure access to infrastructure without credentials. WAR.GOV/UFO Microfilm5 GitHub - scosman/videowright: Build animated explainer videos with your coding agent GitHub - dipankar/dscode: The code editor you can take apart. GitHub - zoharbabin/web-researcher-mcp: MCP server (Go) for AI assistants: web search, content extraction, academic/patent/news research. Multi-provider routing, 4-tier scraping, search lenses. Works with Claude, Cursor, and any MCP client. GitHub - ruvnet/RuView: π RuView turns commodity WiFi signals into real-time spatial intelligence, vital sign monitoring, and presence detection — all without a single pixel of video. GitHub - scanaislop/aislop: Catch the slop AI coding agents leave in your code: narrative comments, swallowed exceptions, as-any casts, dead code, oversized functions. 50+ rules across 7 languages (TypeScript, JavaScript, Python, Go, Rust, Ruby, PHP). Sub-second, deterministic, no LLM at runtime. MIT-licensed. GitHub - kouhxp/cheap-im: CPU-only voice agent approximating Thinking Machines' Interaction Models demo GitHub - unprovable/OrchidMantis: Orchid Mantis — standalone framework for Zero-Knowledge Proofs of eXploit (ZKPoX). GitHub - MarcellM01/TinySearch: Shrink the web for your local LLMs! GitHub - TangibleResearch/Halgorithem: A Algo designed to detect AI Hallucitions GitHub - DO-SAY-GO/freelang: I love freelang GitHub - CarpseDeam/Aura-IDE: An AI coding harness that shaped itself - Planner/Worker agents, repo awareness, surgical edits, validation, recovery, and safe diff approvals. GitHub - chojs23/concord: A feature-rich TUI client for Discord GitHub - tommyjepsen/awesome-ux-skills: UX & AI Product designs skills you can use today in Claude Code GitHub - aerf-spec/aerf: Agent Evidence Receipt Format (AERF) — an open specification for tamper-evident, independently verifiable records of AI agent actions. GitHub - kklimuk/docx-cli: CLI for AI agents (Claude, Codex) to read, edit, and comment on .docx files with full format fidelity. GitHub - Jwrede/tokentoll: Catch LLM cost changes in code review. Infracost for LLM spend. GitHub - samchon/ttsc: A `typescript-go` toolchain for compiler-powered plugins and type-safe execution + 500x faster lint integrated into compiler GitHub - Higangssh/homebutler: 🏠 Manage your homelab from chat. Single binary, zero dependencies. GitHub - olalie/tapmap: See where your computer connects and what stands out on a live world map. GitHub - Diplomat-ai/diplomat-agent: What can your AI agent do to the real world? Scan your code. See which tool calls have zero checks GitHub - Bajusz15/beacon: Open-source agent for secure remote access, monitoring, and deploys across home-lab and self-hosted machines like Raspberry Pi, N100, or any Linux server. Open web based TTY or tunnel Home Assistant and other local services securely without opening ports. BigTech AI News - Chrome 应用商店 GitHub - vinhnx/VTCode: VT Code is an open-source coding agent with LLM-native code understanding and robust shell safety. Supports multiple LLM providers with automatic failover and efficient context management. GitHub - michaelaz774/decision-engine: A decision operating system for startup founders, powered by Claude Code. Synthesizes wisdom from 25+ legendary founders and investors into interactive AI-driven decision frameworks. GitHub - Chrilleweb/dotenv-diff: Validate environment variable usage in your codebase GitHub - Lumen-Labs/brainapi2: BrainAPI is a knowledge graph–powered AI memory layer that transforms unstructured data into structured knowledge, enabling intelligent search, recommendations, and contextual memory for AI agents and applications. GitHub - familiar-software/familiar: Let AI watch you work. Familiar lets your AI update its memory, skills, and knowledge by watching your screen. GitHub - skorotkiewicz/rudo: A small, elegant dock for Wayland GitHub - muxshed/shed: One stream in, or many. Every destination, simultaneously. No cloud middleman, no per-channel fees, no limits. make sidebar/address bar rounded corner toggleable
GitHub - Petersoj/jet: A simple, lightweight, modern, turnkey, Java web client and server library
Petersoj · 2026-06-16 · via Show HN

Table of Contents

  • Introduction
  • Modules
    • Common
      • Installation
      • Guide
        • Header Models
        • Header Data Structures
        • Enums
        • URL Building
        • I/O Utilities
        • Other Utilities
    • Server
      • Installation
      • Guide
        • JetServer.Builder Configuration
        • Handle
          • Exceptions
        • Useful Handler Implementations
          • Directory
          • Redirect
        • Router Implementations
        • Route Implementations
        • Session & SessionStore Implementations
        • Let's Encrypt SSL Certificates
    • OpenAPI Annotations
      • Installation
      • Guide
    • OpenAPI Annotations Plugin
      • Installation
      • Guide
        • Example Configuration (build.gradle.kts)
        • Why not an annotation processor?
    • Client
  • Personal Note About AI
  • Alternatives

Introduction

Jet is a simple, lightweight, modern, turnkey, Java web client and server library.

Jet is a wrapper around the excellent Jetty web client and server library. Jetty provides the battle-tested low-level protocol handling, while Jet focuses on providing a modern and consistent interface with superb documentation and an amazing developer experience.

Jet offers four modules: Common, Server, OpenAPI Annotations, OpenAPI Annotations Plugin, and Client.

A few more awesome things about Jet:

  • Exhaustive Javadoc documentation (all public classes, fields, and methods have Javadocs)
  • Amazing developer experience
  • High quality modern Java code (no AI slop)
  • No runtime annotation magic
  • A library, not a framework (structure your codebase however you prefer)
  • Lightweight with minimal dependencies
  • Wide code coverage via unit testing
  • Releases are built directly from this source repository using GitHub Actions CI
  • Kotlin friendly
  • MIT licensed

Give this repository a star ⭐ and consider sponsoring ❤️

Modules

Common

The common module for various Jet modules.

This module contains many useful model classes and utilities for all your web server and client needs. For example, instead of crafting a response cookie header value using manual string concatenation, you can use Cookie.builder().name("name").value("value").httpOnly().secure().build().toResponseString(). For another example, instead of crafting a URL string using manual string concatenation, you can use Url.builder().scheme(HTTPS).host("example.com").addQueryParameter("key", "value").build().toString(). There are many header models supported, such as Content-Security-Policy, ETag, ContentEncoding. There are many enums supported, such as Method (e.g. GET, POST), Status (e.g. 404 Not Found), Version (e.g. HTTP/2), and more. All of these classes exist in an effort to improve developer experience, increase type-safety and single-source-of-truth (e.g. no scattered variables or duplicate string constants), and decrease bugs. See the guide for all models classes and utilities this module provides.

Installation

This module is transitively depended on by the Server and Client modules, so you typically don't need to install this module directly.

For build.gradle.kts:

dependencies {
    implementation("net.jacobpeterson.jet:common:3.3.0")
}

For build.gradle:

dependencies {
    implementation 'net.jacobpeterson.jet:common:3.3.0'
}

For pom.xml:

<dependency>
    <groupId>net.jacobpeterson.jet</groupId>
    <artifactId>common</artifactId>
    <version>3.3.0</version>
</dependency>

Guide

Most classes in this module are immutable and follow the builder pattern for creation.

Header Models

Header Data Structures

Case-insensitive key-value pairs:

Enums

URL Building

I/O Utilities

Other Utilities

A simple, lightweight, modern, turnkey, Java web server library.

Features:

  • HTTP/1, HTTP/1.1, HTTP/2
  • HTTPS encryption with SSL/TSL certificate hot-swap reloading
  • Custom routing and handlers
  • Sessions
  • Resource serving (classpath files, filesystem files, InputStream)
  • Server-Sent Events (SSE)
  • WebSockets (coming soon)
  • Multipart request body
  • Response body compression (Zstandard, Brotli, Gzip, Deflate)
  • Virtual threads (no more ugly async/reactive programming)

Installation

For build.gradle.kts:

dependencies {
    implementation("net.jacobpeterson.jet:server:3.3.0")
}

For build.gradle:

dependencies {
    implementation 'net.jacobpeterson.jet:server:3.3.0'
}

For pom.xml:

<dependency>
    <groupId>net.jacobpeterson.jet</groupId>
    <artifactId>server</artifactId>
    <version>3.3.0</version>
</dependency>

Guide

Here's a very simple example to get you going:

static void main() {
    JetServer.builder()
            .sslLetsEncrypt() // Enable Let's Encrypt SSL/TLS
            .sessionStore() // Enable in-memory sessions
            .router(ImmutableSimpleRouter.builder()
                    // Add a custom handler
                    .addLast(PathExactRoute.builder().path("/custom-path").build(), handle ->
                            handle.getResponse().responseHtml("<h1>Custom handler!</h1>"))
                    // Serve files from `~/webroot/`
                    .addLast(PathStartsWithRoute.builder().path("/").build(), FileDirectoryHandler
                            .simpleMutable(Path.of(System.getProperty("user.home"), "webroot"), null, true))
                    .build())
            .build(); // Automatically starts the server and adds a JVM shutdown hook to stop the server gracefully
}

JetServer represents the web server instance. Use the builder to configure and start the web server.

JetServer.Builder Configuration

Handle

Handle is a class that represents a web server request and response. It has getters for the Request and Response objects. Features: multipart request bodies, dynamic transparent response compression config, Resource serving, SSE, and more. For HTML templating, Jte is recommended as it provides the best features with the best type-safety out of all major Java templating engine libraries.

The Handler functional interface is provided a Handle instance for the current HTTP request/response lifecycle. Using a functional interface makes it very easy to wrap/nest Handler implementations, which is the mechanism for implementing middleware.

Exceptions

StatusException is thrown by some methods for 400 Bad Request (for example if a request header is malformed) and is used as a silent Exception for breaking the flow of execution in an implementation handler and returning a response status code.

BodyStreamException is thrown by some methods for request/response body streaming client timeouts and early disconnects.

The Handler should not consume these exceptions. JetServer expects these exceptions to be thrown for special processing (e.g. to set the response status upon a StatusException or to not log an error for BodyStreamException).

Useful Handler Implementations

Directory

Handlers for serving files from the classpath or filesystem with support for relativizing request paths, serving a default file (like /index.html) for request paths representing a directory, serving a default file for requests paths representing a file without an extension (like /index), redirecting to default files or default extensions, applying a ResponseCacheControl, and caching Resource instances from with automatic cache invalidation using WatchService:

Redirect

Simple handlers for common redirection cases:

Router Implementations

These Router implementations represent a priority list for registered Route instances. Only one Route will be matched for a request/response lifecycle.

Route Implementations

These Route implementations are added to the Router and tested for a match when a request is received.

Session & SessionStore Implementations

HTTP sessions store data server-side on behalf of a client. The client stores a cookie to be sent to the server to reference the stored server-side data. These simple implementations store data in memory and have sensible defaults to get you started with HTTP sessions:

A recommended, but more advanced, implementation is to use Redis/Valkey via Redisson with RLO for better type-safety and persisting session data after a server restart.

Let's Encrypt SSL Certificates

JetServer has native support for Let's Encrypt SSL certificates. Follow these steps to add HTTPS support to your web server for free. This guide assumes you're using an Ubuntu Linux VM hosted on an AWS EC2 instance, but can be adapted to other Linux distributions.

  1. Install Java Amazon Corretto 25 for an unprivileged user named jet:

    • Via SDKMAN!:
      sudo useradd -ms /bin/bash jet
      sudo apt install zip
      # https://sdkman.io
      curl -s "https://get.sdkman.io" | sudo -u jet bash
      sudo -iu jet bash -ic "sdk install java 25.0.3-amzn"
    • Via Apt repositories:
      sudo useradd -ms /bin/bash jet
      # https://docs.aws.amazon.com/corretto/latest/corretto-25-ug/generic-linux-install.html
      wget -O - https://apt.corretto.aws/corretto.key | \ 
      sudo gpg --dearmor -o /usr/share/keyrings/corretto-keyring.gpg
      echo "deb [signed-by=/usr/share/keyrings/corretto-keyring.gpg] https://apt.corretto.aws stable main" | \ 
      sudo tee /etc/apt/sources.list.d/corretto.list
      sudo apt-get update
      sudo apt-get install -y java-25-amazon-corretto-jdk
  2. Install certbot and add a renewal hook that allows users in the letsencrypt group to read certificate files:

    sudo snap install certbot --classic
    sudo groupadd letsencrypt
    sudo usermod -aG letsencrypt jet
    sudo mkdir -p /etc/letsencrypt/renewal-hooks/deploy/
    sudo tee /etc/letsencrypt/renewal-hooks/deploy/change-permissions.sh <<EOF
    #!/bin/bash
    chgrp -R letsencrypt /etc/letsencrypt/{live,archive}
    chmod -R g+rX /etc/letsencrypt/{live,archive}
    EOF
    sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/change-permissions.sh
  3. Update your DNS A (IPv4) and AAAA (IPv6) records to point to your server. Ensure your AWS EC2 Security Group allows ports 80 and 443 from any IPv4 or any IPv6 address. Configure nftables (successor to the iptables) to redirect privileged ports 80 and 443 to the unprivileged default web server ports 8080 and 8443:

    sudo tee -a /etc/nftables.conf <<EOF
    table inet nat {
        chain prerouting {
            type nat hook prerouting priority dstnat; policy accept;
            tcp dport { 80, 443 } redirect to tcp dport map { 80 : 8080, 443 : 8443 }
        }
        chain output {
            type nat hook output priority dstnat; policy accept;
            fib daddr type local tcp dport { 80, 443 } redirect to tcp dport map { 80 : 8080, 443 : 8443 }
        }
    }
    EOF
    sudo systemctl enable --now nftables
  4. In your Maven or Gradle project with the Server module installed, use FileDirectoryHandler to serve a webroot for the certbot certificate renewal process and run your Maven or Gradle project as the jet user on your server:

    sudo -iu jet bash -ic "mkdir webroot"
    JetServer.builder()
         .router(ImmutableSimpleRouter.builder()
                 .addLast(PathStartsWithRoute.builder().path("/.well-known/acme-challenge/").build(),
                         FileDirectoryHandler.simpleMutable(Path.of("webroot"), null, true))
                 .build())
         .build();
  5. Now request your SSL certificate: sudo certbot certonly -d <your_domain> --webroot /home/jet/webroot/ (specify /home/jet/webroot/ as the webroot)

  6. Now configure JetServer.Builder with .sslLetsEncrypt():

    JetServer.builder()
            .sslLetsEncrypt() // Automatically enables hot-swap SSL reloading
            .router(ImmutableSimpleRouter.builder()
                    // Keep the `FileDirectoryHandler` for future `certbot` auto-renewals.
                    .addLast(PathStartsWithRoute.builder().path("/.well-known/acme-challenge/").build(),
                            FileDirectoryHandler.simpleMutable(Path.of("webroot"), null, true))
                    .addLast(PathExactRoute.builder()
                            .schemeEnum(HTTPS)
                            .path("/")
                            .build(), handle -> handle.getResponse().responseText("HTTPS yay!"))
                    .build())
            .build();

Because of Jet Server's native support for SSL/TLS, it is generally not recommended to put Jet Server behind a reverse proxy (such as Nginx) to handle SSL/TLS termination. Unless you have other web server requirements, like using PHP FPM via Nginx to support PHP applications, you should use Jet Server as the main web server on your Linux VM.

OpenAPI Annotations

A code-first OpenAPI specification annotations library.

This module provides Java annotations for all OpenAPI objects defined within the OpenAPI Description. This allows you to define OpenAPI specifications directly within Java code using Java annotations so that your API specification lives alongside the API implementation handlers. This module is built in a way that enforces type-safety and encourages single-source-of-truth within API specifications. For example, instead of declaring the path "/api/account/profile-picture" separately in your OpenAPI spec, in the Java web server handler code, and in the client API request code (e.g. a JavaScript fetch() call), you can declare the path once as a string constant in Java, reference that same constant within an OpenAPI annotation, generate an OpenAPI spec from those annotations, and then generate a client library from that OpenAPI spec. This way, everything is type-safe and dervied from a single-source-of-truth, which reduces bugs caused by typos, forgetting to update request paths and JSON schemas, etc.

One of the best features this module offers is OpenApiSchema.fromClass() which enables you to include JSON schemas generated directly from Java classes in your OpenAPI specification, which is the pinnacle of type-safety and single-source-of-truth! The enables you to create a strong client-server API contract in which the Java server defines the contract that all generated OpenAPI clients adhere to. The excellent victools/jsonschema-generator library is used to generate the JSON schemas in the OpenAPI Annotations Plugin, so generics, inheritance, nullability, etc. are all supported! Note that you can enforce @Nullable annotations on the server in implementation handlers using NullableUtil.requireNonNullFieldsSet().

Installation

For build.gradle.kts:

dependencies {
    implementation("net.jacobpeterson.jet:openapi-annotations:3.3.0")
}

For build.gradle:

dependencies {
    implementation 'net.jacobpeterson.jet:openapi-annotations:3.3.0'
}

For pom.xml:

<dependency>
    <groupId>net.jacobpeterson.jet</groupId>
    <artifactId>openapi-annotations</artifactId>
    <version>3.3.0</version>
</dependency>

Guide

All OpenAPI objects defined within the OpenAPI description have been implemented, enabling you to define an OpenAPI specification using Java annotations with no limitations:

There are few quirks when working with Java annotations. First, annotation methods/values cannot be set to null, so some OpenAPI annotation methods have been marked with the special annotation AnnotationArrayIsNullableValue to denote that the array represents a nullable value, meaning null is defined as an empty array and non-null is defined as an array with a single element. Second, to support maps/dictionaries, some OpenAPI annotation methods have been marked with the special annotation AnnotationArrayIsMap to denote that the array represents a map with annotation entries that uses the special annotation AnnotationArrayIsMapKey to denote the map key.

Note that some OpenAPI annotations encourage the use of the models and enums from Common module. For example, OpenApiPathItem.MethodEntry has both key which can be set to a String constant, and keyEnum which can be set to a Method enum. However, an unfortunate limitation of Java annotations with methods of enum types is that they cannot be set to constant references and must be set to the enum constant directly. This makes implementing the single-source-of-truth practice a bit harder, since, for example, you cannot declare the constant public static final Method METHOD = Method.GET and use that constant in a type-safe way within the annotation declaration and for the web server implementation handler route registration. A workaround is to use String references instead, which Java allows enum methods to reference. The Common module provides a ToString inner class for all enums so they can be used within Java annotations as constant references. For example, public static final Method METHOD = Method.GET becomes public static final String METHOD = Method.ToString.GET. Now this string constant can be directly referenced both by the OpenAPI annotation and the web server implementation handler route registration, so we keep our type-safety and single-source-of-truth practice!

Here is an example of the recommended approach for using OpenAPI annotations and implementation handlers with Jet using type-safety and single-source-of-truth practices. Note that this example uses static inner classes for Web and AccountHandlers, but ideally these would be separate class files, to better manage separation of concerns and to not include so much code in one file.

import lombok.Builder;
import lombok.Getter;
import lombok.Value;
import net.jacobpeterson.jet.openapiannotations.OpenApi;
import net.jacobpeterson.jet.openapiannotations.OpenApiInfo;
import net.jacobpeterson.jet.openapiannotations.OpenApiMediaType;
import net.jacobpeterson.jet.openapiannotations.OpenApiOperation;
import net.jacobpeterson.jet.openapiannotations.OpenApiParameter;
import net.jacobpeterson.jet.openapiannotations.OpenApiPathItem;
import net.jacobpeterson.jet.openapiannotations.OpenApiPaths;
import net.jacobpeterson.jet.openapiannotations.OpenApiResponse;
import net.jacobpeterson.jet.openapiannotations.OpenApiResponses;
import net.jacobpeterson.jet.openapiannotations.OpenApiSchema;
import net.jacobpeterson.jet.openapiannotations.OpenApiServer;
import net.jacobpeterson.jet.openapiannotations.schemaname.SchemaName;
import net.jacobpeterson.jet.server.JetServer;
import net.jacobpeterson.jet.server.handle.Handle;
import net.jacobpeterson.jet.server.route.simple.pathexact.PathExactRoute;
import net.jacobpeterson.jet.server.router.simple.ImmutableSimpleRouter;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

import static net.jacobpeterson.jet.common.http.header.contenttype.ContentType.APPLICATION_JSON_STRING;
import static net.jacobpeterson.jet.common.http.method.Method.ToString.GET;
import static net.jacobpeterson.jet.common.http.status.Status.OK_200;
import static net.jacobpeterson.jet.openapiannotations.OpenApiParameter.ParameterLocation.QUERY;

@NullMarked
public class Server {

    @OpenApi(
            annotationGroupName = Web.PATH,
            info = @OpenApiInfo(
                    title = "Web",
                    version = "1.0.0"
            ),
            servers = @OpenApiServer(url = "http://localhost/" + Web.PATH)
    )
    public static final class Web {

        public static final String PATH = "web";

        private final @Getter AccountHandlers accountHandlers;

        public Web(final ImmutableSimpleRouter.Builder router) {
            accountHandlers = new AccountHandlers(router);
        }

        @SchemaName("Account")
        public static final class AccountHandlers {

            public AccountHandlers(final ImmutableSimpleRouter.Builder router) {
                router.addLast(PathExactRoute.builder()
                        .method(GetInfo.METHOD)
                        .path("/" + Web.PATH + GetInfo.PATH)
                        .build(), this::getInfo);
            }

            public static final String TAG_NAME = "account";

            public static final class GetInfo {

                public static final String METHOD = GET;
                public static final String PATH = "/" + TAG_NAME + "/info";
                public static final String QUERY_KEY_ID = "id";

                @Value @Builder
                public static class Success {

                    String name;
                    String email;
                    @Nullable String profilePictureUrl;
                }

                public enum FailReason {

                    NOT_LOGGED_IN,
                    INVALID_ID
                }

                @Value @Builder
                public static class Response {

                    @Nullable Success success;
                    @Nullable FailReason failReason;
                }
            }

            @OpenApi(annotationGroupName = PATH, paths = @OpenApiPaths(@OpenApiPathItem.MapEntry(
                    key = GetInfo.PATH, value = @OpenApiPathItem(methods = @OpenApiPathItem.MethodEntry(
                    key = GetInfo.METHOD, value = @OpenApiOperation(tags = TAG_NAME,
                    parameters = @OpenApiParameter(
                            name = GetInfo.QUERY_KEY_ID,
                            in = QUERY,
                            required = true,
                            schema = @OpenApiParameter.Schema(schema = @OpenApiSchema(fromClass = String.class))
                    ),
                    responses = @OpenApiResponses({@OpenApiResponse.MapEntry(
                            keyEnum = OK_200,
                            value = @OpenApiResponse(content = @OpenApiMediaType.MapEntry(
                                    key = APPLICATION_JSON_STRING,
                                    value = @OpenApiMediaType(schema = @OpenApiSchema(fromClass =
                                            GetInfo.Response.class))
                            ))
                    )})
            ))))))
            public void getInfo(final Handle handle) {
                if (<check_logged_in_logic>) {
                    handle.getResponse().responseJson(toJson(GetInfo.Response.builder()
                            .failReason(GetInfo.FailReason.NOT_LOGGED_IN)
                            .build()));
                    return;
                }
                final var id = handle.getRequest().getUrl().getQueryValue(GetInfo.QUERY_KEY_ID);
                if (<check_id_logic>) {
                    handle.getResponse().responseJson(toJson(GetInfo.Response.builder()
                            .failReason(GetInfo.FailReason.INVALID_ID)
                            .build()));
                    return;
                }
                <get_account_info_logic>
                handle.getResponse().responseJson(toJson(GetInfo.Response.builder()
                        .success(new GetInfo.Success(name, email, profilePictureUrl))
                        .build()));
            }
        }
    }

    static void main() {
        final var router = ImmutableSimpleRouter.builder();
        new Web(router);
        JetServer.builder().router(router.build()).build();
    }
}

OpenAPI Annotations Plugin

A code-first OpenAPI specification annotations processor Gradle plugin.

This Gradle plugin generates OpenAPI specifications from the OpenAPI annotations declared in your Java source code. Then you can use the OpenAPI Generator Gradle plugin to generate OpenAPI client libraries all within your Gradle build script. This creates an end-to-end solution for building APIs in a type-safe and single-source-of-truth manner, all with great developer experience. For example, you have a model class called GetInfo inside a class called AccountHandlers. The GetInfo class has the field @Nullable String firstName, but you want to change it to String firstName denoting that it is no longer nullable. If you're using a generated OpenAPI TypeScript client library, your TypeScript codebase will throw a compilation error for all the cases your frontend TypeScript code uses firstName as a nullable value, allowing you to catch type-safety bugs for APIs at compile-time instead of at runtime! This is how full-stack web development should have been all along!

Installation

For build.gradle.kts:

plugins {
    id("net.jacobpeterson.jet.openapiannotationsplugin") version "3.3.0"
}

For build.gradle:

plugins {
    id 'net.jacobpeterson.jet.openapiannotationsplugin' version "3.3.0"
}

There is no Maven plugin available at this time.

Guide

This Gradle plugin registers a task named jetOpenApiAnnotations and an extension also named jetOpenApiAnnotations with the following configurations:

Example Configuration (build.gradle.kts)

jetOpenApiAnnotations {
    schemaGeneratorUseGsonModule = true // Set to this `true` if you're using Gson
    schemaGeneratorUseJacksonModule = false // Set to this `true` if you're using Jackson
    // If you have a custom (de)serializer for `InetAddress` with Gson or Jackson, you can specify that every time
    // `InetAddress` is used in a model class for an OpenAPI schema, it should be treated as a string.
    schemaGeneratorSimpleTypeMappings.put("java.net.InetAddress", """{"type": "string"}""")
}

Why not an annotation processor?

The main reason is that annotation processors cannot load classes if the class is part of the source code currently being compiled, so OpenApiSchema.fromClass() would not be able to utilize the excellent victools/jsonschema-generator library and the class file would have to be manually inspected using TypeMirror. Additionally, annotation processors have various limitations, such as no support for @CompileClasspath for Gradle incremental builds.

Client

A simple, lightweight, modern, turnkey, Java web client library.

The Client module is a WIP and will be released soon! See issue #5.

Personal Note About AI

100% of this codebase was written by a human. 100% of this guide was written by a human. No AI slop is allowed here. I care deeply about good engineering and excellent code quality. AI does not. So, do I use AI? Of course! But it will stay inside my web browser as a chatbot where I'll ask it questions about specific problems, like a glorified StackOverflow. I remain in the driver's seat. It's not in my IDE where an AI autocomplete is constantly suggesting average buggy code, and certainly not where AI agents are mangling the codebase. Yes, AI autocompleted code can be reviewed, but I find myself accepting lower quality code instead of critically thinking through each line and typing it myself, which almost always yields higher quality code, but takes longer. LLMs fundamentally output the mean. I'm not saying that my code is always perfect and better than AI, but I am saying that too many engineers are being sold a lie that if they aren't constantly using AI, they will be left behind. Until there is a fundamental change in how LLMs work, this will remain my opinion on the state of AI coding. </rant>

Contributions are very welcome, but keep these remarks in mind when submitting pull requests.

Alternatives

Please note that the below list focuses on the weaknesses of the alternative projects compared to Jet's strengths. Jet obviously has shortcomings compared to these other projects. Personal note: the below list is highly opinionated as a result of my work as a full-stack software engineer, almost always using a Java backend.

  • Jetty - excellent web server and client library (which is why Jet uses it), but has a complicated setup and lacks exhaustive Javadocs and header models

  • Javalin - awesome library, but requires Kotlin dependency and lacks header models and exhaustive KDocs

  • Spring - enterprise-grade, but bloated, uses lots of runtime annotation magic, and enforces an opinionated codebase structure

  • Vert.x Web - reactive programming is no longer necessary with the advent of Virtual Threads

  • Apache Tomcat - strict legacy Jakarta Servlet specification adherence

  • Spark Java - project abandoned