Skill: wu-uk/fix-druid-loophole-cve-senior-java (version 0.1.0)

--- # === CORE IDENTITY === name: senior-java title: Senior Java Skill Package description: World-class Java and Spring Boot development skill for enterprise applications, microservices, and cloud-native systems. Expertise in Spring Framework, Spring Boot 3.x, Spring Cloud, JPA/Hibernate, and reactive programming with WebFlux. Includes project scaffolding, dependency management, security implementation, and performance optimization. domain: engineering subdomain: java-development # === WEBSITE DISPLAY === difficulty: advanced time-saved: "60%+ on project scaffolding, 40% on security implementation" frequency: "Daily for enterprise development teams" use-cases: - Building enterprise Spring Boot applications with production-ready configuration - Designing microservices with Spring Cloud and service discovery - Implementing JPA/Hibernate data layers with optimized queries - Setting up Spring Security with OAuth2 and JWT authentication - Performance tuning JVM applications and reactive WebFlux systems # === RELATIONSHIPS === related-agents: [cs-java-engineer] related-skills: [senior-backend, senior-architect] related-commands: [] orchestrated-by: [cs-java-engineer] # === TECHNICAL === dependencies: scripts: [spring_project_scaffolder.py, dependency_analyzer.py, entity_generator.py, api_endpoint_generator.py, security_config_generator.py, performance_profiler.py] references: [spring-boot-best-practices.md, microservices-patterns.md, jpa-hibernate-guide.md, spring-security-reference.md, java-performance-tuning.md] assets: [] compatibility: python-version: 3.8+ platforms: [macos, linux, windows] tech-stack: - Java 17/21 LTS - Spring Boot 3.x - Spring Framework 6.x - Spring Cloud - Spring Security - Spring Data JPA - Hibernate ORM - Maven/Gradle - JUnit 5 - Mockito - Docker - Kubernetes # === EXAMPLES === examples: - title: "Spring Boot Project Scaffolding" input: "python scripts/spring_project_scaffolder.py my-service --type microservice --db postgresql" output: "Complete Spring Boot 3.x project with layered architecture, Docker setup, and CI/CD pipeline" - title: "Entity Generation" input: "python scripts/entity_generator.py User --fields 'id:Long,email:String,name:String,createdAt:LocalDateTime'" output: "JPA entity with repository, service, controller, and DTO classes" # === ANALYTICS === stats: downloads: 0 stars: 0 rating: 0.0 reviews: 0 # === VERSIONING === version: v1.0.0 author: Claude Skills Team contributors: [] created: 2025-12-16 updated: 2025-12-16 license: MIT # === DISCOVERABILITY === tags: - java - spring-boot - spring-framework - microservices - jpa - hibernate - spring-cloud - webflux - enterprise - cloud-native - maven - gradle - api - backend featured: false verified: true --- # Senior Java World-class Java and Spring Boot development skill for enterprise applications, microservices, and cloud-native systems. Expertise in Spring Framework, Spring Boot 3.x, Spring Cloud, JPA/Hibernate, and reactive programming with WebFlux. ## Overview This skill provides production-ready Java and Spring Boot development capabilities through six Python automation tools and comprehensive reference documentation. Whether building enterprise monoliths, microservices architectures, or reactive systems, this skill ensures best practices, scalable architecture, and enterprise-grade security. **What This Skill Provides:** - Spring Boot project scaffolding with layered architecture - JPA entity and repository generation with optimized queries - RESTful API endpoint scaffolding with proper error handling - Spring Security configuration (OAuth2, JWT, RBAC) - Dependency analysis and upgrade recommendations - JVM performance profiling and optimization guidance **Use this skill when:** - Starting new Spring Boot projects or microservices - Implementing JPA/Hibernate data layers - Designing RESTful APIs with Spring MVC or WebFlux - Setting up authentication and authorization - Optimizing JVM and application performance - Reviewing Java code for quality and patterns **Core Value:** Save 60%+ time on project scaffolding while ensuring enterprise-grade architecture, security compliance, and performance optimization. ## Core Capabilities - **Spring Boot Scaffolding** - Generate production-ready Spring Boot 3.x projects with layered architecture, Docker configuration, and CI/CD pipelines - **Entity Generation** - Create JPA entities with repositories, services, controllers, DTOs, and mappers following DDD patterns - **API Development** - Scaffold RESTful endpoints with validation, error handling, pagination, and OpenAPI documentation - **Security Implementation** - Configure Spring Security with OAuth2, JWT, role-based access control, and security best practices - **Dependency Management** - Analyze dependencies for vulnerabilities, outdated versions, and upgrade paths - **Performance Optimization** - Profile JVM applications, identify bottlenecks, and generate optimization recommendations ## Quick Start ### Create Spring Boot Project ```bash # Create microservice with PostgreSQL python scripts/spring_project_scaffolder.py order-service --type microservice --db postgresql # Create monolith with MySQL and security python scripts/spring_project_scaffolder.py ecommerce-app --type monolith --db mysql --security oauth2 # Create reactive WebFlux service python scripts/spring_project_scaffolder.py notification-service --type reactive --db mongodb ``` ### Generate JPA Entity ```bash # Generate complete entity stack (entity, repository, service, controller, DTO) python scripts/entity_generator.py Product --fields "id:Long,name:String,price:BigDecimal,category:String,createdAt:LocalDateTime" # Generate with relationships python scripts/entity_generator.py Order --fields "id:Long,customer:Customer,items:List<OrderItem>,total:BigDecimal" --relations "customer:ManyToOne,items:OneToMany" ``` ### Analyze Dependencies ```bash # Check for vulnerabilities and updates python scripts/dependency_analyzer.py pom.xml --check-security # Generate upgrade report python scripts/dependency_analyzer.py build.gradle --output report.md ``` ### Access Documentation - Spring Boot patterns: `references/spring-boot-best-practices.md` - Microservices design: `references/microservices-patterns.md` - JPA/Hibernate guide: `references/jpa-hibernate-guide.md` - Security reference: `references/spring-security-reference.md` - Performance tuning: `references/java-performance-tuning.md` ## Key Workflows ### 1. New Spring Boot Microservice **Time:** 30-45 minutes 1. **Scaffold Project** - Generate microservice with Spring Boot 3.x, Docker, and CI/CD ```bash python scripts/spring_project_scaffolder.py inventory-service --type microservice --db postgresql --security jwt ``` 2. **Configure Environment** - Set up application.yml with profiles (dev, staging, prod) ```bash cd inventory-service # Edit src/main/resources/application.yml # Configure database, security, and service discovery ``` 3. **Generate Entities** - Create domain model with JPA entities ```bash python scripts/entity_generator.py Inventory --fields "id:Long,productId:Long,quantity:Integer,warehouse:String" python scripts/entity_generator.py InventoryMovement --fields "id:Long,inventory:Inventory,quantity:Integer,type:MovementType,timestamp:LocalDateTime" ``` 4. **Implement Business Logic** - Add service layer logic and validation rules 5. **Add Tests** - Generate unit and integration tests ```bash # Run tests ./mvnw test ./mvnw verify # Integration tests ``` 6. **Build and Deploy** ```bash ./mvnw clean package -DskipTests docker build -t inventory-service:latest . ``` See [spring-boot-best-practices.md](references/spring-boot-best-practices.md) for complete setup patterns. ### 2. REST API Development **Time:** 20-30 minutes per endpoint group 1. **Design API Contract** - Define endpoints following REST conventions ```bash python scripts/api_endpoint_generator.py products --methods GET,POST,PUT,DELETE --paginated ``` 2. **Implement Validation** - Add Jakarta validation annotations and custom validators 3. **Configure Error Handling** - Set up global exception handler with problem details (RFC 7807) 4. **Add OpenAPI Documentation** - Configure SpringDoc for automatic API docs 5. **Test Endpoints** - Generate integration tests with MockMvc or WebTestClient See [spring-boot-best-practices.md](references/spring-boot-best-practices.md) for API design patterns. ### 3. JPA/Hibernate Optimization **Time:** 1-2 hours for complex data models 1. **Analyze Current Queries** - Profile repository methods for N+1 problems ```bash python scripts/performance_profiler.py --analyze-queries src/ ``` 2. **Optimize Fetch Strategies** - Configure lazy/eager loading appropriately 3. **Add Query Hints** - Implement entity graphs and query hints for complex queries 4. **Configure Caching** - Set up Hibernate second-level cache with Hazelcast or Redis 5. **Implement Pagination** - Use Spring Data's Slice or Page for large datasets See [jpa-hibernate-guide.md](references/jpa-hibernate-guide.md) for optimization patterns. ### 4. Spring Security Implementation **Time:** 1-2 hours 1. **Generate Security Config** - Create security configuration for chosen auth method ```bash python scripts/security_config_generator.py --type jwt --roles ADMIN,USER,MANAGER ``` 2. **Configure OAuth2/JWT** - Set up token generation, validation, and refresh 3. **Implement RBAC** - Add role-based access control to endpoints 4. **Add Method Security** - Configure @PreAuthorize and @PostAuthorize annotations 5. **Test Security** - Generate security integration tests See [spring-security-reference.md](references/spring-security-reference.md) for security patterns. ## Python Tools ### spring_project_scaffolder.py Generate production-ready Spring Boot project structures with complete configuration. **Key Features:** - Spring Boot 3.x with Java 17/21 support - Multiple project types (microservice, monolith, reactive) - Database configuration (PostgreSQL, MySQL, MongoDB, H2) - Docker and Docker Compose setup - GitHub Actions CI/CD pipeline - Layered architecture (controller, service, repository) - Lombok and MapStruct integration **Common Usage:** ```bash # Microservice with PostgreSQL and JWT security python scripts/spring_project_scaffolder.py user-service --type microservice --db postgresql --security jwt # Monolith with MySQL and OAuth2 python scripts/spring_project_scaffolder.py ecommerce --type monolith --db mysql --security oauth2 # Reactive service with MongoDB python scripts/spring_project_scaffolder.py notification-service --type reactive --db mongodb # Help python scripts/spring_project_scaffolder.py --help ``` ### entity_generator.py Generate complete JPA entity stacks with repository, service, controller, and DTO. **Key Features:** - JPA entity with Lombok annotations - Spring Data JPA repository with custom queries - Service layer with transaction management - REST controller with validation - DTO and mapper (MapStruct) - Relationship support (OneToMany, ManyToOne, ManyToMany) **Common Usage:** ```bash # Basic entity python scripts/entity_generator.py Customer --fields "id:Long,name:String,email:String" # Entity with relationships python scripts/entity_generator.py Order --fields "id:Long,customer:Customer,total:BigDecimal" --relations "customer:ManyToOne" # Entity with audit fields python scripts/entity_generator.py Product --fields "id:Long,name:String,price:BigDecimal" --auditable # Help python scripts/entity_generator.py --help ``` ### api_endpoint_generator.py Scaffold RESTful API endpoints with validation and documentation. **Key Features:** - CRUD endpoint generation - Request/response DTOs - Jakarta validation annotations - OpenAPI annotations - Pagination support - Error handling **Common Usage:** ```bash # Full CRUD endpoints python scripts/api_endpoint_generator.py orders --methods GET,POST,PUT,DELETE # Read-only with pagination python scripts/api_endpoint_generator.py reports --methods GET --paginated # Help python scripts/api_endpoint_generator.py --help ``` ### security_config_generator.py Generate Spring Security configuration for various authentication methods. **Key Features:** - JWT authentication setup - OAuth2 resource server configuration - Role-based access control - Method security configuration - CORS and CSRF configuration - Security filter chain **Common Usage:** ```bash # JWT security with roles python scripts/security_config_generator.py --type jwt --roles ADMIN,USER # OAuth2 resource server python scripts/security_config_generator.py --type oauth2 --issuer-uri https://auth.example.com # Help python scripts/security_config_generator.py --help ``` ### dependency_analyzer.py Analyze Maven/Gradle dependencies for vulnerabilities and updates. **Key Features:** - Security vulnerability scanning - Outdated dependency detection - Upgrade path recommendations - Dependency tree analysis - License compliance checking **Common Usage:** ```bash # Analyze Maven project python scripts/dependency_analyzer.py pom.xml # Analyze Gradle with security focus python scripts/dependency_analyzer.py build.gradle --check-security # Generate markdown report python scripts/dependency_analyzer.py pom.xml --output report.md # Help python scripts/dependency_analyzer.py --help ``` ### performance_profiler.py Profile JVM applications and generate optimization recommendations. **Key Features:** - Query analysis for N+1 detection - Memory usage patterns - GC behavior analysis - Thread pool recommendations - Connection pool optimization - JVM flag recommendations **Common Usage:** ```bash # Analyze source for performance issues python scripts/performance_profiler.py --analyze-queries src/ # Profile running application python scripts/performance_profiler.py --profile http://localhost:8080/actuator # Generate optimization report python scripts/performance_profiler.py src/ --output performance-report.md # Help python scripts/performance_profiler.py --help ``` ## Reference Documentation ### When to Use Each Reference **[spring-boot-best-practices.md](references/spring-boot-best-practices.md)** - Spring Boot Patterns - Project structure and layered architecture - Configuration management with profiles - API design and error handling - Testing strategies (unit, integration, contract) - Production readiness (actuator, monitoring) **[microservices-patterns.md](references/microservices-patterns.md)** - Microservices Architecture - Service decomposition strategies - Spring Cloud components (Config, Gateway, Discovery) - Inter-service communication (REST, gRPC, messaging) - Distributed tracing and observability - Circuit breaker and resilience patterns **[jpa-hibernate-guide.md](references/jpa-hibernate-guide.md)** - Data Layer - Entity design and mapping strategies - Repository patterns and custom queries - Fetch optimization and N+1 prevention - Caching strategies (first-level, second-level) - Transaction management **[spring-security-reference.md](references/spring-security-reference.md)** - Security - Authentication methods (JWT, OAuth2, SAML) - Authorization patterns (RBAC, ABAC) - Security filter chain configuration - Method security annotations - Security testing **[java-performance-tuning.md](references/java-performance-tuning.md)** - Performance - JVM tuning and GC optimization - Connection pool configuration - Caching strategies - Async processing and virtual threads - Profiling and monitoring tools ## Best Practices ### Quality Standards - **Code Coverage:** Target 80%+ for business logic, 60%+ overall - **API Documentation:** 100% endpoint coverage with OpenAPI - **Security Scanning:** Zero critical/high vulnerabilities - **Performance:** P99 latency < 200ms for CRUD operations ### Common Pitfalls to Avoid - **N+1 Queries** - Always use entity graphs or fetch joins for relationships - **Missing Transactions** - Ensure @Transactional on service methods modifying data - **Blocking in WebFlux** - Never use blocking calls in reactive pipelines - **Hardcoded Configuration** - Use externalized configuration with profiles - **Missing Validation** - Always validate input at controller layer - **Open Sessions in View** - Disable OSIV anti-pattern in production See [spring-boot-best-practices.md](references/spring-boot-best-practices.md) for detailed guidelines. ## Performance Metrics **Development Efficiency:** - Project scaffolding time (target: < 30 minutes) - Entity stack generation (target: < 5 minutes per entity) - Security setup time (target: < 1 hour) **Code Quality:** - Test coverage (target: 80%+) - Static analysis issues (target: 0 critical/high) - Documentation coverage (target: 100% public APIs) **Runtime Performance:** - P99 latency (target: < 200ms) - Throughput (target: > 1000 RPS per instance) - Memory efficiency (target: < 512MB heap for typical service) ## Integration This skill works best with: - **senior-backend** - For general API patterns and database design - **senior-architect** - For system design and microservices architecture decisions - **senior-devops** - For CI/CD pipeline and Kubernetes deployment - **senior-security** - For security audits and penetration testing See [spring-boot-best-practices.md](references/spring-boot-best-practices.md) for CI/CD and automation integration examples. ## Composability & Integration ### Skill Composition Patterns **This skill receives input from:** - **senior-architect** - Architecture decisions inform project scaffolding choices - **business-analyst-toolkit** - Requirements define entity models and API contracts - **product-manager-toolkit** - User stories guide feature implementation **This skill provides output to:** - **senior-devops** - Generated projects include Dockerfile and CI/CD configuration - **senior-qa** - Generated code includes test scaffolding for QA automation - **technical-writer** - OpenAPI specs feed API documentation generation ### Recommended Skill Combinations **Workflow Pattern 1: Microservices Development** ``` senior-architect → senior-java → senior-devops ``` Use this pattern for designing and deploying microservices with proper architecture review. **Workflow Pattern 2: Full-Stack Feature** ``` senior-java → senior-frontend → senior-qa ``` Use this pattern for end-to-end feature implementation with backend API, frontend UI, and testing. ## Benefits **Time Savings:** - 60% faster project scaffolding vs. manual setup - 50% reduction in boilerplate code through generation - 40% faster security implementation with templates **Quality Improvements:** - Consistent architecture across projects - Built-in security best practices - Comprehensive test coverage templates **Business Impact:** - Faster time-to-market for new services - Reduced technical debt through standardization - Lower maintenance costs through consistency ## Next Steps **Getting Started:** 1. Run `python scripts/spring_project_scaffolder.py my-service --type microservice --db postgresql` to create your first project 2. Review generated structure and customize configuration 3. Generate entities with `python scripts/entity_generator.py` **Advanced Usage:** - Configure Spring Cloud for service discovery - Implement reactive patterns with WebFlux - Set up distributed tracing with Micrometer ## Additional Resources - **Quick commands** - See tool documentation above - **Best practices** - See [spring-boot-best-practices.md](references/spring-boot-best-practices.md) - **Troubleshooting** - See [java-performance-tuning.md](references/java-performance-tuning.md) - **External documentation** - [Spring Boot Reference](https://docs.spring.io/spring-boot/docs/current/reference/html/) --- **Documentation:** Full skill guide and workflows available in this file **Support:** For issues or questions, refer to domain guide at `../CLAUDE.md` **Version:** 1.0.0 | **Last Updated:** 2025-12-16 | **Status:** Production Ready

path: HOW_TO_USE.md
content: # How to Use the Senior Java Skill

## Quick Start

Hey Claude—I just added the "senior-java" skill. Can you help me with [describe your task]?

## Example Invocations

### Example 1: Basic Usage
```
Hey Claude—I just added the "senior-java" skill. Can you [specific task related to this skill]?
```

### Example 2: Advanced Usage
```
Hey Claude—I just added the "senior-java" skill. Can you [more complex task with specific requirements]?
```

### Example 3: Integration with Other Skills
```
Hey Claude—I just added the "senior-java" skill. Can you use it together with [another skill] to [combined task]?
```

## What to Provide

When using this skill, provide:

- **Primary Input**: [Describe the main input needed]
- **Context** (optional): [What context helps improve results]
- **Preferences** (optional): [Any customization options]

## What You'll Get

This skill will provide:

- **Output Format**: Results formatted according to skill specifications
- **Analysis**: Insights and recommendations based on the input
- **Deliverables**: spring_project_scaffolder, dependency_analyzer, entity_generator, api_endpoint_generator, security_config_generator, performance_profiler

