Skip to main content

plantpulse-server (Web Console + IIoT Engine)

Role

The core module that serves as the face and brain of the Platform. The web console that operators view, the REST API that external systems call, and the IIoT engine that actually processes industrial data all run within this single module.

ItemValue
Module nameplantpulse-server
Containerplantpulse-server-web (own image, own container)
Path in container/opt/kopens/plantpulse-platform/plantpulse-server/
User access80 / 443plantpulse-proxy receives and forwards to this container
Admin console7443 (HTTPS, published by plantpulse-datalake)
Host portNone — runs only behind a proxy
Memory limitDOCKER_SERVER_MEMORY default 16g / heap DOCKER_SERVER_HEAP default 12g
RuntimeApache Tomcat 9 + Java 21
Throughput40,000 messages per second (36-thread pipeline)
This container does not open host ports

User traffic always goes through plantpulse-proxy (80/443). This means even if the backend moves or the name changes, you don't need to touch user or equipment settings. Container state is viewed via bin/status.sh, and logs via bin/logs.sh plantpulse-server-web.

Responsibility area

Directory structure

plantpulse-server/
├── config/ # 서버 관리 설정 (외부화 — 배포해도 유지)
│ ├── plantpulse-engine.properties # IIoT 엔진 튜닝
│ ├── plantpulse-storage.properties # DB 연결
│ ├── plantpulse-mq.properties # 메시징 연결
│ ├── plantpulse-mail.properties # SMTP
│ ├── plantpulse-ai.properties # AI 게이트웨이
│ └── log4j2.xml # 로깅 설정 (자가 시딩 — 최초 기동 시 생성)
├── bin/
│ ├── start.sh
│ ├── stop.sh
│ └── log-viewer.sh
├── logs/
│ └── system.log
├── path/ # 경로 매핑
└── server/ # 내장 Tomcat
├── conf/
│ ├── server.xml # 커넥터 / 포트
│ ├── context.xml # 컨텍스트 / 데이터소스
│ ├── catalina.properties
│ ├── logging.properties
│ ├── tomcat-users.xml
│ ├── web.xml
│ └── Catalina/localhost/
│ └── ROOT.xml # 컨텍스트 설정 (sessionCookieName 등)
└── webapps/
├── ROOT.war # 웹 애플리케이션 (WAR 통배포)
└── ROOT/ # Tomcat 이 unpack 한 디렉토리 (수정 금지)

Deployment method: The web application is deployed via server/webapps/ROOT.war replacement method (full WAR deployment, transition in progress). The legacy app/plantpulse-server-web exploded directory method is deprecated, and Context docBase override in server.xml has been removed. Direct modification of files inside ROOT/ will be lost on the next deployment, so configuration changes must always be made in config/.

Key configuration files

Configuration files are located in the module's config/ directory, and the server searches in the order: -Dpp.conf.dir${catalina.base}/../config → classpath (fallback). When external configuration is loaded, Properties loaded from external conf: is printed in the startup log.

application.properties has been removed (2026.06). Console operation settings such as theme and homepage are now managed in the console's System > Configuration Management (mm_config table), and alarm.duplicate.check.minutes has been migrated to plantpulse-engine.properties.

plantpulse-engine.properties

IIoT engine pipeline tuning.

ItemDefaultDescription
engine.pipeline.threads36Number of parallel processing threads
engine.pipeline.ratelimit40000Maximum messages per second
engine.pipeline.batch.size1000Batch size
engine.pipeline.queue.size1200000Queue capacity
engine.dedup.enabledtrueDeduplication enabled

plantpulse-storage.properties

Data storage connection.

# Cassandra
cassandra.host=${PP_CASSANDRA_HOST}
cassandra.port=${PP_CASSANDRA_PORT}
cassandra.keyspace=${PP_KEYSPACE}
cassandra.user=${PP_CASSANDRA_USER}
cassandra.password=${PP_CASSANDRA_PASSWORD}

# PostgreSQL (Spring DataSource)
postgres.url=jdbc:postgresql://${PP_POSTGRES_HOST}:${PP_POSTGRES_PORT}/${PP_DB_NAME}
postgres.user=${PP_PG_USER}
postgres.password=${PP_PG_PASSWORD}

# Valkey
redis.host=${PP_REDIS_HOST}
redis.port=${PP_REDIS_PORT}
redis.password=${PP_REDIS_PASSWORD}

This application does not render configuration. After splitting into containers, the web server starts without the data lake's configuration renderer and reads values from three places — ① external file placed by operators → ② defaults built into the WAR → ③ environment variables passed by compose. Environment variables override built-in defaults.

Therefore, compose/docker-compose.yml is the sole source of truth for what the app sees. It only exists on the data lake side, and names that are only in the data lake but not in compose never reach this app — this was the kind of accident where a site changed identifiers only on the data lake but five app instances kept using the old names (2026-08-29).

Do not modify files inside the web application (server/webapps/ROOT/) — they will be removed during deployment.

server/conf/server.xml

Tomcat connector configuration. Port changes / TLS / compression / thread pool.

<Connector port="80" protocol="HTTP/1.1"
connectionTimeout="20000"
maxThreads="500"
acceptCount="200"
URIEncoding="UTF-8"
compression="on"
compressibleMimeType="text/html,text/css,application/json,application/javascript" />

