Home / Documentation / LaunchPad User's Guide

LaunchPad User's Guide

Complete guide to deploying Oorian applications as self-contained executable JARs. Learn configuration, deployment strategies, and production best practices.

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:

Java
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:

Java
@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 (or oorian-j2ee.jar for Java EE)
  • oorian-launchpad.jar — The LaunchPad embedded server library
  • jetty-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:

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:

bash
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:

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:

bash
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 configuration

3. 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:

bash
java -jar MyWebApp.jar

That'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

MethodDefaultDescription
setPort(int)8080HTTP port
setShutdownPort(int)8005Port for IDE restart signal
setContextPath(String)/Application context path
setThreadPool(int, int)10, 200Min and max threads
setIdleTimeout(int)30000Connection idle timeout (ms)
setGzipEnabled(boolean)trueGZIP compression
setHealthEndpointEnabled(boolean)trueHealth check endpoint
setHealthEndpointPath(String)/healthHealth endpoint path
setResourceBase(String)nullStatic files directory
setTempDirectory(String)nullTemp directory for uploads

Example Configuration

Java
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

Java
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:

bash
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:

Java
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

Java
launcher.setThreadPool(10, 200)    // min=10, max=200 threads
        .setIdleTimeout(30000);    // 30 second idle timeout

Understanding 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:

bash
$ curl http://localhost:8080/health
OK

Customization

Java
// 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:

bash
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

Java
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:

Java
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

Java
LaunchPad launcher = new LaunchPad();
launcher.setConfigFile("/etc/myapp/server.properties");
launcher.launch(MyWebApp.class);

Configuration File Format

properties
# 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.log

Override Precedence

Programmatic settings always override file settings. This allows you to load a base configuration file while overriding specific values for development or testing:

Java
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

bash
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:

bash
# 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

bash
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-stopped

JVM Options

For containerized deployments, configure JVM memory appropriately:

bash
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

bash
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: ClusterIP

Probe 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

bash
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=true

12. 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

bash
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

bash
java -Xms512m -Xmx1g \
     -XX:+UseG1GC \
     -XX:MaxGCPauseMillis=200 \
     -XX:+HeapDumpOnOutOfMemoryError \
     -XX:HeapDumpPath=/var/log/myapp/ \
     -jar myapp.jar

Logging

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:

bash
# 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    # Windows

Out of Memory Errors

If you encounter OutOfMemoryError, increase heap size:

bash
java -Xmx1g -jar myapp.jar

For thread-related OOM errors, reduce maxThreads or increase thread stack size:

bash
java -Xss512k -jar myapp.jar

SSL 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:

Java
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 -jar instead of container startup