moai-adk 0.4.5__py3-none-any.whl → 0.20.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of moai-adk might be problematic. Click here for more details.
- moai_adk/__init__.py +1 -1
- moai_adk/__main__.py +74 -1
- moai_adk/cli/commands/__init__.py +1 -1
- moai_adk/cli/commands/analyze.py +119 -0
- moai_adk/cli/commands/backup.py +25 -1
- moai_adk/cli/commands/doctor.py +31 -5
- moai_adk/cli/commands/improve_user_experience.py +307 -0
- moai_adk/cli/commands/init.py +111 -10
- moai_adk/cli/commands/status.py +33 -3
- moai_adk/cli/commands/update.py +921 -130
- moai_adk/cli/commands/validate_links.py +120 -0
- moai_adk/cli/prompts/init_prompts.py +22 -87
- moai_adk/core/analysis/__init__.py +9 -0
- moai_adk/core/analysis/session_analyzer.py +388 -0
- moai_adk/core/analysis/tag_chain_analyzer.py +344 -0
- moai_adk/core/analysis/tag_chain_repair.py +879 -0
- moai_adk/core/config/__init__.py +19 -0
- moai_adk/core/config/migration.py +235 -0
- moai_adk/core/git/__init__.py +1 -1
- moai_adk/core/git/branch.py +1 -1
- moai_adk/core/git/commit.py +1 -1
- moai_adk/core/git/manager.py +1 -1
- moai_adk/core/issue_creator.py +313 -0
- moai_adk/core/mcp/setup.py +56 -0
- moai_adk/core/mcp/setup_old.py +296 -0
- moai_adk/core/project/backup_utils.py +1 -1
- moai_adk/core/project/checker.py +2 -2
- moai_adk/core/project/detector.py +211 -12
- moai_adk/core/project/initializer.py +85 -15
- moai_adk/core/project/phase_executor.py +76 -13
- moai_adk/core/project/validator.py +13 -13
- moai_adk/core/quality/__init__.py +1 -1
- moai_adk/core/quality/trust_checker.py +1 -1
- moai_adk/core/quality/validators/__init__.py +1 -1
- moai_adk/core/quality/validators/base_validator.py +1 -1
- moai_adk/core/tags/__init__.py +86 -0
- moai_adk/core/tags/auto_corrector.py +693 -0
- moai_adk/core/tags/ci_validator.py +463 -0
- moai_adk/core/tags/cli.py +283 -0
- moai_adk/core/tags/generator.py +109 -0
- moai_adk/core/tags/inserter.py +99 -0
- moai_adk/core/tags/mapper.py +126 -0
- moai_adk/core/tags/parser.py +76 -0
- moai_adk/core/tags/policy_validator.py +580 -0
- moai_adk/core/tags/pre_commit_validator.py +421 -0
- moai_adk/core/tags/reporter.py +956 -0
- moai_adk/core/tags/rollback_manager.py +525 -0
- moai_adk/core/tags/tags.py +149 -0
- moai_adk/core/tags/validator.py +897 -0
- moai_adk/core/template/__init__.py +1 -1
- moai_adk/core/template/backup.py +1 -1
- moai_adk/core/template/merger.py +50 -1
- moai_adk/core/template/processor.py +119 -13
- moai_adk/core/template_engine.py +268 -0
- moai_adk/templates/.claude/agents/alfred/backend-expert.md +348 -0
- moai_adk/templates/.claude/agents/alfred/cc-manager.md +209 -944
- moai_adk/templates/.claude/agents/alfred/database-expert.md +352 -0
- moai_adk/templates/.claude/agents/alfred/debug-helper.md +34 -5
- moai_adk/templates/.claude/agents/alfred/devops-expert.md +464 -0
- moai_adk/templates/.claude/agents/alfred/doc-syncer.md +38 -8
- moai_adk/templates/.claude/agents/alfred/format-expert.md +469 -0
- moai_adk/templates/.claude/agents/alfred/frontend-expert.md +357 -0
- moai_adk/templates/.claude/agents/alfred/git-manager.md +128 -9
- moai_adk/templates/.claude/agents/alfred/implementation-planner.md +104 -6
- moai_adk/templates/.claude/agents/alfred/project-manager.md +88 -16
- moai_adk/templates/.claude/agents/alfred/quality-gate.md +36 -9
- moai_adk/templates/.claude/agents/alfred/security-expert.md +270 -0
- moai_adk/templates/.claude/agents/alfred/skill-factory.md +865 -0
- moai_adk/templates/.claude/agents/alfred/spec-builder.md +214 -43
- moai_adk/templates/.claude/agents/alfred/tag-agent.md +111 -9
- moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +309 -160
- moai_adk/templates/.claude/agents/alfred/trust-checker.md +36 -7
- moai_adk/templates/.claude/agents/alfred/ui-ux-expert.md +605 -0
- moai_adk/templates/.claude/commands/alfred/0-project.md +393 -966
- moai_adk/templates/.claude/commands/alfred/1-plan.md +651 -367
- moai_adk/templates/.claude/commands/alfred/2-run.md +388 -241
- moai_adk/templates/.claude/commands/alfred/3-sync.md +1921 -410
- moai_adk/templates/.claude/commands/alfred/9-feedback.md +153 -0
- moai_adk/templates/.claude/commands/alfred/release-new.md +3604 -0
- moai_adk/templates/.claude/hooks/alfred/core/project.py +484 -20
- moai_adk/templates/.claude/hooks/alfred/core/timeout.py +136 -0
- moai_adk/templates/.claude/hooks/alfred/core/ttl_cache.py +108 -0
- moai_adk/templates/.claude/hooks/alfred/core/version_cache.py +198 -0
- moai_adk/templates/.claude/hooks/alfred/handlers/__init__.py +14 -6
- moai_adk/templates/.claude/hooks/alfred/post_tool__enable_streaming_ui.py +50 -0
- moai_adk/templates/.claude/hooks/alfred/post_tool__log_changes.py +93 -0
- moai_adk/templates/.claude/hooks/alfred/post_tool__tag_auto_corrector.py +407 -0
- moai_adk/templates/.claude/hooks/alfred/pre_tool__auto_checkpoint.py +99 -0
- moai_adk/templates/.claude/hooks/alfred/pre_tool__realtime_tag_monitor.py +335 -0
- moai_adk/templates/.claude/hooks/alfred/pre_tool__tag_policy_validator.py +325 -0
- moai_adk/templates/.claude/hooks/alfred/session_end__cleanup.py +93 -0
- moai_adk/templates/.claude/hooks/alfred/session_start__auto_cleanup.py +580 -0
- moai_adk/templates/.claude/hooks/alfred/session_start__show_project_info.py +298 -0
- moai_adk/templates/.claude/hooks/alfred/shared/core/__init__.py +170 -0
- moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/checkpoint.py +3 -3
- moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/context.py +5 -5
- moai_adk/templates/.claude/hooks/alfred/shared/core/project.py +749 -0
- moai_adk/templates/.claude/hooks/alfred/shared/core/tags.py +230 -0
- moai_adk/templates/.claude/hooks/alfred/shared/core/version_cache.py +198 -0
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/__init__.py +21 -0
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/daily_analysis.py +351 -0
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/notification.py +154 -0
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/session.py +174 -0
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/tool.py +87 -0
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/user.py +61 -0
- moai_adk/templates/.claude/hooks/alfred/user_prompt__jit_load_docs.py +111 -0
- moai_adk/templates/.claude/hooks/alfred/utils/__init__.py +1 -0
- moai_adk/templates/.claude/hooks/alfred/utils/hook_config.py +94 -0
- moai_adk/templates/.claude/hooks/alfred/utils/timeout.py +161 -0
- moai_adk/templates/.claude/output-styles/alfred/alfred-moai-adk-beginner.md +267 -0
- moai_adk/templates/.claude/output-styles/alfred/keating-personal-tutor.md +440 -0
- moai_adk/templates/.claude/output-styles/alfred/r2d2-agentic-coding.md +583 -0
- moai_adk/templates/.claude/settings.json +96 -14
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/SKILL.md +70 -0
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/examples.md +62 -0
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/reference.md +242 -0
- moai_adk/templates/.claude/skills/moai-alfred-ask-user-questions/SKILL.md +237 -0
- moai_adk/templates/.claude/skills/moai-alfred-ask-user-questions/examples.md +871 -0
- moai_adk/templates/.claude/skills/moai-alfred-ask-user-questions/reference.md +653 -0
- moai_adk/templates/.claude/skills/moai-alfred-clone-pattern/README.md +162 -0
- moai_adk/templates/.claude/skills/moai-alfred-clone-pattern/SKILL.md +227 -0
- moai_adk/templates/.claude/skills/moai-alfred-clone-pattern/examples.md +354 -0
- moai_adk/templates/.claude/skills/moai-alfred-clone-pattern/reference.md +158 -0
- moai_adk/templates/.claude/skills/moai-alfred-code-reviewer/SKILL.md +179 -79
- moai_adk/templates/.claude/skills/moai-alfred-code-reviewer/examples.md +117 -0
- moai_adk/templates/.claude/skills/moai-alfred-code-reviewer/scripts/pre-review-check.sh +62 -0
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/SKILL.md +132 -0
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/examples.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/reference.md +444 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/SKILL.md +62 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/examples.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/reference.md +405 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/SKILL.md +51 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/examples.md +355 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/reference.md +239 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/SKILL.md +323 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/examples.md +286 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/reference.md +126 -0
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/SKILL.md +229 -0
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/reference.md +150 -0
- moai_adk/templates/.claude/skills/moai-alfred-language-detection/SKILL.md +87 -73
- moai_adk/templates/.claude/skills/moai-alfred-language-detection/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-alfred-language-detection/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-personas/README.md +42 -0
- moai_adk/templates/.claude/skills/moai-alfred-personas/SKILL.md +429 -0
- moai_adk/templates/.claude/skills/moai-alfred-personas/examples.md +520 -0
- moai_adk/templates/.claude/skills/moai-alfred-personas/reference.md +405 -0
- moai_adk/templates/.claude/skills/moai-alfred-practices/SKILL.md +89 -0
- moai_adk/templates/.claude/skills/moai-alfred-practices/examples.md +122 -0
- moai_adk/templates/.claude/skills/moai-alfred-practices/reference.md +369 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/SKILL.md +508 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/examples.md +481 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/reference.md +100 -0
- moai_adk/templates/.claude/skills/moai-alfred-rules/SKILL.md +77 -0
- moai_adk/templates/.claude/skills/moai-alfred-rules/examples.md +265 -0
- moai_adk/templates/.claude/skills/moai-alfred-rules/reference.md +539 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/SKILL.md +320 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/reference.md +84 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/README.md +137 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/SKILL.md +219 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/examples/validate-spec.sh +161 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/examples.md +541 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/reference.md +622 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/reference.md +211 -0
- moai_adk/templates/.claude/skills/moai-alfred-workflow/SKILL.md +288 -0
- moai_adk/templates/.claude/skills/moai-cc-agents/SKILL.md +269 -0
- moai_adk/templates/.claude/skills/moai-cc-agents/templates/agent-template.md +32 -0
- moai_adk/templates/.claude/skills/moai-cc-claude-md/SKILL.md +298 -0
- moai_adk/templates/.claude/skills/moai-cc-claude-md/templates/CLAUDE-template.md +26 -0
- moai_adk/templates/.claude/skills/moai-cc-commands/SKILL.md +307 -0
- moai_adk/templates/.claude/skills/moai-cc-commands/templates/command-template.md +21 -0
- moai_adk/templates/.claude/skills/moai-cc-hooks/SKILL.md +252 -0
- moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/pre-bash-check.sh +19 -0
- moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/preserve-permissions.sh +19 -0
- moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/validate-bash-command.py +24 -0
- moai_adk/templates/.claude/skills/moai-cc-mcp-plugins/SKILL.md +199 -0
- moai_adk/templates/.claude/skills/moai-cc-mcp-plugins/templates/settings-mcp-template.json +39 -0
- moai_adk/templates/.claude/skills/moai-cc-memory/SKILL.md +316 -0
- moai_adk/templates/.claude/skills/moai-cc-memory/templates/session-summary-template.md +18 -0
- moai_adk/templates/.claude/skills/moai-cc-settings/SKILL.md +263 -0
- moai_adk/templates/.claude/skills/moai-cc-settings/templates/settings-complete-template.json +30 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/CHECKLIST.md +482 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/EXAMPLES.md +303 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/INTERACTIVE-DISCOVERY.md +524 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/METADATA.md +477 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/PARALLEL-ANALYSIS-REPORT.md +429 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/PYTHON-VERSION-MATRIX.md +391 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/SKILL-FACTORY-WORKFLOW.md +431 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/SKILL-UPDATE-ADVISOR.md +577 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/SKILL.md +273 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/STEP-BY-STEP-GUIDE.md +466 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/STRUCTURE.md +583 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/WEB-RESEARCH.md +526 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/reference.md +608 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/scripts/generate-structure.sh +328 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/scripts/validate-skill.sh +312 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/SKILL_TEMPLATE.md +245 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/examples-template.md +285 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/reference-template.md +278 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/scripts-template.sh +303 -0
- moai_adk/templates/.claude/skills/moai-cc-skills/SKILL.md +291 -0
- moai_adk/templates/.claude/skills/moai-cc-skills/templates/SKILL-template.md +15 -0
- moai_adk/templates/.claude/skills/moai-change-logger/SKILL.md +563 -0
- moai_adk/templates/.claude/skills/moai-design-systems/SKILL.md +802 -0
- moai_adk/templates/.claude/skills/moai-design-systems/examples.md +1238 -0
- moai_adk/templates/.claude/skills/moai-design-systems/reference.md +673 -0
- moai_adk/templates/.claude/skills/moai-domain-backend/SKILL.md +234 -43
- moai_adk/templates/.claude/skills/moai-domain-backend/examples.md +1633 -0
- moai_adk/templates/.claude/skills/moai-domain-backend/reference.md +660 -0
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/SKILL.md +97 -69
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-data-science/SKILL.md +97 -72
- moai_adk/templates/.claude/skills/moai-domain-data-science/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-data-science/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-database/SKILL.md +97 -74
- moai_adk/templates/.claude/skills/moai-domain-database/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-database/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-devops/SKILL.md +98 -74
- moai_adk/templates/.claude/skills/moai-domain-devops/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-devops/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +102 -73
- moai_adk/templates/.claude/skills/moai-domain-frontend/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-domain-ml/SKILL.md +97 -73
- moai_adk/templates/.claude/skills/moai-domain-ml/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-ml/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/SKILL.md +97 -67
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-security/SKILL.md +97 -79
- moai_adk/templates/.claude/skills/moai-domain-security/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-security/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-web-api/SKILL.md +97 -71
- moai_adk/templates/.claude/skills/moai-domain-web-api/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-web-api/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-essentials-debug/SKILL.md +265 -64
- moai_adk/templates/.claude/skills/moai-essentials-debug/examples.md +1064 -0
- moai_adk/templates/.claude/skills/moai-essentials-debug/reference.md +1047 -0
- moai_adk/templates/.claude/skills/moai-essentials-perf/SKILL.md +87 -78
- moai_adk/templates/.claude/skills/moai-essentials-perf/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-essentials-perf/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-essentials-refactor/SKILL.md +87 -70
- moai_adk/templates/.claude/skills/moai-essentials-refactor/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-essentials-refactor/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-essentials-review/SKILL.md +87 -86
- moai_adk/templates/.claude/skills/moai-essentials-review/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-essentials-review/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-foundation-ears/SKILL.md +80 -62
- moai_adk/templates/.claude/skills/moai-foundation-ears/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-ears/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-foundation-git/SKILL.md +207 -50
- moai_adk/templates/.claude/skills/moai-foundation-git/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-git/reference.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-langs/SKILL.md +90 -71
- moai_adk/templates/.claude/skills/moai-foundation-langs/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-langs/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-foundation-specs/SKILL.md +78 -58
- moai_adk/templates/.claude/skills/moai-foundation-specs/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-specs/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-foundation-tags/SKILL.md +78 -51
- moai_adk/templates/.claude/skills/moai-foundation-tags/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-tags/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-foundation-trust/.!11330!examples.md +0 -0
- moai_adk/templates/.claude/skills/moai-foundation-trust/SKILL.md +253 -32
- moai_adk/templates/.claude/skills/moai-foundation-trust/examples.md +0 -0
- moai_adk/templates/.claude/skills/moai-foundation-trust/reference.md +1099 -0
- moai_adk/templates/.claude/skills/moai-jit-docs-enhanced/SKILL.md +460 -0
- moai_adk/templates/.claude/skills/moai-lang-c/SKILL.md +98 -74
- moai_adk/templates/.claude/skills/moai-lang-c/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-c/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-cpp/SKILL.md +98 -76
- moai_adk/templates/.claude/skills/moai-lang-cpp/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-cpp/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/SKILL.md +2358 -70
- moai_adk/templates/.claude/skills/moai-lang-csharp/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-dart/SKILL.md +2962 -68
- moai_adk/templates/.claude/skills/moai-lang-dart/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-dart/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +1898 -70
- moai_adk/templates/.claude/skills/moai-lang-go/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-go/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +1465 -68
- moai_adk/templates/.claude/skills/moai-lang-java/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-java/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-javascript/SKILL.md +2364 -66
- moai_adk/templates/.claude/skills/moai-lang-javascript/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-javascript/reference.md +32 -0
- moai_adk/templates/.claude/skills/moai-lang-kotlin/SKILL.md +1630 -69
- moai_adk/templates/.claude/skills/moai-lang-kotlin/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-kotlin/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +89 -61
- moai_adk/templates/.claude/skills/moai-lang-php/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-php/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +735 -66
- moai_adk/templates/.claude/skills/moai-lang-python/examples.md +624 -0
- moai_adk/templates/.claude/skills/moai-lang-python/reference.md +316 -0
- moai_adk/templates/.claude/skills/moai-lang-r/SKILL.md +97 -73
- moai_adk/templates/.claude/skills/moai-lang-r/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-r/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/SKILL.md +98 -73
- moai_adk/templates/.claude/skills/moai-lang-ruby/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +1834 -70
- moai_adk/templates/.claude/skills/moai-lang-rust/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-rust/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +99 -74
- moai_adk/templates/.claude/skills/moai-lang-scala/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-shell/SKILL.md +97 -74
- moai_adk/templates/.claude/skills/moai-lang-shell/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-shell/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-sql/SKILL.md +98 -74
- moai_adk/templates/.claude/skills/moai-lang-sql/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-sql/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/SKILL.md +1959 -69
- moai_adk/templates/.claude/skills/moai-lang-swift/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-template/SKILL.md +348 -0
- moai_adk/templates/.claude/skills/moai-lang-template/VARIABLES.md +98 -0
- moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +1230 -66
- moai_adk/templates/.claude/skills/moai-lang-typescript/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-typescript/reference.md +34 -0
- moai_adk/templates/.claude/skills/moai-learning-optimizer/SKILL.md +575 -0
- moai_adk/templates/.claude/skills/moai-project-batch-questions/README.md +50 -0
- moai_adk/templates/.claude/skills/moai-project-batch-questions/SKILL.md +304 -0
- moai_adk/templates/.claude/skills/moai-project-batch-questions/examples.md +417 -0
- moai_adk/templates/.claude/skills/moai-project-batch-questions/reference.md +704 -0
- moai_adk/templates/.claude/skills/moai-project-config-manager/README.md +87 -0
- moai_adk/templates/.claude/skills/moai-project-config-manager/SKILL.md +552 -0
- moai_adk/templates/.claude/skills/moai-project-config-manager/examples.md +1109 -0
- moai_adk/templates/.claude/skills/moai-project-config-manager/reference.md +514 -0
- moai_adk/templates/.claude/skills/moai-project-config-manager/validate.py +106 -0
- moai_adk/templates/.claude/skills/moai-project-documentation/README.md +11 -0
- moai_adk/templates/.claude/skills/moai-project-documentation/SKILL.md +622 -0
- moai_adk/templates/.claude/skills/moai-project-documentation/examples.md +20 -0
- moai_adk/templates/.claude/skills/moai-project-documentation/reference.md +12 -0
- moai_adk/templates/.claude/skills/moai-project-language-initializer/README.md +152 -0
- moai_adk/templates/.claude/skills/moai-project-language-initializer/SKILL.md +285 -0
- moai_adk/templates/.claude/skills/moai-project-language-initializer/examples.md +333 -0
- moai_adk/templates/.claude/skills/moai-project-language-initializer/reference.md +386 -0
- moai_adk/templates/.claude/skills/moai-project-template-optimizer/README.md +49 -0
- moai_adk/templates/.claude/skills/moai-project-template-optimizer/SKILL.md +319 -0
- moai_adk/templates/.claude/skills/moai-project-template-optimizer/examples.md +58 -0
- moai_adk/templates/.claude/skills/moai-project-template-optimizer/reference.md +123 -0
- moai_adk/templates/.claude/skills/moai-session-info/SKILL.md +314 -0
- moai_adk/templates/.claude/skills/moai-streaming-ui/SKILL.md +552 -0
- moai_adk/templates/.claude/skills/moai-tag-policy-validator/SKILL.md +570 -0
- moai_adk/templates/.git-hooks/pre-commit +66 -0
- moai_adk/templates/.git-hooks/pre-push +255 -0
- moai_adk/templates/.github/workflows/c-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/cpp-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/csharp-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/dart-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/go-tag-validation.yml +130 -0
- moai_adk/templates/.github/workflows/java-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/javascript-tag-validation.yml +135 -0
- moai_adk/templates/.github/workflows/kotlin-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/moai-gitflow.yml +166 -3
- moai_adk/templates/.github/workflows/moai-release-create.yml +100 -0
- moai_adk/templates/.github/workflows/moai-release-pipeline.yml +188 -0
- moai_adk/templates/.github/workflows/php-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/python-tag-validation.yml +118 -0
- moai_adk/templates/.github/workflows/release.yml +118 -0
- moai_adk/templates/.github/workflows/ruby-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/rust-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/shell-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/spec-issue-sync.yml +338 -0
- moai_adk/templates/.github/workflows/swift-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/tag-report.yml +269 -0
- moai_adk/templates/.github/workflows/tag-validation.yml +186 -0
- moai_adk/templates/.github/workflows/typescript-tag-validation.yml +154 -0
- moai_adk/templates/.mcp.json +31 -0
- moai_adk/templates/.moai/config.json +80 -7
- moai_adk/templates/CLAUDE.md +562 -546
- moai_adk/utils/banner.py +5 -5
- moai_adk/utils/common.py +294 -0
- moai_adk/utils/link_validator.py +235 -0
- moai_adk/utils/logger.py +8 -8
- moai_adk/utils/user_experience.py +451 -0
- moai_adk-0.20.1.dist-info/METADATA +233 -0
- moai_adk-0.20.1.dist-info/RECORD +404 -0
- moai_adk/templates/.claude/hooks/alfred/README.md +0 -230
- moai_adk/templates/.claude/hooks/alfred/alfred_hooks.py +0 -156
- moai_adk/templates/.claude/hooks/alfred/core/__init__.py +0 -85
- moai_adk/templates/.claude/hooks/alfred/handlers/notification.py +0 -25
- moai_adk/templates/.claude/hooks/alfred/handlers/session.py +0 -92
- moai_adk/templates/.claude/hooks/alfred/handlers/tool.py +0 -70
- moai_adk/templates/.claude/hooks/alfred/handlers/user.py +0 -41
- moai_adk/templates/.claude/output-styles/alfred/agentic-coding.md +0 -636
- moai_adk/templates/.claude/output-styles/alfred/moai-adk-learning.md +0 -692
- moai_adk/templates/.claude/output-styles/alfred/study-with-alfred.md +0 -470
- moai_adk/templates/.claude/skills/moai-alfred-debugger-pro/SKILL.md +0 -103
- moai_adk/templates/.claude/skills/moai-alfred-ears-authoring/SKILL.md +0 -103
- moai_adk/templates/.claude/skills/moai-alfred-git-workflow/SKILL.md +0 -95
- moai_adk/templates/.claude/skills/moai-alfred-performance-optimizer/SKILL.md +0 -105
- moai_adk/templates/.claude/skills/moai-alfred-refactoring-coach/SKILL.md +0 -97
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-validation/SKILL.md +0 -97
- moai_adk/templates/.claude/skills/moai-alfred-tag-scanning/SKILL.md +0 -90
- moai_adk/templates/.claude/skills/moai-alfred-trust-validation/SKILL.md +0 -99
- moai_adk/templates/.claude/skills/moai-alfred-tui-survey/SKILL.md +0 -87
- moai_adk/templates/.claude/skills/moai-alfred-tui-survey/examples.md +0 -62
- moai_adk/templates/.claude/skills/moai-claude-code/SKILL.md +0 -94
- moai_adk/templates/.claude/skills/moai-claude-code/examples.md +0 -513
- moai_adk/templates/.claude/skills/moai-claude-code/reference.md +0 -433
- moai_adk/templates/.claude/skills/moai-claude-code/templates/agent-full.md +0 -332
- moai_adk/templates/.claude/skills/moai-claude-code/templates/command-full.md +0 -384
- moai_adk/templates/.claude/skills/moai-claude-code/templates/plugin-full.json +0 -363
- moai_adk/templates/.claude/skills/moai-claude-code/templates/settings-full.json +0 -595
- moai_adk/templates/.claude/skills/moai-claude-code/templates/skill-full.md +0 -496
- moai_adk/templates/.claude/skills/moai-lang-clojure/SKILL.md +0 -100
- moai_adk/templates/.claude/skills/moai-lang-elixir/SKILL.md +0 -99
- moai_adk/templates/.claude/skills/moai-lang-haskell/SKILL.md +0 -100
- moai_adk/templates/.claude/skills/moai-lang-julia/SKILL.md +0 -98
- moai_adk/templates/.claude/skills/moai-lang-lua/SKILL.md +0 -98
- moai_adk/templates/.github/PULL_REQUEST_TEMPLATE.md +0 -69
- moai_adk/templates/.moai/memory/development-guide.md +0 -344
- moai_adk/templates/.moai/memory/gitflow-protection-policy.md +0 -220
- moai_adk/templates/.moai/memory/spec-metadata.md +0 -356
- moai_adk/templates/.moai/project/product.md +0 -161
- moai_adk/templates/.moai/project/structure.md +0 -156
- moai_adk/templates/.moai/project/tech.md +0 -227
- moai_adk/templates/__init__.py +0 -2
- moai_adk-0.4.5.dist-info/METADATA +0 -369
- moai_adk-0.4.5.dist-info/RECORD +0 -152
- {moai_adk-0.4.5.dist-info → moai_adk-0.20.1.dist-info}/WHEEL +0 -0
- {moai_adk-0.4.5.dist-info → moai_adk-0.20.1.dist-info}/entry_points.txt +0 -0
- {moai_adk-0.4.5.dist-info → moai_adk-0.20.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,98 +1,1495 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
2
|
name: moai-lang-java
|
|
4
|
-
|
|
3
|
+
version: 2.0.0
|
|
4
|
+
created: 2025-11-06
|
|
5
|
+
updated: 2025-11-06
|
|
6
|
+
status: active
|
|
7
|
+
description: "Java best practices with Spring ecosystem, enterprise backend development, and JVM optimization for 2025"
|
|
8
|
+
keywords: [java, programming, backend, spring, enterprise, maven, gradle, testing]
|
|
5
9
|
allowed-tools:
|
|
6
10
|
- Read
|
|
11
|
+
- Write
|
|
12
|
+
- Edit
|
|
7
13
|
- Bash
|
|
14
|
+
- WebFetch
|
|
15
|
+
- WebSearch
|
|
8
16
|
---
|
|
9
17
|
|
|
10
|
-
# Java
|
|
18
|
+
# Java Development Mastery
|
|
19
|
+
|
|
20
|
+
**Modern Java Development with 2025 Best Practices**
|
|
21
|
+
|
|
22
|
+
> Comprehensive Java development guidance covering enterprise backend applications, Spring ecosystem integration, and production-ready development using the latest JVM features and frameworks.
|
|
23
|
+
|
|
24
|
+
## What It Does
|
|
25
|
+
|
|
26
|
+
- **Backend API Development**: Spring Boot, Spring Framework applications with modern reactive patterns
|
|
27
|
+
- **Database Integration**: Spring Data JPA, Hibernate, JDBC with query optimization
|
|
28
|
+
- **API Development**: REST, GraphQL, and gRPC services with Spring Security
|
|
29
|
+
- **Testing & Quality**: JUnit 5, Mockito, TestContainers with integration tests
|
|
30
|
+
- **Performance Optimization**: JVM tuning, reactive programming, caching patterns
|
|
31
|
+
- **Production Deployment**: Docker, Kubernetes, CI/CD, monitoring with Spring Actuator
|
|
32
|
+
- **Security Best Practices**: Spring Security, input validation, dependency scanning
|
|
33
|
+
- **Code Quality**: Checkstyle, SpotBugs, pre-commit hooks, static analysis
|
|
34
|
+
|
|
35
|
+
## When to Use
|
|
36
|
+
|
|
37
|
+
### Perfect Scenarios
|
|
38
|
+
- **Building REST APIs and microservices**
|
|
39
|
+
- **Developing enterprise applications and services**
|
|
40
|
+
- **Creating database-driven applications with JPA/Hibernate**
|
|
41
|
+
- **Integrating with enterprise systems and messaging**
|
|
42
|
+
- **Automating backend workflows with Spring Batch**
|
|
43
|
+
- **Deploying scalable server applications on JVM**
|
|
44
|
+
- **Enterprise application development with Spring**
|
|
45
|
+
|
|
46
|
+
### Common Triggers
|
|
47
|
+
- "Create Java backend API"
|
|
48
|
+
- "Set up Spring Boot project"
|
|
49
|
+
- "Optimize Java performance"
|
|
50
|
+
- "Test Java application"
|
|
51
|
+
- "Deploy Spring Boot application"
|
|
52
|
+
- "Java best practices"
|
|
53
|
+
- "Spring Boot microservice"
|
|
54
|
+
|
|
55
|
+
## Tool Version Matrix (2025-11-06)
|
|
56
|
+
|
|
57
|
+
### Core Java
|
|
58
|
+
- **Java**: 23 (current) / 21 (LTS) / 17 (LTS)
|
|
59
|
+
- **Package Managers**: Maven 3.9.x, Gradle 8.8
|
|
60
|
+
- **Build Tools**: Maven Wrapper, Gradle Wrapper
|
|
61
|
+
- **JVM**: OpenJDK, Oracle JDK, Amazon Corretto
|
|
62
|
+
|
|
63
|
+
### Web Frameworks
|
|
64
|
+
- **Spring Boot**: 3.3.x - Production-ready applications
|
|
65
|
+
- **Spring Framework**: 6.2.x - Core application framework
|
|
66
|
+
- **Spring Security**: 6.4.x - Authentication and authorization
|
|
67
|
+
- **Spring Data**: 3.3.x - Database access layer
|
|
68
|
+
- **Micronaut**: 4.7.x - Reactive microservices framework
|
|
69
|
+
- **Quarkus**: 3.17.x - Cloud-native Java framework
|
|
70
|
+
|
|
71
|
+
### Testing Tools
|
|
72
|
+
- **JUnit**: 5.11.x - Primary testing framework
|
|
73
|
+
- **Mockito**: 5.15.x - Mocking framework
|
|
74
|
+
- **TestContainers**: 1.20.x - Integration testing with containers
|
|
75
|
+
- **AssertJ**: 3.26.x - Fluent assertions library
|
|
76
|
+
- **Jacoco**: 0.8.x - Code coverage analysis
|
|
77
|
+
|
|
78
|
+
### Code Quality
|
|
79
|
+
- **Checkstyle**: 10.20.x - Code style checking
|
|
80
|
+
- **SpotBugs**: 4.8.x - Static analysis
|
|
81
|
+
- **PMD**: 7.7.x - Code quality analysis
|
|
82
|
+
- **Error Prone**: 2.36.x - Google's error detection
|
|
83
|
+
|
|
84
|
+
### Database Tools
|
|
85
|
+
- **Hibernate**: 6.6.x - ORM framework
|
|
86
|
+
- **Flyway**: 10.20.x - Database migrations
|
|
87
|
+
- **Liquibase**: 4.29.x - Database schema management
|
|
88
|
+
- **H2**: 2.3.x - In-memory database for testing
|
|
89
|
+
|
|
90
|
+
## Ecosystem Overview
|
|
91
|
+
|
|
92
|
+
### Package Management
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Maven project creation
|
|
96
|
+
mvn archetype:generate -DgroupId=com.example -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
|
|
97
|
+
|
|
98
|
+
# Gradle project creation (modern approach)
|
|
99
|
+
gradle init --type java-application --dsl kotlin --test-framework junit-jupiter --package com.example
|
|
100
|
+
|
|
101
|
+
# Dependency management
|
|
102
|
+
# Maven (pom.xml)
|
|
103
|
+
# Gradle (build.gradle.kts)
|
|
104
|
+
./mvnw spring-boot:run
|
|
105
|
+
./gradlew bootRun
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Project Structure (2025 Best Practice)
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
my-java-project/
|
|
112
|
+
├── build.gradle.kts # Gradle build configuration (Kotlin DSL)
|
|
113
|
+
├── settings.gradle.kts # Gradle settings
|
|
114
|
+
├── gradlew # Gradle wrapper
|
|
115
|
+
├── gradlew.bat # Windows Gradle wrapper
|
|
116
|
+
├── src/
|
|
117
|
+
│ ├── main/
|
|
118
|
+
│ │ ├── java/com/example/
|
|
119
|
+
│ │ │ ├── Application.java # Main application class
|
|
120
|
+
│ │ │ ├── controller/ # REST controllers
|
|
121
|
+
│ │ │ ├── service/ # Business logic
|
|
122
|
+
│ │ │ ├── repository/ # Data access layer
|
|
123
|
+
│ │ │ ├── model/ # Domain models
|
|
124
|
+
│ │ │ ├── config/ # Configuration classes
|
|
125
|
+
│ │ │ └── exception/ # Custom exceptions
|
|
126
|
+
│ │ └── resources/
|
|
127
|
+
│ │ ├── application.yml # Application configuration
|
|
128
|
+
│ │ ├── application-dev.yml # Development profile
|
|
129
|
+
│ │ └── application-prod.yml # Production profile
|
|
130
|
+
│ └── test/
|
|
131
|
+
│ └── java/com/example/
|
|
132
|
+
│ ├── unit/ # Unit tests
|
|
133
|
+
│ ├── integration/ # Integration tests
|
|
134
|
+
│ └── testutil/ # Test utilities
|
|
135
|
+
├── .github/
|
|
136
|
+
│ └── workflows/ # CI/CD pipelines
|
|
137
|
+
├── Dockerfile
|
|
138
|
+
├── docker-compose.yml # Development environment
|
|
139
|
+
├── README.md
|
|
140
|
+
└── .gitignore
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Modern Development Patterns
|
|
144
|
+
|
|
145
|
+
### Modern Java Features (Java 21+)
|
|
146
|
+
|
|
147
|
+
```java
|
|
148
|
+
import java.util.List;
|
|
149
|
+
import java.util.Optional;
|
|
150
|
+
import java.util.concurrent.CompletableFuture;
|
|
151
|
+
import java.util.stream.Stream;
|
|
152
|
+
import java.util.function.Predicate;
|
|
153
|
+
import java.util.record.Record;
|
|
154
|
+
|
|
155
|
+
// Records (immutable data carriers)
|
|
156
|
+
public record User(Long id, String username, String email) {
|
|
157
|
+
// Compact constructor for validation
|
|
158
|
+
public User {
|
|
159
|
+
if (username == null || username.isBlank()) {
|
|
160
|
+
throw new IllegalArgumentException("Username cannot be null or blank");
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Pattern Matching for instanceof
|
|
166
|
+
public void processUser(Object obj) {
|
|
167
|
+
if (obj instanceof User(String username, String email)) {
|
|
168
|
+
System.out.println("User: " + username + ", Email: " + email);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Sealed classes for domain modeling
|
|
173
|
+
public sealed interface Payment permits CreditCardPayment, PayPalPayment {
|
|
174
|
+
BigDecimal getAmount();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
public final record CreditCardPayment(BigDecimal amount, String cardNumber) implements Payment {}
|
|
178
|
+
public final record PayPalPayment(BigDecimal amount, String email) implements Payment {}
|
|
179
|
+
|
|
180
|
+
// Switch expressions with pattern matching
|
|
181
|
+
public String processPayment(Payment payment) {
|
|
182
|
+
return switch (payment) {
|
|
183
|
+
case CreditCardPayment(var amount, var card) ->
|
|
184
|
+
"Processing credit card payment of " + amount;
|
|
185
|
+
case PayPalPayment(var amount, var email) ->
|
|
186
|
+
"Processing PayPal payment of " + amount + " for " + email;
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Virtual Threads (Project Loom)
|
|
191
|
+
import java.util.concurrent.Executors;
|
|
192
|
+
|
|
193
|
+
public class VirtualThreadExample {
|
|
194
|
+
public static void main(String[] args) {
|
|
195
|
+
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
|
|
196
|
+
// Create thousands of virtual threads
|
|
197
|
+
List<CompletableFuture<String>> futures = IntStream.range(0, 10_000)
|
|
198
|
+
.mapToObj(i -> CompletableFuture.supplyAsync(() -> {
|
|
199
|
+
// Simulate I/O-bound operation
|
|
200
|
+
try {
|
|
201
|
+
Thread.sleep(100);
|
|
202
|
+
} catch (InterruptedException e) {
|
|
203
|
+
Thread.currentThread().interrupt();
|
|
204
|
+
}
|
|
205
|
+
return "Task " + i + " completed";
|
|
206
|
+
}, executor))
|
|
207
|
+
.toList();
|
|
208
|
+
|
|
209
|
+
// Wait for all to complete
|
|
210
|
+
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Scoped Values (Structured concurrency)
|
|
216
|
+
import java.util.concurrent.ScopedValue;
|
|
217
|
+
|
|
218
|
+
public final class Context {
|
|
219
|
+
private static final ScopedValue<String> USER_ID = ScopedValue.newInstance();
|
|
220
|
+
|
|
221
|
+
public static void main(String[] args) {
|
|
222
|
+
// Bind value in a structured scope
|
|
223
|
+
ScopedValue.where(USER_ID, "user123").run(() -> {
|
|
224
|
+
// All code in this scope can access USER_ID.get()
|
|
225
|
+
processRequest();
|
|
226
|
+
handleData();
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Spring Boot Reactive Patterns
|
|
233
|
+
|
|
234
|
+
```java
|
|
235
|
+
import org.springframework.boot.SpringApplication;
|
|
236
|
+
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
237
|
+
import org.springframework.context.annotation.Bean;
|
|
238
|
+
import org.springframework.web.reactive.function.server.RouterFunction;
|
|
239
|
+
import org.springframework.web.reactive.function.server.ServerResponse;
|
|
240
|
+
import org.springframework.web.reactive.function.server.RouterFunctions;
|
|
241
|
+
import reactor.core.publisher.Flux;
|
|
242
|
+
import reactor.core.publisher.Mono;
|
|
243
|
+
import org.springframework.web.reactive.function.server.ServerRequest;
|
|
244
|
+
|
|
245
|
+
@SpringBootApplication
|
|
246
|
+
public class ReactiveApplication {
|
|
247
|
+
|
|
248
|
+
@Bean
|
|
249
|
+
public RouterFunction<ServerResponse> userRoutes(UserHandler userHandler) {
|
|
250
|
+
return RouterFunctions.route()
|
|
251
|
+
.GET("/api/users", userHandler::getAllUsers)
|
|
252
|
+
.GET("/api/users/{id}", userHandler::getUserById)
|
|
253
|
+
.POST("/api/users", userHandler::createUser)
|
|
254
|
+
.PUT("/api/users/{id}", userHandler::updateUser)
|
|
255
|
+
.DELETE("/api/users/{id}", userHandler::deleteUser)
|
|
256
|
+
.build();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
public static void main(String[] args) {
|
|
260
|
+
SpringApplication.run(ReactiveApplication.class, args);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Functional endpoint handler
|
|
265
|
+
@Component
|
|
266
|
+
public class UserHandler {
|
|
267
|
+
private final UserRepository userRepository;
|
|
268
|
+
|
|
269
|
+
public UserHandler(UserRepository userRepository) {
|
|
270
|
+
this.userRepository = userRepository;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
public Mono<ServerResponse> getAllUsers(ServerRequest request) {
|
|
274
|
+
Flux<User> users = userRepository.findAll();
|
|
275
|
+
return ServerResponse.ok().body(users, User.class);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
public Mono<ServerResponse> getUserById(ServerRequest request) {
|
|
279
|
+
String id = request.pathVariable("id");
|
|
280
|
+
return userRepository.findById(Long.valueOf(id))
|
|
281
|
+
.flatMap(user -> ServerResponse.ok().bodyValue(user))
|
|
282
|
+
.switchIfEmpty(ServerResponse.notFound().build());
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
public Mono<ServerResponse> createUser(ServerRequest request) {
|
|
286
|
+
Mono<User> userMono = request.bodyToMono(User.class);
|
|
287
|
+
return userMono.flatMap(user ->
|
|
288
|
+
userRepository.save(user)
|
|
289
|
+
.flatMap(savedUser -> ServerResponse.created(URI.create("/api/users/" + savedUser.id()))
|
|
290
|
+
.bodyValue(savedUser))
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Reactive repository
|
|
296
|
+
@Repository
|
|
297
|
+
public interface ReactiveUserRepository extends ReactiveCrudRepository<User, Long> {
|
|
298
|
+
Flux<User> findByUsername(String username);
|
|
299
|
+
Mono<User> findByEmail(String email);
|
|
300
|
+
@Query("SELECT * FROM users WHERE active = true")
|
|
301
|
+
Flux<User> findActiveUsers();
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Modern Testing Patterns
|
|
306
|
+
|
|
307
|
+
```java
|
|
308
|
+
import org.junit.jupiter.api.Test;
|
|
309
|
+
import org.junit.jupiter.api.DisplayName;
|
|
310
|
+
import org.junit.jupiter.api.Nested;
|
|
311
|
+
import org.junit.jupiter.params.ParameterizedTest;
|
|
312
|
+
import org.junit.jupiter.params.provider.ValueSource;
|
|
313
|
+
import org.junit.jupiter.params.provider.CsvSource;
|
|
314
|
+
import org.junit.jupiter.api.extension.ExtendWith;
|
|
315
|
+
import org.mockito.Mock;
|
|
316
|
+
import org.mockito.junit.jupiter.MockitoExtension;
|
|
317
|
+
import org.testcontainers.containers.PostgreSQLContainer;
|
|
318
|
+
import org.testcontainers.junit.jupiter.Container;
|
|
319
|
+
import org.testcontainers.junit.jupiter.Testcontainers;
|
|
320
|
+
import static org.assertj.core.api.Assertions.*;
|
|
321
|
+
import static org.mockito.Mockito.*;
|
|
322
|
+
|
|
323
|
+
@ExtendWith(MockitoExtension.class)
|
|
324
|
+
@DisplayName("UserService Tests")
|
|
325
|
+
class UserServiceTest {
|
|
326
|
+
|
|
327
|
+
@Mock
|
|
328
|
+
private UserRepository userRepository;
|
|
329
|
+
|
|
330
|
+
@Mock
|
|
331
|
+
private EmailService emailService;
|
|
332
|
+
|
|
333
|
+
private UserService userService;
|
|
334
|
+
|
|
335
|
+
@BeforeEach
|
|
336
|
+
void setUp() {
|
|
337
|
+
userService = new UserService(userRepository, emailService);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
@Nested
|
|
341
|
+
@DisplayName("Create User Tests")
|
|
342
|
+
class CreateUserTests {
|
|
343
|
+
|
|
344
|
+
@Test
|
|
345
|
+
@DisplayName("Should create user successfully with valid data")
|
|
346
|
+
void shouldCreateUserSuccessfully() {
|
|
347
|
+
// Given
|
|
348
|
+
UserCreateDto dto = new UserCreateDto("testuser", "test@example.com");
|
|
349
|
+
User expectedUser = new User(1L, "testuser", "test@example.com");
|
|
350
|
+
|
|
351
|
+
when(userRepository.save(any(User.class))).thenReturn(Mono.just(expectedUser));
|
|
352
|
+
when(userRepository.existsByUsername("testuser")).thenReturn(Mono.just(false));
|
|
353
|
+
when(userRepository.existsByEmail("test@example.com")).thenReturn(Mono.just(false));
|
|
354
|
+
|
|
355
|
+
// When
|
|
356
|
+
Mono<User> result = userService.createUser(dto);
|
|
357
|
+
|
|
358
|
+
// Then
|
|
359
|
+
StepVerifier.create(result)
|
|
360
|
+
.assertNext(user -> {
|
|
361
|
+
assertThat(user.id()).isEqualTo(1L);
|
|
362
|
+
assertThat(user.username()).isEqualTo("testuser");
|
|
363
|
+
assertThat(user.email()).isEqualTo("test@example.com");
|
|
364
|
+
})
|
|
365
|
+
.verifyComplete();
|
|
366
|
+
|
|
367
|
+
verify(emailService).sendWelcomeEmail(expectedUser);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
@ParameterizedTest
|
|
371
|
+
@ValueSource(strings = {"", "ab", "a".repeat(51)})
|
|
372
|
+
@DisplayName("Should reject invalid usernames")
|
|
373
|
+
void shouldRejectInvalidUsernames(String invalidUsername) {
|
|
374
|
+
// Given
|
|
375
|
+
UserCreateDto dto = new UserCreateDto(invalidUsername, "test@example.com");
|
|
376
|
+
|
|
377
|
+
// When & Then
|
|
378
|
+
assertThatThrownBy(() -> userService.createUser(dto))
|
|
379
|
+
.isInstanceOf(ValidationException.class)
|
|
380
|
+
.hasMessageContaining("Invalid username");
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Integration testing with TestContainers
|
|
386
|
+
@Testcontainers
|
|
387
|
+
@SpringBootTest
|
|
388
|
+
class UserRepositoryIntegrationTest {
|
|
389
|
+
|
|
390
|
+
@Container
|
|
391
|
+
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16-alpine")
|
|
392
|
+
.withDatabaseName("testdb")
|
|
393
|
+
.withUsername("test")
|
|
394
|
+
.withPassword("test");
|
|
395
|
+
|
|
396
|
+
@DynamicPropertySource
|
|
397
|
+
static void postgresProperties(DynamicPropertyRegistry registry) {
|
|
398
|
+
registry.add("spring.datasource.url", postgres::getJdbcUrl);
|
|
399
|
+
registry.add("spring.datasource.username", postgres::getUsername);
|
|
400
|
+
registry.add("spring.datasource.password", postgres::getPassword);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
@Autowired
|
|
404
|
+
private UserRepository userRepository;
|
|
405
|
+
|
|
406
|
+
@Test
|
|
407
|
+
void shouldSaveAndRetrieveUser() {
|
|
408
|
+
// Given
|
|
409
|
+
User user = new User(null, "testuser", "test@example.com");
|
|
410
|
+
|
|
411
|
+
// When
|
|
412
|
+
User saved = userRepository.save(user);
|
|
413
|
+
|
|
414
|
+
// Then
|
|
415
|
+
assertThat(saved.id()).isNotNull();
|
|
416
|
+
|
|
417
|
+
Optional<User> retrieved = userRepository.findById(saved.id());
|
|
418
|
+
assertThat(retrieved).isPresent();
|
|
419
|
+
assertThat(retrieved.get().username()).isEqualTo("testuser");
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
## Performance Considerations
|
|
425
|
+
|
|
426
|
+
### JVM Performance Tuning
|
|
427
|
+
|
|
428
|
+
```java
|
|
429
|
+
// Efficient collections usage
|
|
430
|
+
public class CollectionOptimization {
|
|
431
|
+
|
|
432
|
+
// Use appropriate collection types
|
|
433
|
+
private final Map<String, User> userCache = new ConcurrentHashMap<>(); // Thread-safe
|
|
434
|
+
private final List<String> recentItems = new CopyOnWriteArrayList<>(); // Read-heavy
|
|
435
|
+
private final Queue<Task> taskQueue = new ConcurrentLinkedQueue<>(); // Lock-free
|
|
436
|
+
|
|
437
|
+
// Use primitive collections for performance-critical code
|
|
438
|
+
private final Int2ObjectHashMap<User> userById = new Int2ObjectHashMap<>();
|
|
439
|
+
|
|
440
|
+
// Lazy initialization
|
|
441
|
+
private volatile ExpensiveResource resource;
|
|
442
|
+
|
|
443
|
+
public ExpensiveResource getResource() {
|
|
444
|
+
ExpensiveResource result = resource;
|
|
445
|
+
if (result == null) {
|
|
446
|
+
synchronized (this) {
|
|
447
|
+
result = resource;
|
|
448
|
+
if (result == null) {
|
|
449
|
+
resource = result = new ExpensiveResource();
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return result;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Memory-efficient data processing
|
|
458
|
+
public class StreamOptimization {
|
|
459
|
+
|
|
460
|
+
public List<UserDto> processUsersLargeDataset() {
|
|
461
|
+
try (Stream<User> userStream = Files.lines(Paths.get("users.csv"))
|
|
462
|
+
.skip(1) // Skip header
|
|
463
|
+
.parallel() // Parallel processing for large files
|
|
464
|
+
.map(this::parseUser)
|
|
465
|
+
.filter(Objects::nonNull)
|
|
466
|
+
.filter(user -> user.isActive())) {
|
|
467
|
+
|
|
468
|
+
return userStream
|
|
469
|
+
.map(this::convertToDto)
|
|
470
|
+
.collect(Collectors.toList());
|
|
471
|
+
} catch (IOException e) {
|
|
472
|
+
throw new RuntimeException("Failed to process users", e);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Use primitive streams for numeric operations
|
|
477
|
+
public IntSummaryStatistics getAgeStatistics(List<User> users) {
|
|
478
|
+
return users.stream()
|
|
479
|
+
.mapToInt(User::age)
|
|
480
|
+
.summaryStatistics();
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Connection pooling and resource management
|
|
485
|
+
@Configuration
|
|
486
|
+
public class DatabaseConfig {
|
|
487
|
+
|
|
488
|
+
@Bean
|
|
489
|
+
@Primary
|
|
490
|
+
public DataSource dataSource() {
|
|
491
|
+
HikariConfig config = new HikariConfig();
|
|
492
|
+
config.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb");
|
|
493
|
+
config.setUsername("user");
|
|
494
|
+
config.setPassword("password");
|
|
495
|
+
config.setMaximumPoolSize(20);
|
|
496
|
+
config.setMinimumIdle(5);
|
|
497
|
+
config.setConnectionTimeout(30000);
|
|
498
|
+
config.setIdleTimeout(600000);
|
|
499
|
+
config.setMaxLifetime(1800000);
|
|
500
|
+
config.setLeakDetectionThreshold(60000);
|
|
501
|
+
return new HikariDataSource(config);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### Reactive Programming Performance
|
|
507
|
+
|
|
508
|
+
```java
|
|
509
|
+
// Backpressure handling
|
|
510
|
+
@Service
|
|
511
|
+
public class DataProcessingService {
|
|
512
|
+
|
|
513
|
+
public Flux<ProcessedData> processDataWithBackpressure(Flux<RawData> rawData) {
|
|
514
|
+
return rawData
|
|
515
|
+
.onBackpressureBuffer(1000, // Buffer up to 1000 items
|
|
516
|
+
rawData::log, // Log dropped items
|
|
517
|
+
BufferOverflowStrategy.DROP_OLDEST)
|
|
518
|
+
.publishOn(Schedulers.boundedElastic(), 256) // Limit concurrency
|
|
519
|
+
.flatMap(this::processItem, 10) // Process 10 items in parallel
|
|
520
|
+
.doOnError(error -> log.error("Processing error", error))
|
|
521
|
+
.retryWhen(Retry.backoff(3, Duration.ofSeconds(1))
|
|
522
|
+
.maxBackoff(Duration.ofSeconds(10))
|
|
523
|
+
.jitter(0.5))
|
|
524
|
+
.timeout(Duration.ofMinutes(5));
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Efficient batching
|
|
528
|
+
public Flux<BatchResult> processInBatches(Flux<DataItem> items, int batchSize) {
|
|
529
|
+
return items
|
|
530
|
+
.window(batchSize)
|
|
531
|
+
.flatMap(window -> window.collectList())
|
|
532
|
+
.flatMap(this::processBatch);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Caching strategies
|
|
537
|
+
@Service
|
|
538
|
+
public class CacheService {
|
|
539
|
+
|
|
540
|
+
private final Cache<String, User> userCache = Caffeine.newBuilder()
|
|
541
|
+
.maximumSize(10_000)
|
|
542
|
+
.expireAfterWrite(Duration.ofMinutes(30))
|
|
543
|
+
.refreshAfterWrite(Duration.ofMinutes(10))
|
|
544
|
+
.recordStats()
|
|
545
|
+
.build();
|
|
546
|
+
|
|
547
|
+
@Cacheable(value = "users", key = "#id")
|
|
548
|
+
public User getUserById(Long id) {
|
|
549
|
+
return userCache.get(id.toString(), key -> userRepository.findById(id)
|
|
550
|
+
.orElseThrow(() -> new UserNotFoundException(id)));
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// Reactive cache with Spring Boot
|
|
554
|
+
@Bean
|
|
555
|
+
public ReactiveCacheManager cacheManager() {
|
|
556
|
+
return new ReactiveCaffeineCacheManager(
|
|
557
|
+
Caffeine.newBuilder()
|
|
558
|
+
.maximumSize(1000)
|
|
559
|
+
.expireAfterWrite(Duration.ofMinutes(10))
|
|
560
|
+
.build()
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
### Profiling and Monitoring
|
|
567
|
+
|
|
568
|
+
```bash
|
|
569
|
+
# JVM monitoring with jcmd
|
|
570
|
+
jcmd <pid> VM.flags
|
|
571
|
+
jcmd <pid> GC.heap_info
|
|
572
|
+
jcmd <pid> Thread.print
|
|
573
|
+
jcmd <pid> GC.class_histogram
|
|
574
|
+
|
|
575
|
+
# Java Flight Recorder
|
|
576
|
+
java -XX:StartFlightRecording=duration=60s,filename=myrecording.jfr -jar app.jar
|
|
577
|
+
|
|
578
|
+
# JDK Mission Control (visual monitoring)
|
|
579
|
+
jmc
|
|
580
|
+
|
|
581
|
+
# Memory analysis with jmap and jhat
|
|
582
|
+
jmap -dump:format=b,file=heapdump.hprof <pid>
|
|
583
|
+
jhat heapdump.hprof
|
|
584
|
+
|
|
585
|
+
# Performance profiling with async-profiler
|
|
586
|
+
./profiler.sh -d 30 -f profile.svg <pid>
|
|
587
|
+
|
|
588
|
+
# Thread dump analysis
|
|
589
|
+
jstack <pid> > thread_dump.txt
|
|
590
|
+
# or with jcmd
|
|
591
|
+
jcmd <pid> Thread.print > thread_dump.txt
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
## Testing Strategy
|
|
595
|
+
|
|
596
|
+
### Gradle Configuration (build.gradle.kts)
|
|
597
|
+
|
|
598
|
+
```kotlin
|
|
599
|
+
plugins {
|
|
600
|
+
application
|
|
601
|
+
jacoco
|
|
602
|
+
checkstyle
|
|
603
|
+
pmd
|
|
604
|
+
id("org.springframework.boot") version "3.3.0"
|
|
605
|
+
id("io.spring.dependency-management") version "1.1.5"
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
group = "com.example"
|
|
609
|
+
version = "0.1.0"
|
|
610
|
+
|
|
611
|
+
java {
|
|
612
|
+
toolchain {
|
|
613
|
+
languageVersion = JavaLanguageVersion.of(21)
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
repositories {
|
|
618
|
+
mavenCentral()
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
dependencies {
|
|
622
|
+
// Spring Boot starters
|
|
623
|
+
implementation("org.springframework.boot:spring-boot-starter-webflux")
|
|
624
|
+
implementation("org.springframework.boot:spring-boot-starter-data-r2dbc")
|
|
625
|
+
implementation("org.springframework.boot:spring-boot-starter-actuator")
|
|
626
|
+
implementation("org.springframework.boot:spring-boot-starter-validation")
|
|
627
|
+
|
|
628
|
+
// Database
|
|
629
|
+
implementation("org.postgresql:r2dbc-postgresql")
|
|
630
|
+
implementation("org.flywaydb:flyway-core")
|
|
631
|
+
implementation("org.springframework:spring-jdbc")
|
|
632
|
+
|
|
633
|
+
// Utility libraries
|
|
634
|
+
implementation("com.fasterxml.jackson.core:jackson-databind")
|
|
635
|
+
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
|
|
636
|
+
implementation("org.apache.commons:commons-lang3")
|
|
637
|
+
|
|
638
|
+
// Development tools
|
|
639
|
+
compileOnly("org.projectlombok:lombok")
|
|
640
|
+
annotationProcessor("org.projectlombok:lombok")
|
|
641
|
+
|
|
642
|
+
// Testing
|
|
643
|
+
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
|
644
|
+
testImplementation("org.springframework.security:spring-security-test")
|
|
645
|
+
testImplementation("io.projectreactor:reactor-test")
|
|
646
|
+
testImplementation("org.testcontainers:junit-jupiter")
|
|
647
|
+
testImplementation("org.testcontainers:postgresql")
|
|
648
|
+
testImplementation("org.testcontainers:r2dbc")
|
|
649
|
+
|
|
650
|
+
// Code quality
|
|
651
|
+
checkstyle("com.github.sevntu-checkstyle:sevntu-checks:1.44.1")
|
|
652
|
+
}
|
|
11
653
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
654
|
+
tasks.test {
|
|
655
|
+
useJUnitPlatform()
|
|
656
|
+
|
|
657
|
+
testLogging {
|
|
658
|
+
events("passed", "skipped", "failed")
|
|
659
|
+
showExceptions = true
|
|
660
|
+
showCauses = true
|
|
661
|
+
showStackTraces = true
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// Parallel test execution
|
|
665
|
+
maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).takeIf { it > 0 } ?: 1
|
|
666
|
+
}
|
|
19
667
|
|
|
20
|
-
|
|
668
|
+
// JaCoCo configuration for code coverage
|
|
669
|
+
jacoco {
|
|
670
|
+
toolVersion = "0.8.12"
|
|
671
|
+
}
|
|
21
672
|
|
|
22
|
-
|
|
673
|
+
tasks.jacocoTestReport {
|
|
674
|
+
dependsOn(tasks.test)
|
|
675
|
+
reports {
|
|
676
|
+
xml.required.set(true)
|
|
677
|
+
html.required.set(true)
|
|
678
|
+
csv.required.set(false)
|
|
679
|
+
}
|
|
680
|
+
}
|
|
23
681
|
|
|
24
|
-
|
|
682
|
+
tasks.jacocoTestCoverageVerification {
|
|
683
|
+
violationRules {
|
|
684
|
+
rule {
|
|
685
|
+
limit {
|
|
686
|
+
minimum = "0.80".toBigDecimal() // 80% coverage minimum
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
25
691
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
692
|
+
// Checkstyle configuration
|
|
693
|
+
tasks.checkstyleMain {
|
|
694
|
+
source = fileTree("src/main/java")
|
|
695
|
+
}
|
|
30
696
|
|
|
31
|
-
|
|
697
|
+
tasks.checkstyleTest {
|
|
698
|
+
source = fileTree("src/test/java")
|
|
699
|
+
}
|
|
32
700
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
701
|
+
// PMD configuration
|
|
702
|
+
pmd {
|
|
703
|
+
ruleSets = listOf(
|
|
704
|
+
"category/java/bestpractices.xml",
|
|
705
|
+
"category/java/codesize.xml",
|
|
706
|
+
"category/java/design.xml",
|
|
707
|
+
"category/java/performance.xml",
|
|
708
|
+
"category/java/security.xml"
|
|
709
|
+
)
|
|
710
|
+
}
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
### Modern Testing Patterns
|
|
714
|
+
|
|
715
|
+
```java
|
|
716
|
+
// Test slices for focused testing
|
|
717
|
+
@WebFluxTest(UserController.class)
|
|
718
|
+
@Import({UserService.class, SecurityConfig.class})
|
|
719
|
+
class UserControllerTest {
|
|
720
|
+
|
|
721
|
+
@Autowired
|
|
722
|
+
private WebTestClient webTestClient;
|
|
723
|
+
|
|
724
|
+
@MockBean
|
|
725
|
+
private UserService userService;
|
|
726
|
+
|
|
727
|
+
@Test
|
|
728
|
+
void shouldReturnUserWhenFound() {
|
|
729
|
+
// Given
|
|
730
|
+
User user = new User(1L, "testuser", "test@example.com");
|
|
731
|
+
when(userService.getUserById(1L)).thenReturn(Mono.just(user));
|
|
732
|
+
|
|
733
|
+
// When & Then
|
|
734
|
+
webTestClient.get()
|
|
735
|
+
.uri("/api/users/1")
|
|
736
|
+
.accept(MediaType.APPLICATION_JSON)
|
|
737
|
+
.exchange()
|
|
738
|
+
.expectStatus().isOk()
|
|
739
|
+
.expectBody()
|
|
740
|
+
.jsonPath("$.id").isEqualTo(1)
|
|
741
|
+
.jsonPath("$.username").isEqualTo("testuser");
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// Property-based testing with jqwik
|
|
746
|
+
@Property
|
|
747
|
+
void stringReversalIsInverse(@ForAll("validStrings") String str) {
|
|
748
|
+
String reversed = new StringBuilder(str).reverse().toString();
|
|
749
|
+
String doubleReversed = new StringBuilder(reversed).reverse().toString();
|
|
750
|
+
assertThat(doubleReversed).isEqualTo(str);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
@Provide
|
|
754
|
+
Arbitrary<String> validStrings() {
|
|
755
|
+
return Arbitraries.strings()
|
|
756
|
+
.withChars("abc")
|
|
757
|
+
.ofMinLength(1)
|
|
758
|
+
.ofMaxLength(100);
|
|
759
|
+
}
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
## Security Best Practices
|
|
763
|
+
|
|
764
|
+
### Input Validation and Sanitization
|
|
38
765
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
766
|
+
```java
|
|
767
|
+
import jakarta.validation.constraints.*;
|
|
768
|
+
import org.hibernate.validator.constraints.URL;
|
|
769
|
+
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
|
770
|
+
import org.springframework.security.crypto.password.PasswordEncoder;
|
|
43
771
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
772
|
+
// DTO with validation annotations
|
|
773
|
+
public record UserCreateDto(
|
|
774
|
+
|
|
775
|
+
@NotBlank(message = "Username is required")
|
|
776
|
+
@Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters")
|
|
777
|
+
@Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "Username can only contain letters, numbers, and underscores")
|
|
778
|
+
String username,
|
|
779
|
+
|
|
780
|
+
@NotBlank(message = "Email is required")
|
|
781
|
+
@Email(message = "Invalid email format")
|
|
782
|
+
@Size(max = 100, message = "Email must be less than 100 characters")
|
|
783
|
+
String email,
|
|
784
|
+
|
|
785
|
+
@NotBlank(message = "Password is required")
|
|
786
|
+
@Size(min = 8, max = 128, message = "Password must be between 8 and 128 characters")
|
|
787
|
+
@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]",
|
|
788
|
+
message = "Password must contain at least one lowercase letter, one uppercase letter, one digit, and one special character")
|
|
789
|
+
String password
|
|
790
|
+
) {
|
|
791
|
+
|
|
792
|
+
// Custom validation method
|
|
793
|
+
public UserCreateDto {
|
|
794
|
+
if (email != null && email.toLowerCase().contains("@admin.com")) {
|
|
795
|
+
throw new IllegalArgumentException("Admin email domains are not allowed");
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
48
799
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
800
|
+
// Password encryption service
|
|
801
|
+
@Service
|
|
802
|
+
public class PasswordService {
|
|
803
|
+
|
|
804
|
+
private final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(12);
|
|
805
|
+
|
|
806
|
+
public String encodePassword(String rawPassword) {
|
|
807
|
+
return passwordEncoder.encode(rawPassword);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
public boolean matches(String rawPassword, String encodedPassword) {
|
|
811
|
+
return passwordEncoder.matches(rawPassword, encodedPassword);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// Input sanitization for XSS prevention
|
|
816
|
+
@Component
|
|
817
|
+
public class InputSanitizer {
|
|
818
|
+
|
|
819
|
+
private static final org.owasp.encoder.Encoder encoder = org.owasp.encoder.Encoder.getInstance();
|
|
820
|
+
|
|
821
|
+
public String sanitizeForHtml(String input) {
|
|
822
|
+
return input == null ? null : encoder.encodeForHTML(input);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
public String sanitizeForJavaScript(String input) {
|
|
826
|
+
return input == null ? null : encoder.encodeForJavaScript(input);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
```
|
|
54
830
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
831
|
+
### Authentication and Authorization
|
|
832
|
+
|
|
833
|
+
```java
|
|
834
|
+
import org.springframework.context.annotation.Bean;
|
|
835
|
+
import org.springframework.context.annotation.Configuration;
|
|
836
|
+
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
|
837
|
+
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
|
838
|
+
import org.springframework.security.web.server.SecurityWebFilterChain;
|
|
839
|
+
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
|
|
840
|
+
|
|
841
|
+
@Configuration
|
|
842
|
+
@EnableWebFluxSecurity
|
|
843
|
+
public class SecurityConfig {
|
|
844
|
+
|
|
845
|
+
@Bean
|
|
846
|
+
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
|
|
847
|
+
return http
|
|
848
|
+
.csrf(ServerHttpSecurity.CsrfSpec::disable)
|
|
849
|
+
.authorizeExchange(exchanges -> exchanges
|
|
850
|
+
.pathMatchers("/api/auth/**").permitAll()
|
|
851
|
+
.pathMatchers("/api/public/**").permitAll()
|
|
852
|
+
.pathMatchers("/actuator/health").permitAll()
|
|
853
|
+
.pathMatchers("/api/admin/**").hasRole("ADMIN")
|
|
854
|
+
.pathMatchers("/api/users/**").hasAnyRole("USER", "ADMIN")
|
|
855
|
+
.anyExchange().authenticated()
|
|
856
|
+
)
|
|
857
|
+
.oauth2ResourceServer(oauth2 -> oauth2
|
|
858
|
+
.jwt(jwt -> jwt.jwtDecoder(jwtDecoder()))
|
|
859
|
+
)
|
|
860
|
+
.addFilterAt(jwtAuthenticationFilter(), AuthenticationWebFilter.class)
|
|
861
|
+
.build();
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
@Bean
|
|
865
|
+
public JwtDecoder jwtDecoder() {
|
|
866
|
+
return NimbusJwtDecoder.withJwkSetUri("https://your-auth-server/.well-known/jwks.json")
|
|
867
|
+
.build();
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
@Bean
|
|
871
|
+
public ReactiveAuthenticationManager authenticationManager() {
|
|
872
|
+
return new UserRepositoryReactiveAuthenticationManager(userRepository, passwordService);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
// Method-level security
|
|
877
|
+
@Service
|
|
878
|
+
public class DocumentService {
|
|
879
|
+
|
|
880
|
+
@PreAuthorize("hasRole('USER') and @documentSecurity.canRead(#documentId, authentication.name)")
|
|
881
|
+
public Mono<Document> getDocument(Long documentId) {
|
|
882
|
+
return documentRepository.findById(documentId);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
@PreAuthorize("hasRole('ADMIN') or @documentSecurity.isOwner(#documentId, authentication.name)")
|
|
886
|
+
public Mono<Document> updateDocument(Long documentId, DocumentUpdateDto updateDto) {
|
|
887
|
+
return documentRepository.findById(documentId)
|
|
888
|
+
.filter(document -> canUpdate(document, AuthenticationHolder.getCurrentUsername()))
|
|
889
|
+
.flatMap(document -> {
|
|
890
|
+
document.update(updateDto);
|
|
891
|
+
return documentRepository.save(document);
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
@PostAuthorize("returnObject.isPresent() && returnObject.get().isPublic()")
|
|
896
|
+
public Optional<Document> getPublicDocument(Long documentId) {
|
|
897
|
+
return documentRepository.findById(documentId);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
### Dependency Security Scanning
|
|
60
903
|
|
|
61
|
-
## Examples
|
|
62
904
|
```bash
|
|
63
|
-
|
|
905
|
+
# Add to build.gradle.kts
|
|
906
|
+
plugins {
|
|
907
|
+
id("org.owasp.dependencycheck") version "10.0.3"
|
|
908
|
+
id("com.github.ben-manes.versions") version "0.51.0"
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// OWASP Dependency Check
|
|
912
|
+
dependencyCheck {
|
|
913
|
+
failBuildOnCVSS = 7.0f
|
|
914
|
+
suppressionFile = "dependency-check-suppressions.xml"
|
|
915
|
+
analyzers {
|
|
916
|
+
experimentalEnabled = false
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// Dependency version updates
|
|
921
|
+
tasks.named("dependencyUpdates") {
|
|
922
|
+
checkForGradleUpdate = true
|
|
923
|
+
outputFormatter = "json"
|
|
924
|
+
outputDir = "build/dependencyUpdates"
|
|
925
|
+
reportfileName = "report"
|
|
926
|
+
}
|
|
927
|
+
```
|
|
928
|
+
|
|
929
|
+
## Integration Patterns
|
|
930
|
+
|
|
931
|
+
### Database Integration with R2DBC
|
|
932
|
+
|
|
933
|
+
```java
|
|
934
|
+
import org.springframework.data.annotation.Id;
|
|
935
|
+
import org.springframework.data.relational.core.mapping.Table;
|
|
936
|
+
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
|
|
937
|
+
import org.springframework.r2dbc.core.DatabaseClient;
|
|
938
|
+
import reactor.core.publisher.Flux;
|
|
939
|
+
import reactor.core.publisher.Mono;
|
|
940
|
+
|
|
941
|
+
@Table("users")
|
|
942
|
+
public record User(
|
|
943
|
+
@Id Long id,
|
|
944
|
+
String username,
|
|
945
|
+
String email,
|
|
946
|
+
Instant createdAt,
|
|
947
|
+
boolean active
|
|
948
|
+
) {
|
|
949
|
+
public User {
|
|
950
|
+
if (createdAt == null) {
|
|
951
|
+
createdAt = Instant.now();
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
@Repository
|
|
957
|
+
public interface UserRepository extends ReactiveCrudRepository<User, Long> {
|
|
958
|
+
Flux<User> findByUsername(String username);
|
|
959
|
+
Flux<User> findByEmail(String email);
|
|
960
|
+
Flux<User> findByActiveTrue();
|
|
961
|
+
|
|
962
|
+
@Query("SELECT * FROM users WHERE username LIKE :pattern")
|
|
963
|
+
Flux<User> findByUsernamePattern(String pattern);
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
// Custom repository with complex queries
|
|
967
|
+
@Repository
|
|
968
|
+
public class CustomUserRepository {
|
|
969
|
+
|
|
970
|
+
private final DatabaseClient databaseClient;
|
|
971
|
+
|
|
972
|
+
public CustomUserRepository(DatabaseClient databaseClient) {
|
|
973
|
+
this.databaseClient = databaseClient;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
public Flux<UserSummary> findUserSummaries() {
|
|
977
|
+
return databaseClient.sql("SELECT id, username, email FROM users WHERE active = true")
|
|
978
|
+
.map((row, metadata) -> new UserSummary(
|
|
979
|
+
row.get("id", Long.class),
|
|
980
|
+
row.get("username", String.class),
|
|
981
|
+
row.get("email", String.class)
|
|
982
|
+
))
|
|
983
|
+
.all();
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
public Mono<Integer> bulkUpdateStatus(List<Long> userIds, boolean active) {
|
|
987
|
+
String placeholders = userIds.stream()
|
|
988
|
+
.map(id -> "?")
|
|
989
|
+
.collect(Collectors.joining(","));
|
|
990
|
+
|
|
991
|
+
return databaseClient.sql(
|
|
992
|
+
"UPDATE users SET active = :active WHERE id IN (" + placeholders + ")"
|
|
993
|
+
)
|
|
994
|
+
.bind("active", active)
|
|
995
|
+
.bind(0, userIds.get(0))
|
|
996
|
+
// ... bind remaining parameters
|
|
997
|
+
.fetch()
|
|
998
|
+
.rowsUpdated();
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
64
1001
|
```
|
|
65
1002
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
1003
|
+
### Message Queue Integration with RabbitMQ
|
|
1004
|
+
|
|
1005
|
+
```java
|
|
1006
|
+
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
|
1007
|
+
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
|
1008
|
+
import org.springframework.context.annotation.Bean;
|
|
1009
|
+
import org.springframework.context.annotation.Configuration;
|
|
1010
|
+
import org.springframework.amqp.core.*;
|
|
1011
|
+
|
|
1012
|
+
@Configuration
|
|
1013
|
+
public class RabbitMQConfig {
|
|
1014
|
+
|
|
1015
|
+
public static final String USER_QUEUE = "user.queue";
|
|
1016
|
+
public static final String USER_EXCHANGE = "user.exchange";
|
|
1017
|
+
public static final String USER_ROUTING_KEY = "user.created";
|
|
1018
|
+
|
|
1019
|
+
@Bean
|
|
1020
|
+
public Queue userQueue() {
|
|
1021
|
+
return QueueBuilder.durable(USER_QUEUE).build();
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
@Bean
|
|
1025
|
+
public TopicExchange userExchange() {
|
|
1026
|
+
return new TopicExchange(USER_EXCHANGE);
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
@Bean
|
|
1030
|
+
public Binding userBinding() {
|
|
1031
|
+
return BindingBuilder.bind(userQueue())
|
|
1032
|
+
.to(userExchange())
|
|
1033
|
+
.with(USER_ROUTING_KEY);
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
@Bean
|
|
1037
|
+
public Jackson2JsonMessageConverter messageConverter() {
|
|
1038
|
+
return new Jackson2JsonMessageConverter();
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
@Bean
|
|
1042
|
+
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
|
|
1043
|
+
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
|
|
1044
|
+
rabbitTemplate.setMessageConverter(messageConverter());
|
|
1045
|
+
return rabbitTemplate;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
@Service
|
|
1050
|
+
public class UserEventPublisher {
|
|
1051
|
+
|
|
1052
|
+
private final RabbitTemplate rabbitTemplate;
|
|
1053
|
+
|
|
1054
|
+
public UserEventPublisher(RabbitTemplate rabbitTemplate) {
|
|
1055
|
+
this.rabbitTemplate = rabbitTemplate;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
public void publishUserCreated(User user) {
|
|
1059
|
+
UserCreatedEvent event = new UserCreatedEvent(user.id(), user.username(), user.email());
|
|
1060
|
+
rabbitTemplate.convertAndSend(
|
|
1061
|
+
RabbitMQConfig.USER_EXCHANGE,
|
|
1062
|
+
RabbitMQConfig.USER_ROUTING_KEY,
|
|
1063
|
+
event
|
|
1064
|
+
);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
@Component
|
|
1069
|
+
public class UserEventConsumer {
|
|
1070
|
+
|
|
1071
|
+
private final EmailService emailService;
|
|
1072
|
+
private final AuditService auditService;
|
|
1073
|
+
|
|
1074
|
+
public UserEventConsumer(EmailService emailService, AuditService auditService) {
|
|
1075
|
+
this.emailService = emailService;
|
|
1076
|
+
this.auditService = auditService;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
@RabbitListener(queues = RabbitMQConfig.USER_QUEUE)
|
|
1080
|
+
public void handleUserCreated(UserCreatedEvent event) {
|
|
1081
|
+
log.info("Received user created event: {}", event);
|
|
1082
|
+
|
|
1083
|
+
// Send welcome email
|
|
1084
|
+
emailService.sendWelcomeEmail(event.userId(), event.email());
|
|
1085
|
+
|
|
1086
|
+
// Create audit record
|
|
1087
|
+
auditService.recordUserCreation(event.userId(), event.username());
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// Event record
|
|
1092
|
+
public record UserCreatedEvent(
|
|
1093
|
+
Long userId,
|
|
1094
|
+
String username,
|
|
1095
|
+
String email,
|
|
1096
|
+
Instant timestamp
|
|
1097
|
+
) {
|
|
1098
|
+
public UserCreatedEvent {
|
|
1099
|
+
if (timestamp == null) {
|
|
1100
|
+
timestamp = Instant.now();
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
```
|
|
1105
|
+
|
|
1106
|
+
### Redis Integration for Caching
|
|
1107
|
+
|
|
1108
|
+
```java
|
|
1109
|
+
import org.springframework.data.redis.core.ReactiveRedisTemplate;
|
|
1110
|
+
import org.springframework.data.redis.core.ReactiveValueOperations;
|
|
1111
|
+
import reactor.core.publisher.Mono;
|
|
1112
|
+
|
|
1113
|
+
@Service
|
|
1114
|
+
public class CacheService {
|
|
1115
|
+
|
|
1116
|
+
private final ReactiveRedisTemplate<String, Object> redisTemplate;
|
|
1117
|
+
private final ReactiveValueOperations<String, Object> valueOps;
|
|
1118
|
+
|
|
1119
|
+
public CacheService(ReactiveRedisTemplate<String, Object> redisTemplate) {
|
|
1120
|
+
this.redisTemplate = redisTemplate;
|
|
1121
|
+
this.valueOps = redisTemplate.opsForValue();
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
public <T> Mono<T> get(String key, Class<T> type) {
|
|
1125
|
+
return valueOps.get(key)
|
|
1126
|
+
.cast(type);
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
public <T> Mono<T> getOrCompute(String key, Supplier<Mono<T>> supplier, Duration ttl, Class<T> type) {
|
|
1130
|
+
return get(key, type)
|
|
1131
|
+
.switchIfEmpty(
|
|
1132
|
+
supplier.get()
|
|
1133
|
+
.flatMap(result -> set(key, result, ttl).thenReturn(result))
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
public <T> Mono<Boolean> set(String key, T value, Duration ttl) {
|
|
1138
|
+
return valueOps.set(key, value, ttl);
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
public Mono<Boolean> delete(String key) {
|
|
1142
|
+
return redisTemplate.delete(key).map(count -> count > 0);
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
// Rate limiting implementation
|
|
1146
|
+
public Mono<Boolean> isRateLimited(String identifier, int maxRequests, Duration window) {
|
|
1147
|
+
String key = "rate_limit:" + identifier;
|
|
1148
|
+
|
|
1149
|
+
return redisTemplate.opsForValue()
|
|
1150
|
+
.increment(key)
|
|
1151
|
+
.flatMap(count -> {
|
|
1152
|
+
if (count == 1) {
|
|
1153
|
+
// Set expiration for first request in window
|
|
1154
|
+
return redisTemplate.expire(key, window).thenReturn(count);
|
|
1155
|
+
}
|
|
1156
|
+
return Mono.just(count);
|
|
1157
|
+
})
|
|
1158
|
+
.map(count -> count > maxRequests);
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
```
|
|
1162
|
+
|
|
1163
|
+
## Modern Development Workflow
|
|
1164
|
+
|
|
1165
|
+
### Gradle Configuration (build.gradle.kts)
|
|
1166
|
+
|
|
1167
|
+
```kotlin
|
|
1168
|
+
plugins {
|
|
1169
|
+
application
|
|
1170
|
+
jacoco
|
|
1171
|
+
checkstyle
|
|
1172
|
+
pmd
|
|
1173
|
+
id("org.springframework.boot") version "3.3.0"
|
|
1174
|
+
id("io.spring.dependency-management") version "1.1.5"
|
|
1175
|
+
id("com.google.cloud.tools.jib") version "3.4.1"
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
group = "com.example"
|
|
1179
|
+
version = "0.1.0"
|
|
70
1180
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
1181
|
+
java {
|
|
1182
|
+
toolchain {
|
|
1183
|
+
languageVersion = JavaLanguageVersion.of(21)
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
74
1186
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
1187
|
+
repositories {
|
|
1188
|
+
mavenCentral()
|
|
1189
|
+
}
|
|
78
1190
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
1191
|
+
dependencies {
|
|
1192
|
+
// Spring Boot starters
|
|
1193
|
+
implementation("org.springframework.boot:spring-boot-starter-webflux")
|
|
1194
|
+
implementation("org.springframework.boot:spring-boot-starter-data-r2dbc")
|
|
1195
|
+
implementation("org.springframework.boot:spring-boot-starter-actuator")
|
|
1196
|
+
implementation("org.springframework.boot:spring-boot-starter-validation")
|
|
1197
|
+
|
|
1198
|
+
// Database
|
|
1199
|
+
implementation("org.postgresql:r2dbc-postgresql")
|
|
1200
|
+
implementation("org.flywaydb:flyway-core")
|
|
1201
|
+
implementation("org.springframework:spring-jdbc")
|
|
1202
|
+
|
|
1203
|
+
// Message queue
|
|
1204
|
+
implementation("org.springframework.boot:spring-boot-starter-amqp")
|
|
1205
|
+
|
|
1206
|
+
// Caching
|
|
1207
|
+
implementation("org.springframework.boot:spring-boot-starter-data-redis-reactive")
|
|
1208
|
+
|
|
1209
|
+
// Security
|
|
1210
|
+
implementation("org.springframework.boot:spring-boot-starter-security")
|
|
1211
|
+
implementation("org.springframework.security:spring-security-oauth2-resource-server")
|
|
1212
|
+
|
|
1213
|
+
// Utility libraries
|
|
1214
|
+
implementation("com.fasterxml.jackson.core:jackson-databind")
|
|
1215
|
+
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
|
|
1216
|
+
implementation("org.apache.commons:commons-lang3")
|
|
1217
|
+
implementation("org.apache.commons:commons-collections4")
|
|
1218
|
+
|
|
1219
|
+
// Development tools
|
|
1220
|
+
compileOnly("org.projectlombok:lombok")
|
|
1221
|
+
annotationProcessor("org.projectlombok:lombok")
|
|
1222
|
+
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
|
1223
|
+
|
|
1224
|
+
// Testing
|
|
1225
|
+
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
|
1226
|
+
testImplementation("org.springframework.security:spring-security-test")
|
|
1227
|
+
testImplementation("io.projectreactor:reactor-test")
|
|
1228
|
+
testImplementation("org.testcontainers:junit-jupiter")
|
|
1229
|
+
testImplementation("org.testcontainers:postgresql")
|
|
1230
|
+
testImplementation("org.testcontainers:r2dbc")
|
|
1231
|
+
testImplementation("org.testcontainers:rabbitmq")
|
|
1232
|
+
|
|
1233
|
+
// Performance testing
|
|
1234
|
+
testImplementation("org.junit.jupiter:junit-jupiter-params")
|
|
1235
|
+
testImplementation("net.jqwik:jqwik:1.8.3")
|
|
1236
|
+
}
|
|
82
1237
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
1238
|
+
application {
|
|
1239
|
+
mainClass.set("com.example.Application")
|
|
1240
|
+
}
|
|
86
1241
|
|
|
87
|
-
|
|
88
|
-
|
|
1242
|
+
tasks.test {
|
|
1243
|
+
useJUnitPlatform()
|
|
1244
|
+
testLogging {
|
|
1245
|
+
events("passed", "skipped", "failed")
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
89
1248
|
|
|
90
|
-
|
|
1249
|
+
// Jib configuration for Docker image generation
|
|
1250
|
+
jib {
|
|
1251
|
+
from {
|
|
1252
|
+
image = "eclipse-temurin:21-jre-alpine"
|
|
1253
|
+
}
|
|
1254
|
+
to {
|
|
1255
|
+
image = "my-registry.com/my-app:latest"
|
|
1256
|
+
}
|
|
1257
|
+
container {
|
|
1258
|
+
jvmFlags = listOf("-Xms512m", "-Xmx2g")
|
|
1259
|
+
ports = listOf("8080")
|
|
1260
|
+
environment = mapOf(
|
|
1261
|
+
"SPRING_PROFILES_ACTIVE" to "prod"
|
|
1262
|
+
)
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
```
|
|
1266
|
+
|
|
1267
|
+
### Pre-commit Configuration
|
|
1268
|
+
|
|
1269
|
+
```yaml
|
|
1270
|
+
# .pre-commit-config.yaml
|
|
1271
|
+
repos:
|
|
1272
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
1273
|
+
rev: v5.0.0
|
|
1274
|
+
hooks:
|
|
1275
|
+
- id: trailing-whitespace
|
|
1276
|
+
- id: end-of-file-fixer
|
|
1277
|
+
- id: check-yaml
|
|
1278
|
+
- id: check-added-large-files
|
|
1279
|
+
- id: check-merge-conflict
|
|
1280
|
+
- id: check-executables-have-shebangs
|
|
1281
|
+
|
|
1282
|
+
- repo: https://github.com/codacy/codacy-checkstyle.git
|
|
1283
|
+
rev: 5.1.0
|
|
1284
|
+
hooks:
|
|
1285
|
+
- id: codacy-checkstyle-java
|
|
1286
|
+
args: ["-c", "/path/to/checkstyle.xml"]
|
|
1287
|
+
|
|
1288
|
+
- repo: https://github.com/ulises-jeremias/pre-commit-pmd
|
|
1289
|
+
rev: v2.3.1
|
|
1290
|
+
hooks:
|
|
1291
|
+
- id: pmd
|
|
1292
|
+
args: ["-d", "src/main/java", "-r", "ruleset.xml"]
|
|
1293
|
+
|
|
1294
|
+
- repo: local
|
|
1295
|
+
hooks:
|
|
1296
|
+
- id: gradle-spotless
|
|
1297
|
+
name: Code formatting with Spotless
|
|
1298
|
+
entry: ./gradlew spotlessApply
|
|
1299
|
+
language: system
|
|
1300
|
+
files: '\.(java|kt|kts)$'
|
|
1301
|
+
|
|
1302
|
+
- id: gradle-test
|
|
1303
|
+
name: Run tests
|
|
1304
|
+
entry: ./gradlew test
|
|
1305
|
+
language: system
|
|
1306
|
+
pass_filenames: false
|
|
1307
|
+
always_run: true
|
|
1308
|
+
|
|
1309
|
+
- id: gradle-check
|
|
1310
|
+
name: Run code quality checks
|
|
1311
|
+
entry: ./gradlew check
|
|
1312
|
+
language: system
|
|
1313
|
+
pass_filenames: false
|
|
1314
|
+
always_run: true
|
|
1315
|
+
```
|
|
1316
|
+
|
|
1317
|
+
### Docker Best Practices
|
|
1318
|
+
|
|
1319
|
+
```dockerfile
|
|
1320
|
+
# Multi-stage Docker build with Jib integration
|
|
1321
|
+
# This Dockerfile is for local development; use Jib for production
|
|
1322
|
+
|
|
1323
|
+
# Build stage
|
|
1324
|
+
FROM eclipse-temurin:21-jdk-alpine AS builder
|
|
1325
|
+
|
|
1326
|
+
WORKDIR /app
|
|
1327
|
+
|
|
1328
|
+
# Copy gradle wrapper and cache dependencies
|
|
1329
|
+
COPY gradlew gradlew.bat gradle/ ./
|
|
1330
|
+
COPY build.gradle.kts settings.gradle.kts ./
|
|
1331
|
+
|
|
1332
|
+
# Download dependencies
|
|
1333
|
+
RUN ./gradlew --no-daemon dependencies
|
|
1334
|
+
|
|
1335
|
+
# Copy source code
|
|
1336
|
+
COPY src/ ./src/
|
|
1337
|
+
|
|
1338
|
+
# Build application
|
|
1339
|
+
RUN ./gradlew --no-daemon bootJar -x test
|
|
1340
|
+
|
|
1341
|
+
# Runtime stage
|
|
1342
|
+
FROM eclipse-temurin:21-jre-alpine
|
|
1343
|
+
|
|
1344
|
+
# Security best practices
|
|
1345
|
+
RUN addgroup --system --gid 1001 appgroup && \
|
|
1346
|
+
adduser --system --uid 1001 --ingroup appgroup appuser && \
|
|
1347
|
+
mkdir -p /app && \
|
|
1348
|
+
chown -R appuser:appgroup /app
|
|
1349
|
+
|
|
1350
|
+
WORKDIR /app
|
|
1351
|
+
|
|
1352
|
+
# Copy application jar
|
|
1353
|
+
COPY --from=builder /app/build/libs/*.jar app.jar
|
|
1354
|
+
|
|
1355
|
+
# Set appropriate permissions
|
|
1356
|
+
RUN chmod +x app.jar
|
|
1357
|
+
|
|
1358
|
+
# Switch to non-root user
|
|
1359
|
+
USER appuser
|
|
1360
|
+
|
|
1361
|
+
# Health check
|
|
1362
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
|
1363
|
+
CMD curl -f http://localhost:8080/actuator/health || exit 1
|
|
1364
|
+
|
|
1365
|
+
EXPOSE 8080
|
|
1366
|
+
|
|
1367
|
+
# JVM optimizations
|
|
1368
|
+
ENV JAVA_OPTS="-Xms512m -Xmx2g -XX:+UseG1GC -XX:+UseStringDeduplication"
|
|
1369
|
+
|
|
1370
|
+
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
|
|
1371
|
+
```
|
|
1372
|
+
|
|
1373
|
+
## Backend Development Patterns
|
|
1374
|
+
|
|
1375
|
+
### Spring Boot Application Structure
|
|
1376
|
+
|
|
1377
|
+
```java
|
|
1378
|
+
@SpringBootApplication
|
|
1379
|
+
@EnableReactiveSecurity
|
|
1380
|
+
@EnableR2dbcAuditing
|
|
1381
|
+
public class Application {
|
|
1382
|
+
|
|
1383
|
+
public static void main(String[] args) {
|
|
1384
|
+
SpringApplication.run(Application.class, args);
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
@Bean
|
|
1388
|
+
public WebFluxConfigurer corsConfigurer() {
|
|
1389
|
+
return new WebFluxConfigurer() {
|
|
1390
|
+
@Override
|
|
1391
|
+
public void addCorsMappings(CorsRegistry registry) {
|
|
1392
|
+
registry.addMapping("/api/**")
|
|
1393
|
+
.allowedOrigins("http://localhost:3000")
|
|
1394
|
+
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
|
1395
|
+
.allowedHeaders("*")
|
|
1396
|
+
.maxAge(3600);
|
|
1397
|
+
}
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
// Global error handling
|
|
1403
|
+
@ControllerAdvice
|
|
1404
|
+
public class GlobalExceptionHandler {
|
|
1405
|
+
|
|
1406
|
+
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
|
1407
|
+
|
|
1408
|
+
@ExceptionHandler(ValidationException.class)
|
|
1409
|
+
public ResponseEntity<ErrorResponse> handleValidationException(ValidationException ex) {
|
|
1410
|
+
log.warn("Validation error: {}", ex.getMessage());
|
|
1411
|
+
return ResponseEntity.badRequest()
|
|
1412
|
+
.body(new ErrorResponse("VALIDATION_ERROR", ex.getMessage()));
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
@ExceptionHandler(ResourceNotFoundException.class)
|
|
1416
|
+
public ResponseEntity<ErrorResponse> handleResourceNotFoundException(ResourceNotFoundException ex) {
|
|
1417
|
+
log.warn("Resource not found: {}", ex.getMessage());
|
|
1418
|
+
return ResponseEntity.status(HttpStatus.NOT_FOUND)
|
|
1419
|
+
.body(new ErrorResponse("RESOURCE_NOT_FOUND", ex.getMessage()));
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
@ExceptionHandler(Exception.class)
|
|
1423
|
+
public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
|
|
1424
|
+
log.error("Unexpected error occurred", ex);
|
|
1425
|
+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
|
1426
|
+
.body(new ErrorResponse("INTERNAL_ERROR", "An unexpected error occurred"));
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
// Audit configuration
|
|
1431
|
+
@Configuration
|
|
1432
|
+
@EnableJpaAuditing(auditorAwareRef = "auditorProvider")
|
|
1433
|
+
public class AuditConfig {
|
|
1434
|
+
|
|
1435
|
+
@Bean
|
|
1436
|
+
public AuditorAware<String> auditorProvider() {
|
|
1437
|
+
return () -> {
|
|
1438
|
+
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
|
1439
|
+
if (authentication == null || !authentication.isAuthenticated()) {
|
|
1440
|
+
return Optional.of("system");
|
|
1441
|
+
}
|
|
1442
|
+
return Optional.of(authentication.getName());
|
|
1443
|
+
};
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
// Custom metrics with Micrometer
|
|
1448
|
+
@Component
|
|
1449
|
+
public class CustomMetrics {
|
|
1450
|
+
|
|
1451
|
+
private final Counter userRegistrationCounter;
|
|
1452
|
+
private final Timer userLoginTimer;
|
|
1453
|
+
private final Gauge activeUserGauge;
|
|
1454
|
+
|
|
1455
|
+
public CustomMetrics(MeterRegistry meterRegistry) {
|
|
1456
|
+
this.userRegistrationCounter = Counter.builder("users.registered")
|
|
1457
|
+
.description("Number of user registrations")
|
|
1458
|
+
.register(meterRegistry);
|
|
1459
|
+
|
|
1460
|
+
this.userLoginTimer = Timer.builder("users.login.duration")
|
|
1461
|
+
.description("User login duration")
|
|
1462
|
+
.register(meterRegistry);
|
|
1463
|
+
|
|
1464
|
+
this.activeUserGauge = Gauge.builder("users.active")
|
|
1465
|
+
.description("Number of active users")
|
|
1466
|
+
.register(meterRegistry, this, CustomMetrics::getActiveUserCount);
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
public void incrementUserRegistration() {
|
|
1470
|
+
userRegistrationCounter.increment();
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
public Timer.Sample startLoginTimer() {
|
|
1474
|
+
return Timer.start();
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
public void recordLoginTime(Timer.Sample sample) {
|
|
1478
|
+
sample.stop(userLoginTimer);
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
private double getActiveUserCount() {
|
|
1482
|
+
// Implementation to count active users
|
|
1483
|
+
return 0.0;
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
```
|
|
1487
|
+
|
|
1488
|
+
---
|
|
91
1489
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
1490
|
+
**Created by**: MoAI Language Skill Factory
|
|
1491
|
+
**Last Updated**: 2025-11-06
|
|
1492
|
+
**Version**: 2.0.0
|
|
1493
|
+
**Java Target**: 21 (LTS) with modern Spring Boot and reactive patterns
|
|
95
1494
|
|
|
96
|
-
|
|
97
|
-
- Enable automatic validation by matching your linter with the language's official style guide.
|
|
98
|
-
- Fix test/build pipelines with reproducible commands in CI.
|
|
1495
|
+
This skill provides comprehensive Java development guidance with 2025 best practices, covering everything from basic project setup to advanced enterprise integration and production deployment patterns.
|