## Python Tools Available

This skill includes the following Python tools:

- **spring_project_scaffolder**: Spring Project Scaffolder functionality
- **dependency_analyzer**: Dependency Analyzer functionality
- **entity_generator**: Entity Generator functionality
- **api_endpoint_generator**: Api Endpoint Generator functionality
- **security_config_generator**: Security Config Generator functionality
- **performance_profiler**: Performance Profiler functionality

You can also run these tools directly:

```bash
python scripts/spring_project_scaffolder --help
```

## Tips for Best Results

1. **Be Specific**: Provide clear, detailed requirements for better results
2. **Provide Context**: Include relevant background information
3. **Iterate**: Start simple, then refine based on initial results
4. **Combine Skills**: This skill works well with other engineering skills

## Related Skills

Consider using these skills together:

- [List related skills from the same domain]
- [Skills that complement this one]

---

**Skill**: senior-java
**Domain**: engineering-team
**Version**: 1.0.0
**Last Updated**: 2025-12-16


path: references/java-performance-tuning.md
content: # Java Performance Tuning Guide

Comprehensive guide for optimizing JVM applications and Spring Boot services.

## JVM Configuration

### Memory Settings

```bash
# Production JVM settings
java -Xms2g -Xmx2g \
     -XX:MetaspaceSize=256m \
     -XX:MaxMetaspaceSize=512m \
     -XX:+UseG1GC \
     -XX:MaxGCPauseMillis=200 \
     -jar application.jar
```

| Flag | Purpose | Recommendation |
|------|---------|----------------|
| `-Xms` | Initial heap size | Set equal to -Xmx for consistent performance |
| `-Xmx` | Maximum heap size | 50-75% of available RAM |
| `-XX:MetaspaceSize` | Initial metaspace | 256m for typical apps |
| `-XX:MaxMetaspaceSize` | Max metaspace | 512m-1g |

### Garbage Collection

#### G1GC (Recommended for most apps)

```bash
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=16m
-XX:InitiatingHeapOccupancyPercent=45
```

#### ZGC (For low-latency requirements)

```bash
-XX:+UseZGC
-XX:+ZGenerational  # Java 21+
-XX:ZCollectionInterval=5
```

#### Shenandoah (Alternative low-pause collector)

```bash
-XX:+UseShenandoahGC
-XX:ShenandoahGCHeuristics=adaptive
```

### GC Logging

```bash
# Java 17+ unified logging
-Xlog:gc*:file=gc.log:time,uptime:filecount=5,filesize=10M

# Detailed GC logging
-Xlog:gc*,gc+phases=debug:file=gc-detailed.log:time,uptime,level,tags:filecount=5,filesize=20M
```

## Connection Pool Optimization

### HikariCP Configuration

```yaml
spring:
  datasource:
    hikari:
      # Pool sizing
      minimum-idle: 5
      maximum-pool-size: 20

      # Connection timeout
      connection-timeout: 30000  # 30 seconds
      idle-timeout: 600000       # 10 minutes
      max-lifetime: 1800000      # 30 minutes

      # Validation
      validation-timeout: 5000

      # Leak detection
      leak-detection-threshold: 60000  # 60 seconds

      # Performance
      auto-commit: true
      pool-name: MyAppPool
```

### Pool Size Formula

```
pool_size = (core_count * 2) + effective_spindle_count
```

For most applications: **10-20 connections** is optimal.

## Query Optimization

### Hibernate Settings

```yaml
spring:
  jpa:
    properties:
      hibernate:
        # Batch operations
        jdbc:
          batch_size: 25
          batch_versioned_data: true
        order_inserts: true
        order_updates: true

        # Fetch optimization
        default_batch_fetch_size: 25

        # Statistics (disable in production)
        generate_statistics: false

        # Query plan cache
        query:
          plan_cache_max_size: 2048
          plan_parameter_metadata_max_size: 128
```

### N+1 Query Prevention

```java
// BAD: N+1 queries
List<Order> orders = orderRepository.findAll();
for (Order order : orders) {
    order.getCustomer().getName();  // Additional query per order
}

// GOOD: Fetch join
@Query("SELECT o FROM Order o JOIN FETCH o.customer")
List<Order> findAllWithCustomer();

// GOOD: EntityGraph
@EntityGraph(attributePaths = {"customer", "items"})
List<Order> findByStatus(OrderStatus status);
```

### Batch Processing

```java
@Transactional
public void batchInsert(List<Product> products) {
    int batchSize = 50;
    for (int i = 0; i < products.size(); i++) {
        entityManager.persist(products.get(i));
        if (i > 0 && i % batchSize == 0) {
            entityManager.flush();
            entityManager.clear();
        }
    }
}
```

## Caching Strategies

### Spring Cache with Caffeine

```java
@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        cacheManager.setCaffeine(Caffeine.newBuilder()
            .initialCapacity(100)
            .maximumSize(1000)
            .expireAfterWrite(Duration.ofMinutes(10))
            .recordStats());
        return cacheManager;
    }
}
```

```java
@Service
public class ProductService {

    @Cacheable(value = "products", key = "#id")
    public Product findById(Long id) {
        return productRepository.findById(id).orElseThrow();
    }

    @CachePut(value = "products", key = "#product.id")
    public Product update(Product product) {
        return productRepository.save(product);
    }

    @CacheEvict(value = "products", key = "#id")
    public void delete(Long id) {
        productRepository.deleteById(id);
    }

    @CacheEvict(value = "products", allEntries = true)
    public void clearCache() {
        // Cache cleared
    }
}
```

### Redis Cache

```yaml
spring:
  cache:
    type: redis
    redis:
      time-to-live: 600000  # 10 minutes
      cache-null-values: false
  redis:
    host: localhost
    port: 6379
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 2
```

## Async Processing

### Configuration

```java
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

    @Override
    @Bean(name = "taskExecutor")
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(4);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("async-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new CustomAsyncExceptionHandler();
    }
}
```

### Async Service Methods

```java
@Service
public class NotificationService {

    @Async
    public CompletableFuture<Void> sendEmailAsync(String to, String subject, String body) {
        // Send email
        return CompletableFuture.completedFuture(null);
    }

    @Async
    public CompletableFuture<Report> generateReportAsync(ReportParams params) {
        Report report = generateReport(params);
        return CompletableFuture.completedFuture(report);
    }
}

// Usage
public void processOrder(Order order) {
    orderRepository.save(order);

    // Non-blocking email
    notificationService.sendEmailAsync(
        order.getCustomer().getEmail(),
        "Order Confirmation",
        "Your order has been placed"
    );
}
```

### Virtual Threads (Java 21+)

```java
@Configuration
public class VirtualThreadConfig {

    @Bean
    public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
        return protocolHandler -> {
            protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
        };
    }
}
```

## Response Compression

```yaml
server:
  compression:
    enabled: true
    mime-types: application/json,application/xml,text/html,text/xml,text/plain
    min-response-size: 1024
```

## Monitoring and Profiling

### Micrometer Metrics

```yaml
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  metrics:
    tags:
      application: ${spring.application.name}
    export:
      prometheus:
        enabled: true
```

```java
@Component
@RequiredArgsConstructor
public class CustomMetrics {

    private final MeterRegistry registry;

    @PostConstruct
    public void init() {
        Gauge.builder("custom.active.orders", this::getActiveOrderCount)
            .description("Number of active orders")
            .register(registry);
    }

    public void recordOrderProcessing(long duration) {
        registry.timer("order.processing.time").record(Duration.ofMillis(duration));
    }

    public void incrementOrderCount(String status) {
        registry.counter("orders.total", "status", status).increment();
    }
}
```

### JFR (Java Flight Recorder)

```bash
# Start with JFR enabled
java -XX:+FlightRecorder \
     -XX:StartFlightRecording=duration=60s,filename=recording.jfr \
     -jar application.jar

# Or start recording dynamically
jcmd <pid> JFR.start duration=60s filename=recording.jfr
```

## Performance Checklist

### Database

- [ ] Use connection pooling (HikariCP)
- [ ] Enable batch operations
- [ ] Add indexes for frequently queried columns
- [ ] Use pagination for large result sets
- [ ] Prevent N+1 queries with fetch joins
- [ ] Enable query caching where appropriate

### Caching

- [ ] Cache frequently accessed, rarely changed data
- [ ] Set appropriate TTL values
- [ ] Use distributed cache for clustered environments
- [ ] Monitor cache hit rates

### JVM

- [ ] Set appropriate heap sizes
- [ ] Choose right GC for workload
- [ ] Enable GC logging
- [ ] Monitor memory usage

### Application

- [ ] Use async processing for I/O operations
- [ ] Enable response compression
- [ ] Optimize JSON serialization
- [ ] Profile and identify bottlenecks

## Common Performance Anti-Patterns

1. **Unbounded queries** - Always use pagination
2. **Synchronous I/O in request threads** - Use async/reactive
3. **Creating objects in tight loops** - Reuse or pool objects
4. **String concatenation in loops** - Use StringBuilder
5. **Excessive logging in hot paths** - Use appropriate log levels
6. **Missing indexes** - Profile queries and add indexes
7. **Oversized connection pools** - Size based on actual needs
8. **Caching too much** - Cache wisely, monitor hit rates


path: references/jpa-hibernate-guide.md
content: # JPA/Hibernate Best Practices Guide

Comprehensive guide for building efficient data layers with Spring Data JPA and Hibernate.

## Entity Design

### Basic Entity Structure

```java
@Entity
@Table(name = "products")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 255)
    private String name;

    @Column(columnDefinition = "TEXT")
    private String description;

    @Column(nullable = false, precision = 10, scale = 2)
    private BigDecimal price;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false, length = 20)
    private ProductStatus status;

    @CreatedDate
    @Column(nullable = false, updatable = false)
    private LocalDateTime createdAt;

    @LastModifiedDate
    @Column(nullable = false)
    private LocalDateTime updatedAt;

    @Version
    private Long version;
}
```

### Relationship Mappings

#### One-to-Many / Many-to-One

```java
@Entity
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "customer_id", nullable = false)
    private Customer customer;

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<OrderItem> items = new ArrayList<>();

    // Helper methods for bidirectional relationship
    public void addItem(OrderItem item) {
        items.add(item);
        item.setOrder(this);
    }

    public void removeItem(OrderItem item) {
        items.remove(item);
        item.setOrder(null);
    }
}

@Entity
public class OrderItem {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id", nullable = false)
    private Order order;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "product_id", nullable = false)
    private Product product;

    private Integer quantity;
    private BigDecimal price;
}
```

#### Many-to-Many

```java
@Entity
public class Student {
    @Id
    private Long id;

    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @JoinTable(
        name = "student_course",
        joinColumns = @JoinColumn(name = "student_id"),
        inverseJoinColumns = @JoinColumn(name = "course_id")
    )
    private Set<Course> courses = new HashSet<>();

    public void addCourse(Course course) {
        courses.add(course);
        course.getStudents().add(this);
    }

    public void removeCourse(Course course) {
        courses.remove(course);
        course.getStudents().remove(this);
    }
}
```

## Repository Patterns

### Basic Repository

```java
@Repository
public interface ProductRepository extends JpaRepository<Product, Long>,
                                          JpaSpecificationExecutor<Product> {

    Optional<Product> findByName(String name);

    List<Product> findByStatusAndPriceGreaterThan(ProductStatus status, BigDecimal price);

    @Query("SELECT p FROM Product p WHERE p.category.id = :categoryId AND p.status = 'ACTIVE'")
    List<Product> findActiveByCategory(@Param("categoryId") Long categoryId);

    @Query(value = "SELECT * FROM products WHERE LOWER(name) LIKE LOWER(CONCAT('%', :search, '%'))",
           nativeQuery = true)
    Page<Product> searchByName(@Param("search") String search, Pageable pageable);

    @Modifying
    @Query("UPDATE Product p SET p.status = :status WHERE p.id IN :ids")
    int updateStatusByIds(@Param("ids") List<Long> ids, @Param("status") ProductStatus status);
}
```

### Specifications for Dynamic Queries

```java
public class ProductSpecifications {

    public static Specification<Product> hasName(String name) {
        return (root, query, cb) ->
            name == null ? null : cb.like(cb.lower(root.get("name")), "%" + name.toLowerCase() + "%");
    }

    public static Specification<Product> hasCategory(Long categoryId) {
        return (root, query, cb) ->
            categoryId == null ? null : cb.equal(root.get("category").get("id"), categoryId);
    }

    public static Specification<Product> hasPriceBetween(BigDecimal min, BigDecimal max) {
        return (root, query, cb) -> {
            if (min == null && max == null) return null;
            if (min == null) return cb.lessThanOrEqualTo(root.get("price"), max);
            if (max == null) return cb.greaterThanOrEqualTo(root.get("price"), min);
            return cb.between(root.get("price"), min, max);
        };
    }

    public static Specification<Product> isActive() {
        return (root, query, cb) ->
            cb.equal(root.get("status"), ProductStatus.ACTIVE);
    }
}

// Usage in service
public Page<Product> search(ProductSearchCriteria criteria, Pageable pageable) {
    Specification<Product> spec = Specification
        .where(ProductSpecifications.hasName(criteria.getName()))
        .and(ProductSpecifications.hasCategory(criteria.getCategoryId()))
        .and(ProductSpecifications.hasPriceBetween(criteria.getMinPrice(), criteria.getMaxPrice()))
        .and(ProductSpecifications.isActive());

    return productRepository.findAll(spec, pageable);
}
```

## N+1 Query Prevention

### Problem: N+1 Queries

```java
// BAD: This causes N+1 queries
List<Order> orders = orderRepository.findAll();
for (Order order : orders) {
    // Each access triggers a separate query!
    String customerName = order.getCustomer().getName();
}
```

### Solution 1: Entity Graph

```java
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {

    @EntityGraph(attributePaths = {"customer", "items", "items.product"})
    List<Order> findAll();

    @EntityGraph(attributePaths = {"customer"})
    Optional<Order> findWithCustomerById(Long id);
}

// Or define named entity graph on entity
@Entity
@NamedEntityGraph(
    name = "Order.withCustomerAndItems",
    attributeNodes = {
        @NamedAttributeNode("customer"),
        @NamedAttributeNode(value = "items", subgraph = "items-subgraph")
    },
    subgraphs = {
        @NamedSubgraph(name = "items-subgraph", attributeNodes = @NamedAttributeNode("product"))
    }
)
public class Order { ... }

// Use in repository
@EntityGraph(value = "Order.withCustomerAndItems")
List<Order> findByCustomerId(Long customerId);
```

### Solution 2: Fetch Join (JPQL)

```java
@Query("SELECT DISTINCT o FROM Order o " +
       "JOIN FETCH o.customer " +
       "JOIN FETCH o.items i " +
       "JOIN FETCH i.product " +
       "WHERE o.status = :status")
List<Order> findByStatusWithDetails(@Param("status") OrderStatus status);
```

### Solution 3: Batch Fetching

```java
@Entity
public class Order {
    @OneToMany(mappedBy = "order")
    @BatchSize(size = 25)  // Fetch in batches of 25
    private List<OrderItem> items;
}

// Or configure globally in application.yml
spring:
  jpa:
    properties:
      hibernate:
        default_batch_fetch_size: 25
```

## Projections

### Interface Projection

```java
public interface ProductSummary {
    Long getId();
    String getName();
    BigDecimal getPrice();

    @Value("#{target.category.name}")
    String getCategoryName();
}

@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {

    List<ProductSummary> findByStatus(ProductStatus status);

    @Query("SELECT p.id as id, p.name as name, p.price as price FROM Product p WHERE p.category.id = :categoryId")
    List<ProductSummary> findSummaryByCategory(@Param("categoryId") Long categoryId);
}
```

### DTO Projection

```java
public record ProductDTO(Long id, String name, BigDecimal price, String categoryName) {}

@Query("SELECT new com.example.dto.ProductDTO(p.id, p.name, p.price, c.name) " +
       "FROM Product p JOIN p.category c WHERE p.status = 'ACTIVE'")
List<ProductDTO> findActiveProductsWithCategory();
```

## Caching

### First-Level Cache (Session Cache)

- Automatic within transaction
- Prevents duplicate queries for same entity

### Second-Level Cache (Shared Cache)

```java
@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "products")
public class Product {
    // ...

    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    @OneToMany(mappedBy = "product")
    private List<Review> reviews;
}
```

```yaml
# application.yml
spring:
  jpa:
    properties:
      hibernate:
        cache:
          use_second_level_cache: true
          use_query_cache: true
          region:
            factory_class: org.hibernate.cache.jcache.JCacheRegionFactory
        javax:
          cache:
            provider: com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider
```

### Query Cache

```java
@QueryHints(@QueryHint(name = org.hibernate.jpa.QueryHints.HINT_CACHEABLE, value = "true"))
List<Product> findByCategory(Category category);
```

## Transaction Management

### Service Layer Transactions

```java
@Service
@Transactional(readOnly = true)  // Default read-only for queries
@RequiredArgsConstructor
public class OrderService {

    private final OrderRepository orderRepository;

    public Order findById(Long id) {
        return orderRepository.findById(id)
            .orElseThrow(() -> new NotFoundException("Order not found"));
    }

    @Transactional  // Override for write operations
    public Order createOrder(CreateOrderDTO dto) {
        Order order = Order.create(dto);
        return orderRepository.save(order);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateOrderStatus(Long orderId, OrderStatus status) {
        // Runs in new transaction
        Order order = orderRepository.findById(orderId).orElseThrow();
        order.setStatus(status);
    }

    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void processPayment(Long orderId) {
        // High isolation for critical operations
    }
}
```

### Optimistic Locking

```java
@Entity
public class Product {
    @Version
    private Long version;
}

// Handle OptimisticLockException
@Transactional
public Product updatePrice(Long id, BigDecimal newPrice) {
    try {
        Product product = productRepository.findById(id).orElseThrow();
        product.setPrice(newPrice);
        return productRepository.save(product);
    } catch (OptimisticLockException e) {
        throw new ConcurrentModificationException("Product was modified by another user");
    }
}
```

## Performance Tips

### 1. Use Proper Fetch Strategy

```java
// LAZY for collections (default for @OneToMany, @ManyToMany)
@OneToMany(fetch = FetchType.LAZY)
private List<OrderItem> items;

// LAZY for single associations when not always needed
@ManyToOne(fetch = FetchType.LAZY)
private Category category;
```

### 2. Avoid OSIV Anti-Pattern

```yaml
spring:
  jpa:
    open-in-view: false  # Disable Open Session in View
```

### 3. Use Pagination

```java
Page<Product> findByCategory(Category category, Pageable pageable);

// Or use Slice for infinite scroll
Slice<Product> findByStatus(ProductStatus status, Pageable pageable);
```

### 4. Bulk Operations

```java
@Modifying
@Query("UPDATE Product p SET p.price = p.price * :multiplier WHERE p.category.id = :categoryId")
int updatePricesByCategory(@Param("categoryId") Long categoryId, @Param("multiplier") BigDecimal multiplier);

@Modifying
@Query("DELETE FROM Product p WHERE p.status = 'DELETED' AND p.deletedAt < :cutoff")
int deleteOldProducts(@Param("cutoff") LocalDateTime cutoff);
```

