Struts is as reliable as ever, and integrating ClickHouse makes your Java web app analytics powerhouse-ready. Here’s how to set it up—locally with Docker, and securely in production using Kubernetes—without complicating your Struts codebase.
1. Add Dependencies to pom.xml
Start by bringing in the essentials:
<dependencies>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>${struts.version}</version>
</dependency>
<dependency>
<groupId>com.clickhouse</groupId>
<artifactId>clickhouse-jdbc</artifactId>
<version>${clickhouse.jdbc.version}</version>
</dependency>
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-client</artifactId>
<version>${fabric8.version}</version>
</dependency>
</dependencies>
That gives you Struts for web logic, the ClickHouse JDBC driver for database access, and the Kubernetes client for managing production environment variables.
2. Run ClickHouse Locally with Docker
Docker makes local setup a breeze:
docker run -d \
--name clickhouse-server \
-p 9000:9000 \
-p 8123:8123 \
clickhouse/clickhouse-server:latest
This spins up a ClickHouse container exposing ports for both native (9000) and HTTP (8123) protocols.
3. Create Dev Configuration: clickhouse-dev.properties
In
src/main/resources
, add:
db.driver=com.clickhouse.jdbc.ClickHouseDriver
db.url=jdbc:clickhouse://localhost:8123/default
db.user=
db.password=
db.validationQuery=SELECT 1
Load it in your Struts helper/DAO:
Properties props = new Properties();
props.load(getClass().getResourceAsStream("/clickhouse-dev.properties"));
Class.forName(props.getProperty("db.driver"));
try (Connection conn = DriverManager.getConnection(
props.getProperty("db.url"),
props.getProperty("db.user"),
props.getProperty("db.password"));
Statement stmt = conn.createStatement()) {
stmt.executeQuery(props.getProperty("db.validationQuery"));
}
This ensures development connectivity to ClickHouse running in Docker.
4. Set Up Kubernetes Config for Production
In production, externalize configs with ConfigMaps and Secrets:
kubectl create configmap clickhouse-config \
--from-literal=DB_DRIVER=com.clickhouse.jdbc.ClickHouseDriver \
--from-literal=DB_URL=jdbc:clickhouse://clickhouse-svc:8123/proddb \
--from-literal=DB_VALIDATION_QUERY=SELECT 1
kubectl create secret generic clickhouse-secret \
--from-literal=DB_USER=produser \
--from-literal=DB_PASSWORD=prodpass
This lets you securely inject production credentials and endpoint settings at runtime.
5. Create Production Configuration File: clickhouse-prod.properties
Include this with your app:
db.driver=${DB_DRIVER}
db.url=${DB_URL}
db.user=${DB_USER}
db.password=${DB_PASSWORD}
db.validationQuery=${DB_VALIDATION_QUERY}
At startup, Struts will pick up environment variables supplied by Kubernetes.
6. Configuration in Kubernetes Deployment
Ensure your pod spec includes these environment variables:
env:
- name: DB_DRIVER
valueFrom:
configMapKeyRef:
name: clickhouse-config
key: DB_DRIVER
- name: DB_URL
valueFrom:
configMapKeyRef:
name: clickhouse-config
key: DB_URL
- name: DB_VALIDATION_QUERY
valueFrom:
configMapKeyRef:
name: clickhouse-config
key: DB_VALIDATION_QUERY
- name: DB_USER
valueFrom:
secretKeyRef:
name: clickhouse-secret
key: DB_USER
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: clickhouse-secret
key: DB_PASSWORD
No code changes required—only config files differ.
7. Struts Action to Verify Connection
Try this Struts action to test the setup:
public class DbCheckAction extends ActionSupport {
public String execute() {
Properties p = new Properties();
String file = System.getenv("DB_URL") != null
? "/clickhouse-prod.properties"
: "/clickhouse-dev.properties";
p.load(getClass().getResourceAsStream(file));
Class.forName(p.getProperty("db.driver"));
try (Connection conn = DriverManager.getConnection(
p.getProperty("db.url"),
p.getProperty("db.user"),
p.getProperty("db.password"));
Statement stmt = conn.createStatement()) {
stmt.executeQuery(p.getProperty("db.validationQuery"));
addActionMessage("Connected to ClickHouse successfully!");
return SUCCESS;
} catch (Exception e) {
addActionError("Connection failed: " + e.getMessage());
return ERROR;
}
}
}
Clean, readable, and works across both environments.
Why This Works
- Quick local setup via Docker.
- Production-grade config using Kubernetes ConfigMaps and Secrets.
- No branching logic in code—just different
.properties
files.
- Consistent environment structure reduces configuration errors.
image quote pre code