# Tomcat Integration 2.1.0

The Tomcat Integration module runs managed Java web applications inside Banalytics Agent.
It starts separate Apache Tomcat 9.0.120 embedded instances in the same JVM, from a private runtime and classloader.
A `TomcatIntegrationThing` owns one such container, its network connectors, TLS configuration, working directory, request statistics, and access log.
Each `TomcatWebApplicationThing` deploys and supervises one WAR in that container.

The runtime implements Servlet 4.0 and uses the `javax.servlet` namespace. It supports traditional Tomcat 9 WAR applications; WARs built only for Tomcat 10+ and `jakarta.servlet` are not compatible.

## Changes in 2.1.0

- Initial release of the Tomcat Integration module.

## Features

- Separate embedded Apache Tomcat 9.0.120 instances, isolated from the Agent's Spring Boot Tomcat
- Servlet 4.0, Expression Language, WebSocket, annotation-processing, and precompiled JSP runtime support
- Multiple independent Tomcat containers with separate connectors and working directories
- HTTP and HTTPS connectors, which can be enabled separately or together
- JKS server keystore selection, key alias selection, and Secret Manager references for passwords
- Optional or required mutual TLS client authentication using a JKS truststore
- Multiple WAR applications per container with unique context paths
- WAR archive validation, SHA-256 version tracking, zip-slip protection, and expansion into generated deployment directories
- Per-application lifecycle and restart handling, so a failed WAR does not stop sibling applications or the container
- Operator-editable application variables exposed as servlet context init-parameters
- Secret Manager references for application variables without exposing resolved values through the API or events
- Initial and periodic HTTP/HTTPS health checks with configurable failure thresholds
- Optional automatic redeployment when the source WAR's SHA-256 changes
- Manual reload, redeploy, and health-check operations
- Bounded in-memory access log with per-application filtering and incremental reads
- Cumulative container and per-context request statistics
- Periodic container statistics events and application deployment lifecycle events

## Things

### TomcatIntegrationThing

Runs one separate embedded Tomcat container, not the Tomcat that serves the Agent's Spring Boot web layer. The Thing owns the connectors shared by all applications in the container, validates that configured ports are available, prepares the private runtime, and starts request statistics collection.

The packaged Tomcat jars are extracted on first use to `<BANALYTICS_HOME>/tomcat-runtime/9.0.120` and loaded through a private classloader. All `TomcatIntegrationThing` instances share this runtime classloader, but each Thing creates a separate Tomcat instance with its own engine name, connectors, base directory, applications, and statistics.

#### Configuration

- `title` (String, required) - container display name
- `host` (String, required, default: `0.0.0.0`) - bind address for enabled connectors
- `httpEnabled` (boolean, default: true) - enable the HTTP connector
- `httpPort` (int, 1-65535, default: 8081) - HTTP listener port; visible when HTTP is enabled
- `httpsEnabled` (boolean, default: false) - enable the HTTPS connector
- `httpsPort` (int, 1-65535, default: 8443) - HTTPS listener port; visible when HTTPS is enabled
- `sslKeystoreThingUuid` (UUID) - running `JksKeystoreThing` containing the HTTPS server key pair; required when HTTPS is enabled
- `sslKeyAlias` (String) - key-pair alias from the selected keystore; when empty, Tomcat selects a usable key entry
- `sslKeystorePassword` (String) - server keystore and key password; supports `${secret:alias}`
- `clientAuth` (ClientAuth, default: `NONE`) - client-certificate policy: `NONE`, `WANT`, or `NEED`
- `sslTruststoreThingUuid` (UUID) - running `JksKeystoreThing` containing trusted client-certificate issuers; required when `clientAuth` is `WANT` or `NEED`
- `sslTruststorePassword` (String) - client truststore password; supports `${secret:alias}`
- `maxThreads` (int, 1-1000, default: 50) - maximum request-processing threads for each enabled connector
- `connectionTimeoutMs` (int, 1000-600000, default: 20000) - connector timeout in milliseconds
- `workingDirectory` (String, required) - existing writable local directory used as the Tomcat base directory
- `accessLogCapacity` (int, 0-10000, default: 500) - maximum configured size of the bounded in-memory request log; the capacity is applied when the container starts
- `statsIntervalSec` (int, 0-3600, default: 60) - interval for publishing `TomcatContainerStatsEvent`; `0` disables periodic statistics events
- `deployJspWarningEnabled` (boolean, default: true) - warn when a selected WAR contains `.jsp` or `.jspx` source files

At least one of `httpEnabled` or `httpsEnabled` must be true. When both connectors are enabled, they must use different ports. Before startup the Thing binds each configured endpoint temporarily to detect conflicts with the Agent, another Thing, or another process.

`clientAuth=WANT` asks clients for a certificate but still accepts a connection without one. `clientAuth=NEED` rejects the TLS handshake unless the client supplies a certificate trusted by the configured truststore. The server keystore and client truststore may reference the same `JksKeystoreThing`, although separate stores are normally easier to rotate and manage.

#### Remote API

Pass the operation name in `params.method`. These operations require read permission.