### 5. Proper Indexing

```java
@Entity
@Table(name = "products", indexes = {
    @Index(name = "idx_product_name", columnList = "name"),
    @Index(name = "idx_product_category_status", columnList = "category_id, status"),
    @Index(name = "idx_product_created", columnList = "created_at DESC")
})
public class Product { ... }
```

## Common Pitfalls

1. **N+1 Queries** - Always use EntityGraph or fetch joins for related entities
2. **Eager Fetching** - Avoid EAGER fetch type, use LAZY with explicit fetching
3. **Missing @Transactional** - Ensure write operations are transactional
4. **Large Result Sets** - Always use pagination
5. **Orphan Entities** - Use `orphanRemoval = true` for owned collections
6. **Detached Entity Issues** - Merge detached entities before modifying
7. **Missing Indexes** - Add indexes for frequently queried columns
8. **toString() Cycles** - Exclude relationship fields from toString


path: references/microservices-patterns.md
content: # Microservices Patterns with Spring Cloud

Comprehensive guide for building microservices with Spring Cloud.

## Service Decomposition

### Domain-Driven Design Approach

- **Bounded Contexts** - Each microservice owns a bounded context
- **Aggregates** - Single transaction boundary
- **Domain Events** - Communication between bounded contexts

### Decomposition Strategies

1. **By Business Capability** - Order service, Payment service, Inventory service
2. **By Subdomain** - Core, Supporting, Generic subdomains
3. **Strangler Pattern** - Gradual migration from monolith

## Spring Cloud Components

### Service Discovery (Eureka)

```yaml
# Eureka Server
spring:
  application:
    name: eureka-server

eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
  server:
    enable-self-preservation: false
```

```yaml
# Eureka Client
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
  instance:
    prefer-ip-address: true
    instance-id: ${spring.application.name}:${random.uuid}
```

### API Gateway (Spring Cloud Gateway)

```yaml
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/users/**
          filters:
            - StripPrefix=1
            - AddRequestHeader=X-Request-Source, gateway

        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/orders/**
          filters:
            - StripPrefix=1
            - CircuitBreaker=name=orderCircuitBreaker,fallbackUri=forward:/fallback/orders
```

### Configuration Server

```yaml
# Config Server
spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/myorg/config-repo
          default-label: main
          search-paths: '{application}'
```

```yaml
# Config Client
spring:
  config:
    import: configserver:http://localhost:8888
  cloud:
    config:
      fail-fast: true
      retry:
        max-attempts: 6
```

## Inter-Service Communication

### Synchronous (REST/OpenFeign)

```java
@FeignClient(name = "user-service", fallback = UserClientFallback.class)
public interface UserClient {

    @GetMapping("/api/users/{id}")
    UserDTO getUserById(@PathVariable Long id);

    @PostMapping("/api/users")
    UserDTO createUser(@RequestBody CreateUserDTO dto);
}

@Component
public class UserClientFallback implements UserClient {

    @Override
    public UserDTO getUserById(Long id) {
        return UserDTO.builder()
            .id(id)
            .username("Unknown")
            .build();
    }

    @Override
    public UserDTO createUser(CreateUserDTO dto) {
        throw new ServiceUnavailableException("User service unavailable");
    }
}
```

### Asynchronous (RabbitMQ/Kafka)

```java
// Publisher
@Service
@RequiredArgsConstructor
public class OrderEventPublisher {

    private final RabbitTemplate rabbitTemplate;

    public void publishOrderCreated(Order order) {
        OrderCreatedEvent event = new OrderCreatedEvent(
            order.getId(),
            order.getCustomerId(),
            order.getTotal(),
            Instant.now()
        );
        rabbitTemplate.convertAndSend("orders.exchange", "order.created", event);
    }
}

// Consumer
@Component
@Slf4j
public class OrderEventConsumer {

    @RabbitListener(queues = "inventory.order.created")
    public void handleOrderCreated(OrderCreatedEvent event) {
        log.info("Processing order: {}", event.getOrderId());
        // Update inventory
    }
}
```

## Resilience Patterns

### Circuit Breaker (Resilience4j)

```java
@Service
@RequiredArgsConstructor
public class PaymentService {

    private final PaymentClient paymentClient;

    @CircuitBreaker(name = "payment", fallbackMethod = "processPaymentFallback")
    @Retry(name = "payment")
    @TimeLimiter(name = "payment")
    public CompletableFuture<PaymentResult> processPayment(PaymentRequest request) {
        return CompletableFuture.supplyAsync(() ->
            paymentClient.process(request)
        );
    }

    private CompletableFuture<PaymentResult> processPaymentFallback(
            PaymentRequest request, Throwable t) {
        log.warn("Payment service unavailable, queuing for retry");
        return CompletableFuture.completedFuture(
            PaymentResult.pending(request.getOrderId())
        );
    }
}
```

```yaml
resilience4j:
  circuitbreaker:
    instances:
      payment:
        sliding-window-size: 10
        failure-rate-threshold: 50
        wait-duration-in-open-state: 10s
        permitted-number-of-calls-in-half-open-state: 3
  retry:
    instances:
      payment:
        max-attempts: 3
        wait-duration: 1s
        exponential-backoff-multiplier: 2
  timelimiter:
    instances:
      payment:
        timeout-duration: 3s
```

### Bulkhead Pattern

```java
@Bulkhead(name = "inventory", fallbackMethod = "checkInventoryFallback")
public boolean checkInventory(Long productId, int quantity) {
    return inventoryClient.check(productId, quantity);
}
```

## Distributed Tracing

### Micrometer + Zipkin

```yaml
management:
  tracing:
    sampling:
      probability: 1.0
  zipkin:
    tracing:
      endpoint: http://localhost:9411/api/v2/spans

logging:
  pattern:
    level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]"
```

### Custom Spans

```java
@Service
@RequiredArgsConstructor
public class OrderService {

    private final Tracer tracer;

    public Order processOrder(CreateOrderDTO dto) {
        Span span = tracer.nextSpan().name("process-order").start();
        try (Tracer.SpanInScope ws = tracer.withSpan(span)) {
            span.tag("order.customer", dto.getCustomerId().toString());
            // Process order
            return order;
        } finally {
            span.end();
        }
    }
}
```

## Saga Pattern

### Choreography-Based Saga

```java
// Order Service - Initiates saga
@Transactional
public Order createOrder(CreateOrderDTO dto) {
    Order order = orderRepository.save(Order.pending(dto));
    eventPublisher.publish(new OrderCreatedEvent(order));
    return order;
}

// Payment Service - Handles event
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
    try {
        Payment payment = paymentService.process(event.getOrderId());
        eventPublisher.publish(new PaymentCompletedEvent(payment));
    } catch (PaymentException e) {
        eventPublisher.publish(new PaymentFailedEvent(event.getOrderId(), e.getMessage()));
    }
}

// Order Service - Compensating transaction
@EventListener
public void handlePaymentFailed(PaymentFailedEvent event) {
    orderService.cancelOrder(event.getOrderId(), event.getReason());
}
```

### Orchestration-Based Saga

```java
@Service
@RequiredArgsConstructor
public class OrderSagaOrchestrator {

    private final OrderService orderService;
    private final PaymentClient paymentClient;
    private final InventoryClient inventoryClient;

    @Transactional
    public Order executeOrderSaga(CreateOrderDTO dto) {
        // Step 1: Create order
        Order order = orderService.createPendingOrder(dto);

        try {
            // Step 2: Reserve inventory
            inventoryClient.reserve(order.getId(), dto.getItems());

            // Step 3: Process payment
            paymentClient.process(order.getId(), dto.getTotal());

            // Step 4: Confirm order
            return orderService.confirmOrder(order.getId());

        } catch (InventoryException e) {
            // Compensate: Cancel order
            orderService.cancelOrder(order.getId(), "Inventory unavailable");
            throw e;

        } catch (PaymentException e) {
            // Compensate: Release inventory, cancel order
            inventoryClient.release(order.getId());
            orderService.cancelOrder(order.getId(), "Payment failed");
            throw e;
        }
    }
}
```

## Event Sourcing

```java
@Entity
@Table(name = "domain_events")
public class DomainEvent {
    @Id
    private UUID eventId;
    private String aggregateId;
    private String aggregateType;
    private String eventType;
    private String payload;
    private Instant occurredAt;
    private Long version;
}

@Service
public class EventStore {

    public void append(String aggregateId, DomainEvent event) {
        event.setVersion(getNextVersion(aggregateId));
        eventRepository.save(event);
        eventPublisher.publish(event);
    }

    public List<DomainEvent> getEvents(String aggregateId) {
        return eventRepository.findByAggregateIdOrderByVersionAsc(aggregateId);
    }

    public <T> T reconstruct(String aggregateId, Class<T> aggregateType) {
        List<DomainEvent> events = getEvents(aggregateId);
        return eventSourcedAggregate.replay(events, aggregateType);
    }
}
```

## CQRS Pattern

```java
// Command side
@Service
public class OrderCommandService {

    @Transactional
    public void createOrder(CreateOrderCommand cmd) {
        Order order = Order.create(cmd);
        orderRepository.save(order);
        eventPublisher.publish(new OrderCreatedEvent(order));
    }
}

// Query side
@Service
public class OrderQueryService {

    private final OrderReadRepository readRepository;

    public OrderView findById(Long id) {
        return readRepository.findById(id)
            .orElseThrow(() -> new NotFoundException("Order not found"));
    }

    public Page<OrderSummary> findByCustomer(Long customerId, Pageable pageable) {
        return readRepository.findByCustomerId(customerId, pageable);
    }
}

// Event handler updates read model
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
    OrderView view = OrderView.from(event);
    orderReadRepository.save(view);
}
```

## Deployment Patterns

### Blue-Green Deployment

```yaml
# Kubernetes service with selector
apiVersion: v1
kind: Service
metadata:
  name: order-service
spec:
  selector:
    app: order-service
    version: blue  # Switch to 'green' for deployment
  ports:
    - port: 80
      targetPort: 8080
```

### Canary Release

```yaml
# Istio VirtualService
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: order-service
spec:
  hosts:
    - order-service
  http:
    - route:
        - destination:
            host: order-service
            subset: stable
          weight: 90
        - destination:
            host: order-service
            subset: canary
          weight: 10
```

## Best Practices Checklist

- [ ] Each service has its own database
- [ ] Services communicate through well-defined APIs
- [ ] Implement circuit breakers for external calls
- [ ] Use distributed tracing for debugging
- [ ] Implement health checks and readiness probes
- [ ] Centralize configuration management
- [ ] Use service discovery for dynamic routing
- [ ] Implement proper logging with correlation IDs
- [ ] Design for failure and implement retries
- [ ] Version your APIs


path: references/spring-boot-best-practices.md
content: # Spring Boot Best Practices

Comprehensive guide for building production-ready Spring Boot applications.

## Project Structure

### Layered Architecture

```
src/main/java/com/example/myapp/
├── MyAppApplication.java          # Main class
├── config/                        # Configuration classes
│   ├── SecurityConfig.java
│   ├── OpenApiConfig.java
│   └── AsyncConfig.java
├── controller/                    # REST controllers
│   ├── UserController.java
│   └── ProductController.java
├── service/                       # Business logic
│   ├── UserService.java
│   └── ProductService.java
├── repository/                    # Data access
│   ├── UserRepository.java
│   └── ProductRepository.java
├── entity/                        # JPA entities
│   ├── User.java
│   └── Product.java
├── dto/                           # Data transfer objects
│   ├── UserDTO.java
│   └── ProductDTO.java
├── mapper/                        # DTO mappers
│   ├── UserMapper.java
│   └── ProductMapper.java
├── exception/                     # Exception handling
│   ├── GlobalExceptionHandler.java
│   └── ResourceNotFoundException.java
└── security/                      # Security components
    ├── JwtTokenProvider.java
    └── JwtAuthenticationFilter.java
```

### Package Naming Conventions

- Use lowercase, dot-separated package names
- Group by feature or layer (prefer layer for smaller apps)
- Example: `com.example.myapp.user.controller`

## Configuration Management

### Profile-Based Configuration

```yaml
# application.yml - Common configuration
spring:
  application:
    name: my-service
  profiles:
    active: ${SPRING_PROFILES_ACTIVE:dev}

---
# Development profile
spring:
  config:
    activate:
      on-profile: dev
  datasource:
    url: jdbc:postgresql://localhost:5432/myapp_dev
    username: postgres
    [REDACTED_SECRET]
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true

---
# Production profile
spring:
  config:
    activate:
      on-profile: prod
  datasource:
    url: ${DATABASE_URL}
    username: ${DATABASE_USERNAME}
    [REDACTED_SECRET]
  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: false
```

### Environment Variables

- Use `${VAR:default}` syntax for fallbacks
- Store secrets in environment variables or secret managers
- Never commit sensitive data to version control

```yaml
jwt:
  [REDACTED_SECRET]
  expiration: ${JWT_EXPIRATION:86400000}
```

## REST API Design

### Controller Structure

```java
@RestController
@RequestMapping("/api/v1/users")
@RequiredArgsConstructor
@Tag(name = "Users", description = "User management APIs")
public class UserController {

    private final UserService userService;

    @GetMapping
    @Operation(summary = "Get all users with pagination")
    public ResponseEntity<Page<UserDTO>> findAll(
            @PageableDefault(size = 20, sort = "createdAt", direction = Sort.Direction.DESC)
            Pageable pageable) {
        return ResponseEntity.ok(userService.findAll(pageable));
    }

    @GetMapping("/{id}")
    @Operation(summary = "Get user by ID")
    public ResponseEntity<UserDTO> findById(@PathVariable Long id) {
        return ResponseEntity.ok(userService.findById(id));
    }

    @PostMapping
    @Operation(summary = "Create a new user")
    public ResponseEntity<UserDTO> create(@Valid @RequestBody CreateUserDTO dto) {
        UserDTO created = userService.create(dto);
        URI location = ServletUriComponentsBuilder
            .fromCurrentRequest()
            .path("/{id}")
            .buildAndExpand(created.getId())
            .toUri();
        return ResponseEntity.created(location).body(created);
    }

    @PutMapping("/{id}")
    @Operation(summary = "Update an existing user")
    public ResponseEntity<UserDTO> update(
            @PathVariable Long id,
            @Valid @RequestBody UpdateUserDTO dto) {
        return ResponseEntity.ok(userService.update(id, dto));
    }

    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    @Operation(summary = "Delete a user")
    public void delete(@PathVariable Long id) {
        userService.delete(id);
    }
}
```

### HTTP Status Codes

| Code | Usage |
|------|-------|
| 200 OK | Successful GET, PUT |
| 201 Created | Successful POST with resource creation |
| 204 No Content | Successful DELETE |
| 400 Bad Request | Validation errors |
| 401 Unauthorized | Authentication required |
| 403 Forbidden | Insufficient permissions |
| 404 Not Found | Resource not found |
| 409 Conflict | Resource conflict (duplicate) |
| 500 Internal Server Error | Server-side errors |

### Error Response Format (RFC 7807)

```java
@Data
@Builder
public class ProblemDetail {
    private String type;
    private String title;
    private int status;
    private String detail;
    private String instance;
    private Map<String, Object> properties;
}
```

## Validation

### Request Validation

```java
@Data
public class CreateUserDTO {

    @NotBlank(message = "Username is required")
    @Size(min = 3, max = 50, message = "Username must be 3-50 characters")
    @Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "Username can only contain letters, numbers, and underscores")
    private String username;

    @NotBlank(message = "Email is required")
    @Email(message = "Invalid email format")
    private String email;

    @NotBlank(message = "Password is required")
    @Size(min = 8, message = "Password must be at least 8 characters")
    @Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).*$",
             message = "Password must contain uppercase, lowercase, and number")
    private String password;

    @PastOrPresent(message = "Birth date cannot be in the future")
    private LocalDate birthDate;
}
```

### Custom Validators

```java
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueEmailValidator.class)
public @interface UniqueEmail {
    String message() default "Email already exists";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

@Component
@RequiredArgsConstructor
public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {

    private final UserRepository userRepository;

    @Override
    public boolean isValid(String email, ConstraintValidatorContext context) {
        return email != null && !userRepository.existsByEmail(email);
    }
}
```

## Exception Handling

### Global Exception Handler

```java
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ProblemDetail> handleNotFound(
            ResourceNotFoundException ex, HttpServletRequest request) {
        log.warn("Resource not found: {}", ex.getMessage());

        ProblemDetail problem = ProblemDetail.builder()
            .type("https://api.example.com/errors/not-found")
            .title("Resource Not Found")
            .status(HttpStatus.NOT_FOUND.value())
            .detail(ex.getMessage())
            .instance(request.getRequestURI())
            .build();

        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(problem);
    }

    @ExceptionHandler(AccessDeniedException.class)
    public ResponseEntity<ProblemDetail> handleAccessDenied(
            AccessDeniedException ex, HttpServletRequest request) {
        log.warn("Access denied: {}", request.getRequestURI());

        ProblemDetail problem = ProblemDetail.builder()
            .type("https://api.example.com/errors/forbidden")
            .title("Access Denied")
            .status(HttpStatus.FORBIDDEN.value())
            .detail("You don't have permission to access this resource")
            .instance(request.getRequestURI())
            .build();

        return ResponseEntity.status(HttpStatus.FORBIDDEN).body(problem);
    }

    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(
            MethodArgumentNotValidException ex,
            HttpHeaders headers,
            HttpStatusCode status,
            WebRequest request) {

        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getFieldErrors().forEach(error ->
            errors.put(error.getField(), error.getDefaultMessage()));

        ProblemDetail problem = ProblemDetail.builder()
            .type("https://api.example.com/errors/validation")
            .title("Validation Failed")
            .status(HttpStatus.BAD_REQUEST.value())
            .detail("One or more fields have invalid values")
            .properties(Map.of("errors", errors))
            .build();

        return ResponseEntity.badRequest().body(problem);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ProblemDetail> handleGeneric(
            Exception ex, HttpServletRequest request) {
        log.error("Unexpected error", ex);

        ProblemDetail problem = ProblemDetail.builder()
            .type("https://api.example.com/errors/internal")
            .title("Internal Server Error")
            .status(HttpStatus.INTERNAL_SERVER_ERROR.value())
            .detail("An unexpected error occurred")
            .instance(request.getRequestURI())
            .build();

        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(problem);
    }
}
```

## Testing

### Unit Tests

```java
@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @Mock
    private UserMapper userMapper;

    @InjectMocks
    private UserService userService;

    @Test
    void findById_WhenUserExists_ReturnsUser() {
        // Given
        Long userId = 1L;
        User user = User.builder().id(userId).username("john").build();
        UserDTO userDTO = UserDTO.builder().id(userId).username("john").build();

        when(userRepository.findById(userId)).thenReturn(Optional.of(user));
        when(userMapper.toDto(user)).thenReturn(userDTO);

        // When
        UserDTO result = userService.findById(userId);

        // Then
        assertThat(result).isNotNull();
        assertThat(result.getUsername()).isEqualTo("john");
        verify(userRepository).findById(userId);
    }

    @Test
    void findById_WhenUserNotExists_ThrowsException() {
        // Given
        Long userId = 1L;
        when(userRepository.findById(userId)).thenReturn(Optional.empty());

        // When/Then
        assertThatThrownBy(() -> userService.findById(userId))
            .isInstanceOf(ResourceNotFoundException.class)
            .hasMessageContaining("User not found");
    }
}
```

### Integration Tests

```java
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@Testcontainers
class UserControllerIntegrationTest {

    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15-alpine");

    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
        registry.add("spring.datasource.password", postgres::getPassword);
    }

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private UserRepository userRepository;

    @BeforeEach
    void setUp() {
        userRepository.deleteAll();
    }

    @Test
    void createUser_WithValidData_ReturnsCreated() throws Exception {
        CreateUserDTO dto = new CreateUserDTO();
        dto.setUsername("john");
        dto.setEmail("[REDACTED_SECRET]");
        dto.setPassword("Password123");

        mockMvc.perform(post("/api/v1/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(dto)))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.username").value("john"))
            .andExpect(jsonPath("$.email").value("[REDACTED_SECRET]"))
            .andExpect(header().exists("Location"));
    }

    @Test
    void createUser_WithInvalidEmail_ReturnsBadRequest() throws Exception {
        CreateUserDTO dto = new CreateUserDTO();
        dto.setUsername("john");
        dto.setEmail("invalid-email");
        dto.setPassword("Password123");

        mockMvc.perform(post("/api/v1/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(dto)))
            .andExpect(status().isBadRequest())
            .andExpect(jsonPath("$.properties.errors.email").exists());
    }
}
```

## Production Readiness

### Actuator Configuration

```yaml
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  endpoint:
    health:
      show-details: when_authorized
      probes:
        enabled: true
  health:
    livenessState:
      enabled: true
    readinessState:
      enabled: true

info:
  app:
    name: ${spring.application.name}
    version: '@project.version@'
    java:
      version: ${java.version}
```

### Health Indicators

```java
@Component
public class DatabaseHealthIndicator implements HealthIndicator {

    private final DataSource dataSource;

    @Override
    public Health health() {
        try (Connection conn = dataSource.getConnection()) {
            if (conn.isValid(1)) {
                return Health.up()
                    .withDetail("database", "Available")
                    .build();
            }
        } catch (SQLException e) {
            return Health.down()
                .withDetail("database", "Unavailable")
                .withException(e)
                .build();
        }
        return Health.down().build();
    }
}
```

### Logging Configuration

```yaml
logging:
  level:
    root: INFO
    com.example.myapp: DEBUG
    org.springframework.web: INFO
    org.hibernate.SQL: DEBUG
    org.hibernate.type.descriptor.sql.BasicBinder: TRACE
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
  file:
    name: logs/application.log
    max-size: 10MB
    max-history: 30
```

## Performance Tips

1. **Use connection pooling** - HikariCP is the default and optimal choice
2. **Enable query caching** - For read-heavy workloads
3. **Use pagination** - Never return unbounded collections
4. **Implement caching** - Use Spring Cache with Redis/Caffeine
5. **Async processing** - For I/O-bound operations
6. **Optimize JPA queries** - Use projections and EntityGraph
7. **Enable compression** - For API responses

## Security Checklist

- [ ] HTTPS only in production
- [ ] CORS properly configured
- [ ] CSRF protection (for browser clients)
- [ ] Input validation on all endpoints
- [ ] SQL injection prevention (use parameterized queries)
- [ ] Rate limiting implemented
- [ ] Sensitive data encrypted
- [ ] Security headers configured
- [ ] Dependencies scanned for vulnerabilities
- [ ] Secrets managed externally


path: references/spring-security-reference.md
content: # Spring Security Reference Guide

Comprehensive guide for implementing authentication and authorization in Spring Boot applications.

## JWT Authentication

### Security Configuration

```java
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    private final JwtAuthenticationFilter jwtAuthFilter;
    private final AuthenticationProvider authenticationProvider;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(AbstractHttpConfigurer::disable)
            .cors(cors -> cors.configurationSource(corsConfigurationSource()))
            .sessionManagement(session ->
                session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/**").permitAll()
                .requestMatchers("/api/public/**").permitAll()
                .requestMatchers("/swagger-ui/**", "/api-docs/**").permitAll()
                .requestMatchers("/actuator/health").permitAll()
                .requestMatchers("/api/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .authenticationProvider(authenticationProvider)
            .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowedOrigins(List.of("http://localhost:3000"));
        config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
        config.setAllowedHeaders(List.of("*"));
        config.setExposedHeaders(List.of("Authorization"));
        config.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/api/**", config);
        return source;
    }
}
```

### JWT Token Provider

```java
@Service
@RequiredArgsConstructor
public class JwtTokenProvider {

    @Value("${jwt.secret}")
    private String jwtSecret;

    @Value("${jwt.expiration}")
    private long jwtExpiration;

    @Value("${jwt.refresh-expiration}")
    private long refreshExpiration;

    private SecretKey getSigningKey() {
        return Keys.hmacShaKeyFor(jwtSecret.getBytes(StandardCharsets.UTF_8));
    }

    public String generateAccessToken(UserDetails userDetails) {
        return generateToken(userDetails, jwtExpiration, TokenType.ACCESS);
    }

    public String generateRefreshToken(UserDetails userDetails) {
        return generateToken(userDetails, refreshExpiration, TokenType.REFRESH);
    }

    private String generateToken(UserDetails userDetails, long expiration, TokenType type) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("type", type.name());
        claims.put("roles", userDetails.getAuthorities().stream()
            .map(GrantedAuthority::getAuthority)
            .collect(Collectors.toList()));

        return Jwts.builder()
            .claims(claims)
            .subject(userDetails.getUsername())
            .issuedAt(new Date())
            .expiration(new Date(System.currentTimeMillis() + expiration))
            .signWith(getSigningKey())
            .compact();
    }

    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    public boolean validateToken(String token, UserDetails userDetails) {
        final String username = extractUsername(token);
        return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
    }

    public boolean isAccessToken(String token) {
        String type = extractClaim(token, claims -> claims.get("type", String.class));
        return TokenType.ACCESS.name().equals(type);
    }

    private boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    private Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    private Claims extractAllClaims(String token) {
        return Jwts.parser()
            .verifyWith(getSigningKey())
            .build()
            .parseSignedClaims(token)
            .getPayload();
    }
}
```

### Authentication Filter

```java
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtTokenProvider tokenProvider;
    private final UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(
            @NonNull HttpServletRequest request,
            @NonNull HttpServletResponse response,
            @NonNull FilterChain filterChain) throws ServletException, IOException {

        final String authHeader = request.getHeader("Authorization");

        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            filterChain.doFilter(request, response);
            return;
        }

        try {
            final String jwt = authHeader.substring(7);
            final String username = tokenProvider.extractUsername(jwt);

            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);

                if (tokenProvider.validateToken(jwt, userDetails) && tokenProvider.isAccessToken(jwt)) {
                    UsernamePasswordAuthenticationToken authToken =
                        new UsernamePasswordAuthenticationToken(
                            userDetails,
                            null,
                            userDetails.getAuthorities()
                        );
                    authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authToken);
                }
            }
        } catch (JwtException e) {
            logger.error("JWT validation failed: {}", e.getMessage());
        }

        filterChain.doFilter(request, response);
    }
}
```

## OAuth2 Resource Server

```java
@Configuration
@EnableWebSecurity
public class OAuth2SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(AbstractHttpConfigurer::disable)
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/public/**").permitAll()
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))
            );

        return http.build();
    }

    @Bean
    public JwtAuthenticationConverter jwtAuthenticationConverter() {
        JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter =
            new JwtGrantedAuthoritiesConverter();
        grantedAuthoritiesConverter.setAuthoritiesClaimName("roles");
        grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");

        JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
        converter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
        return converter;
    }
}
```

```yaml
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://auth.example.com
          jwk-set-uri: https://auth.example.com/.well-known/jwks.json
```

## Role-Based Access Control (RBAC)

### Role Entity

```java
@Entity
@Table(name = "roles")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Enumerated(EnumType.STRING)
    @Column(unique = true, nullable = false)
    private RoleName name;

    @ManyToMany(mappedBy = "roles")
    private Set<User> users = new HashSet<>();

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(
        name = "role_permissions",
        joinColumns = @JoinColumn(name = "role_id"),
        inverseJoinColumns = @JoinColumn(name = "permission_id")
    )
    private Set<Permission> permissions = new HashSet<>();
}

public enum RoleName {
    ROLE_USER,
    ROLE_ADMIN,
    ROLE_MANAGER,
    ROLE_MODERATOR
}
```

### User Entity with Roles

```java
@Entity
@Table(name = "users")
public class User implements UserDetails {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, nullable = false)
    private String username;

    @Column(unique = true, nullable = false)
    private String email;

    @Column(nullable = false)
    private String password;

    private boolean enabled = true;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(
        name = "user_roles",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "role_id")
    )
    private Set<Role> roles = new HashSet<>();

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Set<GrantedAuthority> authorities = new HashSet<>();

        for (Role role : roles) {
            authorities.add(new SimpleGrantedAuthority(role.getName().name()));
            role.getPermissions().forEach(permission ->
                authorities.add(new SimpleGrantedAuthority(permission.getName()))
            );
        }

        return authorities;
    }

    @Override
    public boolean isAccountNonExpired() { return true; }

    @Override
    public boolean isAccountNonLocked() { return true; }

    @Override
    public boolean isCredentialsNonExpired() { return true; }

    @Override
    public boolean isEnabled() { return enabled; }
}
```

## Method Security

### @PreAuthorize Examples

```java
@Service
@RequiredArgsConstructor
public class UserService {

    // Role-based access
    @PreAuthorize("hasRole('ADMIN')")
    public void deleteUser(Long userId) {
        userRepository.deleteById(userId);
    }

    // Multiple roles
    @PreAuthorize("hasAnyRole('ADMIN', 'MANAGER')")
    public List<User> getAllUsers() {
        return userRepository.findAll();
    }

    // Permission-based access
    @PreAuthorize("hasAuthority('USER_READ')")
    public User getUser(Long userId) {
        return userRepository.findById(userId).orElseThrow();
    }

    // SpEL expression with method arguments
    @PreAuthorize("#userId == authentication.principal.id or hasRole('ADMIN')")
    public User updateUser(Long userId, UpdateUserDTO dto) {
        User user = userRepository.findById(userId).orElseThrow();
        // Update logic
        return user;
    }

    // Custom security expression
    @PreAuthorize("@userSecurity.canAccessUser(#userId)")
    public UserDetails getUserDetails(Long userId) {
        return userRepository.findById(userId).orElseThrow();
    }
}

// Custom security service
@Service("userSecurity")
@RequiredArgsConstructor
public class UserSecurityService {

    public boolean canAccessUser(Long userId) {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        User currentUser = (User) auth.getPrincipal();

        // Check if current user owns this resource or is admin
        return currentUser.getId().equals(userId) ||
               auth.getAuthorities().stream()
                   .anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));
    }
}
```

### @PostAuthorize for Response Filtering

```java
@PostAuthorize("returnObject.owner.id == authentication.principal.id or hasRole('ADMIN')")
public Document getDocument(Long documentId) {
    return documentRepository.findById(documentId).orElseThrow();
}

@PostFilter("filterObject.department == authentication.principal.department or hasRole('ADMIN')")
public List<Employee> getEmployees() {
    return employeeRepository.findAll();
}
```

## Security Testing

```java
@SpringBootTest
@AutoConfigureMockMvc
class SecurityIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void publicEndpoint_ShouldBeAccessible() throws Exception {
        mockMvc.perform(get("/api/public/health"))
            .andExpect(status().isOk());
    }

    @Test
    void protectedEndpoint_WithoutAuth_ShouldReturn401() throws Exception {
        mockMvc.perform(get("/api/users"))
            .andExpect(status().isUnauthorized());
    }

    @Test
    @WithMockUser(roles = "USER")
    void protectedEndpoint_WithUser_ShouldBeAccessible() throws Exception {
        mockMvc.perform(get("/api/users/me"))
            .andExpect(status().isOk());
    }

    @Test
    @WithMockUser(roles = "ADMIN")
    void adminEndpoint_WithAdmin_ShouldBeAccessible() throws Exception {
        mockMvc.perform(get("/api/admin/users"))
            .andExpect(status().isOk());
    }

    @Test
    @WithMockUser(roles = "USER")
    void adminEndpoint_WithUser_ShouldReturn403() throws Exception {
        mockMvc.perform(get("/api/admin/users"))
            .andExpect(status().isForbidden());
    }

    @Test
    void loginEndpoint_WithValidCredentials_ShouldReturnToken() throws Exception {
        mockMvc.perform(post("/api/auth/login")
                .contentType(MediaType.APPLICATION_JSON)
                .content("""
                    {"username": "testuser", "password": "password123"}
                    """))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.accessToken").exists())
            .andExpect(jsonPath("$.refreshToken").exists());
    }
}
```

## Security Best Practices Checklist

- [ ] Use HTTPS in production
- [ ] Store passwords with bcrypt (strength 12+)
- [ ] Implement rate limiting for auth endpoints
- [ ] Use short-lived access tokens (15-60 min)
- [ ] Implement token refresh mechanism
- [ ] Validate and sanitize all input
- [ ] Use CSRF protection for browser clients
- [ ] Configure secure headers (CSP, X-Frame-Options)
- [ ] Implement account lockout after failed attempts
- [ ] Log security events (login, logout, failures)
- [ ] Rotate secrets and keys regularly
- [ ] Use environment variables for secrets


path: scripts/api_endpoint_generator.py
content: #!/usr/bin/env python3
"""
API Endpoint Generator
Scaffold RESTful API endpoints with validation and documentation.

Features:
- CRUD endpoint generation
- Request/response DTOs
- Jakarta validation annotations
- OpenAPI annotations
- Pagination support
- Error handling

Standard library only - no external dependencies required.
"""

import argparse
import json
import logging
import sys
from pathlib import Path
from typing import Any, Dict, List

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

__version__ = "1.0.0"


def generate_controller(resource: str, methods: List[str], paginated: bool, package: str) -> str:
    """Generate REST controller code"""
    resource_pascal = resource[0].upper() + resource[1:]
    resource_camel = resource[0].lower() + resource[1:]
    endpoint = resource.lower() + "s"

    method_impls = []

    if "GET" in methods:
        if paginated:
            method_impls.append(f'''
    @GetMapping
    @Operation(summary = "Get all {resource_pascal} entities with pagination")
    public ResponseEntity<Page<{resource_pascal}DTO>> findAll(Pageable pageable) {{
        return ResponseEntity.ok({resource_camel}Service.findAll(pageable));
    }}

    @GetMapping("/{{id}}")
    @Operation(summary = "Get a {resource_pascal} by ID")
    public ResponseEntity<{resource_pascal}DTO> findById(@PathVariable Long id) {{
        return ResponseEntity.ok({resource_camel}Service.findById(id));
    }}''')
        else:
            method_impls.append(f'''
    @GetMapping
    @Operation(summary = "Get all {resource_pascal} entities")
    public ResponseEntity<List<{resource_pascal}DTO>> findAll() {{
        return ResponseEntity.ok({resource_camel}Service.findAll());
    }}

    @GetMapping("/{{id}}")
    @Operation(summary = "Get a {resource_pascal} by ID")
    public ResponseEntity<{resource_pascal}DTO> findById(@PathVariable Long id) {{
        return ResponseEntity.ok({resource_camel}Service.findById(id));
    }}''')

    if "POST" in methods:
        method_impls.append(f'''
    @PostMapping
    @Operation(summary = "Create a new {resource_pascal}")
    public ResponseEntity<{resource_pascal}DTO> create(@Valid @RequestBody {resource_pascal}DTO dto) {{
        return ResponseEntity.status(HttpStatus.CREATED)
            .body({resource_camel}Service.create(dto));
    }}''')

    if "PUT" in methods:
        method_impls.append(f'''
    @PutMapping("/{{id}}")
    @Operation(summary = "Update an existing {resource_pascal}")
    public ResponseEntity<{resource_pascal}DTO> update(
            @PathVariable Long id,
            @Valid @RequestBody {resource_pascal}DTO dto) {{
        return ResponseEntity.ok({resource_camel}Service.update(id, dto));
    }}''')

    if "PATCH" in methods:
        method_impls.append(f'''
    @PatchMapping("/{{id}}")
    @Operation(summary = "Partially update a {resource_pascal}")
    public ResponseEntity<{resource_pascal}DTO> partialUpdate(
            @PathVariable Long id,
            @RequestBody Map<String, Object> updates) {{
        return ResponseEntity.ok({resource_camel}Service.partialUpdate(id, updates));
    }}''')

    if "DELETE" in methods:
        method_impls.append(f'''
    @DeleteMapping("/{{id}}")
    @Operation(summary = "Delete a {resource_pascal}")
    public ResponseEntity<Void> delete(@PathVariable Long id) {{
        {resource_camel}Service.delete(id);
        return ResponseEntity.noContent().build();
    }}''')

    imports = f'''package {package}.controller;

import {package}.dto.{resource_pascal}DTO;
import {package}.service.{resource_pascal}Service;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;'''

    if paginated:
        imports += "\nimport org.springframework.data.domain.Page;\nimport org.springframework.data.domain.Pageable;"
    else:
        imports += "\nimport java.util.List;"

    if "PATCH" in methods:
        imports += "\nimport java.util.Map;"

    imports += '''
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;'''

    return f'''{imports}

@RestController
@RequestMapping("/api/{endpoint}")
@RequiredArgsConstructor
@Tag(name = "{resource_pascal}", description = "{resource_pascal} management APIs")
public class {resource_pascal}Controller {{

    private final {resource_pascal}Service {resource_camel}Service;
{"".join(method_impls)}
}}
'''


def main():
    parser = argparse.ArgumentParser(
        description="API Endpoint Generator - Scaffold REST controllers",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Full CRUD endpoints
  python api_endpoint_generator.py orders --methods GET,POST,PUT,DELETE

  # Read-only with pagination
  python api_endpoint_generator.py reports --methods GET --paginated

  # Custom package
  python api_endpoint_generator.py products --methods GET,POST --package com.myapp
"""
    )

    parser.add_argument("resource", help="Resource name (e.g., order, product)")
    parser.add_argument("--methods", default="GET,POST,PUT,DELETE",
                        help="HTTP methods (default: GET,POST,PUT,DELETE)")
    parser.add_argument("--paginated", action="store_true",
                        help="Enable pagination for GET endpoints")
    parser.add_argument("--package", default="com.example",
                        help="Package name (default: com.example)")
    parser.add_argument("--output", "-o", help="Output file path")
    parser.add_argument("--json", action="store_true", help="Output as JSON")
    parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")

    args = parser.parse_args()
    methods = [m.strip().upper() for m in args.methods.split(',')]

    code = generate_controller(args.resource, methods, args.paginated, args.package)

    result = {
        "resource": args.resource,
        "methods": methods,
        "paginated": args.paginated,
        "package": args.package,
    }

    if args.output:
        Path(args.output).write_text(code)
        result["output_file"] = args.output
        print(f"Controller generated: {args.output}")
    elif args.json:
        result["code"] = code
        print(json.dumps(result, indent=2))
    else:
        print(code)


if __name__ == "__main__":
    main()


path: scripts/dependency_analyzer.py
content: #!/usr/bin/env python3
"""
Maven/Gradle Dependency Analyzer
Analyze dependencies for vulnerabilities and updates.

Features:
- Security vulnerability scanning
- Outdated dependency detection
- Upgrade path recommendations
- Dependency tree analysis
- License compliance checking

Standard library only - no external dependencies required.
"""

import argparse
import json
import logging
import re
import sys
import xml.etree.ElementTree as ET
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

__version__ = "1.0.0"

# Known vulnerable versions (simplified - in production, use NVD database)
KNOWN_VULNERABILITIES = {
    "org.apache.logging.log4j:log4j-core": {
        "vulnerable_versions": ["2.0", "2.1", "2.2", "2.3", "2.4", "2.5", "2.6", "2.7", "2.8", "2.9", "2.10", "2.11", "2.12", "2.13", "2.14"],
        "cve": "CVE-2021-44228",
        "severity": "CRITICAL",
        "fixed_version": "2.17.1",
    },
    "com.fasterxml.jackson.core:jackson-databind": {
        "vulnerable_versions": ["2.9.0", "2.9.1", "2.9.2", "2.9.3", "2.9.4", "2.9.5", "2.9.6", "2.9.7", "2.9.8"],
        "cve": "CVE-2019-12086",
        "severity": "HIGH",
        "fixed_version": "2.9.9",
    },
    "org.springframework:spring-core": {
        "vulnerable_versions": ["5.3.0", "5.3.1", "5.3.2", "5.3.3", "5.3.4", "5.3.5", "5.3.6", "5.3.7", "5.3.8", "5.3.9", "5.3.10", "5.3.11", "5.3.12", "5.3.13", "5.3.14", "5.3.15", "5.3.16", "5.3.17"],
        "cve": "CVE-2022-22965",
        "severity": "CRITICAL",
        "fixed_version": "5.3.18",
    },
}

# Latest stable versions (simplified)
LATEST_VERSIONS = {
    "org.springframework.boot:spring-boot-starter": "3.2.0",
    "org.springframework.boot:spring-boot-starter-web": "3.2.0",
    "org.springframework.boot:spring-boot-starter-data-jpa": "3.2.0",
    "org.springframework.boot:spring-boot-starter-security": "3.2.0",
    "org.projectlombok:lombok": "1.18.30",
    "org.mapstruct:mapstruct": "1.5.5.Final",
    "com.fasterxml.jackson.core:jackson-databind": "2.16.0",
    "org.postgresql:postgresql": "42.7.0",
    "com.mysql:mysql-connector-j": "8.2.0",
    "io.jsonwebtoken:jjwt-api": "0.12.3",
    "org.springdoc:springdoc-openapi-starter-webmvc-ui": "2.3.0",
}


class DependencyAnalyzer:
    """Analyze Maven/Gradle dependencies for security and updates"""

    def __init__(self, file_path: str, check_security: bool = True, verbose: bool = False):
        self.file_path = Path(file_path)
        self.check_security = check_security
        self.verbose = verbose
        self.dependencies: List[Dict[str, str]] = []
        self.vulnerabilities: List[Dict[str, Any]] = []
        self.updates: List[Dict[str, str]] = []

    def analyze(self) -> Dict[str, Any]:
        """Perform dependency analysis"""
        if self.file_path.name == "pom.xml":
            self._parse_maven()
        elif self.file_path.name in ["build.gradle", "build.gradle.kts"]:
            self._parse_gradle()
        else:
            raise ValueError(f"Unsupported file type: {self.file_path.name}")

        if self.check_security:
            self._check_vulnerabilities()

        self._check_updates()

        return self._generate_report()

    def _parse_maven(self):
        """Parse Maven pom.xml"""
        try:
            tree = ET.parse(self.file_path)
            root = tree.getroot()

            # Handle Maven namespace
            ns = {"m": "http://maven.apache.org/POM/4.0.0"}

            # Find dependencies
            deps = root.findall(".//m:dependency", ns) or root.findall(".//dependency")

            for dep in deps:
                group_id = dep.find("m:groupId", ns) or dep.find("groupId")
                artifact_id = dep.find("m:artifactId", ns) or dep.find("artifactId")
                version = dep.find("m:version", ns) or dep.find("version")
                scope = dep.find("m:scope", ns) or dep.find("scope")

                if group_id is not None and artifact_id is not None:
                    self.dependencies.append({
                        "groupId": group_id.text,
                        "artifactId": artifact_id.text,
                        "version": version.text if version is not None else "managed",
                        "scope": scope.text if scope is not None else "compile",
                        "coordinate": f"{group_id.text}:{artifact_id.text}",
                    })
        except ET.ParseError as e:
            logger.error(f"Failed to parse pom.xml: {e}")

    def _parse_gradle(self):
        """Parse Gradle build file"""
        content = self.file_path.read_text()

        # Match implementation, api, compileOnly, etc.
        patterns = [
            r"implementation\s*['\"]([^:]+):([^:]+):([^'\"]+)['\"]",
            r"api\s*['\"]([^:]+):([^:]+):([^'\"]+)['\"]",
            r"compileOnly\s*['\"]([^:]+):([^:]+):([^'\"]+)['\"]",
            r"runtimeOnly\s*['\"]([^:]+):([^:]+):([^'\"]+)['\"]",
            r"testImplementation\s*['\"]([^:]+):([^:]+):([^'\"]+)['\"]",
        ]

        for pattern in patterns:
            matches = re.findall(pattern, content)
            for match in matches:
                group_id, artifact_id, version = match
                self.dependencies.append({
                    "groupId": group_id,
                    "artifactId": artifact_id,
                    "version": version,
                    "scope": "compile",
                    "coordinate": f"{group_id}:{artifact_id}",
                })

    def _check_vulnerabilities(self):
        """Check dependencies against known vulnerabilities"""
        for dep in self.dependencies:
            coord = dep["coordinate"]
            version = dep["version"]

            if coord in KNOWN_VULNERABILITIES:
                vuln_info = KNOWN_VULNERABILITIES[coord]
                # Simple version check (production would use semver)
                if any(version.startswith(v) for v in vuln_info["vulnerable_versions"]):
                    self.vulnerabilities.append({
                        "dependency": coord,
                        "current_version": version,
                        "cve": vuln_info["cve"],
                        "severity": vuln_info["severity"],
                        "fixed_version": vuln_info["fixed_version"],
                        "recommendation": f"Upgrade to {vuln_info['fixed_version']} or later",
                    })

    def _check_updates(self):
        """Check for available updates"""
        for dep in self.dependencies:
            coord = dep["coordinate"]
            current = dep["version"]

            if coord in LATEST_VERSIONS:
                latest = LATEST_VERSIONS[coord]
                if current != "managed" and current != latest:
                    self.updates.append({
                        "dependency": coord,
                        "current_version": current,
                        "latest_version": latest,
                    })

    def _generate_report(self) -> Dict[str, Any]:
        """Generate analysis report"""
        return {
            "file": str(self.file_path),
            "total_dependencies": len(self.dependencies),
            "dependencies": self.dependencies,
            "vulnerabilities": {
                "count": len(self.vulnerabilities),
                "critical": len([v for v in self.vulnerabilities if v["severity"] == "CRITICAL"]),
                "high": len([v for v in self.vulnerabilities if v["severity"] == "HIGH"]),
                "issues": self.vulnerabilities,
            },
            "updates": {
                "count": len(self.updates),
                "available": self.updates,
            },
        }


def format_report(report: Dict[str, Any], output_format: str) -> str:
    """Format report for output"""
    if output_format == "json":
        return json.dumps(report, indent=2)

    # Markdown format
    lines = [
        f"# Dependency Analysis Report",
        f"\n**File:** {report['file']}",
        f"**Total Dependencies:** {report['total_dependencies']}",
        "",
    ]

    # Vulnerabilities section
    vuln = report["vulnerabilities"]
    if vuln["count"] > 0:
        lines.append("## Security Vulnerabilities")
        lines.append(f"\n**Total:** {vuln['count']} (Critical: {vuln['critical']}, High: {vuln['high']})")
        lines.append("")
        for v in vuln["issues"]:
            lines.append(f"### {v['dependency']}")
            lines.append(f"- **Current Version:** {v['current_version']}")
            lines.append(f"- **CVE:** {v['cve']}")
            lines.append(f"- **Severity:** {v['severity']}")
            lines.append(f"- **Fixed Version:** {v['fixed_version']}")
            lines.append(f"- **Recommendation:** {v['recommendation']}")
            lines.append("")
    else:
        lines.append("## Security Vulnerabilities")
        lines.append("\nNo known vulnerabilities found.")
        lines.append("")

    # Updates section
    updates = report["updates"]
    if updates["count"] > 0:
        lines.append("## Available Updates")
        lines.append(f"\n**Total:** {updates['count']} dependencies can be updated")
        lines.append("")
        lines.append("| Dependency | Current | Latest |")
        lines.append("|------------|---------|--------|")
        for u in updates["available"]:
            lines.append(f"| {u['dependency']} | {u['current_version']} | {u['latest_version']} |")
        lines.append("")
    else:
        lines.append("## Available Updates")
        lines.append("\nAll dependencies are up to date.")
        lines.append("")

    return "\n".join(lines)


def main():
    parser = argparse.ArgumentParser(
        description="Maven/Gradle Dependency Analyzer",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Analyze Maven project
  python dependency_analyzer.py pom.xml

  # Analyze Gradle with security focus
  python dependency_analyzer.py build.gradle --check-security

  # Generate markdown report
  python dependency_analyzer.py pom.xml --output report.md

  # JSON output
  python dependency_analyzer.py pom.xml --json
"""
    )

    parser.add_argument("file", help="Path to pom.xml or build.gradle")
    parser.add_argument("--check-security", action="store_true",
                        help="Check for security vulnerabilities")
    parser.add_argument("--output", "-o",
                        help="Output file path (markdown or json based on extension)")
    parser.add_argument("--json", action="store_true",
                        help="Output as JSON")
    parser.add_argument("--verbose", "-v", action="store_true",
                        help="Enable verbose output")
    parser.add_argument("--version", action="version",
                        version=f"%(prog)s {__version__}")

    args = parser.parse_args()

    if not Path(args.file).exists():
        print(f"Error: File not found: {args.file}", file=sys.stderr)
        sys.exit(1)

    analyzer = DependencyAnalyzer(args.file, args.check_security, args.verbose)
    report = analyzer.analyze()

    output_format = "json" if args.json else "markdown"
    if args.output and args.output.endswith(".json"):
        output_format = "json"

    formatted = format_report(report, output_format)

    if args.output:
        Path(args.output).write_text(formatted)
        print(f"Report saved to: {args.output}")
    else:
        print(formatted)

    # Exit with error code if vulnerabilities found
    if report["vulnerabilities"]["critical"] > 0:
        sys.exit(2)
    elif report["vulnerabilities"]["high"] > 0:
        sys.exit(1)


if __name__ == "__main__":
    main()


path: scripts/entity_generator.py
content: #!/usr/bin/env python3
"""
JPA Entity Generator
Generate complete JPA entity stacks with repository, service, controller, and DTO.

Features:
- JPA entity with Lombok annotations
- Spring Data JPA repository with custom queries
- Service layer with transaction management
- REST controller with validation
- DTO and mapper (MapStruct)
- Relationship support (OneToMany, ManyToOne, ManyToMany)

Standard library only - no external dependencies required.
"""

import argparse
import json
import logging
import re
import sys
from dataclasses import dataclass, field
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

__version__ = "1.0.0"


@dataclass
class FieldDefinition:
    """Definition of an entity field"""
    name: str
    java_type: str
    nullable: bool = True
    unique: bool = False
    column_name: Optional[str] = None


@dataclass
class RelationDefinition:
    """Definition of an entity relationship"""
    field_name: str
    relation_type: str  # OneToMany, ManyToOne, ManyToMany, OneToOne
    target_entity: str
    mapped_by: Optional[str] = None
    cascade: bool = True
    fetch_lazy: bool = True


@dataclass
class EntityConfig:
    """Configuration for entity generation"""
    name: str
    fields: List[FieldDefinition]
    relations: List[RelationDefinition] = field(default_factory=list)
    package_name: str = "com.example"
    auditable: bool = False
    soft_delete: bool = False
    table_name: Optional[str] = None

    def __post_init__(self):
        if not self.table_name:
            # Convert CamelCase to snake_case for table name
            self.table_name = re.sub(r'(?<!^)(?=[A-Z])', '_', self.name).lower()


