feat: Add complete HSP implementation with integration tests passing

Initial implementation of HTTP Sender Plugin following TDD methodology
  with hexagonal architecture. All 313 tests passing (0 failures).

  This commit adds:
  - Complete domain model and port interfaces
  - All adapter implementations (HTTP, gRPC, file logging, config)
  - Application services (data collection, transmission, backpressure)
  - Comprehensive test suite with 18 integration tests

  Test fixes applied during implementation:
  - Fix base64 encoding validation in DataCollectionServiceIntegrationTest
  - Fix exception type handling in IConfigurationPortTest
  - Fix CompletionException unwrapping in IHttpPollingPortTest
  - Fix sequential batching in DataTransmissionServiceIntegrationTest
  - Add test adapter failure simulation for reconnection tests
  - Use adapter counters for gRPC verification

  Files added:
  - pom.xml with all dependencies (JUnit 5, Mockito, WireMock, gRPC, Jackson)
  - src/main/java: Domain model, ports, adapters, application services
  - src/test/java: Unit tests, integration tests, test utilities
This commit is contained in:
Christoph Wagner
2025-11-20 22:38:55 +01:00
parent 290a3bc99b
commit a489c15cf5
129 changed files with 41197 additions and 0 deletions
+573
View File
@@ -0,0 +1,573 @@
# Code Review Guidelines
**Version**: 1.0
**Project**: HTTP Sender Plugin (HSP)
**Last Updated**: 2025-11-20
## Purpose
This document defines code review standards and processes for the HSP project, ensuring code quality, maintainability, security, and compliance with hexagonal architecture and TDD methodology.
---
## 🎯 Code Review Objectives
1. **Quality Assurance**: Ensure code meets quality standards and best practices
2. **Knowledge Sharing**: Spread knowledge across the team
3. **Bug Prevention**: Catch defects before they reach production
4. **Architecture Compliance**: Verify hexagonal architecture boundaries
5. **TDD Verification**: Ensure Test-Driven Development methodology followed
6. **Security Review**: Identify potential vulnerabilities
7. **Performance Review**: Spot optimization opportunities
8. **Maintainability**: Ensure code is readable and maintainable
---
## 📋 Review Process Overview
### 1. Pull Request Creation
**Developer Responsibilities**:
- [ ] Create feature branch from develop
- [ ] Implement using TDD (RED-GREEN-REFACTOR)
- [ ] Ensure all tests pass locally
- [ ] Run coverage analysis (≥95%/90%)
- [ ] Self-review changes before creating PR
- [ ] Create PR in Gitea with detailed description
**PR Title Format**:
```
[TYPE] Brief description
Examples:
feat: implement BufferManager with FIFO overflow
fix: correct retry logic in HttpPollingAdapter
refactor: improve DataCollectionService naming
test: add integration tests for gRPC transmission
docs: update architecture diagrams
```
**PR Description Template**:
```markdown
## Summary
[Brief description of changes]
## Related Requirements
- Req-FR-XX: [requirement description]
- Req-NFR-XX: [requirement description]
## TDD Compliance
- [ ] Tests written before implementation
- [ ] Git history shows RED-GREEN-REFACTOR cycles
- [ ] All tests passing
- [ ] Coverage: XX% line, XX% branch
## Testing
- [ ] Unit tests added/updated
- [ ] Integration tests added/updated (if applicable)
- [ ] Manual testing performed
## Architecture Impact
- [ ] Follows hexagonal architecture
- [ ] Port interfaces not violated
- [ ] Domain models remain pure
- [ ] Adapters properly isolated
## Checklist
- [ ] Self-reviewed code
- [ ] Documentation updated
- [ ] No commented-out code
- [ ] No debug statements
- [ ] Thread safety verified (if applicable)
- [ ] CI pipeline passing
```
### 2. Reviewer Assignment
**Assignment Rules**:
- At least **2 reviewers required** for all PRs
- **1 senior developer** must review architectural changes
- **QA engineer** must review test changes
- **Security specialist** for security-sensitive code (authentication, data handling)
**Review SLA**:
- **Initial feedback**: Within 4 hours
- **Complete review**: Within 24 hours
- **Critical fixes**: Within 2 hours
### 3. Review Checklist
Reviewers must verify all items in this checklist before approval.
---
## ✅ Code Review Checklist
### 1. TDD Compliance ⚠️ MANDATORY
**Git History Verification**:
- [ ] **Tests committed BEFORE implementation** (check timestamps)
- [ ] Clear RED-GREEN-REFACTOR cycle in commit history
- [ ] Commit messages follow TDD pattern (`test:`, `feat:`, `refactor:`)
- [ ] Test-to-code commit ratio approximately 1:1
- [ ] No large implementation commits without prior test commits
**Test Quality**:
- [ ] Tests follow AAA pattern (Arrange-Act-Assert)
- [ ] Test names describe behavior: `shouldDoX_whenY()`
- [ ] Tests are independent (no shared state)
- [ ] Tests are repeatable (deterministic results)
- [ ] Edge cases covered (boundary conditions, empty inputs, nulls)
- [ ] Error scenarios tested (exceptions, timeouts, failures)
- [ ] Both happy path and sad path tested
**Coverage Verification**:
- [ ] JaCoCo report shows ≥95% line coverage
- [ ] JaCoCo report shows ≥90% branch coverage
- [ ] New code is covered by new tests (not just existing tests)
- [ ] No critical paths left untested
- [ ] CI pipeline coverage check passes
### 2. Hexagonal Architecture Compliance ⚠️ MANDATORY
**Port Boundary Enforcement**:
- [ ] **Ports not bypassed**: Application core only depends on port interfaces
- [ ] **No adapter imports in core**: Domain services don't import adapters
- [ ] **Port interfaces defined**: All external interactions through ports
- [ ] **Dependency direction correct**: Core → Ports ← Adapters (not Core → Adapters)
**Domain Model Purity**:
- [ ] **No infrastructure in domain**: No HTTP, gRPC, file I/O in domain models
- [ ] **Immutable value objects**: All domain models are immutable (final fields, no setters)
- [ ] **Business logic in core**: Not in adapters
- [ ] **Domain-specific language used**: Clear, business-focused names
**Adapter Isolation**:
- [ ] **Adapters implement ports**: Each adapter implements one or more port interfaces
- [ ] **Infrastructure isolated**: HTTP, gRPC, file I/O only in adapters
- [ ] **Adapters don't talk directly**: Communication through application core
- [ ] **Configuration injected**: No hardcoded values in adapters
**Package Structure Verification**:
```
✓ CORRECT:
com.siemens.coreshield.hsp.
├── domain/ (models, pure Java)
├── application/ (services, depends on ports)
├── ports/
│ ├── inbound/ (primary ports)
│ └── outbound/ (secondary ports)
└── adapter/
├── inbound/ (implements primary ports)
└── outbound/ (implements secondary ports)
✗ WRONG:
application.DataCollectionService imports adapter.HttpPollingAdapter
domain.DiagnosticData imports io.grpc.stub
```
### 3. Thread Safety (Concurrency Requirements)
**Concurrency Review** (CRITICAL for HSP):
- [ ] **Thread-safe where required**: BufferManager, CollectionStatistics
- [ ] **Immutability used**: Value objects are thread-safe by design
- [ ] **Proper synchronization**: `synchronized`, `Lock`, or concurrent collections
- [ ] **Atomic operations**: Use `AtomicLong`, `AtomicInteger` for counters
- [ ] **No race conditions**: Shared state properly protected
- [ ] **Virtual threads used correctly**: For I/O-bound tasks (HTTP polling)
- [ ] **Stress tests exist**: Concurrent tests with 1000+ threads (if applicable)
**Common Thread Safety Issues**:
```java
WRONG:
public class CollectionStatistics {
private int totalPolls = 0; // Not thread-safe!
public void incrementPolls() {
totalPolls++; // Race condition
}
}
CORRECT:
public class CollectionStatistics {
private final AtomicInteger totalPolls = new AtomicInteger(0);
public void incrementPolls() {
totalPolls.incrementAndGet();
}
}
```
### 4. Code Quality
**Readability**:
- [ ] Clear, descriptive variable names (not `x`, `tmp`, `data123`)
- [ ] Methods are small (<30 lines preferred)
- [ ] Classes are focused (Single Responsibility Principle)
- [ ] No commented-out code (use Git history instead)
- [ ] No debug statements (`System.out.println`, excessive logging)
- [ ] Complex logic has explanatory comments
**SOLID Principles**:
- [ ] **Single Responsibility**: Each class has one reason to change
- [ ] **Open/Closed**: Open for extension, closed for modification
- [ ] **Liskov Substitution**: Subtypes are substitutable for base types
- [ ] **Interface Segregation**: Interfaces are client-specific, not fat
- [ ] **Dependency Inversion**: Depend on abstractions (ports), not concretions
**DRY (Don't Repeat Yourself)**:
- [ ] No duplicated code (extract to methods/utilities)
- [ ] No copy-paste programming
- [ ] Common logic extracted to reusable components
**Error Handling**:
- [ ] Exceptions used appropriately (not for control flow)
- [ ] Custom exceptions for domain errors
- [ ] Resources cleaned up (try-with-resources, finally blocks)
- [ ] Error messages are descriptive and actionable
- [ ] Logging at appropriate levels (ERROR, WARN, INFO, DEBUG)
### 5. Performance
**Algorithm Efficiency**:
- [ ] Appropriate data structures used (O(1) for lookups if possible)
- [ ] No unnecessary iterations (nested loops reviewed)
- [ ] No premature optimization (profile first)
- [ ] Database queries optimized (no N+1 queries, though HSP has no DB)
**Resource Management**:
- [ ] No memory leaks (collections not unbounded)
- [ ] Connections properly closed (HTTP clients, gRPC channels)
- [ ] Streams closed (try-with-resources)
- [ ] Thread pools bounded (use virtual threads or fixed pools)
**Caching** (if applicable):
- [ ] Appropriate cache eviction strategy
- [ ] Thread-safe cache access
- [ ] Cache invalidation logic correct
### 6. Security
**Input Validation**:
- [ ] **All external inputs validated** (HTTP responses, configuration)
- [ ] Size limits enforced (Req-FR-21: 1MB limit)
- [ ] URL validation (protocol, host, port)
- [ ] JSON validation (schema compliance)
- [ ] No injection vulnerabilities (though HSP has limited attack surface)
**Data Handling**:
- [ ] **Sensitive data not logged** (no raw data in logs)
- [ ] Base64 encoding used for binary data (Req-FR-22)
- [ ] No credentials in code (use configuration)
- [ ] Configuration file permissions documented
**Error Information Disclosure**:
- [ ] Error messages don't expose internals
- [ ] Stack traces not sent to external systems
- [ ] Logs sanitized (no sensitive data)
### 7. Documentation
**Code Documentation**:
- [ ] **Javadoc on all public APIs** (classes, methods, interfaces)
- [ ] **Requirement traceability in Javadoc**: `@requirement Req-FR-XX`
- [ ] Complex algorithms explained (comments or Javadoc)
- [ ] Non-obvious behavior documented
- [ ] TODOs tracked (with ticket numbers if applicable)
**Javadoc Example**:
```java
/**
* Polls the specified HTTP endpoint and returns the response data.
*
* <p>This method implements the HTTP polling logic with retry and backoff
* strategy as specified in requirements Req-FR-17 and Req-FR-18.
*
* @requirement Req-FR-14 HTTP endpoint polling
* @requirement Req-FR-17 Retry mechanism (3 attempts, 5s interval)
* @requirement Req-FR-18 Linear backoff (5s to 300s)
*
* @param url the HTTP endpoint URL to poll
* @return a CompletableFuture containing the response data
* @throws PollingException if all retry attempts fail
*/
CompletableFuture<byte[]> pollEndpoint(String url);
```
**README/Documentation Updates**:
- [ ] README updated if public APIs changed
- [ ] Architecture diagrams updated if structure changed
- [ ] Configuration examples updated if config changed
- [ ] Operations documentation updated if deployment changed
### 8. Testing
**Unit Tests**:
- [ ] All public methods tested
- [ ] Edge cases covered (empty, null, boundary values)
- [ ] Exception handling tested
- [ ] Mock objects used appropriately (not over-mocked)
- [ ] Tests are fast (<100ms per test for unit tests)
**Integration Tests**:
- [ ] Component boundaries tested (adapter ↔ application core)
- [ ] External systems mocked (WireMock for HTTP, gRPC test server)
- [ ] Retry and backoff logic tested
- [ ] Timeout behavior tested
- [ ] Failure scenarios tested
**Test Naming**:
```java
GOOD:
shouldRetryThreeTimes_whenHttpReturns500()
shouldDiscardOldest_whenBufferFull()
shouldRejectData_whenSizeExceeds1MB()
BAD:
testBuffer()
testMethod1()
test_success()
```
### 9. Configuration and Deployment
**Configuration**:
- [ ] No hardcoded values (use configuration)
- [ ] Environment-specific values externalized
- [ ] Configuration validated at startup (Req-FR-11)
- [ ] Configuration schema documented
**Build and Deployment**:
- [ ] Maven build succeeds: `mvn clean package`
- [ ] All tests pass: `mvn test`
- [ ] Coverage check passes: `mvn jacoco:check`
- [ ] Fat JAR builds correctly (if applicable)
- [ ] No build warnings (deprecations, unchecked casts)
---
## 🔍 Review Depth by Change Type
### Minor Changes (< 50 lines)
- Quick review (15-30 minutes)
- Focus on correctness and tests
- One reviewer sufficient
### Medium Changes (50-200 lines)
- Detailed review (30-60 minutes)
- Full checklist review
- Architecture impact assessment
- Two reviewers required
### Major Changes (> 200 lines)
- In-depth review (1-2 hours)
- Break into smaller PRs if possible
- Architecture review session if needed
- Two reviewers + architect approval
### Critical Components
**Always require senior review**:
- BufferManager (thread safety critical)
- DataTransmissionService (data loss prevention)
- GrpcStreamAdapter (protocol correctness)
- ConfigurationManager (system stability)
- Security-related code
---
## 💬 Review Feedback Guidelines
### How to Provide Feedback
**Be Constructive**:
- Focus on the code, not the person
- Explain WHY something is an issue
- Provide concrete suggestions or alternatives
- Acknowledge good practices
**Use Clear Categories**:
- **🚨 BLOCKER**: Must be fixed before merge (security, correctness)
- **⚠️ MAJOR**: Should be fixed before merge (quality, maintainability)
- **💡 SUGGESTION**: Consider improving (nice-to-have)
- **❓ QUESTION**: Clarification needed
- **👍 PRAISE**: Good work, best practice
**Example Feedback**:
```markdown
🚨 BLOCKER: Thread Safety Issue
Line 45: `totalPolls++` is not thread-safe.
Use `AtomicInteger.incrementAndGet()` instead.
This could cause incorrect statistics under concurrent access.
⚠️ MAJOR: Missing Null Check
Line 120: `data.getBytes()` could throw NPE if data is null.
Add validation: `Objects.requireNonNull(data, "data cannot be null")`
💡 SUGGESTION: Consider Extracting Method
Lines 200-250: This method is 50 lines long and does multiple things.
Consider extracting validation logic to `validateConfiguration()`.
👍 PRAISE: Excellent Test Coverage
Great job on the comprehensive edge case testing! The boundary value
tests (lines 50-75) are exactly what we need for high-quality code.
```
### How to Receive Feedback
**Be Professional**:
- Don't take feedback personally
- Ask for clarification if unclear
- Discuss disagreements respectfully
- Update code based on feedback
- Respond to all comments (even "Done" is helpful)
**Address All Feedback**:
- Fix blockers immediately
- Discuss major issues if disagreement
- Consider suggestions (but can defer)
- Answer questions thoroughly
---
## 🎯 Review Priorities (What to Focus On)
### Priority 1: CRITICAL (Must Review)
1. **TDD Compliance**: Tests before code
2. **Thread Safety**: Concurrent access correctness
3. **Architecture Boundaries**: Hexagonal architecture
4. **Security**: Input validation, data handling
5. **Correctness**: Logic errors, edge cases
### Priority 2: IMPORTANT (Should Review)
6. **Test Coverage**: 95%/90% thresholds
7. **Error Handling**: Exception handling, resource cleanup
8. **Performance**: Algorithm efficiency, resource management
9. **Documentation**: Javadoc, requirement traceability
10. **Code Quality**: SOLID principles, readability
### Priority 3: NICE-TO-HAVE (Good to Review)
11. **Style**: Consistent formatting (automate with Checkstyle)
12. **Naming**: Improved variable names
13. **Comments**: Additional explanatory comments
14. **Refactoring**: Simplification opportunities
---
## 🚀 Approval Criteria
**Code can be merged when**:
- [ ] **All blockers resolved** (no outstanding 🚨 issues)
- [ ] **At least 2 approvals** (or 1 if minor change)
- [ ] **CI pipeline green** (all tests passing, coverage met)
- [ ] **TDD compliance verified** (tests before code in Git history)
- [ ] **Architecture compliance verified** (hexagonal boundaries respected)
- [ ] **Documentation updated** (Javadoc, README if needed)
- [ ] **All reviewer comments addressed** (or explicitly deferred with reason)
**Merge Process**:
1. Developer resolves all feedback
2. Reviewers re-review and approve
3. CI pipeline passes (automated check)
4. Developer merges to develop branch (or tech lead merges to main)
5. Delete feature branch after merge
---
## 📊 Review Metrics
### Track Per Sprint
| Metric | Target | Purpose |
|--------|--------|---------|
| Average Review Time | <24h | Responsiveness |
| Review Thoroughness | 100% checklist | Completeness |
| Blockers Found | Track trend | Code quality indicator |
| TDD Compliance Rate | 100% | Process adherence |
| PRs Requiring Rework | <30% | Quality of first submission |
### Review Effectiveness Indicators
- **Bugs found in review vs. post-merge**: Higher ratio = better reviews
- **Time to merge**: Faster = efficient process
- **Review comments per PR**: Too high = need better self-review; too low = superficial review
---
## 🛠️ Review Tools
### Gitea Code Review Features
- **Line-by-line comments**: Comment on specific code lines
- **Review status**: Request changes, approve, comment
- **PR templates**: Use standardized PR descriptions
- **Labels**: Tag PRs (bug, feature, refactor, etc.)
- **Assignees**: Assign specific reviewers
### IDE Integration
- **IntelliJ IDEA**: Gitea plugin for in-IDE reviews
- **Eclipse**: EGit for Git integration
- **VS Code**: GitLens for Git history visualization
### Automated Checks (CI Pipeline)
- **Compile Check**: Code compiles without errors
- **Unit Tests**: All tests pass
- **Coverage Check**: JaCoCo enforces 95%/90%
- **Checkstyle**: Code style compliance (if configured)
- **SpotBugs**: Static analysis for bugs (if configured)
---
## 🎓 Training and Resources
### New Reviewers
- Pair with senior reviewer for first 5 PRs
- Review this document thoroughly
- Practice with historical PRs
- Ask questions in team chat
### Internal Resources
- [TDD Compliance Checklist](TDD_COMPLIANCE_CHECKLIST.md)
- [Thread Safety Guidelines](THREAD_SAFETY_GUIDELINES.md)
- [Pull Request Template](PULL_REQUEST_TEMPLATE.md)
- [Architecture Decisions](../ARCHITECTURE_DECISIONS.md)
### External Resources
- "Code Complete" by Steve McConnell (Chapter on Code Reviews)
- "The Art of Readable Code" by Boswell & Foucher
- Google Engineering Practices: Code Review Guide
---
## 📞 Questions and Support
**Code Review Questions**:
- **Process**: Project Manager
- **Technical**: Tech Lead, Senior Developers
- **Architecture**: Architect
- **Testing/TDD**: QA Lead
**Escalation**:
- If reviewers disagree: Tech Lead decides
- If unsure about architecture: Architect reviews
- If security concern: Security specialist reviews
---
## 🎯 Summary: Review Mindset
> **"Code review is not about finding fault—it's about building better software together."**
### The Three Goals of Code Review
1. **Improve the code**: Make it better through collaboration
2. **Share knowledge**: Learn from each other
3. **Maintain quality**: Ensure standards are met consistently
### The Review Promise
- **Be respectful**: Critique code, not people
- **Be thorough**: Follow the checklist consistently
- **Be timely**: Review within 24 hours
- **Be constructive**: Suggest improvements, don't just criticize
---
**Document Control**:
- Version: 1.0
- Created: 2025-11-20
- Status: Active
- Review Cycle: After each sprint
+385
View File
@@ -0,0 +1,385 @@
# Pull Request Template
**Project**: HTTP Sender Plugin (HSP)
---
## 📝 PR Title
<!-- Use format: [TYPE] Brief description -->
<!-- Examples:
feat: implement BufferManager with FIFO overflow
fix: correct retry logic in HttpPollingAdapter
refactor: improve DataCollectionService naming
test: add integration tests for gRPC transmission
docs: update architecture diagrams
-->
---
## 📋 Summary
<!-- Provide a brief description of what this PR does (2-3 sentences) -->
---
## 🎯 Related Requirements
<!-- List all requirements this PR implements/addresses -->
<!-- Use format: - Req-XX: Description -->
<!-- Find requirements in: docs/requirements/DataCollector SRS.md -->
- Req-FR-XX: [Requirement description]
- Req-NFR-XX: [Requirement description]
- Req-Arch-XX: [Requirement description]
---
## ✅ TDD Compliance ⚠️ MANDATORY
### Git History Verification
<!-- Reviewers will verify the following -->
- [ ] **Tests committed BEFORE implementation** (check Git timestamps)
- [ ] **Git history shows RED-GREEN-REFACTOR cycle** (test → feat → refactor commits)
- [ ] **Commit messages follow TDD pattern** (`test: ... (RED)`, `feat: ... (GREEN)`, `refactor: ...`)
### Test Quality
- [ ] **Tests follow AAA pattern** (Arrange-Act-Assert)
- [ ] **Test names describe behavior**: `shouldDoX_whenY()`
- [ ] **Edge cases covered** (boundary conditions, nulls, empty)
- [ ] **Error scenarios tested** (exceptions, timeouts, failures)
- [ ] **Both happy path and sad path tested**
### Coverage Verification
- [ ] **Line coverage**: ___% (target: ≥95%)
- [ ] **Branch coverage**: ___% (target: ≥90%)
- [ ] **JaCoCo report generated**: `mvn jacoco:report` (attach link or screenshot)
- [ ] **CI coverage check passes**
### TDD Evidence
<!-- Provide Git log excerpt showing RED-GREEN-REFACTOR cycle -->
```
Example:
abc1234 test: add BufferManager offer() test (RED)
def5678 feat: implement BufferManager offer() method (GREEN)
ghi9012 refactor: add javadoc to BufferManager
jkl3456 test: add BufferManager overflow test (RED)
mno7890 feat: implement FIFO overflow handling (GREEN)
pqr1234 refactor: improve naming in overflow logic
```
**Git Log**:
```
<!-- Paste relevant commit history here -->
```
---
## 🏗️ Architecture Impact
### Hexagonal Architecture Compliance
- [ ] **Follows hexagonal architecture** (domain → ports → adapters)
- [ ] **Port interfaces not violated** (no direct adapter access from core)
- [ ] **Domain models remain pure** (no infrastructure dependencies)
- [ ] **Adapters properly isolated** (implement port interfaces)
- [ ] **Dependency direction correct**: Core → Ports ← Adapters
### Components Affected
<!-- Check all that apply -->
- [ ] Domain Models (`com.siemens.coreshield.hsp.domain`)
- [ ] Application Services (`com.siemens.coreshield.hsp.application`)
- [ ] Port Interfaces (`com.siemens.coreshield.hsp.ports.inbound|outbound`)
- [ ] Inbound Adapters (`com.siemens.coreshield.hsp.adapter.inbound`)
- [ ] Outbound Adapters (`com.siemens.coreshield.hsp.adapter.outbound`)
- [ ] Configuration
- [ ] Build/Deployment
### Architecture Diagram Impact
- [ ] **No architecture changes** (implementation only)
- [ ] **Minor architecture changes** (new adapter, new port method)
- [ ] **Major architecture changes** (new component, structural change)
<!-- If major changes, update diagrams in docs/diagrams/ -->
---
## 🧪 Testing
### Unit Tests
- [ ] **Unit tests added** for all new code
- [ ] **Unit tests updated** for changed code
- [ ] **All unit tests pass** locally: `mvn test`
- [ ] **Mock objects used appropriately** (external dependencies mocked)
### Integration Tests
<!-- Check if applicable -->
- [ ] **Integration tests added** (if new component or adapter)
- [ ] **Integration tests updated** (if component behavior changed)
- [ ] **All integration tests pass** locally: `mvn verify -P integration-tests`
- [ ] **External systems mocked** (WireMock for HTTP, gRPC test server)
### Performance Tests
<!-- Check if applicable to performance-sensitive code -->
- [ ] **Performance tests added** (if performance-sensitive code)
- [ ] **Benchmarks run** (if applicable)
- [ ] **No performance regression** (compared to baseline)
### Manual Testing
- [ ] **Manual testing performed** (describe below)
**Manual Testing Details**:
```
<!-- Describe what you tested manually -->
Example:
- Tested BufferManager with 1000 concurrent threads
- Verified overflow behavior discards oldest items
- Confirmed statistics are accurate under load
```
---
## 🔒 Thread Safety (if applicable)
<!-- Only fill out if code involves concurrency/shared state -->
- [ ] **Thread-safe implementation** (if required by requirements)
- [ ] **Immutability used** (value objects, final fields)
- [ ] **Proper synchronization** (`synchronized`, `Lock`, concurrent collections)
- [ ] **Atomic operations used** (`AtomicLong`, `AtomicInteger` for counters)
- [ ] **No race conditions** (shared state properly protected)
- [ ] **Stress tests exist** (concurrent tests with 100+ threads)
**Thread Safety Justification**:
```
<!-- Explain why this code is thread-safe, or why it doesn't need to be -->
```
---
## 🔐 Security Considerations
### Input Validation
- [ ] **All external inputs validated** (HTTP responses, configuration)
- [ ] **Size limits enforced** (Req-FR-21: 1MB limit if applicable)
- [ ] **URL validation** (protocol, host, port if applicable)
- [ ] **JSON validation** (schema compliance if applicable)
### Data Handling
- [ ] **Sensitive data not logged** (no raw data in logs)
- [ ] **Base64 encoding used** (for binary data if applicable)
- [ ] **No credentials in code** (use configuration)
### Security Review Needed?
- [ ] **No security-sensitive code** (skip security review)
- [ ] **Security review recommended** (involves authentication, data handling, external input)
---
## 📚 Documentation
### Code Documentation
- [ ] **Javadoc added** for all public APIs (classes, methods, interfaces)
- [ ] **Requirement traceability in Javadoc**: `@requirement Req-FR-XX`
- [ ] **Complex logic explained** (comments or Javadoc)
- [ ] **TODOs tracked** (with ticket numbers if applicable)
### External Documentation
- [ ] **README updated** (if public APIs changed)
- [ ] **Architecture diagrams updated** (if structure changed)
- [ ] **Configuration examples updated** (if config changed)
- [ ] **Operations documentation updated** (if deployment changed)
**Documentation Changes**:
<!-- List files changed or reference "None" -->
-
-
-
---
## ⚙️ Configuration Changes
<!-- Only fill out if configuration changed -->
- [ ] **No configuration changes**
- [ ] **Configuration schema updated** (hsp-config.json)
- [ ] **Configuration validation updated** (ConfigurationValidator)
- [ ] **Configuration example updated** (docs/examples/)
- [ ] **Backward compatible** (or migration path documented)
**Configuration Impact**:
```
<!-- Describe configuration changes -->
```
---
## 🚀 Build and Deployment
### Build Verification
- [ ] **Maven build succeeds**: `mvn clean package`
- [ ] **All tests pass**: `mvn test`
- [ ] **Coverage check passes**: `mvn jacoco:check`
- [ ] **No build warnings** (deprecations, unchecked casts)
### CI/CD Pipeline
- [ ] **CI pipeline passing** (all checks green)
- [ ] **No new dependencies** (or dependencies documented below)
- [ ] **Fat JAR builds correctly** (if applicable)
**New Dependencies** (if any):
<!-- List new Maven dependencies -->
```xml
<!-- Example:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.3-jre</version>
</dependency>
-->
```
---
## 🔄 Migration/Upgrade Path
<!-- Only fill out if breaking changes -->
- [ ] **No breaking changes**
- [ ] **Breaking changes documented** (describe below)
- [ ] **Migration guide provided** (for users/operators)
**Breaking Changes**:
```
<!-- Describe breaking changes and migration steps -->
```
---
## 📸 Screenshots/Logs (if applicable)
<!-- Add screenshots for UI changes, logs for behavior changes -->
**Coverage Report**:
<!-- Attach screenshot or link to JaCoCo HTML report -->
**Test Results**:
<!-- Attach screenshot or paste test output if relevant -->
**Logs/Output**:
<!-- Paste relevant log output if demonstrating behavior -->
```
```
---
## ✅ Pre-Merge Checklist
<!-- Verify before requesting review -->
### Code Quality
- [ ] **Self-reviewed code** (read through all changes)
- [ ] **No commented-out code** (removed debug code)
- [ ] **No debug statements** (removed `System.out.println`, excessive logging)
- [ ] **Code formatted consistently** (IDE auto-format applied)
### Testing
- [ ] **All tests passing locally** (`mvn test`)
- [ ] **Coverage thresholds met** (95%/90%)
- [ ] **Integration tests passing** (if applicable)
### Documentation
- [ ] **Javadoc complete** (all public APIs)
- [ ] **README updated** (if needed)
- [ ] **Requirement traceability added** (in Javadoc)
### Process
- [ ] **Feature branch up-to-date** with develop (rebased or merged)
- [ ] **Conflicts resolved** (if any)
- [ ] **CI pipeline green** (all checks passing)
---
## 👥 Reviewers
### Required Reviewers
<!-- Tag specific reviewers if needed -->
- [ ] **Senior Developer**: @[username] (for architectural changes)
- [ ] **QA Engineer**: @[username] (for test changes)
- [ ] **Security Specialist**: @[username] (for security-sensitive code)
### Review Priority
<!-- Check one -->
- [ ] **Low Priority**: Minor change, no rush
- [ ] **Normal Priority**: Standard feature/fix
- [ ] **High Priority**: Blocking other work
- [ ] **Critical Priority**: Production issue, needs immediate review
---
## 💬 Additional Notes
<!-- Any additional context, explanations, or questions for reviewers -->
---
## 🔗 Related Issues/PRs
<!-- Link to related Gitea issues or PRs -->
- Closes #XX (issue number)
- Related to #YY
- Depends on #ZZ
---
## ✍️ Author Checklist
<!-- Final verification before submitting -->
- [ ] I have read the [Code Review Guidelines](CODE_REVIEW_GUIDELINES.md)
- [ ] I have read the [TDD Compliance Checklist](TDD_COMPLIANCE_CHECKLIST.md)
- [ ] I have followed TDD methodology (tests before code)
- [ ] I have self-reviewed my code
- [ ] I have tested all changes locally
- [ ] I have updated documentation
- [ ] I have added requirement traceability
- [ ] I am ready for code review
---
**Submitted by**: @[your-username]
**Date**: [YYYY-MM-DD]
**Branch**: `feature/[branch-name]``develop` (or `main`)
---
<!--
REVIEWER INSTRUCTIONS:
1. Review using the Code Review Guidelines checklist
2. Verify TDD compliance (Git history, tests before code)
3. Check hexagonal architecture boundaries
4. Verify thread safety (if applicable)
5. Check test coverage (≥95%/90%)
6. Provide constructive feedback with categories (🚨 BLOCKER, ⚠️ MAJOR, 💡 SUGGESTION)
7. Approve when all blockers resolved and checklist complete
-->
+474
View File
@@ -0,0 +1,474 @@
# TDD Compliance Checklist
**Version**: 1.0
**Project**: HTTP Sender Plugin (HSP)
**Last Updated**: 2025-11-20
## Purpose
This checklist ensures that all development follows Test-Driven Development (TDD) methodology as mandated by the project implementation plan. **ALL code MUST be developed using the Red-Green-Refactor cycle**.
---
## 🚨 TDD Non-Negotiable Rules
### Rule 1: Tests First, Code Second
- [ ] **No production code written without a failing test**
- [ ] Test defines the interface and expected behavior
- [ ] Implementation satisfies the test requirements
- [ ] Test is committed to Git BEFORE implementation
### Rule 2: Red-Green-Refactor Cycle Documented
- [ ] **RED**: Failing test committed with message `test: description (RED)`
- [ ] **GREEN**: Minimal implementation committed with message `feat: description (GREEN)`
- [ ] **REFACTOR**: Code improvements committed with message `refactor: description`
- [ ] Git history clearly shows TDD cycle for each feature
### Rule 3: All Tests Must Pass
- [ ] Never commit with broken tests
- [ ] CI pipeline must be green
- [ ] Fix build breaks immediately (within 15 minutes)
- [ ] All existing tests pass before adding new features
### Rule 4: Coverage Thresholds Mandatory
- [ ] **95% line coverage minimum** (enforced by JaCoCo)
- [ ] **90% branch coverage minimum** (enforced by JaCoCo)
- [ ] CI pipeline fails if coverage drops below threshold
- [ ] Coverage trends tracked in sprint metrics
---
## 📋 Pre-Commit Checklist
Before committing any code, verify:
### Test Verification
- [ ] Tests written BEFORE implementation
- [ ] Tests follow AAA pattern (Arrange-Act-Assert)
- [ ] Test names clearly describe behavior: `shouldDoX_whenY()`
- [ ] Tests are independent (no shared state between tests)
- [ ] Tests are repeatable (same result every time)
- [ ] Tests are fast (unit tests < 100ms each)
### Git Commit Verification
- [ ] RED commit exists (failing test)
- [ ] GREEN commit follows RED (passing implementation)
- [ ] REFACTOR commit (if applicable)
- [ ] Commit messages follow TDD pattern
- [ ] Multiple commits per day (frequent integration)
### Code Coverage Verification
- [ ] JaCoCo report generated: `mvn jacoco:report`
- [ ] Line coverage ≥ 95%
- [ ] Branch coverage ≥ 90%
- [ ] No uncovered critical paths
- [ ] Coverage report reviewed in IDE
### Test Quality Verification
- [ ] Tests actually fail when they should (verify RED phase)
- [ ] Tests pass for correct reasons (not false positives)
- [ ] Edge cases covered (boundary conditions)
- [ ] Error scenarios tested (exceptions, failures)
- [ ] Happy path and sad path both tested
---
## 🔍 Pull Request TDD Compliance Review
### Git History Review
**Requirement**: Every PR must show clear TDD cycle in commits
Check commit history for pattern:
```
✓ test: add BufferManager offer() test (RED)
✓ feat: implement BufferManager offer() method (GREEN)
✓ refactor: add javadoc to BufferManager
✓ test: add BufferManager overflow test (RED)
✓ feat: implement FIFO overflow handling (GREEN)
✓ refactor: improve naming in overflow logic
```
**Red Flags**:
```
✗ feat: implement entire BufferManager (no tests first)
✗ test: add tests for BufferManager (tests after implementation)
✗ test + feat: implement BufferManager with tests (combined commit)
```
### Test-to-Code Commit Ratio
- [ ] Approximately 1:1 ratio of test commits to implementation commits
- [ ] Tests consistently appear BEFORE implementations in history
- [ ] No large blocks of code without corresponding tests
- [ ] Refactor commits are smaller, incremental improvements
### Coverage Report Review
Reviewer must verify:
- [ ] JaCoCo report attached to PR or accessible in CI
- [ ] Coverage meets 95%/90% thresholds
- [ ] No suspicious untested code paths
- [ ] New code covered by new tests (not just existing tests)
### Test Quality Review
- [ ] Tests follow AAA pattern consistently
- [ ] Test names are descriptive and behavior-focused
- [ ] Tests use appropriate assertions (not just `assertTrue`)
- [ ] Mock objects used appropriately (not over-mocking)
- [ ] Integration tests cover inter-component boundaries
---
## 📊 TDD Metrics Dashboard
### Weekly Metrics to Track
| Metric | Target | How to Measure |
|--------|--------|----------------|
| Test-to-Code Commit Ratio | 1:1 | Count test commits vs implementation commits |
| Line Coverage | ≥95% | JaCoCo report |
| Branch Coverage | ≥90% | JaCoCo report |
| Mutation Score | ≥75% | PIT mutation testing |
| Unit Test Execution Time | <5 min | CI pipeline logs |
| Flaky Test Rate | <1% | Track test failures/re-runs |
| TDD Compliance Rate | 100% | PR reviews with TDD violations |
### Sprint Retrospective TDD Questions
1. **Did we follow TDD for all code this sprint?**
- If no, what were the exceptions and why?
2. **Where did we skip tests first?**
- Identify patterns and root causes
3. **What slowed down our TDD workflow?**
- Tool issues, environment problems, knowledge gaps?
4. **How can we improve TDD practices next sprint?**
- Training needs, tooling improvements, pair programming?
---
## 🧪 TDD by Component Type
### Port Interfaces (Test-First Design)
**Checklist**:
- [ ] Test written defining interface contract FIRST
- [ ] Test shows expected method signatures and return types
- [ ] Interface defined to satisfy test
- [ ] Mock implementation created for testing
- [ ] Adapter implementation follows with its own TDD cycle
**Example Test (RED)**:
```java
@Test
void shouldPollEndpoint_whenUrlProvided() {
// Given
IHttpPollingPort httpPort = new HttpPollingAdapter(config);
String url = "http://example.com/data";
// When
CompletableFuture<byte[]> result = httpPort.pollEndpoint(url);
// Then
assertThat(result).isCompletedWithValue(expectedData);
}
```
### Domain Models (Value Object TDD)
**Checklist**:
- [ ] Test immutability (no setters, final fields)
- [ ] Test equality (equals/hashCode contract)
- [ ] Test serialization (JSON, Base64 encoding)
- [ ] Test validation (constructor throws on invalid data)
- [ ] Test thread safety (concurrent access if applicable)
**Example Test (RED)**:
```java
@Test
void shouldBeImmutable_whenCreated() {
// Given
DiagnosticData data1 = new DiagnosticData("url", new byte[]{1,2,3});
DiagnosticData data2 = new DiagnosticData("url", new byte[]{1,2,3});
// Then
assertThat(data1).isEqualTo(data2);
assertThat(data1).isNotSameAs(data2);
// Verify no setters exist (compilation check)
}
```
### Core Services (Business Logic TDD)
**Checklist**:
- [ ] Test business rules and invariants
- [ ] Test orchestration logic (calls to ports)
- [ ] Test error handling and exceptions
- [ ] Test statistics and monitoring
- [ ] Test concurrency if applicable
- [ ] Use mocks for port dependencies
**Example Test (RED)**:
```java
@Test
void shouldRejectOversizedData_whenFileExceeds1MB() {
// Given
DataCollectionService service = new DataCollectionService(httpPort, bufferPort);
byte[] largeData = new byte[2_000_000]; // 2 MB
// When / Then
assertThatThrownBy(() -> service.validateData(largeData, "http://test"))
.isInstanceOf(OversizedDataException.class)
.hasMessageContaining("1MB");
}
```
### Adapters (Infrastructure TDD)
**Checklist**:
- [ ] Unit tests with mocks for quick feedback
- [ ] Integration tests with real infrastructure (WireMock, gRPC test server)
- [ ] Test retry logic and backoff strategies
- [ ] Test timeouts and error scenarios
- [ ] Test thread safety and concurrency
- [ ] Test resource cleanup (connections, streams)
**Example Test (RED)**:
```java
@Test
void shouldRetryThreeTimes_whenHttpFails() {
// Given
stubFor(get("/endpoint").willReturn(aResponse().withStatus(500)));
HttpPollingAdapter adapter = new HttpPollingAdapter(config);
// When
assertThatThrownBy(() -> adapter.pollEndpoint(url).join())
.hasCauseInstanceOf(PollingFailedException.class);
// Then (Req-FR-17: 3 retries)
verify(3, getRequestedFor(urlEqualTo("/endpoint")));
}
```
---
## 🔄 Daily TDD Workflow
### Morning (Start of Day)
1. [ ] Pull latest from main/develop branch
2. [ ] Review overnight CI builds (all green?)
3. [ ] Check JaCoCo coverage report (≥95%/90%?)
4. [ ] Pick next user story from sprint backlog
5. [ ] Create feature branch: `git checkout -b feature/buffer-manager`
6. [ ] Review requirements and acceptance criteria
### During Development (5-10 TDD Cycles per Day)
**Per Feature/Method**:
1. [ ] **Write Test (RED)**: 15-30 minutes
- Write failing test for next requirement
- Run test, verify it fails: `mvn test -Dtest=ClassName#testMethod`
- Commit: `git commit -m "test: add test for X (RED)"`
2. [ ] **Write Code (GREEN)**: 15-45 minutes
- Write minimal code to make test pass
- Run test, verify it passes: `mvn test`
- Run all tests, verify no regressions: `mvn test`
- Commit: `git commit -m "feat: implement X (GREEN)"`
3. [ ] **Refactor**: 10-20 minutes (if needed)
- Improve code quality, remove duplication
- Run all tests, verify still passing: `mvn test`
- Commit: `git commit -m "refactor: improve X naming/structure"`
4. [ ] **Verify Coverage**: 5 minutes
- Generate coverage: `mvn jacoco:report`
- Check coverage in IDE or HTML report
- Ensure new code is covered
5. [ ] **Push Frequently**: Every 2-3 cycles
- Push to remote: `git push origin feature/buffer-manager`
- Verify CI pipeline runs and passes
### End of Day
1. [ ] Push feature branch: `git push origin feature/buffer-manager`
2. [ ] Create pull request in Gitea (if feature complete)
3. [ ] Verify CI pipeline passes (green build)
4. [ ] Request code review from team member
5. [ ] Update sprint board (move tasks to "In Review")
---
## ⚠️ TDD Anti-Patterns to Avoid
### 1. Writing Code Before Tests
```
✗ WRONG:
- Implement entire BufferManager class
- Then write tests to cover it
✓ CORRECT:
- Write test for offer() method
- Implement offer() method
- Write test for poll() method
- Implement poll() method
```
### 2. Testing Implementation Details
```
✗ WRONG:
@Test
void shouldUseArrayBlockingQueue_inBufferManager() {
// Testing internal implementation choice
}
✓ CORRECT:
@Test
void shouldStoreFIFO_whenMultipleOffersAndPolls() {
// Testing observable behavior
}
```
### 3. Over-Mocking
```
✗ WRONG:
@Test
void shouldCalculateSum() {
Calculator calc = mock(Calculator.class);
when(calc.add(2, 3)).thenReturn(5);
assertThat(calc.add(2, 3)).isEqualTo(5); // Circular mocking
}
✓ CORRECT:
@Test
void shouldCalculateSum() {
Calculator calc = new Calculator(); // Real object
assertThat(calc.add(2, 3)).isEqualTo(5);
}
```
### 4. Flaky Tests
```
✗ WRONG:
@Test
void shouldComplete_withinReasonableTime() {
Thread.sleep(100); // Time-dependent test
assertThat(result).isNotNull();
}
✓ CORRECT:
@Test
void shouldComplete_whenDataAvailable() {
CountDownLatch latch = new CountDownLatch(1);
// Use proper synchronization primitives
}
```
### 5. Large, Monolithic Tests
```
✗ WRONG:
@Test
void shouldTestEntireSystem() {
// 200 lines of test code testing everything
}
✓ CORRECT:
@Test
void shouldPollEndpoint_whenUrlValid() { /* 10 lines */ }
@Test
void shouldRetryOnFailure_whenHttpError() { /* 10 lines */ }
@Test
void shouldStoreInBuffer_whenDataReceived() { /* 10 lines */ }
```
---
## 📚 TDD Resources
### Internal Documentation
- [Project Implementation Plan](../PROJECT_IMPLEMENTATION_PLAN.md) - TDD Section (lines 644-880)
- [Test Strategy](../testing/test-strategy.md) - Testing approach
- [Architecture Decisions](../ARCHITECTURE_DECISIONS.md) - Design rationale
### TDD Training Materials
- Kent Beck - "Test-Driven Development by Example"
- Martin Fowler - "Refactoring: Improving the Design of Existing Code"
- Uncle Bob Martin - "Clean Code" (Chapter on Unit Tests)
### Tools and IDE Setup
- **IntelliJ IDEA**: Enable "Run tests on save" for instant feedback
- **Eclipse**: Install EclEmma for coverage visualization
- **JaCoCo**: Maven plugin for coverage enforcement
- **PIT**: Mutation testing for test quality validation
- **WireMock**: HTTP mocking for integration tests
- **gRPC Testing**: Mock gRPC servers for integration tests
---
## ✅ Definition of Done (TDD Perspective)
A user story/task is considered DONE when:
- [ ] All tests written BEFORE implementation (verified in Git history)
- [ ] All tests pass (green CI build)
- [ ] Line coverage ≥ 95%, branch coverage ≥ 90%
- [ ] Code review completed with TDD compliance verified
- [ ] No TDD violations found in PR review
- [ ] Git history shows clear RED-GREEN-REFACTOR cycles
- [ ] Integration tests pass (if applicable)
- [ ] Documentation updated (Javadoc, README)
- [ ] Merged to develop branch
---
## 📞 TDD Support and Questions
### Who to Ask
- **TDD Questions**: Tech Lead, Senior Developers
- **Tool Setup**: DevOps Engineer
- **Coverage Issues**: QA Lead
- **Git Workflow**: Tech Lead
### Pair Programming Sessions
- **Daily TDD Pairs**: Rotate pairs daily for knowledge sharing
- **TDD Mob Sessions**: Weekly mob programming on complex TDD scenarios
- **TDD Code Reviews**: All PRs require TDD compliance review
---
## 🎯 Summary: The TDD Mindset
> **"If it's worth building, it's worth testing. If it's not worth testing, why are you wasting your time working on it?"**
**TDD is not optional—it's how we build software at HSP.**
### The TDD Promise
- Tests document behavior
- Refactoring is safe
- Bugs are caught early
- Code is more modular
- Coverage is not a goal—it's a side effect
### The TDD Reality Check
If you find yourself:
- Writing code without tests → STOP
- Committing untested code → STOP
- Skipping tests "just this once" → STOP
**Return to RED-GREEN-REFACTOR. Always.**
---
**Document Control**:
- Version: 1.0
- Created: 2025-11-20
- Status: Active
- Review Cycle: After each sprint
+725
View File
@@ -0,0 +1,725 @@
# Thread Safety Guidelines
**Version**: 1.0
**Project**: HTTP Sender Plugin (HSP)
**Last Updated**: 2025-11-20
## Purpose
This document defines thread safety requirements, patterns, and best practices for the HSP project. The HSP system uses **Java 25 Virtual Threads** for concurrent HTTP polling and must ensure thread-safe operations for shared state.
---
## 🎯 Thread Safety Requirements
### 1. Critical Thread-Safe Components
The following components **MUST be thread-safe** per requirements:
| Component | Requirement | Concurrency Pattern | Justification |
|-----------|-------------|---------------------|---------------|
| **BufferManager** | Req-FR-26, Arch-7 | Thread-safe queue | Multiple producers (HTTP pollers), single consumer (gRPC transmitter) |
| **CollectionStatistics** | Req-NFR-8, Arch-8 | Atomic counters | Multiple threads updating statistics concurrently |
| **DataCollectionService** | Req-FR-14, Arch-6 | Virtual threads | 1000 concurrent HTTP polling tasks |
| **RateLimiter** | Enhancement | Thread-safe rate limiting | Multiple threads requesting rate limit permits |
| **BackpressureController** | Req-FR-27 | Atomic monitoring | Buffer usage checked by multiple threads |
### 2. Immutable Components (Thread-Safe by Design)
The following components **MUST be immutable**:
| Component | Type | Thread Safety |
|-----------|------|---------------|
| **DiagnosticData** | Value Object | Immutable (final fields, no setters) |
| **Configuration** | Value Object | Immutable (loaded once at startup) |
| **HealthCheckResponse** | Value Object | Immutable (snapshot of current state) |
| **BufferStatistics** | Value Object | Immutable (snapshot of buffer state) |
### 3. Single-Threaded Components (No Thread Safety Needed)
The following components run on **dedicated threads** (no concurrent access):
| Component | Thread Model | Justification |
|-----------|-------------|---------------|
| **DataTransmissionService** | Single consumer thread | Req-FR-25: One thread consumes from buffer |
| **ConfigurationManager** | Startup only | Loaded once before concurrent operations start |
| **Adapters** | Per-request isolation | Each request creates new adapter instance or uses thread-local state |
---
## 🧵 Concurrency Model Overview
### System Threading Architecture
```
┌─────────────────────────────────────────────────────────┐
│ HTTP Sender Plugin (HSP) Threading Model │
├─────────────────────────────────────────────────────────┤
│ │
│ Main Thread │
│ └─> Startup & Configuration │
│ │
│ Virtual Thread Pool (HTTP Polling) │
│ ├─> Virtual Thread 1 → HttpPollingAdapter │
│ ├─> Virtual Thread 2 → HttpPollingAdapter │
│ ├─> Virtual Thread 3 → HttpPollingAdapter │
│ └─> ... (up to 1000 concurrent virtual threads) │
│ ↓ │
│ [Thread-Safe BufferManager] (ArrayBlockingQueue) │
│ ↓ │
│ Single Consumer Thread (gRPC Transmission) │
│ └─> DataTransmissionService → GrpcStreamAdapter │
│ │
│ Health Check HTTP Server Thread │
│ └─> HealthCheckController (embedded Jetty) │
│ │
└─────────────────────────────────────────────────────────┘
```
### Virtual Threads (Java 25)
**Why Virtual Threads?**
- **Requirement**: Req-NFR-1: Support 1000 concurrent endpoints
- **Benefit**: Lightweight threads (millions possible vs. thousands of platform threads)
- **Use Case**: I/O-bound HTTP polling (mostly waiting for network responses)
**Virtual Thread Best Practices**:
-**DO**: Use for I/O-bound tasks (HTTP requests, file I/O)
-**DO**: Create one virtual thread per endpoint poll
-**DO**: Let virtual threads block (don't use async APIs unnecessarily)
-**DON'T**: Use for CPU-bound tasks (use platform threads instead)
-**DON'T**: Use with `synchronized` on long-running operations (use `ReentrantLock`)
**Creating Virtual Threads**:
```java
// CORRECT: Virtual thread executor for HTTP polling
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
// Schedule polling tasks
for (String url : endpoints) {
executor.submit(() -> pollEndpoint(url));
}
```
---
## 🔒 Thread Safety Patterns
### Pattern 1: Immutability (Preferred)
**When to Use**: Value objects, configuration, data transfer objects
**Benefits**:
- Thread-safe by design (no synchronization needed)
- No defensive copying required
- Easier to reason about
**Implementation**:
```java
/**
* Immutable value object representing diagnostic data.
* Thread-safe by design (immutable).
*
* @requirement Req-FR-22 Immutable data representation
*/
public final class DiagnosticData {
private final String endpointUrl;
private final byte[] data;
private final Instant timestamp;
public DiagnosticData(String endpointUrl, byte[] data) {
this.endpointUrl = Objects.requireNonNull(endpointUrl);
// Defensive copy of mutable array
this.data = Arrays.copyOf(data, data.length);
this.timestamp = Instant.now();
}
// Only getters, no setters
public String getEndpointUrl() {
return endpointUrl;
}
public byte[] getData() {
// Return defensive copy
return Arrays.copyOf(data, data.length);
}
public Instant getTimestamp() {
return timestamp; // Instant is immutable
}
// equals, hashCode, toString...
}
```
**Checklist**:
- [ ] Class declared `final` (cannot be subclassed)
- [ ] All fields declared `final` (assigned once in constructor)
- [ ] No setter methods (only getters)
- [ ] Defensive copies for mutable fields (arrays, collections)
- [ ] Getters return defensive copies of mutable fields
### Pattern 2: Concurrent Collections
**When to Use**: Shared data structures accessed by multiple threads
**Preferred Collections**:
- `ArrayBlockingQueue<T>`: Fixed-size blocking queue (buffer)
- `ConcurrentHashMap<K,V>`: Thread-safe map (if needed)
- `CopyOnWriteArrayList<T>`: Thread-safe list for read-heavy workloads
**Implementation (BufferManager)**:
```java
/**
* Thread-safe buffer manager using ArrayBlockingQueue.
*
* <p>Supports multiple producers (HTTP polling threads) and single
* consumer (gRPC transmission thread).
*
* @requirement Req-FR-26 Thread-safe buffer
* @requirement Req-Arch-7 Concurrent collection usage
*/
public class BufferManager {
private final BlockingQueue<DiagnosticData> buffer;
private final int capacity;
// Statistics (atomic counters)
private final AtomicLong totalOffered = new AtomicLong(0);
private final AtomicLong totalDiscarded = new AtomicLong(0);
public BufferManager(int capacity) {
this.capacity = capacity;
// ArrayBlockingQueue is thread-safe
this.buffer = new ArrayBlockingQueue<>(capacity);
}
/**
* Offers data to buffer. Thread-safe.
* Discards oldest if buffer is full (FIFO).
*
* @requirement Req-FR-27 FIFO overflow handling
*/
public void offer(DiagnosticData data) {
Objects.requireNonNull(data, "data cannot be null");
totalOffered.incrementAndGet();
if (!buffer.offer(data)) {
// Buffer full, discard oldest (FIFO)
buffer.poll(); // Remove oldest
buffer.offer(data); // Add new
totalDiscarded.incrementAndGet();
}
}
/**
* Polls data from buffer. Thread-safe.
* Blocks if buffer is empty (up to timeout).
*/
public DiagnosticData poll(long timeout, TimeUnit unit)
throws InterruptedException {
return buffer.poll(timeout, unit);
}
/**
* Returns current buffer size. Thread-safe.
*/
public int size() {
return buffer.size();
}
/**
* Returns buffer statistics snapshot. Thread-safe.
*/
public BufferStatistics getStatistics() {
return new BufferStatistics(
size(),
capacity,
totalOffered.get(),
totalDiscarded.get()
);
}
}
```
**Checklist**:
- [ ] Use `BlockingQueue` for producer-consumer patterns
- [ ] Use `ArrayBlockingQueue` for bounded buffers
- [ ] Use `ConcurrentHashMap` for thread-safe maps
- [ ] Avoid `synchronized` on collection itself (use concurrent collection)
### Pattern 3: Atomic Variables
**When to Use**: Counters, flags, simple shared state
**Atomic Classes**:
- `AtomicInteger`: Thread-safe integer counter
- `AtomicLong`: Thread-safe long counter
- `AtomicBoolean`: Thread-safe boolean flag
- `AtomicReference<T>`: Thread-safe object reference
**Implementation (CollectionStatistics)**:
```java
/**
* Thread-safe collection statistics using atomic variables.
*
* @requirement Req-NFR-8 Statistics tracking
* @requirement Req-Arch-8 Atomic operations
*/
public class CollectionStatistics {
// Atomic counters for thread safety
private final AtomicLong totalPolls = new AtomicLong(0);
private final AtomicLong successfulPolls = new AtomicLong(0);
private final AtomicLong failedPolls = new AtomicLong(0);
// Time-windowed metrics (last 30 seconds)
private final Queue<Long> recentPolls = new ConcurrentLinkedQueue<>();
/**
* Increments total poll count. Thread-safe.
*/
public void incrementTotalPolls() {
long count = totalPolls.incrementAndGet();
recentPolls.offer(System.currentTimeMillis());
cleanupOldMetrics();
}
/**
* Increments successful poll count. Thread-safe.
*/
public void incrementSuccessfulPolls() {
successfulPolls.incrementAndGet();
}
/**
* Increments failed poll count. Thread-safe.
*/
public void incrementFailedPolls() {
failedPolls.incrementAndGet();
}
/**
* Returns snapshot of current statistics. Thread-safe.
*/
public StatisticsSnapshot getSnapshot() {
return new StatisticsSnapshot(
totalPolls.get(),
successfulPolls.get(),
failedPolls.get(),
calculateRecentRate()
);
}
/**
* Removes metrics older than 30 seconds.
*/
private void cleanupOldMetrics() {
long cutoff = System.currentTimeMillis() - 30_000;
recentPolls.removeIf(timestamp -> timestamp < cutoff);
}
private double calculateRecentRate() {
return recentPolls.size() / 30.0; // polls per second
}
}
```
**Checklist**:
- [ ] Use `AtomicLong` for counters (not `long` with `synchronized`)
- [ ] Use `incrementAndGet()` for atomic increment-and-read
- [ ] Use `get()` for atomic read
- [ ] Use `compareAndSet()` for atomic compare-and-swap (if needed)
### Pattern 4: Locks (When Needed)
**When to Use**: Complex synchronized operations, multiple state updates
**Lock Types**:
- `ReentrantLock`: Exclusive lock (mutual exclusion)
- `ReentrantReadWriteLock`: Read-write lock (multiple readers, one writer)
- `StampedLock`: Optimistic locking (Java 8+)
**Implementation (if needed)**:
```java
public class RateLimitedAdapter {
private final ReentrantLock lock = new ReentrantLock();
private long lastRequestTime = 0;
private final long minIntervalMs;
/**
* Thread-safe rate limiting with explicit lock.
*
* Prefer ReentrantLock over synchronized for virtual threads.
*/
public void acquirePermit() throws InterruptedException {
lock.lock();
try {
long now = System.currentTimeMillis();
long elapsed = now - lastRequestTime;
if (elapsed < minIntervalMs) {
Thread.sleep(minIntervalMs - elapsed);
}
lastRequestTime = System.currentTimeMillis();
} finally {
lock.unlock(); // ALWAYS unlock in finally
}
}
}
```
**Checklist**:
- [ ] Use `ReentrantLock` instead of `synchronized` for virtual threads
- [ ] Always unlock in `finally` block
- [ ] Avoid holding locks during I/O operations (risk of pinning virtual threads)
- [ ] Document lock ordering if multiple locks used (prevent deadlock)
### Pattern 5: Thread Confinement
**When to Use**: State that doesn't need to be shared
**Strategies**:
- **Stack Confinement**: Use local variables (method parameters, local vars)
- **Thread-Local**: Use `ThreadLocal<T>` for thread-specific state
- **Instance-Per-Thread**: Create new instances per thread
**Implementation**:
```java
/**
* HttpPollingAdapter is thread-confined by design.
* Each virtual thread creates its own adapter instance.
* No shared mutable state → no thread safety needed.
*/
public class HttpPollingAdapter implements IHttpPollingPort {
// Immutable configuration (thread-safe)
private final Configuration config;
// Thread-confined HttpClient (one per instance)
private final HttpClient httpClient;
public HttpPollingAdapter(Configuration config) {
this.config = config; // Immutable
this.httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(30))
.build();
}
@Override
public CompletableFuture<byte[]> pollEndpoint(String url) {
// This method is called by a single virtual thread
// No shared mutable state → thread-safe by design
return httpClient.sendAsync(buildRequest(url), BodyHandlers.ofByteArray())
.thenApply(HttpResponse::body);
}
}
```
**Checklist**:
- [ ] Prefer immutability and thread confinement over synchronization
- [ ] Document thread ownership in Javadoc
- [ ] Avoid sharing mutable state when possible
---
## 🧪 Testing Thread Safety
### Test Strategy
**1. Unit Tests with Concurrent Access**:
```java
@Test
void shouldBeThreadSafe_whenMultipleThreadsOfferConcurrently() {
// Given
BufferManager buffer = new BufferManager(100);
int numThreads = 50;
int offersPerThread = 100;
// When: Multiple threads offer concurrently
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
List<Future<?>> futures = new ArrayList<>();
for (int i = 0; i < numThreads; i++) {
futures.add(executor.submit(() -> {
for (int j = 0; j < offersPerThread; j++) {
buffer.offer(new DiagnosticData("url", new byte[]{1,2,3}));
}
}));
}
// Wait for completion
for (Future<?> future : futures) {
future.get();
}
executor.shutdown();
// Then: All offers processed (no data loss except overflow)
BufferStatistics stats = buffer.getStatistics();
assertThat(stats.totalOffered()).isEqualTo(numThreads * offersPerThread);
}
```
**2. Stress Tests**:
```java
@Test
void shouldHandleHighConcurrency_with1000ProducersAndConsumers() {
BufferManager buffer = new BufferManager(300);
// 1000 producers
ExecutorService producers = Executors.newVirtualThreadPerTaskExecutor();
for (int i = 0; i < 1000; i++) {
producers.submit(() -> {
for (int j = 0; j < 1000; j++) {
buffer.offer(new DiagnosticData("url", new byte[]{1,2,3}));
}
});
}
// 1 consumer
ExecutorService consumer = Executors.newSingleThreadExecutor();
AtomicLong consumed = new AtomicLong(0);
consumer.submit(() -> {
while (consumed.get() < 1_000_000) {
try {
DiagnosticData data = buffer.poll(1, TimeUnit.SECONDS);
if (data != null) {
consumed.incrementAndGet();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
});
// Wait for completion and verify
producers.shutdown();
producers.awaitTermination(10, TimeUnit.MINUTES);
consumer.shutdown();
consumer.awaitTermination(1, TimeUnit.MINUTES);
// Verify: No deadlock, no data corruption
assertThat(consumed.get()).isGreaterThan(0);
}
```
**3. Race Condition Detection**:
```java
@Test
void shouldNotHaveRaceCondition_inAtomicIncrement() {
CollectionStatistics stats = new CollectionStatistics();
int numThreads = 100;
int incrementsPerThread = 10000;
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
for (int i = 0; i < numThreads; i++) {
executor.submit(() -> {
for (int j = 0; j < incrementsPerThread; j++) {
stats.incrementTotalPolls();
}
});
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
// Verify: Exact count (no lost updates)
assertThat(stats.getSnapshot().totalPolls())
.isEqualTo((long) numThreads * incrementsPerThread);
}
```
### Thread Safety Test Checklist
- [ ] **Concurrent access tests**: Multiple threads accessing shared state
- [ ] **Stress tests**: 100+ threads, 1000+ operations per thread
- [ ] **Race condition tests**: Verify atomic operations (no lost updates)
- [ ] **Deadlock tests**: Complex locking scenarios (if applicable)
- [ ] **Immutability tests**: Verify no setters, defensive copies
---
## 🚨 Common Thread Safety Mistakes
### Mistake 1: Non-Atomic Check-Then-Act
```java
WRONG: Race condition
public void offer(DiagnosticData data) {
if (buffer.size() < capacity) { // Check
buffer.add(data); // Act (another thread may have added meanwhile)
}
}
CORRECT: Atomic operation
public void offer(DiagnosticData data) {
buffer.offer(data); // ArrayBlockingQueue handles atomicity
}
```
### Mistake 2: Mutable Shared State
```java
WRONG: Mutable shared field
public class Statistics {
private long totalPolls = 0; // Not thread-safe!
public void increment() {
totalPolls++; // Race condition: read-modify-write
}
}
CORRECT: Atomic variable
public class Statistics {
private final AtomicLong totalPolls = new AtomicLong(0);
public void increment() {
totalPolls.incrementAndGet(); // Atomic operation
}
}
```
### Mistake 3: Exposing Mutable Internal State
```java
WRONG: Exposing internal array
public class DiagnosticData {
private final byte[] data;
public byte[] getData() {
return data; // Caller can modify internal state!
}
}
CORRECT: Defensive copy
public class DiagnosticData {
private final byte[] data;
public byte[] getData() {
return Arrays.copyOf(data, data.length); // Safe copy
}
}
```
### Mistake 4: Synchronized on Long-Running Operation
```java
WRONG: Holding lock during I/O (blocks virtual threads)
public synchronized byte[] pollEndpoint(String url) {
return httpClient.send(request).body(); // I/O while holding lock!
}
CORRECT: No synchronization for thread-confined code
public byte[] pollEndpoint(String url) {
// Each thread has its own adapter instance
return httpClient.send(request).body(); // No shared state
}
```
### Mistake 5: Inconsistent Locking
```java
WRONG: Inconsistent synchronization
public void increment() {
synchronized (this) {
count++;
}
}
public int getCount() {
return count; // Not synchronized! Can see stale value
}
CORRECT: Consistent synchronization or atomic variable
private final AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet();
}
public int getCount() {
return count.get();
}
```
---
## 📊 Thread Safety Review Checklist
Use this checklist during code reviews:
### Immutability
- [ ] Value objects are `final` classes
- [ ] All fields are `final`
- [ ] No setter methods
- [ ] Defensive copies for mutable fields (arrays, collections)
- [ ] Getters return defensive copies of mutable fields
### Concurrent Collections
- [ ] Use `BlockingQueue` for producer-consumer patterns
- [ ] Use `ArrayBlockingQueue` for bounded buffers
- [ ] Use `ConcurrentHashMap` for thread-safe maps
- [ ] Avoid manual synchronization on collections
### Atomic Variables
- [ ] Use `AtomicLong`/`AtomicInteger` for counters
- [ ] Use atomic operations (`incrementAndGet`, `get`, `compareAndSet`)
- [ ] No read-modify-write with plain `long`/`int`
### Locks
- [ ] Use `ReentrantLock` instead of `synchronized` (for virtual threads)
- [ ] Always unlock in `finally` block
- [ ] No I/O operations while holding lock
- [ ] Lock ordering documented (if multiple locks)
### Virtual Threads
- [ ] Virtual threads used for I/O-bound tasks
- [ ] No `synchronized` on long-running operations
- [ ] No CPU-bound work in virtual threads
### Testing
- [ ] Concurrent access tests exist (multiple threads)
- [ ] Stress tests exist (100+ threads, 1000+ operations)
- [ ] Race condition tests verify atomic operations
- [ ] No flaky tests (deterministic results)
---
## 📚 Resources
### Java Concurrency References
- "Java Concurrency in Practice" by Brian Goetz (Chapter 2-5)
- JDK 25 Documentation: Virtual Threads (JEP 444)
- Java Memory Model (JLS §17.4)
### Internal Documentation
- [Project Implementation Plan](../PROJECT_IMPLEMENTATION_PLAN.md)
- [Architecture Decisions](../ARCHITECTURE_DECISIONS.md)
- [Code Review Guidelines](CODE_REVIEW_GUIDELINES.md)
---
## 🎯 Summary: Thread Safety Mindset
> **"Thread safety is not optional—it's correctness."**
### The Thread Safety Hierarchy (Prefer in Order)
1. **Immutability** (best): No shared mutable state
2. **Thread Confinement**: State not shared between threads
3. **Concurrent Collections**: Use built-in thread-safe collections
4. **Atomic Variables**: For simple shared state (counters, flags)
5. **Locks**: For complex synchronized operations (last resort)
### Key Principles
- **Design for immutability first**: Mutable shared state is the enemy
- **Prefer composition over manual synchronization**: Use `BlockingQueue`, `AtomicLong`, etc.
- **Test concurrency explicitly**: Don't rely on "it works in single-threaded tests"
- **Document thread safety**: Javadoc must state thread safety guarantees
**When in doubt, ask: "What happens if two threads call this at the same time?"**
---
**Document Control**:
- Version: 1.0
- Created: 2025-11-20
- Status: Active
- Review Cycle: After each sprint