- `readStatus` - returns lifecycle state, connector settings, base URL, working and runtime directories, Tomcat runtime version, and short status records for applications assigned to the container.
- `readStats` - returns container uptime, request and error counts, traffic, processing times, active requests, and per-context counters.
- `readAccessLog` - returns access-log entries after optional `fromSequence`; optional `contextPath` restricts results to one application.

Each access-log entry contains `sequence`, `timestamp`, `contextPath`, HTTP `method`, request `uri`, response `status`, `durationMs`, `bytesSent`, and `remoteAddr`. The log is held only in memory and is cleared when the container stops. It is intended for current diagnostics, not durable auditing.

Use cases:

- Local application host: bind to `127.0.0.1` when WAR applications are reached only through a local reverse proxy or another Agent component.
- LAN application host: bind to a specific LAN address, or to `0.0.0.0`, when trusted clients must reach the applications directly. Protect the ports with host and network firewall rules.
- HTTPS application host: enable HTTPS, select a server keystore and alias, and use Secret Manager for the password. One connector certificate is shared by every context in the container.
- mTLS application host: select `WANT` during a staged rollout or `NEED` when every direct client must authenticate with a trusted certificate.
- Mixed migration endpoint: enable HTTP and HTTPS together while clients are moved to TLS. Application health probes use local HTTP while it is available.

Operational notes:

- Connector, TLS, thread, timeout, and working-directory changes require a container restart and temporarily affect every application assigned to it.
- The working directory must already exist and be writable. Tomcat writes work, temporary, and expanded application files below it.
- Restarting a referenced keystore or truststore cascades a restart to the container so renewed certificate material is loaded.
- The default container failure policy is stop-on-failure. A busy port, missing keystore, or invalid working directory is not retried indefinitely.
- Request statistics are cumulative from container start. An HTTP response status of 400 or higher increments `errorCount`.
- Per-context statistics contain request count, error count, and total processing time. The business API also calculates container-wide average and maximum processing time.
- When both connectors are enabled, the displayed application base URL prefers HTTPS. Internal health probes prefer HTTP to avoid an unnecessary loopback TLS handshake.
- An HTTPS-only container using `clientAuth=NEED` cannot perform an HTTP health request with a client certificate. In this case application health falls back to Tomcat's deployed-context state.

### TomcatWebApplicationThing

Deploys one WAR archive into a selected `TomcatIntegrationThing`. The application has its own UUID, lifecycle, permissions, deployment state, runtime variables, health policy, and business UI. A deployment failure puts this Thing into error handling without stopping the container or sibling applications.

The Thing validates the source WAR, calculates its SHA-256, expands it into a generated directory below the container workspace, and explicitly registers the resulting context with Tomcat. Tomcat file-system auto-deployment is disabled. Generated directories prevent Tomcat from locking the source WAR and allow the release process to replace it on Windows.

#### Configuration

- `title` (String, required) - application display name
- `tomcatIntegrationUuid` (UUID, required) - active `TomcatIntegrationThing` that hosts the application
- `warUri` (String, required) - local file-system path to a readable `.war` archive
- `contextPath` (String, required) - application URL path; `/` selects the root context
- `healthUrl` (String, default: empty) - health endpoint relative to the context; empty disables HTTP/HTTPS probing
- `healthTimeoutMs` (int, 100-120000, default: 5000) - connect and read timeout for one health request
- `healthIntervalSec` (int, 0-3600, default: 0) - recurring health-check interval; `0` performs only the post-deployment check
- `healthFailureThreshold` (int, 1-100, default: 3) - consecutive failed recurring checks before the Thing enters error handling
- `autoDeployOnWarChange` (boolean, default: false) - compare the source WAR with the deployed SHA-256 and redeploy when it changes

The context path is trimmed and normalized to start with `/`; trailing slashes are removed except for the root context. Named segments may contain ASCII letters, digits, `.`, `_`, `~`, and `-`. The combination of container UUID and normalized context path must be unique.

The WAR must be a readable ZIP archive and contain a `WEB-INF` directory or standard `WEB-INF` content. Expansion rejects entries that would escape the generated deployment directory. The module includes the Jasper runtime needed by precompiled JSPs but intentionally does not include the ECJ JSP compiler. Precompile JSP/JSPX files with JspC or package the required compiler/runtime in the WAR.

#### Application variables

Operator-editable application variables are persisted separately from the Thing's system configuration in `<CONFIG>/instances/<thing-uuid>.tomcat-app`. At application start, each variable is added as a servlet context init-parameter and can be read with `ServletContext.getInitParameter(name)`.

A variable contains `name`, `value`, `secret`, and `description`. When `secret=true`, `value` should be a `${secret:alias}` reference. The reference is resolved only while context parameters are built; remote reads return the reference rather than its resolved value. Resolved secret values are also removed from deployment error text where possible. Variable changes take effect after application redeploy.

#### Health and automatic redeployment

The health endpoint is requested with HTTP `GET`. Status codes from 200 through 399 are healthy; redirects are not followed. The same value is used for connection and read timeouts.