class EntityGenerator:
    """
    JPA Entity Generator for creating complete entity stacks.
    """

    JAVA_TYPE_IMPORTS = {
        "Long": None,
        "Integer": None,
        "String": None,
        "Boolean": None,
        "Double": None,
        "Float": None,
        "BigDecimal": "java.math.BigDecimal",
        "LocalDate": "java.time.LocalDate",
        "LocalDateTime": "java.time.LocalDateTime",
        "LocalTime": "java.time.LocalTime",
        "Instant": "java.time.Instant",
        "UUID": "java.util.UUID",
        "List": "java.util.List",
        "Set": "java.util.Set",
    }

    def __init__(self, config: EntityConfig, output_dir: str, verbose: bool = False):
        if verbose:
            logging.getLogger().setLevel(logging.DEBUG)
        logger.debug("EntityGenerator initialized")

        self.config = config
        self.output_dir = Path(output_dir)
        self.verbose = verbose
        self.files_created: List[str] = []

    def generate(self) -> Dict[str, Any]:
        """Generate all entity-related files"""
        logger.debug(f"Generating entity: {self.config.name}")
        if self.verbose:
            print(f"Generating entity stack for: {self.config.name}")
            print(f"Package: {self.config.package_name}")
            print(f"Fields: {len(self.config.fields)}")
            print(f"Relations: {len(self.config.relations)}")
            print(f"Output: {self.output_dir}\n")

        # Create directory structure
        self._create_directories()

        # Generate files
        self._generate_entity()
        self._generate_repository()
        self._generate_service()
        self._generate_controller()
        self._generate_dto()
        self._generate_mapper()

        return self._generate_report()

    def _create_directories(self):
        """Create package directories"""
        package_path = self.config.package_name.replace('.', '/')
        directories = [
            f"src/main/java/{package_path}/entity",
            f"src/main/java/{package_path}/repository",
            f"src/main/java/{package_path}/service",
            f"src/main/java/{package_path}/controller",
            f"src/main/java/{package_path}/dto",
            f"src/main/java/{package_path}/mapper",
        ]
        for directory in directories:
            (self.output_dir / directory).mkdir(parents=True, exist_ok=True)

    def _generate_entity(self):
        """Generate JPA entity class"""
        package_path = self.config.package_name.replace('.', '/')
        imports = self._collect_entity_imports()

        # Build field declarations
        field_decls = []
        for f in self.config.fields:
            annotations = []
            if f.name == "id":
                annotations.append("@Id")
                annotations.append("@GeneratedValue(strategy = GenerationType.IDENTITY)")
            if not f.nullable and f.name != "id":
                annotations.append(f'@Column(nullable = false{", unique = true" if f.unique else ""})')
            elif f.unique:
                annotations.append("@Column(unique = true)")

            annotation_str = "\n    ".join(annotations)
            if annotation_str:
                annotation_str = "    " + annotation_str + "\n"
            field_decls.append(f"{annotation_str}    private {f.java_type} {f.name};")

        # Build relationship declarations
        relation_decls = []
        for r in self.relations_sorted():
            annotations = []
            fetch = "FetchType.LAZY" if r.fetch_lazy else "FetchType.EAGER"
            cascade = ", cascade = CascadeType.ALL" if r.cascade else ""

            if r.relation_type == "ManyToOne":
                annotations.append(f"@ManyToOne(fetch = {fetch})")
                annotations.append(f'@JoinColumn(name = "{self._to_snake_case(r.field_name)}_id")')
            elif r.relation_type == "OneToMany":
                mapped = f', mappedBy = "{r.mapped_by}"' if r.mapped_by else ""
                annotations.append(f"@OneToMany(fetch = {fetch}{cascade}{mapped})")
            elif r.relation_type == "ManyToMany":
                annotations.append(f"@ManyToMany(fetch = {fetch}{cascade})")
            elif r.relation_type == "OneToOne":
                annotations.append(f"@OneToOne(fetch = {fetch}{cascade})")

            annotation_str = "\n    ".join(annotations)
            if annotation_str:
                annotation_str = "    " + annotation_str + "\n"

            java_type = r.target_entity
            if r.relation_type in ["OneToMany", "ManyToMany"]:
                java_type = f"List<{r.target_entity}>"

            relation_decls.append(f"{annotation_str}    private {java_type} {r.field_name};")

        # Audit fields
        audit_fields = ""
        if self.config.auditable:
            audit_fields = """
    @CreatedDate
    @Column(nullable = false, updatable = false)
    private LocalDateTime createdAt;

    @LastModifiedDate
    @Column(nullable = false)
    private LocalDateTime updatedAt;

    @CreatedBy
    @Column(updatable = false)
    private String createdBy;

    @LastModifiedBy
    private String updatedBy;
"""

        # Soft delete field
        soft_delete = ""
        if self.config.soft_delete:
            soft_delete = """
    @Column(nullable = false)
    private boolean deleted = false;

    private LocalDateTime deletedAt;
"""

        entity_listener = ""
        if self.config.auditable:
            entity_listener = "@EntityListeners(AuditingEntityListener.class)\n"

        content = f'''package {self.config.package_name}.entity;

{imports}

@Entity
@Table(name = "{self.config.table_name}")
{entity_listener}@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class {self.config.name} {{

{chr(10).join(field_decls)}
{chr(10).join(relation_decls)}{audit_fields}{soft_delete}}}
'''
        self._write_file(f"src/main/java/{package_path}/entity/{self.config.name}.java", content)

    def _generate_repository(self):
        """Generate Spring Data JPA repository"""
        package_path = self.config.package_name.replace('.', '/')

        # Find ID type
        id_type = "Long"
        for f in self.config.fields:
            if f.name == "id":
                id_type = f.java_type
                break

        content = f'''package {self.config.package_name}.repository;

import {self.config.package_name}.entity.{self.config.name};
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface {self.config.name}Repository extends JpaRepository<{self.config.name}, {id_type}>, JpaSpecificationExecutor<{self.config.name}> {{

    // Add custom query methods here
    // Example: Optional<{self.config.name}> findByEmail(String email);
}}
'''
        self._write_file(f"src/main/java/{package_path}/repository/{self.config.name}Repository.java", content)

    def _generate_service(self):
        """Generate service class with transaction management"""
        package_path = self.config.package_name.replace('.', '/')
        entity_var = self._to_camel_case(self.config.name)

        # Find ID type
        id_type = "Long"
        for f in self.config.fields:
            if f.name == "id":
                id_type = f.java_type
                break

        content = f'''package {self.config.package_name}.service;

import {self.config.package_name}.dto.{self.config.name}DTO;
import {self.config.package_name}.entity.{self.config.name};
import {self.config.package_name}.exception.ResourceNotFoundException;
import {self.config.package_name}.mapper.{self.config.name}Mapper;
import {self.config.package_name}.repository.{self.config.name}Repository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class {self.config.name}Service {{

    private final {self.config.name}Repository {entity_var}Repository;
    private final {self.config.name}Mapper {entity_var}Mapper;

    public Page<{self.config.name}DTO> findAll(Pageable pageable) {{
        log.debug("Finding all {self.config.name} entities with pagination");
        return {entity_var}Repository.findAll(pageable)
            .map({entity_var}Mapper::toDto);
    }}

    public {self.config.name}DTO findById({id_type} id) {{
        log.debug("Finding {self.config.name} by id: {{}}", id);
        return {entity_var}Repository.findById(id)
            .map({entity_var}Mapper::toDto)
            .orElseThrow(() -> new ResourceNotFoundException("{self.config.name}", "id", id));
    }}

    @Transactional
    public {self.config.name}DTO create({self.config.name}DTO dto) {{
        log.debug("Creating new {self.config.name}: {{}}", dto);
        {self.config.name} entity = {entity_var}Mapper.toEntity(dto);
        entity = {entity_var}Repository.save(entity);
        return {entity_var}Mapper.toDto(entity);
    }}

    @Transactional
    public {self.config.name}DTO update({id_type} id, {self.config.name}DTO dto) {{
        log.debug("Updating {self.config.name} id: {{}}", id);
        {self.config.name} existing = {entity_var}Repository.findById(id)
            .orElseThrow(() -> new ResourceNotFoundException("{self.config.name}", "id", id));

        {entity_var}Mapper.updateEntityFromDto(dto, existing);
        existing = {entity_var}Repository.save(existing);
        return {entity_var}Mapper.toDto(existing);
    }}

    @Transactional
    public void delete({id_type} id) {{
        log.debug("Deleting {self.config.name} id: {{}}", id);
        if (!{entity_var}Repository.existsById(id)) {{
            throw new ResourceNotFoundException("{self.config.name}", "id", id);
        }}
        {entity_var}Repository.deleteById(id);
    }}
}}
'''
        self._write_file(f"src/main/java/{package_path}/service/{self.config.name}Service.java", content)

    def _generate_controller(self):
        """Generate REST controller with validation"""
        package_path = self.config.package_name.replace('.', '/')
        entity_var = self._to_camel_case(self.config.name)
        endpoint = self._to_kebab_case(self.config.name) + "s"

        # Find ID type
        id_type = "Long"
        for f in self.config.fields:
            if f.name == "id":
                id_type = f.java_type
                break

        content = f'''package {self.config.package_name}.controller;

import {self.config.package_name}.dto.{self.config.name}DTO;
import {self.config.package_name}.service.{self.config.name}Service;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/{endpoint}")
@RequiredArgsConstructor
@Tag(name = "{self.config.name}", description = "{self.config.name} management APIs")
public class {self.config.name}Controller {{

    private final {self.config.name}Service {entity_var}Service;

    @GetMapping
    @Operation(summary = "Get all {self.config.name} entities with pagination")
    public ResponseEntity<Page<{self.config.name}DTO>> findAll(Pageable pageable) {{
        return ResponseEntity.ok({entity_var}Service.findAll(pageable));
    }}

    @GetMapping("/{{id}}")
    @Operation(summary = "Get a {self.config.name} by ID")
    public ResponseEntity<{self.config.name}DTO> findById(@PathVariable {id_type} id) {{
        return ResponseEntity.ok({entity_var}Service.findById(id));
    }}

    @PostMapping
    @Operation(summary = "Create a new {self.config.name}")
    public ResponseEntity<{self.config.name}DTO> create(@Valid @RequestBody {self.config.name}DTO dto) {{
        return ResponseEntity.status(HttpStatus.CREATED)
            .body({entity_var}Service.create(dto));
    }}

    @PutMapping("/{{id}}")
    @Operation(summary = "Update an existing {self.config.name}")
    public ResponseEntity<{self.config.name}DTO> update(
            @PathVariable {id_type} id,
            @Valid @RequestBody {self.config.name}DTO dto) {{
        return ResponseEntity.ok({entity_var}Service.update(id, dto));
    }}

    @DeleteMapping("/{{id}}")
    @Operation(summary = "Delete a {self.config.name}")
    public ResponseEntity<Void> delete(@PathVariable {id_type} id) {{
        {entity_var}Service.delete(id);
        return ResponseEntity.noContent().build();
    }}
}}
'''
        self._write_file(f"src/main/java/{package_path}/controller/{self.config.name}Controller.java", content)

    def _generate_dto(self):
        """Generate DTO class"""
        package_path = self.config.package_name.replace('.', '/')
        imports = self._collect_dto_imports()

        # Build field declarations
        field_decls = []
        for f in self.config.fields:
            annotations = []
            if not f.nullable and f.name != "id":
                if f.java_type == "String":
                    annotations.append("@NotBlank")
                else:
                    annotations.append("@NotNull")

            annotation_str = "\n    ".join(annotations)
            if annotation_str:
                annotation_str = "    " + annotation_str + "\n"
            field_decls.append(f"{annotation_str}    private {f.java_type} {f.name};")

        content = f'''package {self.config.package_name}.dto;

{imports}

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class {self.config.name}DTO {{

{chr(10).join(field_decls)}
}}
'''
        self._write_file(f"src/main/java/{package_path}/dto/{self.config.name}DTO.java", content)

    def _generate_mapper(self):
        """Generate MapStruct mapper"""
        package_path = self.config.package_name.replace('.', '/')

        content = f'''package {self.config.package_name}.mapper;

import {self.config.package_name}.dto.{self.config.name}DTO;
import {self.config.package_name}.entity.{self.config.name};
import org.mapstruct.*;

@Mapper(componentModel = "spring")
public interface {self.config.name}Mapper {{

    {self.config.name}DTO toDto({self.config.name} entity);

    @Mapping(target = "id", ignore = true)
    {self.config.name} toEntity({self.config.name}DTO dto);

    @Mapping(target = "id", ignore = true)
    @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
    void updateEntityFromDto({self.config.name}DTO dto, @MappingTarget {self.config.name} entity);
}}
'''
        self._write_file(f"src/main/java/{package_path}/mapper/{self.config.name}Mapper.java", content)

    def _collect_entity_imports(self) -> str:
        """Collect required imports for entity class"""
        imports = set()

        # JPA imports
        imports.add("import jakarta.persistence.*;")
        imports.add("import lombok.*;")

        # Field type imports
        for f in self.config.fields:
            base_type = f.java_type.split('<')[0]  # Handle generics
            if base_type in self.JAVA_TYPE_IMPORTS and self.JAVA_TYPE_IMPORTS[base_type]:
                imports.add(f"import {self.JAVA_TYPE_IMPORTS[base_type]};")

        # Relation imports
        if self.config.relations:
            imports.add("import java.util.List;")
            imports.add("import java.util.ArrayList;")

        # Audit imports
        if self.config.auditable:
            imports.add("import org.springframework.data.annotation.CreatedBy;")
            imports.add("import org.springframework.data.annotation.CreatedDate;")
            imports.add("import org.springframework.data.annotation.LastModifiedBy;")
            imports.add("import org.springframework.data.annotation.LastModifiedDate;")
            imports.add("import org.springframework.data.jpa.domain.support.AuditingEntityListener;")
            imports.add("import java.time.LocalDateTime;")

        return "\n".join(sorted(imports))

    def _collect_dto_imports(self) -> str:
        """Collect required imports for DTO class"""
        imports = set()
        imports.add("import lombok.*;")

        # Validation imports
        has_validation = False
        for f in self.config.fields:
            if not f.nullable and f.name != "id":
                has_validation = True
                break

        if has_validation:
            imports.add("import jakarta.validation.constraints.*;")

        # Field type imports
        for f in self.config.fields:
            base_type = f.java_type.split('<')[0]
            if base_type in self.JAVA_TYPE_IMPORTS and self.JAVA_TYPE_IMPORTS[base_type]:
                imports.add(f"import {self.JAVA_TYPE_IMPORTS[base_type]};")

        return "\n".join(sorted(imports))

    def relations_sorted(self) -> List[RelationDefinition]:
        """Return relations sorted by type"""
        return sorted(self.config.relations, key=lambda r: r.relation_type)

    def _write_file(self, path: str, content: str):
        """Write content to a file"""
        file_path = self.output_dir / path
        file_path.parent.mkdir(parents=True, exist_ok=True)
        file_path.write_text(content)
        self.files_created.append(path)
        logger.debug(f"Created file: {path}")

    def _generate_report(self) -> Dict[str, Any]:
        """Generate a summary report"""
        return {
            "entity_name": self.config.name,
            "package": self.config.package_name,
            "fields": len(self.config.fields),
            "relations": len(self.config.relations),
            "auditable": self.config.auditable,
            "soft_delete": self.config.soft_delete,
            "files_created": self.files_created,
        }

    @staticmethod
    def _to_camel_case(name: str) -> str:
        """Convert PascalCase to camelCase"""
        return name[0].lower() + name[1:] if name else name

    @staticmethod
    def _to_snake_case(name: str) -> str:
        """Convert camelCase/PascalCase to snake_case"""
        return re.sub(r'(?<!^)(?=[A-Z])', '_', name).lower()

    @staticmethod
    def _to_kebab_case(name: str) -> str:
        """Convert PascalCase to kebab-case"""
        return re.sub(r'(?<!^)(?=[A-Z])', '-', name).lower()


def parse_fields(fields_str: str) -> List[FieldDefinition]:
    """Parse field definitions from command line argument"""
    fields = []
    for field_def in fields_str.split(','):
        parts = field_def.strip().split(':')
        if len(parts) >= 2:
            name = parts[0].strip()
            java_type = parts[1].strip()
            nullable = True
            unique = False
            if len(parts) > 2:
                modifiers = parts[2].strip().lower()
                nullable = 'notnull' not in modifiers and 'required' not in modifiers
                unique = 'unique' in modifiers
            fields.append(FieldDefinition(name=name, java_type=java_type, nullable=nullable, unique=unique))
    return fields


def parse_relations(relations_str: str) -> List[RelationDefinition]:
    """Parse relation definitions from command line argument"""
    relations = []
    for rel_def in relations_str.split(','):
        parts = rel_def.strip().split(':')
        if len(parts) >= 2:
            field_name = parts[0].strip()
            rel_type = parts[1].strip()

            # Infer target entity from field name
            target = field_name[0].upper() + field_name[1:]
            if rel_type in ["OneToMany", "ManyToMany"]:
                # Remove trailing 's' for collection types
                if target.endswith('s'):
                    target = target[:-1]

            relations.append(RelationDefinition(
                field_name=field_name,
                relation_type=rel_type,
                target_entity=target
            ))
    return relations


def main():
    """CLI entry point"""
    parser = argparse.ArgumentParser(
        description="JPA Entity Generator - Generate complete entity stacks",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Basic entity
  python entity_generator.py Customer --fields "id:Long,name:String,email:String"

  # Entity with relationships
  python entity_generator.py Order --fields "id:Long,total:BigDecimal" --relations "customer:ManyToOne,items:OneToMany"

  # Entity with audit fields
  python entity_generator.py Product --fields "id:Long,name:String,price:BigDecimal" --auditable

  # Entity with custom package
  python entity_generator.py User --fields "id:Long,email:String:unique" --package com.myapp
"""
    )

    parser.add_argument("name", help="Entity name (PascalCase)")
    parser.add_argument("--fields", required=True,
                        help="Field definitions: 'name:Type,name:Type:modifiers'")
    parser.add_argument("--relations",
                        help="Relation definitions: 'field:RelationType,field:RelationType'")
    parser.add_argument("--package",
                        default="com.example",
                        help="Package name (default: com.example)")
    parser.add_argument("--output", "-o",
                        default=".",
                        help="Output directory (default: current directory)")
    parser.add_argument("--auditable", action="store_true",
                        help="Add audit fields (createdAt, updatedAt, etc.)")
    parser.add_argument("--soft-delete", action="store_true",
                        help="Add soft delete support")
    parser.add_argument("--verbose", "-v", action="store_true",
                        help="Enable verbose output")
    parser.add_argument("--json", action="store_true",
                        help="Output result as JSON")
    parser.add_argument("--version", action="version",
                        version=f"%(prog)s {__version__}")

    args = parser.parse_args()

    fields = parse_fields(args.fields)
    relations = parse_relations(args.relations) if args.relations else []

    config = EntityConfig(
        name=args.name,
        fields=fields,
        relations=relations,
        package_name=args.package,
        auditable=args.auditable,
        soft_delete=args.soft_delete,
    )

    generator = EntityGenerator(config, args.output, args.verbose)
    result = generator.generate()

    if args.json:
        print(json.dumps(result, indent=2))
    else:
        print(f"\nEntity '{result['entity_name']}' generated successfully!")
        print(f"Package: {result['package']}")
        print(f"\nFiles created:")
        for f in result['files_created']:
            print(f"  - {f}")


if __name__ == "__main__":
    main()


path: scripts/performance_profiler.py
content: #!/usr/bin/env python3
"""
JVM Performance Profiler
Profile JVM applications and generate optimization recommendations.

Features:
- Query analysis for N+1 detection
- Memory usage patterns
- GC behavior analysis
- Thread pool recommendations
- Connection pool optimization
- JVM flag recommendations

Standard library only - no external dependencies required.
"""

import argparse
import json
import logging
import os
import re
import sys
from pathlib import Path
from typing import Any, Dict, List, Optional

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

__version__ = "1.0.0"


class QueryAnalyzer:
    """Analyze JPA/Hibernate queries for performance issues"""

    N1_PATTERNS = [
        # Find entity collections without fetch join
        r"@OneToMany.*?\n.*?private\s+(?:List|Set)<(\w+)>",
        r"@ManyToMany.*?\n.*?private\s+(?:List|Set)<(\w+)>",
        # Repository methods that might cause N+1
        r"findAll\(\)",
        r"findBy\w+\(",
    ]

    MISSING_INDEX_PATTERNS = [
        r"findBy(\w+)\(",  # Custom finder methods need indexes
        r"@Query.*?WHERE.*?(\w+)\s*=",  # JPQL WHERE clauses
    ]

    EAGER_FETCH_PATTERN = r"fetch\s*=\s*FetchType\.EAGER"

    def __init__(self, source_dir: str):
        self.source_dir = Path(source_dir)
        self.issues: List[Dict[str, Any]] = []
        self.recommendations: List[str] = []

    def analyze(self) -> Dict[str, Any]:
        """Analyze source files for query performance issues"""
        # Find all Java files
        java_files = list(self.source_dir.rglob("*.java"))

        for file_path in java_files:
            self._analyze_file(file_path)

        return self._generate_report()

    def _analyze_file(self, file_path: Path):
        """Analyze a single Java file"""
        try:
            content = file_path.read_text()
            relative_path = file_path.relative_to(self.source_dir)

            # Check for N+1 query patterns
            self._check_n1_queries(content, str(relative_path))

            # Check for eager fetch
            self._check_eager_fetch(content, str(relative_path))

            # Check for missing @EntityGraph
            self._check_entity_graph(content, str(relative_path))

            # Check repository methods
            self._check_repository_methods(content, str(relative_path))

        except Exception as e:
            logger.warning(f"Failed to analyze {file_path}: {e}")

    def _check_n1_queries(self, content: str, file_path: str):
        """Check for potential N+1 query issues"""
        # Check for collection relationships without proper fetching
        collection_pattern = r"@(OneToMany|ManyToMany)(?:\([^)]*\))?\s*\n\s*private\s+(?:List|Set)<(\w+)>\s+(\w+)"
        matches = re.finditer(collection_pattern, content)

        for match in matches:
            relation_type, entity_type, field_name = match.groups()

            # Check if there's no fetch strategy defined or if lazy loading
            # without EntityGraph could cause N+1
            if "FetchType.LAZY" in content or "fetch" not in match.group(0):
                self.issues.append({
                    "type": "POTENTIAL_N1_QUERY",
                    "severity": "HIGH",
                    "file": file_path,
                    "field": field_name,
                    "description": f"Collection field '{field_name}' ({relation_type}) may cause N+1 queries",
                    "recommendation": f"Use @EntityGraph or fetch join when loading {field_name}",
                })

    def _check_eager_fetch(self, content: str, file_path: str):
        """Check for inappropriate eager fetching"""
        if re.search(self.EAGER_FETCH_PATTERN, content):
            self.issues.append({
                "type": "EAGER_FETCH",
                "severity": "MEDIUM",
                "file": file_path,
                "description": "Eager fetch detected - may cause performance issues with large datasets",
                "recommendation": "Consider using LAZY fetch with EntityGraph for better control",
            })

    def _check_entity_graph(self, content: str, file_path: str):
        """Check if EntityGraph is used for complex queries"""
        if "Repository" in content and "interface" in content:
            # It's a repository interface
            if "@OneToMany" not in content and "@ManyToMany" not in content:
                return  # No collections, skip

            if "@EntityGraph" not in content:
                self.issues.append({
                    "type": "MISSING_ENTITY_GRAPH",
                    "severity": "MEDIUM",
                    "file": file_path,
                    "description": "Repository without @EntityGraph - may need optimization for related entities",
                    "recommendation": "Add @EntityGraph to repository methods that load entities with relations",
                })

    def _check_repository_methods(self, content: str, file_path: str):
        """Check repository methods for potential issues"""
        # Check for findAll without pagination
        if "findAll()" in content and "Pageable" not in content:
            self.issues.append({
                "type": "UNBOUNDED_QUERY",
                "severity": "HIGH",
                "file": file_path,
                "description": "findAll() without pagination may cause memory issues with large datasets",
                "recommendation": "Use findAll(Pageable) or add pagination to avoid loading entire tables",
            })

        # Check for custom queries that might need indexes
        finder_methods = re.findall(r"findBy(\w+)\(", content)
        for method in finder_methods:
            self.recommendations.append(
                f"Ensure database index exists for: {self._camel_to_snake(method)}"
            )

    def _generate_report(self) -> Dict[str, Any]:
        """Generate analysis report"""
        # Group issues by severity
        critical = [i for i in self.issues if i["severity"] == "CRITICAL"]
        high = [i for i in self.issues if i["severity"] == "HIGH"]
        medium = [i for i in self.issues if i["severity"] == "MEDIUM"]
        low = [i for i in self.issues if i["severity"] == "LOW"]

        return {
            "source_directory": str(self.source_dir),
            "summary": {
                "total_issues": len(self.issues),
                "critical": len(critical),
                "high": len(high),
                "medium": len(medium),
                "low": len(low),
            },
            "issues": self.issues,
            "recommendations": list(set(self.recommendations)),  # Dedupe
            "jvm_recommendations": self._get_jvm_recommendations(),
        }

    def _get_jvm_recommendations(self) -> List[Dict[str, str]]:
        """Get general JVM optimization recommendations"""
        return [
            {
                "category": "Memory",
                "recommendation": "Set -Xms equal to -Xmx for consistent heap size",
                "example": "-Xms2g -Xmx2g",
            },
            {
                "category": "GC",
                "recommendation": "Use G1GC for balanced throughput and latency",
                "example": "-XX:+UseG1GC -XX:MaxGCPauseMillis=200",
            },
            {
                "category": "JIT",
                "recommendation": "Enable tiered compilation for faster startup",
                "example": "-XX:+TieredCompilation",
            },
            {
                "category": "Monitoring",
                "recommendation": "Enable GC logging for production",
                "example": "-Xlog:gc*:file=gc.log:time,uptime:filecount=5,filesize=10M",
            },
            {
                "category": "Connection Pool",
                "recommendation": "Use HikariCP with appropriate pool size",
                "example": "spring.datasource.hikari.maximum-pool-size=10",
            },
        ]

    @staticmethod
    def _camel_to_snake(name: str) -> str:
        """Convert camelCase to snake_case"""
        return re.sub(r'(?<!^)(?=[A-Z])', '_', name).lower()


def format_report(report: Dict[str, Any], output_format: str) -> str:
    """Format report for output"""
    if output_format == "json":
        return json.dumps(report, indent=2)

    # Markdown format
    lines = [
        "# JVM Performance Analysis Report",
        f"\n**Source Directory:** {report['source_directory']}",
        "",
        "## Summary",
        f"- Total Issues: {report['summary']['total_issues']}",
        f"- Critical: {report['summary']['critical']}",
        f"- High: {report['summary']['high']}",
        f"- Medium: {report['summary']['medium']}",
        f"- Low: {report['summary']['low']}",
        "",
    ]

    if report["issues"]:
        lines.append("## Issues Found")
        lines.append("")
        for issue in report["issues"]:
            lines.append(f"### [{issue['severity']}] {issue['type']}")
            lines.append(f"**File:** {issue['file']}")
            lines.append(f"**Description:** {issue['description']}")
            lines.append(f"**Recommendation:** {issue['recommendation']}")
            lines.append("")

    if report["recommendations"]:
        lines.append("## Database Recommendations")
        lines.append("")
        for rec in report["recommendations"]:
            lines.append(f"- {rec}")
        lines.append("")

    lines.append("## JVM Optimization Recommendations")
    lines.append("")
    for rec in report["jvm_recommendations"]:
        lines.append(f"### {rec['category']}")
        lines.append(f"**Recommendation:** {rec['recommendation']}")
        lines.append(f"```")
        lines.append(rec['example'])
        lines.append("```")
        lines.append("")

    return "\n".join(lines)


def main():
    parser = argparse.ArgumentParser(
        description="JVM Performance Profiler - Analyze Java code for performance issues",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Analyze source for performance issues
  python performance_profiler.py --analyze-queries src/

  # Generate markdown report
  python performance_profiler.py --analyze-queries src/ --output performance-report.md

  # JSON output
  python performance_profiler.py --analyze-queries src/ --json
"""
    )

    parser.add_argument("--analyze-queries", metavar="DIR",
                        help="Analyze source directory for query performance issues")
    parser.add_argument("--output", "-o",
                        help="Output file path")
    parser.add_argument("--json", action="store_true",
                        help="Output as JSON")
    parser.add_argument("--verbose", "-v", action="store_true",
                        help="Enable verbose output")
    parser.add_argument("--version", action="version",
                        version=f"%(prog)s {__version__}")

    args = parser.parse_args()

    if not args.analyze_queries:
        parser.print_help()
        sys.exit(1)

    source_dir = Path(args.analyze_queries)
    if not source_dir.exists():
        print(f"Error: Directory not found: {source_dir}", file=sys.stderr)
        sys.exit(1)

    analyzer = QueryAnalyzer(str(source_dir))
    report = analyzer.analyze()

    output_format = "json" if args.json else "markdown"
    if args.output and args.output.endswith(".json"):
        output_format = "json"

    formatted = format_report(report, output_format)

    if args.output:
        Path(args.output).write_text(formatted)
        print(f"Report saved to: {args.output}")
    else:
        print(formatted)

    # Exit with appropriate code
    if report["summary"]["critical"] > 0:
        sys.exit(2)
    elif report["summary"]["high"] > 0:
        sys.exit(1)


if __name__ == "__main__":
    main()


path: scripts/security_config_generator.py
content: #!/usr/bin/env python3
"""
Spring Security Configuration Generator
Generate Spring Security configuration for various authentication methods.

Features:
- JWT authentication setup
- OAuth2 resource server configuration
- Role-based access control
- Method security configuration
- CORS and CSRF configuration
- Security filter chain

Standard library only - no external dependencies required.
"""

import argparse
import json
import logging
import sys
from pathlib import Path
from typing import Any, Dict, List

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

__version__ = "1.0.0"


def generate_jwt_config(roles: List[str], package: str) -> Dict[str, str]:
    """Generate JWT security configuration files"""
    files = {}

    roles_enum = "\n".join([f"    {r}," for r in roles])

    # Role enum
    files["Role.java"] = f'''package {package}.security;

public enum Role {{
{roles_enum}
}}
'''

    # Security Config
    files["SecurityConfig.java"] = f'''package {package}.security;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;
import java.util.List;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {{

    private final JwtAuthenticationFilter jwtAuthenticationFilter;
    private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {{
        http
            .cors(cors -> cors.configurationSource(corsConfigurationSource()))
            .csrf(csrf -> csrf.disable())
            .exceptionHandling(ex -> ex.authenticationEntryPoint(jwtAuthenticationEntryPoint))
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/**").permitAll()
                .requestMatchers("/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll()
                .requestMatchers("/actuator/health", "/actuator/info").permitAll()
                .anyRequest().authenticated()
            )
            .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }}

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {{
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(List.of("http://localhost:3000", "http://localhost:8080"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
        configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type", "X-Requested-With"));
        configuration.setExposedHeaders(List.of("Authorization"));
        configuration.setAllowCredentials(true);
        configuration.setMaxAge(3600L);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/api/**", configuration);
        return source;
    }}

    @Bean
    public PasswordEncoder passwordEncoder() {{
        return new BCryptPasswordEncoder();
    }}

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {{
        return config.getAuthenticationManager();
    }}
}}
'''

    # JWT Authentication Entry Point
    files["JwtAuthenticationEntryPoint.java"] = f'''package {package}.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@Component
@RequiredArgsConstructor
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {{

    private final ObjectMapper objectMapper;

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
                         AuthenticationException authException) throws IOException {{
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

        Map<String, Object> body = new HashMap<>();
        body.put("timestamp", LocalDateTime.now().toString());
        body.put("status", HttpServletResponse.SC_UNAUTHORIZED);
        body.put("error", "Unauthorized");
        body.put("message", authException.getMessage());
        body.put("path", request.getServletPath());

        objectMapper.writeValue(response.getOutputStream(), body);
    }}
}}
'''

    # Auth Controller
    files["AuthController.java"] = f'''package {package}.controller;

import {package}.dto.AuthRequest;
import {package}.dto.AuthResponse;
import {package}.dto.RegisterRequest;
import {package}.service.AuthService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
@Tag(name = "Authentication", description = "Authentication APIs")
public class AuthController {{

    private final AuthService authService;

    @PostMapping("/login")
    @Operation(summary = "Authenticate user and get JWT token")
    public ResponseEntity<AuthResponse> login(@Valid @RequestBody AuthRequest request) {{
        return ResponseEntity.ok(authService.authenticate(request));
    }}

    @PostMapping("/register")
    @Operation(summary = "Register a new user")
    public ResponseEntity<AuthResponse> register(@Valid @RequestBody RegisterRequest request) {{
        return ResponseEntity.ok(authService.register(request));
    }}

    @PostMapping("/refresh")
    @Operation(summary = "Refresh JWT token")
    public ResponseEntity<AuthResponse> refresh(@RequestHeader("Authorization") String refreshToken) {{
        return ResponseEntity.ok(authService.refresh(refreshToken));
    }}
}}
'''

    # Auth DTOs
    files["AuthRequest.java"] = f'''package {package}.dto;

import jakarta.validation.constraints.NotBlank;
import lombok.Data;

@Data
public class AuthRequest {{
    @NotBlank(message = "Username is required")
    private String username;

    @NotBlank(message = "Password is required")
    private String password;
}}
'''

    files["RegisterRequest.java"] = f'''package {package}.dto;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;

@Data
public class RegisterRequest {{
    @NotBlank(message = "Username is required")
    @Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters")
    private String username;

    @NotBlank(message = "Email is required")
    @Email(message = "Email must be valid")
    private String email;

    @NotBlank(message = "Password is required")
    @Size(min = 8, message = "Password must be at least 8 characters")
    private String password;
}}
'''

    files["AuthResponse.java"] = f'''package {package}.dto;

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class AuthResponse {{
    private String accessToken;
    private String refreshToken;
    private String tokenType;
    private Long expiresIn;
}}
'''

    return files


def generate_oauth2_config(issuer_uri: str, package: str) -> Dict[str, str]:
    """Generate OAuth2 resource server configuration"""
    files = {}

    files["SecurityConfig.java"] = f'''package {package}.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {{

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {{
        http
            .csrf(csrf -> csrf.disable())
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api-docs/**", "/swagger-ui/**").permitAll()
                .requestMatchers("/actuator/health").permitAll()
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))
            );

        return http.build();
    }}

    @Bean
    public JwtAuthenticationConverter jwtAuthenticationConverter() {{
        JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        grantedAuthoritiesConverter.setAuthoritiesClaimName("roles");
        grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");

        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
        return jwtAuthenticationConverter;
    }}
}}
'''

    files["application-security.yml"] = f'''spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: {issuer_uri}
          # jwk-set-uri: {issuer_uri}/.well-known/jwks.json
'''

    return files


def main():
    parser = argparse.ArgumentParser(
        description="Spring Security Configuration Generator",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # JWT security with roles
  python security_config_generator.py --type jwt --roles ADMIN,USER,MANAGER

  # OAuth2 resource server
  python security_config_generator.py --type oauth2 --issuer-uri https://auth.example.com

  # Custom package
  python security_config_generator.py --type jwt --roles ADMIN,USER --package com.myapp
"""
    )

    parser.add_argument("--type", required=True, choices=["jwt", "oauth2"],
                        help="Security type")
    parser.add_argument("--roles", default="ADMIN,USER",
                        help="Comma-separated roles (default: ADMIN,USER)")
    parser.add_argument("--issuer-uri",
                        help="OAuth2 issuer URI (required for oauth2 type)")
    parser.add_argument("--package", default="com.example",
                        help="Package name (default: com.example)")
    parser.add_argument("--output", "-o",
                        help="Output directory")
    parser.add_argument("--json", action="store_true",
                        help="Output as JSON")
    parser.add_argument("--version", action="version",
                        version=f"%(prog)s {__version__}")

    args = parser.parse_args()

    if args.type == "jwt":
        roles = [r.strip().upper() for r in args.roles.split(',')]
        files = generate_jwt_config(roles, args.package)
    else:
        if not args.issuer_uri:
            parser.error("--issuer-uri is required for oauth2 type")
        files = generate_oauth2_config(args.issuer_uri, args.package)

    result = {
        "type": args.type,
        "package": args.package,
        "files_generated": list(files.keys()),
    }

    if args.output:
        output_dir = Path(args.output)
        output_dir.mkdir(parents=True, exist_ok=True)
        for filename, content in files.items():
            (output_dir / filename).write_text(content)
        result["output_directory"] = str(output_dir)
        print(f"Security configuration generated in: {args.output}")
        for f in files.keys():
            print(f"  - {f}")
    elif args.json:
        result["files"] = files
        print(json.dumps(result, indent=2))
    else:
        for filename, content in files.items():
            print(f"\n{'='*60}")
            print(f"File: {filename}")
            print('='*60)
            print(content)


if __name__ == "__main__":
    main()


path: scripts/spring_project_scaffolder.py
content: #!/usr/bin/env python3
"""
Spring Boot Project Scaffolder
Generate production-ready Spring Boot project structures with complete configuration.

Features:
- Spring Boot 3.x with Java 17/21 support
- Multiple project types (microservice, monolith, reactive)
- Database configuration (PostgreSQL, MySQL, MongoDB, H2)
- Docker and Docker Compose setup
- GitHub Actions CI/CD pipeline
- Layered architecture (controller, service, repository)
- Lombok and MapStruct integration

Standard library only - no external dependencies required.
"""

import argparse
import json
import logging
import os
import sys
from dataclasses import dataclass, field
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List, Optional

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

__version__ = "1.0.0"


@dataclass
class ProjectConfig:
    """Configuration for the Spring Boot project"""
    name: str
    project_type: str  # microservice, monolith, reactive
    database: str = "postgresql"  # postgresql, mysql, mongodb, h2
    security: Optional[str] = None  # jwt, oauth2, basic, None
    java_version: int = 17
    spring_boot_version: str = "3.2.0"
    include_docker: bool = True
    include_ci: bool = True
    group_id: str = "com.example"
    package_name: str = ""

    def __post_init__(self):
        if not self.package_name:
            self.package_name = f"{self.group_id}.{self.name.replace('-', '')}"


class SpringProjectScaffolder:
    """
    Spring Boot project scaffolding tool for generating production-ready Java applications.
    """

    def __init__(self, config: ProjectConfig, output_dir: str, verbose: bool = False):
        if verbose:
            logging.getLogger().setLevel(logging.DEBUG)
        logger.debug("SpringProjectScaffolder initialized")

        self.config = config
        self.output_dir = Path(output_dir) / config.name
        self.verbose = verbose
        self.files_created: List[str] = []

    def scaffold(self) -> Dict[str, Any]:
        """Generate the complete project structure"""
        logger.debug(f"Scaffolding {self.config.project_type} Spring Boot project: {self.config.name}")
        if self.verbose:
            print(f"Scaffolding Spring Boot {self.config.project_type}: {self.config.name}")
            print(f"Java Version: {self.config.java_version}")
            print(f"Database: {self.config.database}")
            print(f"Security: {self.config.security or 'None'}")
            print(f"Output: {self.output_dir}\n")

        # Create project directory
        self.output_dir.mkdir(parents=True, exist_ok=True)

        # Generate base structure
        self._create_directory_structure()
        self._create_pom_xml()
        self._create_application_properties()
        self._create_main_class()

        # Generate source code structure
        self._create_config_classes()
        self._create_exception_handling()

        # Generate security if enabled
        if self.config.security:
            self._create_security_config()

        # Generate infrastructure files
        if self.config.include_docker:
            self._create_docker_files()

        if self.config.include_ci:
            self._create_github_actions()

        return self._generate_report()

    def _create_directory_structure(self):
        """Create the Maven project directory structure"""
        package_path = self.config.package_name.replace('.', '/')

        directories = [
            f"src/main/java/{package_path}",
            f"src/main/java/{package_path}/config",
            f"src/main/java/{package_path}/controller",
            f"src/main/java/{package_path}/service",
            f"src/main/java/{package_path}/repository",
            f"src/main/java/{package_path}/entity",
            f"src/main/java/{package_path}/dto",
            f"src/main/java/{package_path}/mapper",
            f"src/main/java/{package_path}/exception",
            "src/main/resources",
            "src/main/resources/db/migration",
            f"src/test/java/{package_path}",
            f"src/test/java/{package_path}/controller",
            f"src/test/java/{package_path}/service",
            "src/test/resources",
        ]

        if self.config.security:
            directories.append(f"src/main/java/{package_path}/security")

        for directory in directories:
            dir_path = self.output_dir / directory
            dir_path.mkdir(parents=True, exist_ok=True)
            logger.debug(f"Created directory: {directory}")

    def _create_pom_xml(self):
        """Generate Maven pom.xml"""
        dependencies = self._get_dependencies()

        pom_content = f'''<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>{self.config.spring_boot_version}</version>
        <relativePath/>
    </parent>

    <groupId>{self.config.group_id}</groupId>
    <artifactId>{self.config.name}</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>{self.config.name}</name>
    <description>Spring Boot {self.config.project_type} application</description>

    <properties>
        <java.version>{self.config.java_version}</java.version>
        <mapstruct.version>1.5.5.Final</mapstruct.version>
    </properties>

    <dependencies>
{dependencies}
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>1.18.30</version>
                        </path>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${{mapstruct.version}}</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok-mapstruct-binding</artifactId>
                            <version>0.2.0</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
'''
        self._write_file("pom.xml", pom_content)

    def _get_dependencies(self) -> str:
        """Get Maven dependencies based on configuration"""
        deps = []

        # Core dependencies
        if self.config.project_type == "reactive":
            deps.append(('org.springframework.boot', 'spring-boot-starter-webflux'))
        else:
            deps.append(('org.springframework.boot', 'spring-boot-starter-web'))

        deps.append(('org.springframework.boot', 'spring-boot-starter-validation'))
        deps.append(('org.springframework.boot', 'spring-boot-starter-actuator'))

        # Database dependencies
        if self.config.database == "postgresql":
            deps.append(('org.springframework.boot', 'spring-boot-starter-data-jpa'))
            deps.append(('org.postgresql', 'postgresql', 'runtime'))
        elif self.config.database == "mysql":
            deps.append(('org.springframework.boot', 'spring-boot-starter-data-jpa'))
            deps.append(('com.mysql', 'mysql-connector-j', 'runtime'))
        elif self.config.database == "mongodb":
            if self.config.project_type == "reactive":
                deps.append(('org.springframework.boot', 'spring-boot-starter-data-mongodb-reactive'))
            else:
                deps.append(('org.springframework.boot', 'spring-boot-starter-data-mongodb'))
        elif self.config.database == "h2":
            deps.append(('org.springframework.boot', 'spring-boot-starter-data-jpa'))
            deps.append(('com.h2database', 'h2', 'runtime'))

        # Security dependencies
        if self.config.security:
            deps.append(('org.springframework.boot', 'spring-boot-starter-security'))
            if self.config.security == "oauth2":
                deps.append(('org.springframework.boot', 'spring-boot-starter-oauth2-resource-server'))
            elif self.config.security == "jwt":
                deps.append(('io.jsonwebtoken', 'jjwt-api', None, '0.12.3'))
                deps.append(('io.jsonwebtoken', 'jjwt-impl', 'runtime', '0.12.3'))
                deps.append(('io.jsonwebtoken', 'jjwt-jackson', 'runtime', '0.12.3'))

        # Utility dependencies
        deps.append(('org.projectlombok', 'lombok', 'provided'))
        deps.append(('org.mapstruct', 'mapstruct', None, '${mapstruct.version}'))

        # OpenAPI documentation
        deps.append(('org.springdoc', [REDACTED_SECRET], None, '2.3.0'))

        # Test dependencies
        deps.append(('org.springframework.boot', 'spring-boot-starter-test', 'test'))
        if self.config.security:
            deps.append(('org.springframework.security', 'spring-security-test', 'test'))

        # Format dependencies as XML
        xml_deps = []
        for dep in deps:
            if len(dep) == 2:
                group_id, artifact_id = dep
                xml_deps.append(f'''        <dependency>
            <groupId>{group_id}</groupId>
            <artifactId>{artifact_id}</artifactId>
        </dependency>''')
            elif len(dep) == 3:
                group_id, artifact_id, scope = dep
                scope_xml = f"\n            <scope>{scope}</scope>" if scope else ""
                xml_deps.append(f'''        <dependency>
            <groupId>{group_id}</groupId>
            <artifactId>{artifact_id}</artifactId>{scope_xml}
        </dependency>''')
            elif len(dep) == 4:
                group_id, artifact_id, scope, version = dep
                scope_xml = f"\n            <scope>{scope}</scope>" if scope else ""
                version_xml = f"\n            <version>{version}</version>" if version else ""
                xml_deps.append(f'''        <dependency>
            <groupId>{group_id}</groupId>
            <artifactId>{artifact_id}</artifactId>{version_xml}{scope_xml}
        </dependency>''')

        return '\n'.join(xml_deps)

    def _create_application_properties(self):
        """Generate application.yml configuration"""
        db_config = self._get_database_config()

        content = f'''spring:
  application:
    name: {self.config.name}
  profiles:
    active: dev

---
spring:
  config:
    activate:
      on-profile: dev
{db_config}
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        format_sql: true

server:
  port: 8080

logging:
  level:
    root: INFO
    {self.config.package_name}: DEBUG
    org.hibernate.SQL: DEBUG

springdoc:
  api-docs:
    path: /api-docs
  swagger-ui:
    path: /swagger-ui.html

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  endpoint:
    health:
      show-details: always

---
spring:
  config:
    activate:
      on-profile: prod
{db_config}
  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: false

server:
  port: 8080

logging:
  level:
    root: WARN
    {self.config.package_name}: INFO
'''

        if self.config.security == "jwt":
            content += f'''
jwt:
  [REDACTED_SECRET]
  expiration: 86400000  # 24 hours
  refresh-expiration: 604800000  # 7 days
'''

        self._write_file("src/main/resources/application.yml", content)

    def _get_database_config(self) -> str:
        """Get database-specific configuration"""
        if self.config.database == "postgresql":
            return '''  datasource:
    url: jdbc:postgresql://localhost:5432/{}
    username: ${{DB_USERNAME:postgres}}
    [REDACTED_SECRET]
    driver-class-name: org.postgresql.Driver'''.format(self.config.name.replace('-', '_'))
        elif self.config.database == "mysql":
            return '''  datasource:
    url: jdbc:mysql://localhost:3306/{}
    username: ${{DB_USERNAME:root}}
    [REDACTED_SECRET]
    driver-class-name: com.mysql.cj.jdbc.Driver'''.format(self.config.name.replace('-', '_'))
        elif self.config.database == "mongodb":
            return '''  data:
    mongodb:
      uri: mongodb://localhost:27017/{}'''.format(self.config.name.replace('-', '_'))
        elif self.config.database == "h2":
            return '''  datasource:
    url: jdbc:h2:mem:testdb
    driver-class-name: org.h2.Driver
    username: sa
    password:
  h2:
    console:
      enabled: true'''
        return ""

    def _create_main_class(self):
        """Generate main application class"""
        package_path = self.config.package_name.replace('.', '/')
        class_name = ''.join(word.capitalize() for word in self.config.name.split('-')) + "Application"

        content = f'''package {self.config.package_name};

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class {class_name} {{

    public static void main(String[] args) {{
        SpringApplication.run({class_name}.class, args);
    }}
}}
'''
        self._write_file(f"src/main/java/{package_path}/{class_name}.java", content)

    def _create_config_classes(self):
        """Generate configuration classes"""
        package_path = self.config.package_name.replace('.', '/')

        # OpenAPI config
        openapi_config = f'''package {self.config.package_name}.config;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.Contact;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class OpenApiConfig {{

    @Bean
    public OpenAPI customOpenAPI() {{
        return new OpenAPI()
            .info(new Info()
                .title("{self.config.name} API")
                .version("1.0.0")
                .description("API documentation for {self.config.name}")
                .contact(new Contact()
                    .name("API Support")
                    .email("[REDACTED_SECRET]")));
    }}
}}
'''
        self._write_file(f"src/main/java/{package_path}/config/OpenApiConfig.java", openapi_config)

    def _create_exception_handling(self):
        """Generate global exception handler"""
        package_path = self.config.package_name.replace('.', '/')

        # Custom exception
        not_found = f'''package {self.config.package_name}.exception;

public class ResourceNotFoundException extends RuntimeException {{

    public ResourceNotFoundException(String message) {{
        super(message);
    }}

    public ResourceNotFoundException(String resourceName, String fieldName, Object fieldValue) {{
        super(String.format("%s not found with %s: '%s'", resourceName, fieldName, fieldValue));
    }}
}}
'''
        self._write_file(f"src/main/java/{package_path}/exception/ResourceNotFoundException.java", not_found)

        # Error response DTO
        error_response = f'''package {self.config.package_name}.exception;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Builder;
import lombok.Data;

import java.time.LocalDateTime;
import java.util.List;

@Data
@Builder
public class ErrorResponse {{

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime timestamp;

    private int status;
    private String error;
    private String message;
    private String path;
    private List<FieldError> fieldErrors;

    @Data
    @Builder
    public static class FieldError {{
        private String field;
        private String message;
    }}
}}
'''
        self._write_file(f"src/main/java/{package_path}/exception/ErrorResponse.java", error_response)

        # Global exception handler
        handler = f'''package {self.config.package_name}.exception;

import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {{

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleResourceNotFound(
            ResourceNotFoundException ex, HttpServletRequest request) {{
        log.error("Resource not found: {{}}", ex.getMessage());

        ErrorResponse error = ErrorResponse.builder()
            .timestamp(LocalDateTime.now())
            .status(HttpStatus.NOT_FOUND.value())
            .error(HttpStatus.NOT_FOUND.getReasonPhrase())
            .message(ex.getMessage())
            .path(request.getRequestURI())
            .build();

        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
    }}

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> handleValidationErrors(
            MethodArgumentNotValidException ex, HttpServletRequest request) {{
        log.error("Validation error: {{}}", ex.getMessage());

        BindingResult result = ex.getBindingResult();
        List<ErrorResponse.FieldError> fieldErrors = result.getFieldErrors().stream()
            .map(error -> ErrorResponse.FieldError.builder()
                .field(error.getField())
                .message(error.getDefaultMessage())
                .build())
            .collect(Collectors.toList());

        ErrorResponse error = ErrorResponse.builder()
            .timestamp(LocalDateTime.now())
            .status(HttpStatus.BAD_REQUEST.value())
            .error(HttpStatus.BAD_REQUEST.getReasonPhrase())
            .message("Validation failed")
            .path(request.getRequestURI())
            .fieldErrors(fieldErrors)
            .build();

        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
    }}

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGenericException(
            Exception ex, HttpServletRequest request) {{
        log.error("Unexpected error: {{}}", ex.getMessage(), ex);

        ErrorResponse error = ErrorResponse.builder()
            .timestamp(LocalDateTime.now())
            .status(HttpStatus.INTERNAL_SERVER_ERROR.value())
            .error(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase())
            .message("An unexpected error occurred")
            .path(request.getRequestURI())
            .build();

        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
    }}
}}
'''
        self._write_file(f"src/main/java/{package_path}/exception/GlobalExceptionHandler.java", handler)

    def _create_security_config(self):
        """Generate Spring Security configuration"""
        package_path = self.config.package_name.replace('.', '/')

        if self.config.security == "jwt":
            security_config = f'''package {self.config.package_name}.security;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {{

    private final JwtAuthenticationFilter jwtAuthenticationFilter;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {{
        http
            .csrf(csrf -> csrf.disable())
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/**").permitAll()
                .requestMatchers("/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll()
                .requestMatchers("/actuator/**").permitAll()
                .anyRequest().authenticated()
            )
            .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }}

    @Bean
    public PasswordEncoder passwordEncoder() {{
        return new BCryptPasswordEncoder();
    }}

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {{
        return config.getAuthenticationManager();
    }}
}}
'''
            self._write_file(f"src/main/java/{package_path}/security/SecurityConfig.java", security_config)

            # JWT Authentication Filter
            jwt_filter = f'''package {self.config.package_name}.security;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.NonNull;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Slf4j
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {{

    private final JwtTokenProvider tokenProvider;
    private final UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(
            @NonNull HttpServletRequest request,
            @NonNull HttpServletResponse response,
            @NonNull FilterChain filterChain) throws ServletException, IOException {{

        try {{
            String jwt = getJwtFromRequest(request);

            if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {{
                String username = tokenProvider.getUsernameFromToken(jwt);
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);

                UsernamePasswordAuthenticationToken authentication =
                    new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

                SecurityContextHolder.getContext().setAuthentication(authentication);
            }}
        }} catch (Exception ex) {{
            log.error("Could not set user authentication in security context", ex);
        }}

        filterChain.doFilter(request, response);
    }}

    private String getJwtFromRequest(HttpServletRequest request) {{
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {{
            return bearerToken.substring(7);
        }}
        return null;
    }}
}}
'''
            self._write_file(f"src/main/java/{package_path}/security/JwtAuthenticationFilter.java", jwt_filter)

            # JWT Token Provider
            jwt_provider = f'''package {self.config.package_name}.security;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;

@Slf4j
@Component
public class JwtTokenProvider {{

    @Value("${{jwt.secret}}")
    private String jwtSecret;

    @Value("${{jwt.expiration}}")
    private long jwtExpiration;

    @Value("${{jwt.refresh-expiration}}")
    private long jwtRefreshExpiration;

    private SecretKey getSigningKey() {{
        return Keys.hmacShaKeyFor(jwtSecret.getBytes(StandardCharsets.UTF_8));
    }}

    public String generateToken(Authentication authentication) {{
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + jwtExpiration);

        return Jwts.builder()
            .subject(userDetails.getUsername())
            .issuedAt(now)
            .expiration(expiryDate)
            .signWith(getSigningKey())
            .compact();
    }}

    public String generateRefreshToken(Authentication authentication) {{
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + jwtRefreshExpiration);

        return Jwts.builder()
            .subject(userDetails.getUsername())
            .issuedAt(now)
            .expiration(expiryDate)
            .signWith(getSigningKey())
            .compact();
    }}

    public String getUsernameFromToken(String token) {{
        Claims claims = Jwts.parser()
            .verifyWith(getSigningKey())
            .build()
            .parseSignedClaims(token)
            .getPayload();

        return claims.getSubject();
    }}

    public boolean validateToken(String token) {{
        try {{
            Jwts.parser()
                .verifyWith(getSigningKey())
                .build()
                .parseSignedClaims(token);
            return true;
        }} catch (MalformedJwtException ex) {{
            log.error("Invalid JWT token");
        }} catch (ExpiredJwtException ex) {{
            log.error("Expired JWT token");
        }} catch (UnsupportedJwtException ex) {{
            log.error("Unsupported JWT token");
        }} catch (IllegalArgumentException ex) {{
            log.error("JWT claims string is empty");
        }}
        return false;
    }}
}}
'''
            self._write_file(f"src/main/java/{package_path}/security/JwtTokenProvider.java", jwt_provider)

    def _create_docker_files(self):
        """Generate Docker and Docker Compose files"""
        dockerfile = f'''FROM eclipse-temurin:{self.config.java_version}-jdk-alpine as build
WORKDIR /app
COPY mvnw .
COPY .mvn .mvn
COPY pom.xml .
COPY src src
RUN chmod +x ./mvnw
RUN ./mvnw clean package -DskipTests

FROM eclipse-temurin:{self.config.java_version}-jre-alpine
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
'''
        self._write_file("Dockerfile", dockerfile)

        # Docker Compose
        db_service = self._get_docker_db_service()
        compose = f'''version: '3.8'

services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=dev
{self._get_docker_env_vars()}
    depends_on:
      - db
    networks:
      - app-network

{db_service}

networks:
  app-network:
    driver: bridge

volumes:
  db-data:
'''
        self._write_file("docker-compose.yml", compose)

    def _get_docker_db_service(self) -> str:
        """Get Docker Compose database service configuration"""
        if self.config.database == "postgresql":
            return '''  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: {}
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    ports:
      - "5432:5432"
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - app-network'''.format(self.config.name.replace('-', '_'))
        elif self.config.database == "mysql":
            return '''  db:
    image: mysql:8
    environment:
      MYSQL_DATABASE: {}
      MYSQL_ROOT_PASSWORD: root
    ports:
      - "3306:3306"
    volumes:
      - db-data:/var/lib/mysql
    networks:
      - app-network'''.format(self.config.name.replace('-', '_'))
        elif self.config.database == "mongodb":
            return '''  db:
    image: mongo:6
    ports:
      - "27017:27017"
    volumes:
      - db-data:/data/db
    networks:
      - app-network'''
        return ""

    def _get_docker_env_vars(self) -> str:
        """Get Docker environment variables"""
        if self.config.database == "postgresql":
            return '''      - DB_USERNAME=postgres
      - DB_PASSWORD=postgres
      - SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/{}'''.format(self.config.name.replace('-', '_'))
        elif self.config.database == "mysql":
            return '''      - DB_USERNAME=root
      - DB_PASSWORD=root
      - SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/{}'''.format(self.config.name.replace('-', '_'))
        elif self.config.database == "mongodb":
            return '''      - SPRING_DATA_MONGODB_URI=mongodb://db:27017/{}'''.format(self.config.name.replace('-', '_'))
        return ""

    def _create_github_actions(self):
        """Generate GitHub Actions CI/CD workflow"""
        workflow = f'''name: CI/CD Pipeline

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4

    - name: Set up JDK {self.config.java_version}
      uses: actions/setup-java@v4
      with:
        java-version: '{self.config.java_version}'
        distribution: 'temurin'
        cache: maven

    - name: Build with Maven
      run: mvn -B package --file pom.xml

    - name: Run tests
      run: mvn test

    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v3
      with:
        file: ./target/site/jacoco/jacoco.xml

  docker:
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'

    steps:
    - uses: actions/checkout@v4

    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v3

    - name: Login to Docker Hub
      uses: docker/login-action@v3
      with:
        username: ${{{{ secrets.DOCKERHUB_USERNAME }}}}
        password: ${{{{ secrets.DOCKERHUB_TOKEN }}}}

    - name: Build and push
      uses: docker/build-push-action@v5
      with:
        context: .
        push: true
        tags: ${{{{ secrets.DOCKERHUB_USERNAME }}}}/{self.config.name}:latest
'''

        workflow_dir = self.output_dir / ".github" / "workflows"
        workflow_dir.mkdir(parents=True, exist_ok=True)
        self._write_file(".github/workflows/ci-cd.yml", workflow)

    def _write_file(self, path: str, content: str):
        """Write content to a file"""
        file_path = self.output_dir / path
        file_path.parent.mkdir(parents=True, exist_ok=True)
        file_path.write_text(content)
        self.files_created.append(path)
        logger.debug(f"Created file: {path}")

    def _generate_report(self) -> Dict[str, Any]:
        """Generate a summary report of the scaffolded project"""
        return {
            "project_name": self.config.name,
            "project_type": self.config.project_type,
            "java_version": self.config.java_version,
            "spring_boot_version": self.config.spring_boot_version,
            "database": self.config.database,
            "security": self.config.security,
            "output_directory": str(self.output_dir),
            "files_created": len(self.files_created),
            "includes": {
                "docker": self.config.include_docker,
                "ci_cd": self.config.include_ci,
            }
        }


def main():
    """CLI entry point"""
    parser = argparse.ArgumentParser(
        description="Spring Boot Project Scaffolder - Generate production-ready Spring Boot projects",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Create microservice with PostgreSQL and JWT security
  python spring_project_scaffolder.py user-service --type microservice --db postgresql --security jwt

  # Create monolith with MySQL
  python spring_project_scaffolder.py ecommerce --type monolith --db mysql

  # Create reactive service with MongoDB
  python spring_project_scaffolder.py notification-service --type reactive --db mongodb

  # Minimal project without Docker/CI
  python spring_project_scaffolder.py simple-api --no-docker --no-ci
"""
    )

    parser.add_argument("name", help="Project name (kebab-case)")
    parser.add_argument("--type", dest="project_type",
                        choices=["microservice", "monolith", "reactive"],
                        default="microservice",
                        help="Project type (default: microservice)")
    parser.add_argument("--db", dest="database",
                        choices=["postgresql", "mysql", "mongodb", "h2"],
                        default="postgresql",
                        help="Database type (default: postgresql)")
    parser.add_argument("--security",
                        choices=["jwt", "oauth2", "basic"],
                        help="Security type (optional)")
    parser.add_argument("--java", dest="java_version",
                        type=int, choices=[17, 21],
                        default=17,
                        help="Java version (default: 17)")
    parser.add_argument("--group-id",
                        default="com.example",
                        help="Maven group ID (default: com.example)")
    parser.add_argument("--output", "-o",
                        default=".",
                        help="Output directory (default: current directory)")
    parser.add_argument("--no-docker", action="store_true",
                        help="Skip Docker configuration")
    parser.add_argument("--no-ci", action="store_true",
                        help="Skip CI/CD configuration")
    parser.add_argument("--verbose", "-v", action="store_true",
                        help="Enable verbose output")
    parser.add_argument("--json", action="store_true",
                        help="Output result as JSON")
    parser.add_argument("--version", action="version",
                        version=f"%(prog)s {__version__}")

    args = parser.parse_args()

    config = ProjectConfig(
        name=args.name,
        project_type=args.project_type,
        database=args.database,
        security=args.security,
        java_version=args.java_version,
        group_id=args.group_id,
        include_docker=not args.no_docker,
        include_ci=not args.no_ci,
    )

    scaffolder = SpringProjectScaffolder(config, args.output, args.verbose)
    result = scaffolder.scaffold()

    if args.json:
        print(json.dumps(result, indent=2))
    else:
        print(f"\n{'='*60}")
        print(f"Project '{result['project_name']}' created successfully!")
        print(f"{'='*60}")
        print(f"Location: {result['output_directory']}")
        print(f"Files created: {result['files_created']}")
        print(f"\nNext steps:")
        print(f"  cd {args.name}")
        print(f"  docker-compose up -d  # Start database")
        print(f"  ./mvnw spring-boot:run  # Start application")
        print(f"\nAPI available at: http://localhost:8080")
        print(f"Swagger UI: http://localhost:8080/swagger-ui.html")


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)