Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

# ∞ do more, more easily

[Jooby](https://jooby.io) is a modern, high-performance web framework for Java and Kotlin, designed to run seamlessly atop your preferred web server.
[Jooby](https://jooby.io) is a modular, high-performance web framework for Java and Kotlin. Designed for simplicity and speed, it gives you the freedom to build on your favorite server with a clean, modern API.

## 🚀 Built for Speed
- **High Performance**: Consistently ranks among the fastest Java frameworks in TechEmpower benchmarks.
Expand Down
13 changes: 7 additions & 6 deletions docs/asciidoc/configuration.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -168,14 +168,15 @@ To ensure environment-specific logging works correctly, avoid using **static** l

==== Application Properties

[cols="2,1,3"]
|===
|Property | Description | Default
|Property | Default | Description

|`application.charset` | Charset for encoding/decoding and templates. | `UTF-8`
|`application.env` | Active environment names. Jooby optimizes performance for non-`dev` environments. | `dev`
|`application.lang` | Supported languages for `Context.locale()`. | `Locale.getDefault()`
|`application.tmpdir` | Temporary directory for the application. | `tmp`
|`application.pid` | The JVM process ID. | System assigned
|`application.charset` | `UTF-8` | Charset for encoding/decoding and templates.
|`application.env` | `dev` | Active environment names. Jooby optimizes performance for non-`dev` environments.
|`application.lang` | `Locale.getDefault()` | Supported languages for `Context.locale()`.
|`application.tmpdir` | `tmp` | Temporary directory for the application.
|`application.pid` | System assigned | The JVM process ID.
|===

See javadoc:AvailableSettings[] for a complete reference.
2 changes: 2 additions & 0 deletions docs/asciidoc/core.adoc
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
== Core
[.lead]
The foundational building blocks of your Jooby application. This section covers how to bootstrap the server, manage application environments and configuration, and leverage the modular architecture that makes Jooby both lightweight and highly extensible.

include::routing.adoc[]

Expand Down
169 changes: 151 additions & 18 deletions docs/asciidoc/docinfo-footer.html
Original file line number Diff line number Diff line change
@@ -1,24 +1,157 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/zepto/1.2.0/zepto.min.js"></script>
<script>
$(function () {
/** Tabs: */
$('.primary .switch .switch--item').on('click', function () {
const option = $(this)[0].className.match(/option-\d+/)[0].split('option-')[1];
$('.primary .switch .switch--item').removeClass('selected');
$(`.primary .content`).addClass('hidden');

$(`.primary .switch .switch--item.option-${option}`).addClass('selected');
$(`.primary .content.option-${option}`).removeClass('hidden');
});
});
</script>
<script src="/js/toc.js"></script>
<button id="theme-toggle" class="theme-toggle" aria-label="Toggle Dark Mode">
<svg class="sun-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 2.25a.75.75 0 01.75.75v2.25a.75.75 0 01-1.5 0V3a.75.75 0 01.75-.75zM7.5 12a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0zM18.894 6.166a.75.75 0 00-1.06-1.06l-1.591 1.59a.75.75 0 101.06 1.061l1.591-1.59zM21.75 12a.75.75 0 01-.75.75h-2.25a.75.75 0 010-1.5H21a.75.75 0 01.75.75zM17.834 18.894a.75.75 0 001.06-1.06l-1.59-1.591a.75.75 0 10-1.061 1.06l1.59 1.591zM12 18.75a.75.75 0 01.75.75V21a.75.75 0 01-1.5 0v-1.5a.75.75 0 01.75-.75zM6.166 18.894a.75.75 0 01-1.06-1.06l1.59-1.591a.75.75 0 111.061 1.06l-1.59 1.591zM2.25 12a.75.75 0 01.75-.75h2.25a.75.75 0 010 1.5H3a.75.75 0 01-.75-.75zM5.106 6.166a.75.75 0 011.06-1.06l1.591 1.59a.75.75 0 11-1.06 1.061l-1.591-1.59z"/></svg>
<svg class="moon-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill-rule="evenodd" d="M9.528 1.718a.75.75 0 01.162.819A8.97 8.97 0 009 6a9 9 0 009 9 8.97 8.97 0 003.463-.69.75.75 0 01.981.98 10.503 10.503 0 01-9.694 6.46c-5.799 0-10.5-4.701-10.5-10.5 0-4.368 2.667-8.112 6.46-9.694a.75.75 0 01.818.162z" clip-rule="evenodd"/></svg>
</button>

<script src="/js/clipboard.min.js"></script>
<script src="/js/toc.js"></script>

<script>
$(function() {
new ClipboardJS('.clipboard')
.on('success', function(e) {
document.addEventListener('DOMContentLoaded', () => {

/** 1. Theme Switcher Logic */
const themeToggle = document.getElementById('theme-toggle');
const htmlElement = document.documentElement;
const hljsThemeLink = document.getElementById('hljs-theme'); // Grab the CSS link

if (themeToggle) {
themeToggle.addEventListener('click', () => {
if (htmlElement.getAttribute('data-theme') === 'dark') {
// Switch to Light Mode
htmlElement.removeAttribute('data-theme');
localStorage.setItem('jooby-theme', 'light');

// Switch code block to Agate
if (hljsThemeLink) hljsThemeLink.href = 'js/styles/agate.min.css';

} else {
// Switch to Dark Mode
htmlElement.setAttribute('data-theme', 'dark');
localStorage.setItem('jooby-theme', 'dark');

// Switch code block to Atom One Dark
if (hljsThemeLink) hljsThemeLink.href = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css';
}
});
}

/** 2. Tabs Logic */
const tabButtons = document.querySelectorAll('.primary .switch .switch--item');
const contents = document.querySelectorAll('.primary .content');

tabButtons.forEach(button => {
button.addEventListener('click', function () {
const match = this.className.match(/option-(\d+)/);
if (!match) return;
const option = match[1];

// Reset states
tabButtons.forEach(btn => btn.classList.remove('selected'));
contents.forEach(content => content.classList.add('hidden'));

// Set active states
document.querySelectorAll(`.primary .switch .switch--item.option-${option}`)
.forEach(el => el.classList.add('selected'));

document.querySelectorAll(`.primary .content.option-${option}`)
.forEach(el => el.classList.remove('hidden'));
});
});

/** 3. Clipboard.js Initialization */
const clipboard = new ClipboardJS('.clipboard');
clipboard.on('success', (e) => {
e.clearSelection();
const btn = e.trigger;
const originalHTML = btn.innerHTML;

// Temporarily change background and insert a checkmark SVG
btn.style.backgroundColor = '#10b981'; // Tailwind emerald-500
btn.style.borderColor = '#059669';
btn.innerHTML = `<svg fill="none" stroke="white" stroke-width="2.5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"></path></svg>`;

// Revert back after 2 seconds
setTimeout(() => {
btn.style.backgroundColor = '';
btn.style.borderColor = '';
btn.innerHTML = originalHTML;
}, 2000);
});
});
/** 4. Code Callout Badges (Zero-Flash & Auto-Format) */
document.querySelectorAll('pre.highlightjs').forEach(pre => {
// Check every 10ms if highlight.js has finished formatting this block
const checkInterval = setInterval(() => {
if (pre.dataset.highlighted === "yes" || pre.querySelector('.hljs')) {
clearInterval(checkInterval);

const codeEl = pre.querySelector('code');
if (codeEl) {
// Upgraded regex to handle consecutive callouts like: // (1) (2)
// The "Lookahead" (?= ... ) now checks if the rest of the line
// is empty OR contains subsequent (N) callout numbers.
const calloutRegex = /(\/\/\s*|\s+)\(\s*(?:<[^>]+>\s*)*(\d+)\s*(?:<[^>]+>\s*)*\)(?=\s*(?:<[^>]+>\s*)*(?:\(\s*(?:<[^>]+>\s*)*\d+\s*(?:<[^>]+>\s*)*\)\s*(?:<[^>]+>\s*)*)*(\n|<|$))/g;

codeEl.innerHTML = codeEl.innerHTML.replace(
calloutRegex,
(match, prefix, num) => {
// Injects a hidden "//" before EVERY badge so that
// multiple badges paste safely as "// 1 // 2"
return ` <span class="visually-hidden">//</span> <span class="conum-badge">${num}</span>`;
}
);
}

// Reveal the block smoothly
pre.classList.add('badges-loaded');
}
}, 10);

// Fallback: Reveal the block anyway after 500ms
setTimeout(() => {
pre.classList.add('badges-loaded');
clearInterval(checkInterval);
}, 500);
});
/** 5. Copy Header Anchor Links to Clipboard */
document.querySelectorAll('a.anchor').forEach(anchor => {
anchor.addEventListener('click', function(e) {
// We do NOT prevent default, so the browser still scrolls to the header naturally

// Construct the full absolute URL for the clipboard
const fullUrl = window.location.origin + window.location.pathname + window.location.search + this.getAttribute('href');

// Use native clipboard API
navigator.clipboard.writeText(fullUrl).then(() => {
// Add the 'copied' class for visual feedback
this.classList.add('copied');

// Remove the checkmark after 2 seconds
setTimeout(() => {
this.classList.remove('copied');
}, 2000);
}).catch(err => console.error('Failed to copy anchor:', err));
});
});
/** 6. Hamburger Menu */
document.addEventListener('DOMContentLoaded', function() {
const menuToggle = document.getElementById('menu-toggle');

// Try to find the Toc (Table of Contents) or the Navigation Container
const sidebar = document.getElementById('toc') ||
document.querySelector('.nav-container') ||
document.getElementById('sidebar');

if (menuToggle && sidebar) {
menuToggle.addEventListener('click', function() {
sidebar.classList.toggle('active');
menuToggle.classList.toggle('open');

// Optional: Prevent body scrolling when menu is open
document.body.classList.toggle('menu-open');
});
} else {
console.warn("Hamburger menu: Could not find the navigation element to toggle.");
}
});
</script>
6 changes: 6 additions & 0 deletions docs/asciidoc/docinfo-header.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<div class="mobile-nav-bar">
<span style="font-weight: 700; color: var(--heading-color);">Jooby</span>
<button id="menu-toggle" class="hamburger" aria-label="Toggle Menu">
<span></span><span></span><span></span>
</button>
</div>
121 changes: 12 additions & 109 deletions docs/asciidoc/docinfo.html
Original file line number Diff line number Diff line change
@@ -1,109 +1,12 @@
<link rel="stylesheet" href="/js/styles/toc.css">
<style>
/** Tabs */
.hidden {
display: none;
}

.switch {
display: inline-block;
}

.switch--item {
padding: 5px 10px;
color: #fff;
display: inline-block;
cursor: pointer;
background-color: #333;
border-left: 1px solid #ffa;
}

.switch--item:not(:first-child) {
}

.switch--item:last-child {
border-radius: 0 5px 0 0;
}

.switch--item.selected {
color: #ffa;
border-bottom: 1px solid #ffa;
}

.clipboard {
position: absolute;
bottom: .1rem;
right: .1rem;
display: inline-block;
padding: 4px 8px;
font-size: 13px;
font-weight: 700;
line-height: 20px;
color: #333;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
background-color: #eee;
background-image: linear-gradient(#fcfcfc, #eee);
border: 1px solid #d5d5d5;
border-radius: 3px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-appearance: none
}

.clipboard i {
font-style: normal;
font-weight: 500;
opacity: .6
}

.clipboard .octicon {
vertical-align: text-top
}

.clipboard .counter {
text-shadow: none;
background-color: #e5e5e5
}

.clipboard:focus {
text-decoration: none;
border-color: #51a7e8;
outline: none;
box-shadow: 0 0 5px rgba(81, 167, 232, .5)
}

.clipboard:focus:hover, .clipboard.selected:focus {
border-color: #51a7e8
}

.clipboard:hover, .clipboard:active, .clipboard.zeroclipboard-is-hover, .clipboard.zeroclipboard-is-active {
text-decoration: none;
background-color: #ddd;
background-image: linear-gradient(#eee, #ddd);
border-color: #ccc
}

.clipboard:active, .clipboard.selected, .clipboard.zeroclipboard-is-active {
background-color: #dcdcdc;
background-image: none;
border-color: #b5b5b5;
box-shadow: inset 0 2px 4px rgba(0, 0, 0, .15)
}

.clipboard.selected:hover {
background-color: #cfcfcf
}

.clipboard:disabled, .clipboard:disabled:hover, .clipboard.disabled, .clipboard.disabled:hover {
color: rgba(102, 102, 102, .5);
cursor: default;
background-color: rgba(229, 229, 229, .5);
background-image: none;
border-color: rgba(197, 197, 197, .5);
box-shadow: none
}
</style>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<script>
// This MUST be in the <head>. It runs before the body is rendered, preventing the white flash.
(function() {
const savedTheme = localStorage.getItem('jooby-theme');
if (savedTheme === 'dark' || (!savedTheme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.setAttribute('data-theme', 'dark');
}
})();
</script>
10 changes: 6 additions & 4 deletions docs/asciidoc/ecosystem.adoc
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
== Ecosystem
[.lead]
Extend the power of Jooby through its rich ecosystem of modules and standards. Learn how to seamlessly integrate with OpenAPI 3 to automatically generate interactive documentation and client SDKs, and explore a wide array of community and first-party modules that bring database access, security, and messaging to your application with minimal configuration.

The Jooby ecosystem is built on three core, interconnected concepts:

Expand Down Expand Up @@ -131,7 +133,7 @@ public class MyExtension implements Extension {
public void install(Jooby app) {
DataSource dataSource = createDataSource(); // <1>

app.getServices().put(DataSource.class, dataSource); // <2>
app.getServices().put(DataSource.class, dataSource);// <2>

app.onStop(dataSource::close); // <3>
}
Expand All @@ -150,11 +152,11 @@ import io.jooby.Jooby

class MyExtension : Extension {
override fun install(app: Jooby) {
val dataSource = createDataSource() // <1>
val dataSource = createDataSource() // <1>

app.services.put(DataSource::class.java, dataSource) // <2>
app.services.put(DataSource::class.java, dataSource) // <2>

app.onStop(dataSource::close) // <3>
app.onStop(dataSource::close) // <3>
}

private fun createDataSource(): DataSource {
Expand Down
2 changes: 1 addition & 1 deletion docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Style guidelines:
[discrete]
== &infin; do more, more easily

Jooby is a modern, fast, and easy-to-use web framework for Java and Kotlin, built on top of your favorite web server.
Jooby is a modular, high-performance web framework for Java and Kotlin. Designed for simplicity and speed, it gives you the freedom to build on your favorite server with a clean, modern API.

.Welcome!
[source,java,role="primary"]
Expand Down
Loading
Loading