The initial probe runs immediately after every deployment. With a positive `healthIntervalSec`, recurring failures are counted and the application enters the platform's normal error/restart handling after `healthFailureThreshold` consecutive failures. The default application failure policy is exponential-backoff restart.

Automatic WAR change detection compares the current source archive SHA-256 with the hash stored for the deployed application. It runs on the application's scheduled health tick. A non-empty `healthUrl` is therefore required to schedule change detection. If `healthIntervalSec` is `0` while automatic deployment is enabled, the hash is checked every 60 seconds without performing recurring HTTP health checks.

#### Remote API

Pass the operation name in `params.method`. Read operations require read permission; modifying operations require update permission.

- `readStatus` - returns Thing and deployment states, context path, source WAR, deployed SHA-256, deployment time, health state, last health-check time, last error, and application URL.
- `readVariables` - lists application variables; secret references are returned unresolved.
- `updateVariable` - adds or updates a variable. The response contains the legacy `reloadRequired` flag; use `updateRedeploy` to apply the changed variable values.
- `updateDeleteVariable` - removes a variable. The response contains the legacy `reloadRequired` flag; use `updateRedeploy` to apply the changed variable values.
- `readAccessLog` - returns this application's access-log entries after optional `fromSequence`.
- `updateReload` - reloads the existing Tomcat context, emits reload lifecycle events, and checks health.
- `updateRedeploy` - undeploys the context, expands the source WAR again, records its new SHA-256, deploys it, and checks health.
- `updateHealthCheck` - runs a health check immediately and returns the current result and last error.

Use cases:

- Operator portal: deploy a self-contained Tomcat 9 WAR at a stable path such as `/portal` and provide a lightweight unauthenticated readiness endpoint such as `/health`.
- Existing `javax.servlet` application: place the release WAR on the Agent's local file system, package all application dependencies inside it, and manage its context independently from other applications.
- Site-specific configuration: expose environment-specific non-secret values and Secret Manager references as servlet context init-parameters instead of rebuilding the WAR for every site. Redeploy the application after changing them.
- Controlled delivery: replace the source WAR atomically and trigger `updateRedeploy`, or enable SHA-256 change detection when the deployment pipeline manages the selected file.
- Multiple applications: assign several application Things to the same container using unique named contexts. Use separate containers when applications require different ports, certificates, trust policies, thread limits, or failure boundaries.

Operational notes:

- Starting or restarting the container cascades lifecycle handling to its subscribed application Things.
- Reload restarts the existing context. Redeploy expands the source archive into a new directory and replaces the context; it is also required to apply changed application variables.
- The deployed SHA-256 identifies the exact source WAR content, not an application-provided version string.
- Stopping the application undeploys its context and emits `UNDEPLOYED`. Destroying the Thing also removes its generated deployment directory and persisted application-variable file.
- A health request is sent over local HTTP whenever the container has an HTTP connector, even if HTTPS is also enabled.
- Without `healthUrl`, health means that Tomcat reports the context as deployed.
- The WAR is trusted code. The runtime exposes a narrow Agent classloader bridge and places `BoxEngine` in the servlet context under its fully qualified class name, allowing approved applications to integrate with Agent events. Deploy only reviewed WAR artifacts.
- Application authentication, authorization, session security, input validation, and data access remain the responsibility of the WAR.

## Tasks

This module does not define any tasks.

## Actions

This module does not define any actions.

## Events

### TomcatContainerStatsEvent

Published periodically by `TomcatIntegrationThing` when `statsIntervalSec` is greater than zero. The event type is `EVT_TOMCAT_STATS`. All counters except `activeRequests` are cumulative from container start, which makes successive samples suitable for rate calculation and charting.

- `requestCount` (long) - total completed requests
- `errorCount` (long) - responses with HTTP status 400 or higher
- `bytesSent` (long) - response bytes written
- `bytesReceived` (long) - request bytes read
- `avgProcessingTimeMs` (long) - total processing time divided by completed request count
- `maxProcessingTimeMs` (long) - longest completed request time
- `activeRequests` (int) - requests currently being processed when the sample is created
- `uptimeMs` (long) - elapsed time since the container statistics valve started

The inherited node metadata identifies the producing `TomcatIntegrationThing`. Container lifecycle changes are not duplicated in this event because standard `StatusEvent` records the Thing's state transitions and error description.

### TomcatWebApplicationDeploymentEvent

Published by `TomcatWebApplicationThing` during deployment, reload, undeployment, and failure transitions. The event type is `EVT_TOMCAT_DEPLOYMENT`. The inherited node metadata identifies the application Thing; `containerUuid` identifies its separate host container.

- `state` (DeploymentState) - `DEPLOYING`, `DEPLOYED`, `RELOADING`, `UNDEPLOYED`, or `FAILED`
- `contextPath` (String) - normalized application context
- `containerUuid` (UUID) - hosting `TomcatIntegrationThing`
- `warHash` (String) - SHA-256 of the deployed WAR known to the application
- `errorText` (String) - sanitized deployment or reload failure reason; empty for successful transitions

`state`, `contextPath`, and `containerUuid` are exposed as Event Manager filter fields. Deployment failure also participates in the Thing's normal lifecycle and standard `StatusEvent` handling.