<Connector port="443" protocol="org.apache.coyote.http11.Http11Nio2Protocol"
SSLEnabled="true"
maxThreads="500"
sslEnabledProtocols="TLSv1.3,TLSv1.2">
<SSLHostConfig>
<Certificate certificateKeystoreFile="/var/security/plantpulse/server.jks"
certificateKeystorePassword="${PP_TLS_KEYSTORE_PASSWORD}" />
</SSLHostConfig>
</Connector>

Operational commands

From host — container level

Bringing a container up is equivalent to restarting this module. Infrastructure and other applications are not affected.

cd /opt/kopens/plantpulse-platform-docker

# 서버만 재시작
docker compose -f compose/docker-compose.yml restart plantpulse-server-web

# 상태 · 로그
bin/status.sh
bin/logs.sh plantpulse-server-web -n 200

Entering the container

cd /opt/kopens/plantpulse-platform-docker/bin
./shell.sh plantpulse-server-web
Not restarted by restart-server.sh from the data lake

That script is a thin delegation that calls the module's launcher within each app's own container. The data lake container has no web server executable, so it ends with an "module not found" error. Use the container-level restart above.

Hot reload (reflect settings without restart)

Some settings can be hot-reloaded from the console's System > Configuration Management menu. Property files in config/ and boot-time settings such as server.xml require a restart.

Logs

LogPathContents
Main loglogs/system.logApplication logic, errors
Tomcat catalinaserver/logs/catalina.outTomcat standard output
Access logserver/logs/localhost_access_log.YYYY-MM-DD.txtHTTP request log
Engine loglogs/engine.logIIoT engine processing details
Slow querylogs/slow.logQueries taking 200ms or longer

Log level change — logging configuration (log4j2.xml) is externalized to module config/ (self-seeding: on first startup, WAR defaults are copied to config/log4j2.xml, and Logging reconfigured from external conf: is printed in the startup log when applied):

# 웹 서버 컨테이너 안에서
cd /opt/kopens/plantpulse-platform-docker/bin
./shell.sh plantpulse-server-web
vi /opt/kopens/plantpulse-platform/plantpulse-server/config/log4j2.xml
# <Logger name="plantpulse" level="DEBUG"/> 등 수정 후 exit

# 호스트에서 그 컨테이너만 재시작
cd /opt/kopens/plantpulse-platform-docker
docker compose -f compose/docker-compose.yml restart plantpulse-server-web

Since it is an external file, it persists even across WAR redeployments. Do not modify the web app internal (server/webapps/ROOT/WEB-INF/classes/log4j2.xml) — it will be removed during deployment.

Performance tuning

JVM heap

# server/bin/setenv.sh (없으면 생성)
export CATALINA_OPTS="-Xms16g -Xmx32g \
-XX:+UseG1GC -XX:MaxGCPauseMillis=200 \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/var/log/plantpulse/heap/ \
-Xlog:gc*:file=/var/log/plantpulse/gc.log:time,uptime:filecount=10,filesize=100M"

Tomcat thread pool

Adjust maxThreads / acceptCount in the Connector of server.xml. For high-concurrency environments, the following is recommended:

Concurrent connectionsmaxThreadsacceptCount
~ 100200100
~ 500500200
500+1000500

IIoT engine throughput

Set engine.pipeline.threads in plantpulse-engine.properties to 70–80% of host core count. If messages are backing up, also increase engine.pipeline.queue.capacity.

For detailed tuning, see the Performance tuning page.

Health check

# 표준 헬스체크 (익명, readiness — 엔진이 전 단계 기동을 완료(RUNNING)했을 때만 UP. 배포 자동검증 / 모니터링용)
curl -fsS http://127.0.0.1/api/health
# 준비 완료: 200 {"status":"UP","service":"plantpulse-server-web","ts":1765500000000,"checks":{"engine":"RUNNING"}}
# 기동 중: 503 {"status":"STARTING",...} / 기동 실패·중지: 503 {"status":"DEGRADED",...}

# 콘솔 ping
curl -fsS http://127.0.0.1/api/v5/ping

# 인증 필요 헬스체크
curl -fsS -u admin:admin123! http://127.0.0.1/api/v5/health

# WebSocket 핸드셰이크 확인
curl -i -N \
-H "Connection: Upgrade" -H "Upgrade: websocket" \
-H "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==" \
-H "Sec-WebSocket-Version: 13" \
http://127.0.0.1:8000/ws

Common issues

SymptomCauseAction
OutOfMemoryErrorHeap shortageIncrease -Xmx in setenv.sh + analyze GC logs
Too many open filesFile descriptor limitSet nofile in /etc/security/limits.conf to 65535+
Console slow responseSlow DB queriesCheck PostgreSQL slow query log, Valkey cache hit rate
Login loopSession / cookie issueClear browser cache, check sessionCookieName in server/conf/Catalina/localhost/ROOT.xml
502 / 504 (reverse proxy)Backend response timeoutIncrease nginx proxy_read_timeout
WebSocket disconnectionFirewall idle timeoutFirewall timeout > 60s, adjust tomcat.websocket.session.timeout

For detailed diagnostics, see the Troubleshooting page.

Security checklist

  • Change default admin password (admin / admin123!)
  • Remove or use strong password for manager account in tomcat-users.xml
  • Block external access to shutdown port (7000) in server.xml
  • Apply HTTPS certificate (prepare-ssl.sh result or external CA)
  • Verify X-Frame-Options / CSP / HSTS headers are set
  • Store API Bearer Token in operations vault
  • Disable debug options and restrict permissions (chmod 600) for config/ property file

For detailed security configuration, see the Security configuration page.