Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,23 @@ updates:
day: "monday"
time: "10:00"
open-pull-requests-limit: 5
groups:
gradle-quality-stack:
patterns:
- "com.github.spotbugs*"
- "org.junit*"
- "org.awaitility*"
jackson:
patterns:
- "com.fasterxml.jackson*"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "10:30"
open-pull-requests-limit: 5
groups:
github-actions:
patterns:
- "*"
25 changes: 25 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,31 @@ jobs:
gradle-version: "8.10.2"
- name: Run tests and checks
run: ./gradlew check jacocoAllReport --stacktrace
- name: Publish coverage summary
if: matrix.os == 'ubuntu-latest'
shell: bash
run: |
report="build/reports/jacoco/jacocoAllReport/jacocoAllReport.csv"
awk -F, '
NR > 1 {
branchMissed += $6
branchCovered += $7
lineMissed += $8
lineCovered += $9
}
END {
lineTotal = lineMissed + lineCovered
branchTotal = branchMissed + branchCovered
lineCoverage = lineTotal == 0 ? 100 : (lineCovered * 100 / lineTotal)
branchCoverage = branchTotal == 0 ? 100 : (branchCovered * 100 / branchTotal)
print "### JaCoCo coverage"
print ""
print "| Metric | Coverage |"
print "| --- | ---: |"
printf "| Lines | %.2f%% |\n", lineCoverage
printf "| Branches | %.2f%% |\n", branchCoverage
}
' "$report" >> "$GITHUB_STEP_SUMMARY"
- name: Upload coverage report
if: matrix.os == 'ubuntu-latest'
uses: actions/upload-artifact@v4
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## Unreleased

- Added `ChatServerConfig` with server limits and socket timeout settings.
- Made server client handling bounded and explicit about busy rejections.
- Split protocol text payloads from display formatting: `data` is raw text, `sender` is the author.
- Fixed GUI connection lifecycle by preserving the base client status update path.
- Added architecture documentation, grouped Dependabot updates, version catalog, and CI coverage summary.

## 1.0.0

