--- name: java-rules description: "Java coding rules: style, patterns, security, testing. Triggers: .java, pom.xml, build.gradle, Spring, Spring Boot, JPA, Hibernate, JUnit, Maven, Gradle." effort: medium user-invocable: false allowed-tools: Read --- # Java Rules These rules come from `app/rules/java/` in ai-toolkit. They cover the project's standards for coding style, frameworks, patterns, security, and testing in Java. Apply them when writing or reviewing Java code. # Java Coding Style ## Naming - PascalCase: classes, interfaces, enums, records, annotations. - camelCase: methods, variables, parameters. - UPPER_SNAKE: constants (`static final`). - Package names: lowercase, dot-separated, reverse domain (`com.company.project`). - No Hungarian notation. No `I` prefix on interfaces. ## Modern Java (17+) - Use `record` for immutable data carriers. No need for Lombok in most cases. - Use `sealed` classes/interfaces for restricted hierarchies. - Use pattern matching: `if (obj instanceof String s)` instead of cast. - Use `switch` expressions with arrow syntax and exhaustiveness. - Use text blocks (`"""`) for multiline strings (SQL, JSON, HTML). ## Types - Use `var` for local variables when the type is obvious from the right-hand side. - Use `Optional` for return types that may be absent. Never for fields or params. - Prefer `List.of()`, `Map.of()`, `Set.of()` for immutable collections. - Use `Stream` for collection transformations. Avoid streams for simple iterations. ## Classes - Prefer composition over inheritance. Use interfaces for abstraction. - Keep classes focused: single responsibility. - Use `final` on classes not designed for extension. - Use `private` constructors + static factory methods for controlled instantiation. - Records over POJOs for value types. Lombok only if records are insufficient. ## Methods - Max 20-30 lines per method. Extract when longer. - Use `@Override` on every overridden method. - Return empty collections over `null`. Use `Collections.emptyList()` or `List.of()`. - Avoid checked exceptions for programming errors. Use runtime exceptions. ## Formatting - Use project formatter (Google Java Format or IDE-configured). - Use `@SuppressWarnings` sparingly and with specific warning names. - Use `final` for parameters and local variables where practical. ## Nullability - Annotate with `@Nullable` / `@NonNull` from JSpecify or JetBrains. - Use `Objects.requireNonNull()` at public API boundaries. - Never return `null` from collections or arrays. Return empty. - Use `Optional` for genuinely optional return values. ## Documentation - Javadoc on all public classes and methods. - Use `@param`, `@return`, `@throws` tags for public API methods. - Skip Javadoc for obvious getters, `toString()`, and `equals()`. # Java Frameworks ## Spring Boot - Use Spring Boot 3+ with Java 17+ minimum. - Use `@RestController` for REST APIs. Return `ResponseEntity` for status control. - Use `@Valid` + Jakarta Bean Validation for request validation. - Use profiles (`@Profile`) for environment-specific configuration. - Use `application.yml` over `application.properties` for readability. - Externalize config: env vars > config files > hardcoded defaults. ## Spring Data JPA - Use repository interfaces extending `JpaRepository`. - Use `@Query` with JPQL for custom queries. Use native queries only when needed. - Use `@EntityGraph` to prevent N+1 queries in associations. - Use `Specification` for dynamic query building. - Always use `@Transactional` at the service layer, not repository. ## Spring Security - Use `SecurityFilterChain` bean configuration (not `WebSecurityConfigurerAdapter`). - Use `@PreAuthorize` / `@Secured` for method-level authorization. - Use BCrypt for password encoding: `new BCryptPasswordEncoder()`. - Configure CORS, CSRF, and session management explicitly. - Use OAuth2 Resource Server for JWT validation in APIs. ## Hibernate / JPA - Use `FetchType.LAZY` by default on all associations. - Use `@BatchSize` or `@Fetch(FetchMode.SUBSELECT)` to avoid N+1. - Use `@Version` for optimistic locking on entities. - Use DTOs (records) for read queries. Do not expose entities in APIs. - Use Flyway or Liquibase for schema migrations. ## Quarkus / Micronaut - Use for microservices and serverless where startup time matters. - Use compile-time DI (Micronaut) or build-time optimization (Quarkus). - Use reactive patterns with Mutiny (Quarkus) or Reactor (Micronaut). - Use native image builds with GraalVM for production deployments. ## Build Tools - Use Gradle (Kotlin DSL) for new projects. Maven for enterprise legacy. - Use dependency management to unify versions across modules. - Use Bill of Materials (BOM) imports for consistent Spring versions. - Use Spotless or Checkstyle for enforced code formatting. ## Logging - Use SLF4J facade with Logback or Log4j2 backend. - Use structured logging with MDC for correlation IDs. - Use parameterized logging: `log.info("User {} created", userId)`. - Never log sensitive data (passwords, tokens, PII). # Java Patterns ## Error Handling - Use unchecked exceptions for programming errors (`IllegalArgumentException`). - Use checked exceptions only for recoverable conditions the caller must handle. - Create domain exception hierarchy: `AppException` -> `NotFoundException`, etc. - Never catch `Exception` or `Throwable` broadly. Catch specific types. - Use `try-with-resources` for all `AutoCloseable` resources. ## Immutability - Use `record` for immutable value objects (Java 16+). - Use `List.copyOf()`, `Map.copyOf()` to create unmodifiable copies. - Make fields `private final`. No setters unless mutation is required. - Return defensive copies of mutable collections from getters. - Use builder pattern for constructing immutable objects with many fields. ## Optional - Use `Optional` as return type for methods that may not return a value. - Chain: `optional.map(...).orElseThrow(...)`. Avoid `isPresent()` + `get()`. - Never use `Optional` for fields, method parameters, or collection elements. - Use `Optional.empty()` over `null`. Use `Optional.ofNullable()` at boundaries. ## Streams - Use streams for transformations: `filter`, `map`, `collect`. - Avoid side effects in stream operations. Keep them pure. - Use `Collectors.toUnmodifiableList()` for immutable results. - Prefer `for` loop for simple iterations that do not transform data. - Use `Stream.of()` or `IntStream.range()` for generating sequences. ## Dependency Injection - Use constructor injection exclusively. No field or setter injection. - Accept interfaces in constructors, not implementations. - Use `@Component`, `@Service`, `@Repository` for Spring-managed beans. - Keep the number of constructor dependencies under 5. Split if more. ## Concurrency - Use `ExecutorService` and `CompletableFuture` for async operations. - Use `virtual threads` (Java 21+) for I/O-bound concurrent work. - Use `ConcurrentHashMap`, `AtomicInteger` for thread-safe operations. - Avoid `synchronized` blocks when possible -- use higher-level concurrency. - Use `ReentrantReadWriteLock` for read-heavy shared state. ## Design Patterns - Use Strategy pattern (via interfaces) over switch/if-else chains. - Use Factory methods for flexible object creation. - Use Decorator pattern for composable behavior augmentation. - Avoid Singleton pattern -- use DI container for lifecycle management. ## Anti-Patterns - Returning `null` from methods -- use `Optional` or empty collections. - Mutable DTOs with getters/setters -- use records. - God classes with 20+ dependencies -- split by responsibility. - String typing for domain values -- use types, enums, or value objects. # Java Security ## Input Validation - Validate all input with Jakarta Bean Validation (`@NotNull`, `@Size`, `@Email`). - Use `@Valid` on controller parameters to trigger validation automatically. - Create custom validators for domain-specific rules. - Never trust client-provided IDs. Verify resource ownership server-side. ## SQL Injection - Use JPA/Hibernate parameterized queries. Never concatenate input into JPQL/SQL. - Use `CriteriaBuilder` or Specifications for dynamic queries. - For native queries, use named parameters: `@Query(value = "... WHERE id = :id", nativeQuery = true)`. - Use `PreparedStatement` if using JDBC directly. Never `Statement` with concatenation. ## Authentication - Use Spring Security with BCrypt (`BCryptPasswordEncoder`) for password hashing. - Use JWT with short expiration (15 min) + refresh tokens for APIs. - Implement account lockout after N failed attempts. - Use `@AuthenticationPrincipal` to access the current user in controllers. ## Authorization - Use `@PreAuthorize("hasRole('ADMIN')")` for role-based access control. - Use method security for fine-grained authorization. - Check resource ownership in service layer, not just role membership. - Default deny: require explicit authorization for every endpoint. ## XSS and CSRF - Spring auto-escapes Thymeleaf output. Do not use `th:utext` with user data. - Enable CSRF protection for session-based auth. Disable only for stateless JWT APIs. - Set `Content-Type` headers explicitly on responses. - Use CSP headers to restrict script sources. ## Serialization - Do not deserialize untrusted data with `ObjectInputStream` (RCE risk). - Use Jackson with `@JsonIgnoreProperties(ignoreUnknown = true)`. - Disable default typing in Jackson: never use `enableDefaultTyping()`. - Validate deserialized objects with Bean Validation after parsing. ## Dependencies - Run OWASP Dependency-Check in CI: `mvn verify -P owasp-check`. - Update Spring Boot regularly -- security patches are frequent. - Use `dependencyManagement` to control transitive dependency versions. - Audit `mvn dependency:tree` for unexpected transitive dependencies. ## Secrets - Use Spring Cloud Config or Vault for secrets management. - Use `@Value("${secret}")` with env var placeholders, not hardcoded values. - Never log request headers containing `Authorization` or session tokens. - Use separate config profiles for dev/staging/prod with different secrets. ## Logging Security - Use parameterized logging to prevent log injection. - Sanitize user input before logging: remove newlines and control characters. - Never log stack traces to API responses. Return generic error messages. # Java Testing ## Framework - Use JUnit 5 (Jupiter) for all new tests. No JUnit 4. - Use AssertJ for fluent, readable assertions. - Use Mockito for mocking dependencies. - Use Testcontainers for integration tests with databases/services. ## File Naming - Test classes: `FooTest.java` in `src/test/java/` mirroring source package. - Integration tests: `FooIT.java` or use `@Tag("integration")`. - Test utilities: `src/test/java/.../support/` or `TestUtils.java`. ## Structure - Use `@Nested` classes to group related tests within a test class. - Use `@DisplayName` for human-readable test descriptions. - Use `@BeforeEach` for setup, `@AfterEach` for cleanup. - Use `@ParameterizedTest` with `@ValueSource`, `@CsvSource`, `@MethodSource`. ## Assertions (AssertJ) - Use `assertThat(actual).isEqualTo(expected)` over JUnit assertions. - Use `assertThatThrownBy(() -> ...).isInstanceOf(FooException.class)`. - Use `assertThat(list).hasSize(3).extracting("name").contains("Ada")`. - Chain assertions for readable, self-documenting tests. ## Mocking (Mockito) - Use `@Mock` + `@ExtendWith(MockitoExtension.class)` for injection. - Use `when().thenReturn()` for stubbing. `verify()` for interaction checking. - Use `@InjectMocks` to auto-inject mocks into the class under test. - Prefer constructor injection in production code for testability. - Use `ArgumentCaptor` to inspect complex arguments. ## Integration Testing - Use Testcontainers for PostgreSQL, Redis, Kafka, etc. - Use `@SpringBootTest` sparingly -- it starts the full context. Prefer slices. - Use `@WebMvcTest` for controller tests, `@DataJpaTest` for repository tests. - Use `@TestConfiguration` for test-specific bean overrides. ## Test Data - Use test builders or factory methods for creating test objects. - Use `@Sql` annotation to load test data from SQL files. - Keep test data minimal. Only set fields relevant to the behavior under test. - Use random UUIDs for IDs in tests to avoid collision. ## Performance - Run tests in parallel: configure `junit.jupiter.execution.parallel.enabled=true`. - Use `@SpringBootTest` only when integration context is needed. - Mock external dependencies in unit tests for speed. - Keep the full test suite under 5 minutes.