LaunchPad User's Guide
Complete guide to deploying Oorian applications as self-contained executable JARs. Learn configuration, deployment strategies, and production best practices.
Table of Contents
1. Getting Started
LaunchPad enables you to run standard Oorian web applications as self-contained executable JARs with an embedded Jetty server. No external application server installation required—just compile and run.
LaunchPad doesn't change how you build Oorian applications. Your @Page annotated pages, event handlers, and extension library usage all work exactly the same. LaunchPad simply provides the embedded server to run them.
New to Oorian? See the Quick Start Guide to learn how to create an Oorian web application first, then return here to deploy it with LaunchPad.
Minimal Example
Here's the simplest possible LaunchPad launcher. Create a separate class to start your Oorian web application:
public class MyLauncher
{
public static void main(String[] args)
{
LaunchPad launcher = new LaunchPad();
launcher.setPort(8080);
launcher.launch(MyWebApp.class);
}
}Where MyWebApp is your standard Oorian Application class annotated with @WebListener—the same class you would use with a traditional WAR deployment:
@WebListener
public class MyWebApp extends Application
{
@Override
protected void registerPackages()
{
registerPackage("com.mycompany.myapp");
}
}Run the main method, and your application is available at http://localhost:8080.
Key Benefits
- Zero configuration: Sensible defaults for immediate productivity
- Single JAR deployment: Package everything into one executable file
- IDE-friendly: Run and debug directly from your IDE
- Container-ready: Perfect for Docker and Kubernetes
- Production-proven: Built on battle-tested Jetty
2. Project Setup
Create a Java Application Project
LaunchPad applications are standard Java applications with a main() method—not web application projects. In your IDE, create a new Java Application project (not a "Java Web" or "Web Application" project).
For example, in NetBeans: File → New Project → Java with Maven → Java Application. In IntelliJ: File → New → Project → Java (or Maven/Gradle). The key is that you're creating a regular Java project that will produce a JAR, not a WAR.
Dependencies
LaunchPad has minimal dependencies. You need just three JAR files to get started:
oorian-jakarta.jar— The Oorian core library (oroorian-j2ee.jarfor Java EE)oorian-launchpad.jar— The LaunchPad embedded server libraryjetty-all.jar— The embedded Jetty server (bundled with LaunchPad)
That's it. No complex dependency trees, no version conflicts, no transitive dependency surprises. Add these JARs to your project's classpath and you're ready to go.
Maven
If you're using Maven, add these dependencies to your pom.xml:
<dependency>
<groupId>com.oorian</groupId>
<artifactId>oorian-jakarta</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.oorian</groupId>
<artifactId>oorian-launchpad</artifactId>
<version>2.0.0</version>
</dependency>The LaunchPad dependency automatically includes Jetty as a transitive dependency.
Gradle
If you're using Gradle, add these dependencies to your build.gradle:
dependencies {
implementation 'com.oorian:oorian-jakarta:2.0.0'
implementation 'com.oorian:oorian-launchpad:2.0.0'
}Ant
If you're using Ant, download the JAR files and place them in a lib folder. Add them to your classpath in build.xml:
<path id="classpath">
<fileset dir="lib">
<include name="oorian-jakarta.jar"/>
<include name="oorian-launchpad.jar"/>
<include name="jetty-all.jar"/>
</fileset>
</path>
<target name="compile">
<javac srcdir="src" destdir="build/classes" classpathref="classpath"/>
</target>Project Structure
A typical LaunchPad project structure:
myapp/
├── pom.xml
├── src/main/java/com/mycompany/myapp/
│ ├── MyLauncher.java # Main class with LaunchPad
│ ├── MyWebApp.java # @WebListener Application
│ └── pages/
│ ├── HomePage.java # @Page("/")
│ └── AboutPage.java # @Page("/about")
└── src/main/resources/
└── server.properties # Optional configuration3. Building and Running
Build your project as you normally would—using your IDE's build command or your build tool (mvn package, gradle build, ant jar). The result is a JAR file containing your compiled classes.
During Development
Simply run your launcher class directly from your IDE. Click Run, set breakpoints, debug—it works like any other Java application. No server to start, no deployment step.
For Deployment
Copy your JAR to any machine with Java installed and run:
java -jar MyWebApp.jarThat's it. Your application is now running at http://localhost:8080. No application server to install, no WAR file to deploy, no container configuration—just Java.
4. Basic Configuration
LaunchPad provides a fluent API for configuring your embedded server. All configuration methods return the LaunchPad instance for method chaining.
Available Options
| Method | Default | Description |
|---|---|---|
setPort(int) | 8080 | HTTP port |
setShutdownPort(int) | 8005 | Port for IDE restart signal |
setContextPath(String) | / | Application context path |
setThreadPool(int, int) | 10, 200 | Min and max threads |
setIdleTimeout(int) | 30000 | Connection idle timeout (ms) |
setGzipEnabled(boolean) | true | GZIP compression |
setHealthEndpointEnabled(boolean) | true | Health check endpoint |
setHealthEndpointPath(String) | /health | Health endpoint path |
setResourceBase(String) | null | Static files directory |
setTempDirectory(String) | null | Temp directory for uploads |
Example Configuration
LaunchPad launcher = new LaunchPad();
launcher.setPort(8080)
.setContextPath("/myapp")
.setShutdownPort(8005)
.setThreadPool(10, 200)
.setIdleTimeout(30000)
.setGzipEnabled(true)
.setHealthEndpointEnabled(true)
.setHealthEndpointPath("/health");
launcher.launch(MyWebApp.class);Context Path
The context path determines the base URL for your application. With setContextPath("/myapp"), your pages are accessed at http://localhost:8080/myapp/... instead of the root.
Shutdown Port
The shutdown port enables graceful restarts during development. When you restart your application from your IDE, LaunchPad sends a shutdown signal to any existing instance on this port, preventing "Address already in use" errors. The default port 8005 matches Tomcat's convention.
5. SSL/HTTPS Configuration
LaunchPad provides built-in SSL support for secure HTTPS connections.
Enabling SSL
LaunchPad launcher = new LaunchPad();
launcher.setPort(8080)
.enableSsl("/path/to/keystore.jks", "keystorePassword")
.setSslPort(8443);
launcher.launch(MyWebApp.class);With SSL enabled, your application serves both HTTP (port 8080) and HTTPS (port 8443).
Creating a Keystore
For development, create a self-signed certificate using the Java keytool:
keytool -genkeypair -alias myapp -keyalg RSA -keysize 2048 \
-storetype PKCS12 -keystore keystore.jks -validity 365 \
-storepass changeit -keypass changeit \
-dname "CN=localhost, OU=Development, O=MyCompany, L=City, ST=State, C=US"Separate Key Manager Password
If your keystore uses a different password for the key manager:
launcher.enableSsl("/path/to/keystore.jks", "keystorePassword", "keyManagerPassword");Production Considerations
In production environments, you typically terminate SSL at a reverse proxy (nginx, HAProxy) or load balancer rather than at the application level. This provides:
- Centralized certificate management
- Automatic certificate renewal (Let's Encrypt)
- Hardware SSL acceleration
- Simplified application configuration
LaunchPad's built-in SSL is still valuable for development, internal services, and simple deployments.
6. Thread Pool Tuning
LaunchPad uses Jetty's QueuedThreadPool to handle incoming requests. Proper tuning ensures your application can handle expected load while avoiding resource exhaustion.
Configuration
launcher.setThreadPool(10, 200) // min=10, max=200 threads
.setIdleTimeout(30000); // 30 second idle timeoutUnderstanding the Parameters
- minThreads (default: 10): Minimum threads kept alive even when idle. Set higher if your application has consistent baseline traffic.
- maxThreads (default: 200): Maximum concurrent request handlers. Limit this to prevent resource exhaustion under heavy load.
- idleTimeout (default: 30000ms): How long a connection can be idle before being closed. Lower values free resources faster; higher values benefit keep-alive connections.
Tuning Guidelines
For CPU-bound applications (computation-heavy): Set maxThreads close to the number of CPU cores. More threads won't help and will increase context-switching overhead.
For I/O-bound applications (database queries, external API calls): Higher maxThreads allows more concurrent requests while threads wait on I/O. Start with 200 and adjust based on monitoring.
Memory consideration: Each thread consumes stack memory (typically 512KB-1MB). With 200 threads, expect 100-200MB of memory just for thread stacks.
7. Health Endpoints
LaunchPad includes a built-in health endpoint that returns a simple "OK" response. This endpoint is essential for load balancers, container orchestrators, and monitoring systems.
Default Behavior
The health endpoint is enabled by default at /health. Access it to verify your application is running:
$ curl http://localhost:8080/health
OKCustomization
// Disable the health endpoint
launcher.setHealthEndpointEnabled(false);
// Or change the path
launcher.setHealthEndpointPath("/api/health");
launcher.setHealthEndpointPath("/status");
launcher.setHealthEndpointPath("/ping");Load Balancer Configuration
Configure your load balancer to check the health endpoint. Example nginx upstream configuration:
upstream myapp {
server app1:8080;
server app2:8080;
# Health check (nginx plus or third-party module)
health_check uri=/health interval=10s;
}8. Static Resources
LaunchPad can serve static files (CSS, JavaScript, images) from a specified directory.
Configuration
launcher.setResourceBase("/var/www/myapp/static");Files in the resource base directory are served at the root of your context path. For example, /var/www/myapp/static/css/style.css would be accessible at http://localhost:8080/css/style.css.
Temporary Directory
For file uploads and other temporary processing, configure a temp directory:
launcher.setTempDirectory("/tmp/myapp");Best Practices
- Use a CDN for static assets in production
- Set appropriate cache headers via a reverse proxy
- Keep the resource base outside your application JAR for easy updates
- Ensure the temp directory has sufficient space for uploads
9. Configuration Files
For production deployments, externalize configuration to a properties file instead of hardcoding values.
Loading a Configuration File
LaunchPad launcher = new LaunchPad();
launcher.setConfigFile("/etc/myapp/server.properties");
launcher.launch(MyWebApp.class);Configuration File Format
# server.properties
# Server
server.port=8080
server.shutdown.port=8005
server.context.path=/
# Thread Pool
server.threads.min=10
server.threads.max=200
server.idle.timeout=30000
# SSL (optional)
server.ssl.enabled=true
server.ssl.port=8443
server.ssl.keystore=/etc/myapp/keystore.jks
server.ssl.keystore.password=changeit
server.ssl.key.password=changeit
# Features
server.gzip.enabled=true
server.health.enabled=true
server.health.path=/health
# Resources
server.resource.base=/var/www/myapp/static
server.temp.directory=/tmp/myapp
# Access Logging
server.accesslog.enabled=true
server.accesslog.path=/var/log/myapp/access.logOverride Precedence
Programmatic settings always override file settings. This allows you to load a base configuration file while overriding specific values for development or testing:
LaunchPad launcher = new LaunchPad();
// Load production config
launcher.setConfigFile("/etc/myapp/server.properties");
// Override port for development
launcher.setPort(9090);
launcher.launch(MyWebApp.class);10. Docker Deployment
LaunchPad applications are ideal for Docker containerization. The self-contained JAR model aligns perfectly with Docker's philosophy.
Dockerfile
FROM eclipse-temurin:21-jre-alpine
# Create app directory
WORKDIR /app
# Copy the executable JAR
COPY target/myapp-with-dependencies.jar /app/myapp.jar
# Expose ports
EXPOSE 8080
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=30s --retries=3 \
CMD wget --quiet --tries=1 --spider http://localhost:8080/health || exit 1
# Run the application
CMD ["java", "-jar", "/app/myapp.jar"]Multi-Stage Build
For smaller images, use a multi-stage build:
# Build stage
FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /build
COPY pom.xml .
COPY src ./src
RUN mvn package -DskipTests
# Runtime stage
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY --from=build /build/target/myapp-with-dependencies.jar /app/myapp.jar
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget --quiet --tries=1 --spider http://localhost:8080/health || exit 1
CMD ["java", "-jar", "/app/myapp.jar"]Docker Compose
version: '3.8'
services:
myapp:
build: .
ports:
- "8080:8080"
environment:
- JAVA_OPTS=-Xmx512m
volumes:
- ./config:/etc/myapp:ro
- ./logs:/var/log/myapp
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/health"]
interval: 30s
timeout: 3s
retries: 3
restart: unless-stoppedJVM Options
For containerized deployments, configure JVM memory appropriately:
CMD ["java", "-Xms256m", "-Xmx512m", \
"-XX:+UseContainerSupport", \
"-XX:MaxRAMPercentage=75.0", \
"-jar", "/app/myapp.jar"]11. Kubernetes Integration
LaunchPad's health endpoint integrates naturally with Kubernetes probes for reliable orchestration.
Deployment Manifest
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
labels:
app: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:latest
ports:
- containerPort: 8080
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
env:
- name: JAVA_OPTS
value: "-Xms256m -Xmx512m"
---
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 8080
type: ClusterIPProbe Configuration
- livenessProbe: Restarts the container if it becomes unresponsive. Use a longer initialDelaySeconds to allow for JVM warmup.
- readinessProbe: Determines when the pod is ready to receive traffic. Can use the same health endpoint with shorter intervals.
ConfigMap for External Configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
data:
server.properties: |
server.port=8080
server.threads.min=10
server.threads.max=100
server.health.enabled=true12. Production Best Practices
Reverse Proxy
In production, place a reverse proxy (nginx, HAProxy) in front of LaunchPad for:
- SSL/TLS termination with automatic certificate renewal
- Load balancing across multiple instances
- Static file caching
- Request rate limiting
- Security headers
Example nginx Configuration
upstream myapp {
server 127.0.0.1:8080;
keepalive 32;
}
server {
listen 443 ssl http2;
server_name myapp.example.com;
ssl_certificate /etc/letsencrypt/live/myapp.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/myapp.example.com/privkey.pem;
location / {
proxy_pass http://myapp;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}JVM Tuning
java -Xms512m -Xmx1g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/var/log/myapp/ \
-jar myapp.jarLogging
Configure structured logging for production monitoring. Use JSON format for easy parsing by log aggregation systems like ELK Stack or Splunk.
Monitoring
LaunchPad supports JMX monitoring out of the box. Connect with JConsole or VisualVM to monitor:
- Thread pool utilization
- Connection counts
- Request statistics
- JVM memory and GC metrics
13. Troubleshooting
Address Already in Use
If you see "Address already in use" errors, another process is using the port. The shutdown port mechanism should handle this during development, but you can also:
# Find process using port 8080
lsof -i :8080 # macOS/Linux
netstat -ano | findstr :8080 # Windows
# Kill the process
kill -9 <PID> # macOS/Linux
taskkill /PID <PID> /F # WindowsOut of Memory Errors
If you encounter OutOfMemoryError, increase heap size:
java -Xmx1g -jar myapp.jarFor thread-related OOM errors, reduce maxThreads or increase thread stack size:
java -Xss512k -jar myapp.jarSSL Handshake Failures
Common causes of SSL issues:
- Incorrect keystore password
- Expired certificate
- Certificate doesn't match hostname
- Missing intermediate certificates
Connection Timeouts
If clients experience timeouts, check:
- Thread pool exhaustion (increase maxThreads)
- Slow downstream services
- Network issues or firewall rules
- Idle timeout too aggressive
14. Migration from WAR Deployment
Migrating an existing Oorian application from WAR deployment to LaunchPad is straightforward.
Step 1: Add LaunchPad Dependency
Add the LaunchPad library to your project (see Project Setup section).
Step 2: Create Main Class
Create a new class with a main method:
package com.mycompany.myapp;
import com.oorian.launchpad.LaunchPad;
public class Main
{
public static void main(String[] args)
{
LaunchPad launcher = new LaunchPad();
launcher.setPort(8080);
launcher.launch(MyWebApp.class); // Your existing Application class
}
}Step 3: Update Build Configuration
Configure your build to produce an executable JAR instead of (or in addition to) a WAR file. Use Maven Shade plugin or Gradle Shadow plugin.
Step 4: Externalize Configuration
Move any container-specific configuration (from web.xml or server configs) to LaunchPad's programmatic API or configuration file.
What Stays the Same
- Your Application class
- All your @Page annotated pages
- Event handlers and listeners
- Extension library usage
- All Oorian features (AJAX, SSE, WebSocket)
What Changes
- Deployment: single JAR instead of WAR to container
- Configuration: LaunchPad API instead of container config
- Running:
java -jarinstead of container startup