- Reworked project into Gradle Java 21 multi-layer architecture.
Expand Down
43 changes: 37 additions & 6 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[![CI](https://github.com/krotname/JavaNetworkChat/actions/workflows/ci.yml/badge.svg)](https://github.com/krotname/JavaNetworkChat/actions/workflows/ci.yml)
[![CodeQL](https://github.com/krotname/JavaNetworkChat/actions/workflows/codeql.yml/badge.svg)](https://github.com/krotname/JavaNetworkChat/actions/workflows/codeql.yml)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/krotname/JavaNetworkChat/badge)](https://securityscorecards.dev/viewer/?uri=github.com/krotname/JavaNetworkChat)
[![coverage](https://img.shields.io/badge/coverage-70%2B-green)](https://github.com/krotname/JavaNetworkChat/actions/workflows/ci.yml)
[![coverage summary](https://img.shields.io/badge/coverage-CI%20summary-blue)](https://github.com/krotname/JavaNetworkChat/actions/workflows/ci.yml)
[![Java](https://img.shields.io/badge/Java-21-007396)](https://adoptium.net/)
[![License](https://img.shields.io/badge/license-GPL--3.0-blue)](LICENSE)

Expand All @@ -17,6 +17,8 @@ Network Chat is a Java 21 chat application over TCP sockets with:
- a Swing GUI client using MVC style structure.
- a production-like project layout with Gradle, tests, and CI.

![Swing GUI client](docs/images/gui-client.svg)

## Run locally

### Prerequisites
Expand All @@ -34,12 +36,27 @@ Network Chat is a Java 21 chat application over TCP sockets with:

Server and clients can also be run directly with Java by building jars from Gradle.

The default server port is `1500`. Programmatic server startup can use `ChatServerConfig` to set the
port, maximum client count, handshake timeout, and post-handshake read timeout.

## Architecture and protocol

The compact architecture contract is documented in [docs/architecture.md](docs/architecture.md).

- `ChatServer` accepts TCP connections and handles clients in a bounded executor.
- `ChatConnection` reads and writes one-line UTF-8 JSON frames.
- `ChatProtocol` serializes `ChatMessage`.
- For `TEXT` messages, `data` contains only raw text and `sender` contains the author.
- Console and Swing clients format display text such as `alice: hello`.
- The bot client reads date/time commands from `data` and uses the author from `sender`.

## Project structure

- `src/main/java` — core application classes.
- `src/test/java` — unit tests.
- `src/integrationTest/java` — protocol and network integration tests.
- `src/uiTest/java` — Swing smoke tests.
- `docs` — architecture notes and visual assets.
- `.github/workflows` — CI, CodeQL and Scorecard workflows.

## Testing
Expand All @@ -51,12 +68,14 @@ Server and clients can also be run directly with Java by building jars from Grad
- `./gradlew jacocoTestCoverageVerification`
- `./gradlew jacocoAllReport` (CI artifact source)

Coverage thresholds are enforced in Gradle and CI.
Coverage thresholds are enforced in Gradle and CI. The HTML JaCoCo report is uploaded as a CI
artifact, and line/branch coverage is published to the GitHub Actions Summary for the Linux job.

### Test strategy

- **Unit tests** (`src/test/java`) check protocol and UI model invariants.
- **Integration tests** (`src/integrationTest/java`) exercise full server/client socket flow with multiple peers.
- **Unit tests** (`src/test/java`) check protocol, bot command handling, and UI model invariants.
- **Integration tests** (`src/integrationTest/java`) exercise full server/client socket flow, handshake
failures, resource limits, timeouts, and multiple peers.
- **UI smoke tests** (`src/uiTest/java`) verify Swing state rendering.
- **Future hardening tests**: contract validation and error-handling matrix can be added in the same
structure.
Expand All @@ -68,9 +87,9 @@ The repository runs:
- `checkstyle` for style and API cleanliness,
- `spotless` for deterministic formatting,
- `spotbugs` for bug-pattern analysis,
- `jaCoCo` line/branch coverage gate on core network/protocol layers (`70%/55%`),
- `jaCoCo` line/branch coverage gate on core network/protocol layers (`80%/65%`),
- GitHub Actions pipeline on Linux + Windows,
- dependency and workflow update signals via Dependabot,
- grouped dependency and workflow update signals via Dependabot,
- CodeQL and OpenSSF Scorecard security scans.

The quality surface is intentionally structured for a public review: clean `main` surface, automated checks,
Expand Down Expand Up @@ -98,3 +117,15 @@ This repository is organized to be review-friendly:
- Security checks via CodeQL and OpenSSF Scorecard.
- Dependency and workflow automation via Dependabot.
- Explicit contributor and security docs.

## Troubleshooting

- `Address already in use`: run the server on another port, for example `./gradlew runServer --args="--port 1600"`.
- GUI does not render in CI: UI smoke tests skip automatically in headless environments.
- Client disconnects immediately: check username uniqueness and nickname length (`3..64`, letters, digits, `_`, `-`).
- Client receives `Server is busy`: the configured `ChatServerConfig.maxClients` limit has been reached.

## Roadmap

- v1.1.x: stabilize protocol/server lifecycle, expand negative tests, and improve documentation.
- Later: rooms, message history, TLS, and persistent accounts as separate product-focused phases.
45 changes: 39 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[![CI](https://github.com/krotname/JavaNetworkChat/actions/workflows/ci.yml/badge.svg)](https://github.com/krotname/JavaNetworkChat/actions/workflows/ci.yml)
[![CodeQL](https://github.com/krotname/JavaNetworkChat/actions/workflows/codeql.yml/badge.svg)](https://github.com/krotname/JavaNetworkChat/actions/workflows/codeql.yml)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/krotname/JavaNetworkChat/badge)](https://securityscorecards.dev/viewer/?uri=github.com/krotname/JavaNetworkChat)
[![coverage](https://img.shields.io/badge/coverage-70%2B-green)](https://github.com/krotname/JavaNetworkChat/actions/workflows/ci.yml)
[![coverage summary](https://img.shields.io/badge/coverage-CI%20summary-blue)](https://github.com/krotname/JavaNetworkChat/actions/workflows/ci.yml)
[![Java](https://img.shields.io/badge/Java-21-007396)](https://adoptium.net/)
[![License](https://img.shields.io/badge/license-GPL--3.0-blue)](LICENSE)

Expand All @@ -21,6 +21,8 @@ Network Chat — это Java 21 приложение для сетевого ч
- GUI клиент на Swing с разделением на MVC;
- структуру продакшн-проекта с Gradle, тестами и CI.

![Swing GUI client](docs/images/gui-client.svg)

## Запуск

```bash
Expand All @@ -30,6 +32,21 @@ Network Chat — это Java 21 приложение для сетевого ч
./gradlew runGuiClient
```

Сервер по умолчанию слушает порт `1500`. Для программного запуска используйте
`ChatServerConfig`: он задаёт порт, максимальное число клиентов, timeout handshake и timeout чтения
после handshake.

## Архитектура и протокол

Краткий архитектурный контракт описан в [docs/architecture.md](docs/architecture.md).

- `ChatServer` принимает TCP-соединения и обрабатывает клиентов в bounded executor.
- `ChatConnection` читает и пишет однострочные UTF-8 JSON frames.
- `ChatProtocol` сериализует `ChatMessage`.
- Для `TEXT` сообщений `data` содержит только исходный текст, а `sender` содержит автора.
- Console и Swing клиенты сами форматируют отображение вида `alice: hello`.
- Bot client отвечает на команды времени/даты по `data`, используя автора из `sender`.

## Тесты и качество

- `./gradlew test`
Expand All @@ -39,27 +56,43 @@ Network Chat — это Java 21 приложение для сетевого ч
- `./gradlew jacocoAllReport`
- `./gradlew jacocoTestCoverageVerification`

В CI HTML-отчёт JaCoCo публикуется как artifact, а line/branch coverage добавляется в GitHub
Actions Summary для Linux job.

## Стратегия тестирования

- **Unit-тесты** (`src/test/java`) — протокол и модель GUI.
- **Интеграционные тесты** (`src/integrationTest/java`) — подключение нескольких клиентов к серверу и обмен сообщениями.
- **Unit-тесты** (`src/test/java`) — протокол, bot-команды, модель GUI.
- **Интеграционные тесты** (`src/integrationTest/java`) — подключение клиентов, handshake, лимиты сервера, timeout и обмен сообщениями.
- **UI smoke тесты** (`src/uiTest/java`) — проверка отрисовки состояния окна чата.
- **План роста** — контракты протокола и матрицы негативных сценариев (подключение дубликатов, некорректные пакеты).
- **План роста** — больше негативных сценариев протокола и проверок отказоустойчивости медленных клиентов.

Для оценки покрытия используется JaCoCo: в CI порог для ядра (`network` + `protocol`) — `80%/65%` (`line`/`branch`).

## Troubleshooting

Для оценки покрытия используется JaCoCo: в CI порог для ядра (`network` + `protocol`) — `70%/55%` (`line`/`branch`).
- `Address already in use`: запустите сервер на другом порту, например `./gradlew runServer --args="--port 1600"`.
- GUI не показывает окно в CI: UI smoke тесты автоматически пропускаются в headless окружении.
- Клиент сразу отключился: проверьте уникальность имени и длину ника (`3..64`, буквы, цифры, `_`, `-`).
- Клиент получил `Server is busy`: достигнут `maxClients` из `ChatServerConfig`.

## Структура репозитория

- `src/main/java` — код приложения.
- `src/test/java` — unit-тесты.
- `src/integrationTest/java` — интеграционные тесты.
- `src/uiTest/java` — smoke тесты UI.
- `docs` — архитектурные заметки и визуальные материалы.
- `.github/workflows` — CI и проверки безопасности.

## Дополнительные сигналы качества

- CI на Linux и Windows.
- Авто-проверки: Checkstyle, Spotless, SpotBugs, JaCoCo.
- Security проверки: CodeQL и OpenSSF Scorecard.
- Dependabot для обновлений зависимостей и Actions.
- Dependabot с группировкой обновлений зависимостей и Actions.
- Явно оформленные файлы `CONTRIBUTING.md` и `SECURITY.md`.

## Roadmap

- v1.1.x: стабилизация protocol/server lifecycle, расширение негативных тестов, улучшение документации.
- Позже: комнаты, история сообщений, TLS и персистентные аккаунты отдельными product-focused этапами.
27 changes: 14 additions & 13 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ plugins {
java
application
jacoco
id("com.diffplug.spotless") version "6.25.0"
alias(libs.plugins.spotless)
id("checkstyle")
id("com.github.spotbugs") version "6.0.12"
alias(libs.plugins.spotbugs)
}

group = "dev.krotname"
version = "1.0.0"
version = "1.1.0-SNAPSHOT"

java {
toolchain {
Expand All @@ -21,17 +21,17 @@ repositories {
}

dependencies {
implementation("com.fasterxml.jackson.core:jackson-databind:2.17.2")
compileOnly("com.github.spotbugs:spotbugs-annotations:4.8.6")
implementation(libs.jackson.databind)
compileOnly(libs.spotbugs.annotations)

testImplementation("org.junit.jupiter:junit-jupiter:5.10.2")
testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2")
testImplementation("org.awaitility:awaitility:4.2.1")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testImplementation(libs.junit.jupiter)
testImplementation(libs.junit.jupiter.params)
testImplementation(libs.awaitility)
testRuntimeOnly(libs.junit.platform.launcher)
}

checkstyle {
toolVersion = "10.12.5"
toolVersion = libs.versions.checkstyle.get()
configFile = file("config/checkstyle/checkstyle.xml")
}

Expand Down Expand Up @@ -119,6 +119,7 @@ tasks.register<JacocoReport>("jacocoAllReport") {
)
reports {
xml.required.set(true)
csv.required.set(true)
html.required.set(true)
}
}
Expand All @@ -128,7 +129,7 @@ tasks.withType<JacocoCoverageVerification>().configureEach {
}

jacoco {
toolVersion = "0.8.12"
toolVersion = libs.versions.jacoco.get()
}

tasks.named<JacocoCoverageVerification>("jacocoTestCoverageVerification") {
Expand Down Expand Up @@ -157,12 +158,12 @@ tasks.named<JacocoCoverageVerification>("jacocoTestCoverageVerification") {
limit {
counter = "LINE"
value = "COVEREDRATIO"
minimum = "0.70".toBigDecimal()
minimum = "0.80".toBigDecimal()
}
limit {
counter = "BRANCH"
value = "COVEREDRATIO"
minimum = "0.55".toBigDecimal()
minimum = "0.65".toBigDecimal()
}
}
}
Expand Down
45 changes: 45 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Network Chat Architecture

Network Chat is a small Java 21 TCP chat application with a deliberately simple runtime shape:

```text
clients <-> ChatConnection <-> ChatProtocol <-> ChatServer
```

## Runtime Flow

1. `ChatServer` opens a `ServerSocket` on the configured port.
2. Each accepted socket is handled by a bounded client executor.
3. The server sends `NAME_REQUEST`, waits for `USER_NAME`, validates uniqueness, then responds with
`NAME_ACCEPTED`.
4. Existing users are sent to the new client as `USER_ADDED` events.
5. `TEXT` messages are broadcast to other clients with raw text in `data` and the author in
`sender`.
6. Closing or failed connections are removed and announced with `USER_REMOVED`.

## Message Frames

Frames are one-line UTF-8 JSON objects serialized by `ChatProtocol`.

- `type` is required for every frame.
- `data` is optional for control frames, but required and non-blank for `TEXT`.
- `sender` carries the author for `TEXT`; clients own display formatting.
- `timestamp` and `messageId` are generated when a frame is created.
- `data` is limited by `ChatMessage.MAX_DATA_LENGTH`.

## Server Limits

`ChatServerConfig` centralizes runtime limits:

- `port` - TCP port, default `1500`.
- `maxClients` - maximum concurrent client handler threads, default `100`.
- `handshakeTimeout` - maximum time to complete username registration, default `10s`.
- `readTimeout` - idle socket read timeout after handshake, default `5m`.

The legacy `new ChatServer(int port)` constructor delegates to `ChatServerConfig.ofPort(port)`.

## Client Model

`ChatClient` owns the socket lifecycle and exposes hooks for console, bot, and Swing clients.
Connection status is updated through a final template method before client-specific UI or console
side effects run, so subclasses cannot skip the shared latch/status update.
18 changes: 18 additions & 0 deletions docs/images/gui-client.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading